fullcalendar-rails 2.8.0.0 → 3.0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +59 -57
- data/lib/fullcalendar-rails/version.rb +1 -1
- data/vendor/assets/javascripts/fullcalendar.js +1468 -896
- data/vendor/assets/javascripts/fullcalendar/gcal.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ar-ma.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ar-sa.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ar-tn.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ar.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/bg.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ca.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/cs.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/da.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/de-at.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/de.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/el.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/en-au.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/en-ca.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/en-gb.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/en-ie.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/en-nz.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/es-do.js +1 -0
- data/vendor/assets/javascripts/fullcalendar/lang/es.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/eu.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fa.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fi.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fr-ca.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fr-ch.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fr.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/gl.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/he.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/hi.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/hr.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/hu.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/id.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/is.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/it.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ja.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ko.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/lb.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/lt.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/lv.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/mk.js +1 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ms-my.js +1 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ms.js +1 -0
- data/vendor/assets/javascripts/fullcalendar/lang/nb.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/nl.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/nn.js +1 -0
- data/vendor/assets/javascripts/fullcalendar/lang/pl.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/pt-br.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/pt.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ro.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ru.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/sk.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/sl.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/sr-cyrl.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/sr.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/sv.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/th.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/tr.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/uk.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/vi.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/zh-cn.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/zh-tw.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/locale-all.js +5 -0
- data/vendor/assets/stylesheets/fullcalendar.css +170 -34
- data/vendor/assets/stylesheets/fullcalendar.print.css +1 -1
- metadata +8 -3
- data/vendor/assets/javascripts/fullcalendar/lang-all.js +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8773252a35a176af5a30a696c6f5d850ad818da1
|
4
|
+
data.tar.gz: 846cdb8578a403af18fd01d5a85dda750bbd6c74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90d7ab81c55d9fc97353af6c7f26f7b9c96b097fe8c9099c971f2f146617156ed5a7da127f87dd8afc20d4228ee607748fda8d5ba0a4e5160ef5634c874be802
|
7
|
+
data.tar.gz: ba68426e2a511b8b7226036ab4524458820e841e1cff661b39fc4e7e4b27060500a44519f8be752016173af34a901e4b5cddaadb90939d7d25b8df4dbd0b316b
|
data/LICENSE
CHANGED
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,82 +1,84 @@
|
|
1
1
|
# Fullcalendar::Rails
|
2
2
|
|
3
|
-
This gem for Ruby on Rails
|
4
|
-
|
5
|
-
http://arshaw.com/fullcalendar/
|
6
|
-
|
7
|
-
I had created an earlier version of a demonstration of using FullCalendar in a Rails app. That code is available at:
|
8
|
-
|
9
|
-
https://github.com/bokmann/fullcalendar_assets
|
10
|
-
|
11
|
-
But I have always disliked finding random files and copying them into a public directory. So I made an asset gem for use with the asset pipeline.
|
12
|
-
|
3
|
+
This gem for Ruby on Rails adds the content of the jQuery FullCalendar plugin from Adam Shaw (found here http://arshaw.com/fullcalendar/) within your RoR application such that you do not have to download and install all the FullCalendar assets yourself.
|
13
4
|
|
14
5
|
## Installation
|
6
|
+
In order to install the fullcalendar-rails gem and get FullCalendar working within your application, do the following steps:
|
15
7
|
|
16
|
-
Add
|
17
|
-
|
8
|
+
1. Add to `gemfile`
|
9
|
+
```ruby
|
18
10
|
gem 'fullcalendar-rails'
|
19
|
-
|
20
|
-
If you need a specific version of FullCalendar (e.g X.Y.Z), you can explicitly require it like this:
|
21
|
-
|
22
|
-
gem 'fullcalendar-rails', '~> X.Y.Z.0'
|
23
|
-
|
24
|
-
(Note that the last number ("0" in the line above) indicates the release of this gem, so it may change for the same version of FullCalender, see Versioning section below)
|
25
|
-
|
26
|
-
Since version 2.1.1.0 of this gem, the gems `jquery-rails` and `momentjs-rails` are included as dependencies. If you copied moment.js manually into your project, you have to delete the file so it doesn't clash with the required version from the gem. You have to also add
|
27
|
-
|
28
11
|
gem 'momentjs-rails'
|
12
|
+
```
|
29
13
|
|
30
|
-
|
31
|
-
|
32
|
-
//= require moment
|
33
|
-
|
34
|
-
to your application.js manifest (before requiring fullcalendar).
|
35
|
-
|
36
|
-
Finally execute:
|
14
|
+
1. Bundle install and restart rails server.
|
37
15
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
16
|
+
1. Add to `application.js`
|
17
|
+
```jquery
|
18
|
+
//= require moment
|
19
|
+
//= require fullcalendar
|
20
|
+
|
21
|
+
$('#calendar').fullCalendar({});
|
22
|
+
```
|
45
23
|
|
24
|
+
1. Add to `application.css`
|
25
|
+
```css
|
46
26
|
*= require fullcalendar
|
27
|
+
```
|
28
|
+
|
29
|
+
1. In view, include the following html:
|
30
|
+
```html
|
31
|
+
<div id="calendar"></div>
|
32
|
+
```
|
33
|
+
Now if you go to that view you should see the FullCalendar.
|
47
34
|
|
48
|
-
|
35
|
+
1. Reference the Using FullCalendar section for details on populating FullCalendar.
|
49
36
|
|
50
|
-
|
37
|
+
### Installing Google Calander support
|
38
|
+
FullCalendar comes with Google calendar support, which can be implemented within your application with the following step:
|
51
39
|
|
52
|
-
|
40
|
+
* Using `gem fullcalendar-rails >= 2.1.1`, add `//= require fullcalendar/gcal` to `application.js`
|
41
|
+
* Using `gem fullcalendar-rails < 2.1.1`, add `//= require gcal` to `application.js`
|
53
42
|
|
54
|
-
|
43
|
+
### Installing a specific version:
|
44
|
+
If you want a specific version of FullCalendar, use the following line in your Gemfile:
|
55
45
|
|
56
|
-
|
46
|
+
gem 'fullcalendar-rails', '~> X.Y.Z.0'
|
47
|
+
|
48
|
+
where **X.Y.Z** is the specific version of FullCalendar you wish to install (**Note: the last number "0" in the line above indicates the version of the fullcalendar-rails gem and may be something other than "0", but will still provide the FullCalendar version specified by X.Y.Z**).
|
57
49
|
|
58
|
-
|
50
|
+
### Install for fullcalendar-print
|
51
|
+
After following the above instalations steps, you may choose to use the `fullcalendar-print` file within your application to better customize the appearance of FullCalandar. To do so, follow these steps:
|
59
52
|
|
53
|
+
+ Option 1: Add to `application.css`
|
54
|
+
```css
|
55
|
+
*= require fullcalendar.print
|
56
|
+
```
|
57
|
+
*Note: This method causes issues with changing the color of events within FullCalendar, pointed out in issue #11.*
|
58
|
+
|
59
|
+
+ Option 2:
|
60
|
+
1. Create `application-print.css.scss`.
|
61
|
+
2. Add to `application-print.css.scss`
|
62
|
+
```
|
63
|
+
@import 'fullcalendar.print';
|
64
|
+
```
|
65
|
+
|
66
|
+
3. Add to `config/application.rb`
|
67
|
+
```
|
60
68
|
config.assets.precompile += ['application-print.css']
|
61
|
-
|
62
|
-
|
63
|
-
|
69
|
+
```
|
70
|
+
|
71
|
+
4. Add to `layouts`
|
72
|
+
```ruby
|
64
73
|
<%= stylesheet_link_tag "application-print", :media => "print" %>
|
74
|
+
```
|
65
75
|
|
76
|
+
## Using FullCalendar
|
77
|
+
A step by step tutorial for creating events for FullCalendar in rails may be followed here:
|
78
|
+
http://blog.crowdint.com/2014/02/18/fancy-calendars-for-your-web-application-with-fullcalendar.html
|
66
79
|
|
67
|
-
|
68
|
-
|
69
|
-
//= require gcal
|
70
|
-
|
71
|
-
Since version 2.1.1 of this gem, gcal and all other related javascript files (lang-all.js and individual language files) have been relocated to a subdirectory as requested in issue #24:
|
72
|
-
|
73
|
-
//= require fullcalendar/gcal
|
74
|
-
|
75
|
-
## Usage
|
76
|
-
|
77
|
-
See the website of the original project for the usage, or my original Rails 3 example I point to above. This gem just saves you from having to locate and copy the javascript and stylesheet into place.
|
78
|
-
|
79
|
-
http://arshaw.com/fullcalendar/
|
80
|
+
And general documentation for FullCalendar may be found here:
|
81
|
+
http://fullcalendar.io/docs/
|
80
82
|
|
81
83
|
## Motivations
|
82
84
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
* FullCalendar
|
2
|
+
* FullCalendar v3.0.0
|
3
3
|
* Docs & License: http://fullcalendar.io/
|
4
4
|
* (c) 2016 Adam Shaw
|
5
5
|
*/
|
@@ -19,8 +19,8 @@
|
|
19
19
|
;;
|
20
20
|
|
21
21
|
var FC = $.fullCalendar = {
|
22
|
-
version: "
|
23
|
-
internalApiVersion:
|
22
|
+
version: "3.0.0",
|
23
|
+
internalApiVersion: 6
|
24
24
|
};
|
25
25
|
var fcViews = FC.views = {};
|
26
26
|
|
@@ -71,56 +71,6 @@ function mergeOptions(optionObjs) {
|
|
71
71
|
return mergeProps(optionObjs, complexOptions);
|
72
72
|
}
|
73
73
|
|
74
|
-
|
75
|
-
// Given options specified for the calendar's constructor, massages any legacy options into a non-legacy form.
|
76
|
-
// Converts View-Option-Hashes into the View-Specific-Options format.
|
77
|
-
function massageOverrides(input) {
|
78
|
-
var overrides = { views: input.views || {} }; // the output. ensure a `views` hash
|
79
|
-
var subObj;
|
80
|
-
|
81
|
-
// iterate through all option override properties (except `views`)
|
82
|
-
$.each(input, function(name, val) {
|
83
|
-
if (name != 'views') {
|
84
|
-
|
85
|
-
// could the value be a legacy View-Option-Hash?
|
86
|
-
if (
|
87
|
-
$.isPlainObject(val) &&
|
88
|
-
!/(time|duration|interval)$/i.test(name) && // exclude duration options. might be given as objects
|
89
|
-
$.inArray(name, complexOptions) == -1 // complex options aren't allowed to be View-Option-Hashes
|
90
|
-
) {
|
91
|
-
subObj = null;
|
92
|
-
|
93
|
-
// iterate through the properties of this possible View-Option-Hash value
|
94
|
-
$.each(val, function(subName, subVal) {
|
95
|
-
|
96
|
-
// is the property targeting a view?
|
97
|
-
if (/^(month|week|day|default|basic(Week|Day)?|agenda(Week|Day)?)$/.test(subName)) {
|
98
|
-
if (!overrides.views[subName]) { // ensure the view-target entry exists
|
99
|
-
overrides.views[subName] = {};
|
100
|
-
}
|
101
|
-
overrides.views[subName][name] = subVal; // record the value in the `views` object
|
102
|
-
}
|
103
|
-
else { // a non-View-Option-Hash property
|
104
|
-
if (!subObj) {
|
105
|
-
subObj = {};
|
106
|
-
}
|
107
|
-
subObj[subName] = subVal; // accumulate these unrelated values for later
|
108
|
-
}
|
109
|
-
});
|
110
|
-
|
111
|
-
if (subObj) { // non-View-Option-Hash properties? transfer them as-is
|
112
|
-
overrides[name] = subObj;
|
113
|
-
}
|
114
|
-
}
|
115
|
-
else {
|
116
|
-
overrides[name] = val; // transfer normal options as-is
|
117
|
-
}
|
118
|
-
}
|
119
|
-
});
|
120
|
-
|
121
|
-
return overrides;
|
122
|
-
}
|
123
|
-
|
124
74
|
;;
|
125
75
|
|
126
76
|
// exports
|
@@ -247,7 +197,7 @@ function undistributeHeight(els) {
|
|
247
197
|
function matchCellWidths(els) {
|
248
198
|
var maxInnerWidth = 0;
|
249
199
|
|
250
|
-
els.find('>
|
200
|
+
els.find('> *').each(function(i, innerEl) {
|
251
201
|
var innerWidth = $(innerEl).outerWidth();
|
252
202
|
if (innerWidth > maxInnerWidth) {
|
253
203
|
maxInnerWidth = innerWidth;
|
@@ -628,7 +578,8 @@ function flexibleCompare(a, b) {
|
|
628
578
|
----------------------------------------------------------------------------------------------------------------------*/
|
629
579
|
|
630
580
|
|
631
|
-
// Computes the intersection of the two ranges.
|
581
|
+
// Computes the intersection of the two ranges. Will return fresh date clones in a range.
|
582
|
+
// Returns undefined if no intersection.
|
632
583
|
// Expects all dates to be normalized to the same timezone beforehand.
|
633
584
|
// TODO: move to date section?
|
634
585
|
function intersectRanges(subjectRange, constraintRange) {
|
@@ -908,22 +859,6 @@ function copyOwnProps(src, dest) {
|
|
908
859
|
}
|
909
860
|
|
910
861
|
|
911
|
-
// Copies over certain methods with the same names as Object.prototype methods. Overcomes an IE<=8 bug:
|
912
|
-
// https://developer.mozilla.org/en-US/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
|
913
|
-
function copyNativeMethods(src, dest) {
|
914
|
-
var names = [ 'constructor', 'toString', 'valueOf' ];
|
915
|
-
var i, name;
|
916
|
-
|
917
|
-
for (i = 0; i < names.length; i++) {
|
918
|
-
name = names[i];
|
919
|
-
|
920
|
-
if (src[name] !== Object.prototype[name]) {
|
921
|
-
dest[name] = src[name];
|
922
|
-
}
|
923
|
-
}
|
924
|
-
}
|
925
|
-
|
926
|
-
|
927
862
|
function hasOwnProp(obj, name) {
|
928
863
|
return hasOwnPropMethod.call(obj, name);
|
929
864
|
}
|
@@ -989,6 +924,21 @@ function cssToStr(cssProps) {
|
|
989
924
|
}
|
990
925
|
|
991
926
|
|
927
|
+
// Given an object hash of HTML attribute names to values,
|
928
|
+
// generates a string that can be injected between < > in HTML
|
929
|
+
function attrsToStr(attrs) {
|
930
|
+
var parts = [];
|
931
|
+
|
932
|
+
$.each(attrs, function(name, val) {
|
933
|
+
if (val != null) {
|
934
|
+
parts.push(name + '="' + htmlEscape(val) + '"');
|
935
|
+
}
|
936
|
+
});
|
937
|
+
|
938
|
+
return parts.join(' ');
|
939
|
+
}
|
940
|
+
|
941
|
+
|
992
942
|
function capitaliseFirstLetter(str) {
|
993
943
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
994
944
|
}
|
@@ -1070,14 +1020,24 @@ function syncThen(promise, thenFunc) {
|
|
1070
1020
|
|
1071
1021
|
;;
|
1072
1022
|
|
1023
|
+
/*
|
1024
|
+
GENERAL NOTE on moments throughout the *entire rest* of the codebase:
|
1025
|
+
All moments are assumed to be ambiguously-zoned unless otherwise noted,
|
1026
|
+
with the NOTABLE EXCEOPTION of start/end dates that live on *Event Objects*.
|
1027
|
+
Ambiguously-TIMED moments are assumed to be ambiguously-zoned by nature.
|
1028
|
+
*/
|
1029
|
+
|
1073
1030
|
var ambigDateOfMonthRegex = /^\s*\d{4}-\d\d$/;
|
1074
1031
|
var ambigTimeOrZoneRegex =
|
1075
1032
|
/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/;
|
1076
1033
|
var newMomentProto = moment.fn; // where we will attach our new methods
|
1077
1034
|
var oldMomentProto = $.extend({}, newMomentProto); // copy of original moment methods
|
1078
|
-
|
1079
|
-
|
1080
|
-
var
|
1035
|
+
|
1036
|
+
// tell momentjs to transfer these properties upon clone
|
1037
|
+
var momentProperties = moment.momentProperties;
|
1038
|
+
momentProperties.push('_fullCalendar');
|
1039
|
+
momentProperties.push('_ambigTime');
|
1040
|
+
momentProperties.push('_ambigZone');
|
1081
1041
|
|
1082
1042
|
|
1083
1043
|
// Creating
|
@@ -1123,12 +1083,8 @@ function makeMoment(args, parseAsUTC, parseZone) {
|
|
1123
1083
|
var ambigMatch;
|
1124
1084
|
var mom;
|
1125
1085
|
|
1126
|
-
if (moment.isMoment(input)) {
|
1127
|
-
mom = moment.apply(null, args);
|
1128
|
-
transferAmbigs(input, mom); // the ambig flags weren't transfered with the clone
|
1129
|
-
}
|
1130
|
-
else if (isNativeDate(input) || input === undefined) {
|
1131
|
-
mom = moment.apply(null, args); // will be local
|
1086
|
+
if (moment.isMoment(input) || isNativeDate(input) || input === undefined) {
|
1087
|
+
mom = moment.apply(null, args);
|
1132
1088
|
}
|
1133
1089
|
else { // "parsing" is required
|
1134
1090
|
isAmbigTime = false;
|
@@ -1169,12 +1125,7 @@ function makeMoment(args, parseAsUTC, parseZone) {
|
|
1169
1125
|
mom._ambigZone = true;
|
1170
1126
|
}
|
1171
1127
|
else if (isSingleString) {
|
1172
|
-
|
1173
|
-
mom.utcOffset(input); // if not a valid zone, will assign UTC
|
1174
|
-
}
|
1175
|
-
else {
|
1176
|
-
mom.zone(input); // for moment-pre-2.9
|
1177
|
-
}
|
1128
|
+
mom.utcOffset(input); // if not a valid zone, will assign UTC
|
1178
1129
|
}
|
1179
1130
|
}
|
1180
1131
|
}
|
@@ -1185,21 +1136,6 @@ function makeMoment(args, parseAsUTC, parseZone) {
|
|
1185
1136
|
}
|
1186
1137
|
|
1187
1138
|
|
1188
|
-
// A clone method that works with the flags related to our enhanced functionality.
|
1189
|
-
// In the future, use moment.momentProperties
|
1190
|
-
newMomentProto.clone = function() {
|
1191
|
-
var mom = oldMomentProto.clone.apply(this, arguments);
|
1192
|
-
|
1193
|
-
// these flags weren't transfered with the clone
|
1194
|
-
transferAmbigs(this, mom);
|
1195
|
-
if (this._fullCalendar) {
|
1196
|
-
mom._fullCalendar = true;
|
1197
|
-
}
|
1198
|
-
|
1199
|
-
return mom;
|
1200
|
-
};
|
1201
|
-
|
1202
|
-
|
1203
1139
|
// Week Number
|
1204
1140
|
// -------------------------------------------------------------------------------------------------
|
1205
1141
|
|
@@ -1207,8 +1143,7 @@ newMomentProto.clone = function() {
|
|
1207
1143
|
// Returns the week number, considering the locale's custom week number calcuation
|
1208
1144
|
// `weeks` is an alias for `week`
|
1209
1145
|
newMomentProto.week = newMomentProto.weeks = function(input) {
|
1210
|
-
var weekCalc =
|
1211
|
-
._fullCalendar_weekCalc;
|
1146
|
+
var weekCalc = this._locale._fullCalendar_weekCalc;
|
1212
1147
|
|
1213
1148
|
if (input == null && typeof weekCalc === 'function') { // custom function only works for getter
|
1214
1149
|
return weekCalc(this);
|
@@ -1275,19 +1210,21 @@ newMomentProto.time = function(time) {
|
|
1275
1210
|
// but preserving its YMD. A moment with a stripped time will display no time
|
1276
1211
|
// nor timezone offset when .format() is called.
|
1277
1212
|
newMomentProto.stripTime = function() {
|
1278
|
-
var a;
|
1279
1213
|
|
1280
1214
|
if (!this._ambigTime) {
|
1281
1215
|
|
1282
|
-
//
|
1283
|
-
a = this.toArray(); // array of y/m/d/h/m/s/ms
|
1216
|
+
this.utc(true); // keepLocalTime=true (for keeping *date* value)
|
1284
1217
|
|
1285
|
-
//
|
1286
|
-
this.
|
1287
|
-
|
1218
|
+
// set time to zero
|
1219
|
+
this.set({
|
1220
|
+
hours: 0,
|
1221
|
+
minutes: 0,
|
1222
|
+
seconds: 0,
|
1223
|
+
ms: 0
|
1224
|
+
});
|
1288
1225
|
|
1289
1226
|
// Mark the time as ambiguous. This needs to happen after the .utc() call, which might call .utcOffset(),
|
1290
|
-
// which clears all ambig flags.
|
1227
|
+
// which clears all ambig flags.
|
1291
1228
|
this._ambigTime = true;
|
1292
1229
|
this._ambigZone = true; // if ambiguous time, also ambiguous timezone offset
|
1293
1230
|
}
|
@@ -1307,24 +1244,20 @@ newMomentProto.hasTime = function() {
|
|
1307
1244
|
// Converts the moment to UTC, stripping out its timezone offset, but preserving its
|
1308
1245
|
// YMD and time-of-day. A moment with a stripped timezone offset will display no
|
1309
1246
|
// timezone offset when .format() is called.
|
1310
|
-
// TODO: look into Moment's keepLocalTime functionality
|
1311
1247
|
newMomentProto.stripZone = function() {
|
1312
|
-
var
|
1248
|
+
var wasAmbigTime;
|
1313
1249
|
|
1314
1250
|
if (!this._ambigZone) {
|
1315
1251
|
|
1316
|
-
// get the values before any conversion happens
|
1317
|
-
a = this.toArray(); // array of y/m/d/h/m/s/ms
|
1318
1252
|
wasAmbigTime = this._ambigTime;
|
1319
1253
|
|
1320
|
-
this.utc(); //
|
1321
|
-
setUTCValues(this, a); // will set the year/month/date/hours/minutes/seconds/ms
|
1254
|
+
this.utc(true); // keepLocalTime=true (for keeping date and time values)
|
1322
1255
|
|
1323
1256
|
// the above call to .utc()/.utcOffset() unfortunately might clear the ambig flags, so restore
|
1324
1257
|
this._ambigTime = wasAmbigTime || false;
|
1325
1258
|
|
1326
1259
|
// Mark the zone as ambiguous. This needs to happen after the .utc() call, which might call .utcOffset(),
|
1327
|
-
// which clears the ambig flags.
|
1260
|
+
// which clears the ambig flags.
|
1328
1261
|
this._ambigZone = true;
|
1329
1262
|
}
|
1330
1263
|
|
@@ -1337,32 +1270,26 @@ newMomentProto.hasZone = function() {
|
|
1337
1270
|
};
|
1338
1271
|
|
1339
1272
|
|
1340
|
-
//
|
1341
|
-
newMomentProto.local = function() {
|
1342
|
-
var a = this.toArray(); // year,month,date,hours,minutes,seconds,ms as an array
|
1343
|
-
var wasAmbigZone = this._ambigZone;
|
1273
|
+
// implicitly marks a zone
|
1274
|
+
newMomentProto.local = function(keepLocalTime) {
|
1344
1275
|
|
1345
|
-
|
1276
|
+
// for when converting from ambiguously-zoned to local,
|
1277
|
+
// keep the time values when converting from UTC -> local
|
1278
|
+
oldMomentProto.local.call(this, this._ambigZone || keepLocalTime);
|
1346
1279
|
|
1347
1280
|
// ensure non-ambiguous
|
1348
1281
|
// this probably already happened via local() -> utcOffset(), but don't rely on Moment's internals
|
1349
1282
|
this._ambigTime = false;
|
1350
1283
|
this._ambigZone = false;
|
1351
1284
|
|
1352
|
-
if (wasAmbigZone) {
|
1353
|
-
// If the moment was ambiguously zoned, the date fields were stored as UTC.
|
1354
|
-
// We want to preserve these, but in local time.
|
1355
|
-
// TODO: look into Moment's keepLocalTime functionality
|
1356
|
-
setLocalValues(this, a);
|
1357
|
-
}
|
1358
|
-
|
1359
1285
|
return this; // for chaining
|
1360
1286
|
};
|
1361
1287
|
|
1362
1288
|
|
1363
1289
|
// implicitly marks a zone
|
1364
|
-
newMomentProto.utc = function() {
|
1365
|
-
|
1290
|
+
newMomentProto.utc = function(keepLocalTime) {
|
1291
|
+
|
1292
|
+
oldMomentProto.utc.call(this, keepLocalTime);
|
1366
1293
|
|
1367
1294
|
// ensure non-ambiguous
|
1368
1295
|
// this probably already happened via utc() -> utcOffset(), but don't rely on Moment's internals
|
@@ -1373,28 +1300,18 @@ newMomentProto.utc = function() {
|
|
1373
1300
|
};
|
1374
1301
|
|
1375
1302
|
|
1376
|
-
//
|
1377
|
-
|
1378
|
-
$.each([
|
1379
|
-
'zone', // only in moment-pre-2.9. deprecated afterwards
|
1380
|
-
'utcOffset'
|
1381
|
-
], function(i, name) {
|
1382
|
-
if (oldMomentProto[name]) { // original method exists?
|
1383
|
-
|
1384
|
-
// this method implicitly marks a zone (will probably get called upon .utc() and .local())
|
1385
|
-
newMomentProto[name] = function(tzo) {
|
1303
|
+
// implicitly marks a zone (will probably get called upon .utc() and .local())
|
1304
|
+
newMomentProto.utcOffset = function(tzo) {
|
1386
1305
|
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
}
|
1393
|
-
|
1394
|
-
return oldMomentProto[name].apply(this, arguments);
|
1395
|
-
};
|
1306
|
+
if (tzo != null) { // setter
|
1307
|
+
// these assignments needs to happen before the original zone method is called.
|
1308
|
+
// I forget why, something to do with a browser crash.
|
1309
|
+
this._ambigTime = false;
|
1310
|
+
this._ambigZone = false;
|
1396
1311
|
}
|
1397
|
-
|
1312
|
+
|
1313
|
+
return oldMomentProto.utcOffset.apply(this, arguments);
|
1314
|
+
};
|
1398
1315
|
|
1399
1316
|
|
1400
1317
|
// Formatting
|
@@ -1423,156 +1340,6 @@ newMomentProto.toISOString = function() {
|
|
1423
1340
|
return oldMomentProto.toISOString.apply(this, arguments);
|
1424
1341
|
};
|
1425
1342
|
|
1426
|
-
|
1427
|
-
// Querying
|
1428
|
-
// -------------------------------------------------------------------------------------------------
|
1429
|
-
|
1430
|
-
// Is the moment within the specified range? `end` is exclusive.
|
1431
|
-
// FYI, this method is not a standard Moment method, so always do our enhanced logic.
|
1432
|
-
newMomentProto.isWithin = function(start, end) {
|
1433
|
-
var a = commonlyAmbiguate([ this, start, end ]);
|
1434
|
-
return a[0] >= a[1] && a[0] < a[2];
|
1435
|
-
};
|
1436
|
-
|
1437
|
-
// When isSame is called with units, timezone ambiguity is normalized before the comparison happens.
|
1438
|
-
// If no units specified, the two moments must be identically the same, with matching ambig flags.
|
1439
|
-
newMomentProto.isSame = function(input, units) {
|
1440
|
-
var a;
|
1441
|
-
|
1442
|
-
// only do custom logic if this is an enhanced moment
|
1443
|
-
if (!this._fullCalendar) {
|
1444
|
-
return oldMomentProto.isSame.apply(this, arguments);
|
1445
|
-
}
|
1446
|
-
|
1447
|
-
if (units) {
|
1448
|
-
a = commonlyAmbiguate([ this, input ], true); // normalize timezones but don't erase times
|
1449
|
-
return oldMomentProto.isSame.call(a[0], a[1], units);
|
1450
|
-
}
|
1451
|
-
else {
|
1452
|
-
input = FC.moment.parseZone(input); // normalize input
|
1453
|
-
return oldMomentProto.isSame.call(this, input) &&
|
1454
|
-
Boolean(this._ambigTime) === Boolean(input._ambigTime) &&
|
1455
|
-
Boolean(this._ambigZone) === Boolean(input._ambigZone);
|
1456
|
-
}
|
1457
|
-
};
|
1458
|
-
|
1459
|
-
// Make these query methods work with ambiguous moments
|
1460
|
-
$.each([
|
1461
|
-
'isBefore',
|
1462
|
-
'isAfter'
|
1463
|
-
], function(i, methodName) {
|
1464
|
-
newMomentProto[methodName] = function(input, units) {
|
1465
|
-
var a;
|
1466
|
-
|
1467
|
-
// only do custom logic if this is an enhanced moment
|
1468
|
-
if (!this._fullCalendar) {
|
1469
|
-
return oldMomentProto[methodName].apply(this, arguments);
|
1470
|
-
}
|
1471
|
-
|
1472
|
-
a = commonlyAmbiguate([ this, input ]);
|
1473
|
-
return oldMomentProto[methodName].call(a[0], a[1], units);
|
1474
|
-
};
|
1475
|
-
});
|
1476
|
-
|
1477
|
-
|
1478
|
-
// Misc Internals
|
1479
|
-
// -------------------------------------------------------------------------------------------------
|
1480
|
-
|
1481
|
-
// given an array of moment-like inputs, return a parallel array w/ moments similarly ambiguated.
|
1482
|
-
// for example, of one moment has ambig time, but not others, all moments will have their time stripped.
|
1483
|
-
// set `preserveTime` to `true` to keep times, but only normalize zone ambiguity.
|
1484
|
-
// returns the original moments if no modifications are necessary.
|
1485
|
-
function commonlyAmbiguate(inputs, preserveTime) {
|
1486
|
-
var anyAmbigTime = false;
|
1487
|
-
var anyAmbigZone = false;
|
1488
|
-
var len = inputs.length;
|
1489
|
-
var moms = [];
|
1490
|
-
var i, mom;
|
1491
|
-
|
1492
|
-
// parse inputs into real moments and query their ambig flags
|
1493
|
-
for (i = 0; i < len; i++) {
|
1494
|
-
mom = inputs[i];
|
1495
|
-
if (!moment.isMoment(mom)) {
|
1496
|
-
mom = FC.moment.parseZone(mom);
|
1497
|
-
}
|
1498
|
-
anyAmbigTime = anyAmbigTime || mom._ambigTime;
|
1499
|
-
anyAmbigZone = anyAmbigZone || mom._ambigZone;
|
1500
|
-
moms.push(mom);
|
1501
|
-
}
|
1502
|
-
|
1503
|
-
// strip each moment down to lowest common ambiguity
|
1504
|
-
// use clones to avoid modifying the original moments
|
1505
|
-
for (i = 0; i < len; i++) {
|
1506
|
-
mom = moms[i];
|
1507
|
-
if (!preserveTime && anyAmbigTime && !mom._ambigTime) {
|
1508
|
-
moms[i] = mom.clone().stripTime();
|
1509
|
-
}
|
1510
|
-
else if (anyAmbigZone && !mom._ambigZone) {
|
1511
|
-
moms[i] = mom.clone().stripZone();
|
1512
|
-
}
|
1513
|
-
}
|
1514
|
-
|
1515
|
-
return moms;
|
1516
|
-
}
|
1517
|
-
|
1518
|
-
// Transfers all the flags related to ambiguous time/zone from the `src` moment to the `dest` moment
|
1519
|
-
// TODO: look into moment.momentProperties for this.
|
1520
|
-
function transferAmbigs(src, dest) {
|
1521
|
-
if (src._ambigTime) {
|
1522
|
-
dest._ambigTime = true;
|
1523
|
-
}
|
1524
|
-
else if (dest._ambigTime) {
|
1525
|
-
dest._ambigTime = false;
|
1526
|
-
}
|
1527
|
-
|
1528
|
-
if (src._ambigZone) {
|
1529
|
-
dest._ambigZone = true;
|
1530
|
-
}
|
1531
|
-
else if (dest._ambigZone) {
|
1532
|
-
dest._ambigZone = false;
|
1533
|
-
}
|
1534
|
-
}
|
1535
|
-
|
1536
|
-
|
1537
|
-
// Sets the year/month/date/etc values of the moment from the given array.
|
1538
|
-
// Inefficient because it calls each individual setter.
|
1539
|
-
function setMomentValues(mom, a) {
|
1540
|
-
mom.year(a[0] || 0)
|
1541
|
-
.month(a[1] || 0)
|
1542
|
-
.date(a[2] || 0)
|
1543
|
-
.hours(a[3] || 0)
|
1544
|
-
.minutes(a[4] || 0)
|
1545
|
-
.seconds(a[5] || 0)
|
1546
|
-
.milliseconds(a[6] || 0);
|
1547
|
-
}
|
1548
|
-
|
1549
|
-
// Can we set the moment's internal date directly?
|
1550
|
-
allowValueOptimization = '_d' in moment() && 'updateOffset' in moment;
|
1551
|
-
|
1552
|
-
// Utility function. Accepts a moment and an array of the UTC year/month/date/etc values to set.
|
1553
|
-
// Assumes the given moment is already in UTC mode.
|
1554
|
-
setUTCValues = allowValueOptimization ? function(mom, a) {
|
1555
|
-
// simlate what moment's accessors do
|
1556
|
-
mom._d.setTime(Date.UTC.apply(Date, a));
|
1557
|
-
moment.updateOffset(mom, false); // keepTime=false
|
1558
|
-
} : setMomentValues;
|
1559
|
-
|
1560
|
-
// Utility function. Accepts a moment and an array of the local year/month/date/etc values to set.
|
1561
|
-
// Assumes the given moment is already in local mode.
|
1562
|
-
setLocalValues = allowValueOptimization ? function(mom, a) {
|
1563
|
-
// simlate what moment's accessors do
|
1564
|
-
mom._d.setTime(+new Date( // FYI, there is now way to apply an array of args to a constructor
|
1565
|
-
a[0] || 0,
|
1566
|
-
a[1] || 0,
|
1567
|
-
a[2] || 0,
|
1568
|
-
a[3] || 0,
|
1569
|
-
a[4] || 0,
|
1570
|
-
a[5] || 0,
|
1571
|
-
a[6] || 0
|
1572
|
-
));
|
1573
|
-
moment.updateOffset(mom, false); // keepTime=false
|
1574
|
-
} : setMomentValues;
|
1575
|
-
|
1576
1343
|
;;
|
1577
1344
|
|
1578
1345
|
// Single Date Formatting
|
@@ -1653,7 +1420,7 @@ function formatRange(date1, date2, formatStr, separator, isRTL) {
|
|
1653
1420
|
date1 = FC.moment.parseZone(date1);
|
1654
1421
|
date2 = FC.moment.parseZone(date2);
|
1655
1422
|
|
1656
|
-
localeData =
|
1423
|
+
localeData = date1.localeData();
|
1657
1424
|
|
1658
1425
|
// Expand localized format strings, like "LL" -> "MMMM D YYYY"
|
1659
1426
|
formatStr = localeData.longDateFormat(formatStr) || formatStr;
|
@@ -1858,7 +1625,6 @@ function extendClass(superClass, members) {
|
|
1858
1625
|
|
1859
1626
|
// copy each member variable/method onto the the subclass's prototype
|
1860
1627
|
copyOwnProps(members, subClass.prototype);
|
1861
|
-
copyNativeMethods(members, subClass.prototype); // hack for IE8
|
1862
1628
|
|
1863
1629
|
// copy over all class variables/methods to the subclass, such as `extend` and `mixin`
|
1864
1630
|
copyOwnProps(superClass, subClass);
|
@@ -1868,7 +1634,7 @@ function extendClass(superClass, members) {
|
|
1868
1634
|
|
1869
1635
|
|
1870
1636
|
function mixIntoClass(theClass, members) {
|
1871
|
-
copyOwnProps(members, theClass.prototype);
|
1637
|
+
copyOwnProps(members, theClass.prototype);
|
1872
1638
|
}
|
1873
1639
|
;;
|
1874
1640
|
|
@@ -2263,17 +2029,6 @@ var CoordCache = FC.CoordCache = Class.extend({
|
|
2263
2029
|
},
|
2264
2030
|
|
2265
2031
|
|
2266
|
-
// Compute and return what the elements' bounding rectangle is, from the user's perspective.
|
2267
|
-
// Right now, only returns a rectangle if constrained by an overflow:scroll element.
|
2268
|
-
queryBoundingRect: function() {
|
2269
|
-
var scrollParentEl = getScrollParent(this.els.eq(0));
|
2270
|
-
|
2271
|
-
if (!scrollParentEl.is(document)) {
|
2272
|
-
return getClientRect(scrollParentEl);
|
2273
|
-
}
|
2274
|
-
},
|
2275
|
-
|
2276
|
-
|
2277
2032
|
// Populates the left/right internal coordinate arrays
|
2278
2033
|
buildElHorizontals: function() {
|
2279
2034
|
var lefts = [];
|
@@ -2313,42 +2068,36 @@ var CoordCache = FC.CoordCache = Class.extend({
|
|
2313
2068
|
|
2314
2069
|
|
2315
2070
|
// Given a left offset (from document left), returns the index of the el that it horizontally intersects.
|
2316
|
-
// If no intersection is made,
|
2071
|
+
// If no intersection is made, returns undefined.
|
2317
2072
|
getHorizontalIndex: function(leftOffset) {
|
2318
2073
|
this.ensureBuilt();
|
2319
2074
|
|
2320
|
-
var boundingRect = this.boundingRect;
|
2321
2075
|
var lefts = this.lefts;
|
2322
2076
|
var rights = this.rights;
|
2323
2077
|
var len = lefts.length;
|
2324
2078
|
var i;
|
2325
2079
|
|
2326
|
-
|
2327
|
-
|
2328
|
-
|
2329
|
-
return i;
|
2330
|
-
}
|
2080
|
+
for (i = 0; i < len; i++) {
|
2081
|
+
if (leftOffset >= lefts[i] && leftOffset < rights[i]) {
|
2082
|
+
return i;
|
2331
2083
|
}
|
2332
2084
|
}
|
2333
2085
|
},
|
2334
2086
|
|
2335
2087
|
|
2336
2088
|
// Given a top offset (from document top), returns the index of the el that it vertically intersects.
|
2337
|
-
// If no intersection is made,
|
2089
|
+
// If no intersection is made, returns undefined.
|
2338
2090
|
getVerticalIndex: function(topOffset) {
|
2339
2091
|
this.ensureBuilt();
|
2340
2092
|
|
2341
|
-
var boundingRect = this.boundingRect;
|
2342
2093
|
var tops = this.tops;
|
2343
2094
|
var bottoms = this.bottoms;
|
2344
2095
|
var len = tops.length;
|
2345
2096
|
var i;
|
2346
2097
|
|
2347
|
-
|
2348
|
-
|
2349
|
-
|
2350
|
-
return i;
|
2351
|
-
}
|
2098
|
+
for (i = 0; i < len; i++) {
|
2099
|
+
if (topOffset >= tops[i] && topOffset < bottoms[i]) {
|
2100
|
+
return i;
|
2352
2101
|
}
|
2353
2102
|
}
|
2354
2103
|
},
|
@@ -2424,6 +2173,32 @@ var CoordCache = FC.CoordCache = Class.extend({
|
|
2424
2173
|
getHeight: function(topIndex) {
|
2425
2174
|
this.ensureBuilt();
|
2426
2175
|
return this.bottoms[topIndex] - this.tops[topIndex];
|
2176
|
+
},
|
2177
|
+
|
2178
|
+
|
2179
|
+
// Bounding Rect
|
2180
|
+
// TODO: decouple this from CoordCache
|
2181
|
+
|
2182
|
+
// Compute and return what the elements' bounding rectangle is, from the user's perspective.
|
2183
|
+
// Right now, only returns a rectangle if constrained by an overflow:scroll element.
|
2184
|
+
queryBoundingRect: function() {
|
2185
|
+
var scrollParentEl = getScrollParent(this.els.eq(0));
|
2186
|
+
|
2187
|
+
if (!scrollParentEl.is(document)) {
|
2188
|
+
return getClientRect(scrollParentEl);
|
2189
|
+
}
|
2190
|
+
},
|
2191
|
+
|
2192
|
+
isPointInBounds: function(leftOffset, topOffset) {
|
2193
|
+
return this.isLeftInBounds(leftOffset) && this.isTopInBounds(topOffset);
|
2194
|
+
},
|
2195
|
+
|
2196
|
+
isLeftInBounds: function(leftOffset) {
|
2197
|
+
return !this.boundingRect || (leftOffset >= this.boundingRect.left && leftOffset < this.boundingRect.right);
|
2198
|
+
},
|
2199
|
+
|
2200
|
+
isTopInBounds: function(topOffset) {
|
2201
|
+
return !this.boundingRect || (topOffset >= this.boundingRect.top && topOffset < this.boundingRect.bottom);
|
2427
2202
|
}
|
2428
2203
|
|
2429
2204
|
});
|
@@ -2437,10 +2212,7 @@ var CoordCache = FC.CoordCache = Class.extend({
|
|
2437
2212
|
var DragListener = FC.DragListener = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
2438
2213
|
|
2439
2214
|
options: null,
|
2440
|
-
|
2441
|
-
// for IE8 bug-fighting behavior
|
2442
2215
|
subjectEl: null,
|
2443
|
-
subjectHref: null,
|
2444
2216
|
|
2445
2217
|
// coordinates of the initial mousedown
|
2446
2218
|
originX: null,
|
@@ -2631,7 +2403,6 @@ var DragListener = FC.DragListener = Class.extend(ListenerMixin, MouseIgnorerMix
|
|
2631
2403
|
|
2632
2404
|
handleDragStart: function(ev) {
|
2633
2405
|
this.trigger('dragStart', ev);
|
2634
|
-
this.initHrefHack();
|
2635
2406
|
},
|
2636
2407
|
|
2637
2408
|
|
@@ -2671,7 +2442,6 @@ var DragListener = FC.DragListener = Class.extend(ListenerMixin, MouseIgnorerMix
|
|
2671
2442
|
|
2672
2443
|
handleDragEnd: function(ev) {
|
2673
2444
|
this.trigger('dragEnd', ev);
|
2674
|
-
this.destroyHrefHack();
|
2675
2445
|
},
|
2676
2446
|
|
2677
2447
|
|
@@ -2747,33 +2517,6 @@ var DragListener = FC.DragListener = Class.extend(ListenerMixin, MouseIgnorerMix
|
|
2747
2517
|
},
|
2748
2518
|
|
2749
2519
|
|
2750
|
-
// <A> HREF Hack
|
2751
|
-
// -----------------------------------------------------------------------------------------------------------------
|
2752
|
-
|
2753
|
-
|
2754
|
-
initHrefHack: function() {
|
2755
|
-
var subjectEl = this.subjectEl;
|
2756
|
-
|
2757
|
-
// remove a mousedown'd <a>'s href so it is not visited (IE8 bug)
|
2758
|
-
if ((this.subjectHref = subjectEl ? subjectEl.attr('href') : null)) {
|
2759
|
-
subjectEl.removeAttr('href');
|
2760
|
-
}
|
2761
|
-
},
|
2762
|
-
|
2763
|
-
|
2764
|
-
destroyHrefHack: function() {
|
2765
|
-
var subjectEl = this.subjectEl;
|
2766
|
-
var subjectHref = this.subjectHref;
|
2767
|
-
|
2768
|
-
// restore a mousedown'd <a>'s href (for IE8 bug)
|
2769
|
-
setTimeout(function() { // must be outside of the click's execution
|
2770
|
-
if (subjectHref) {
|
2771
|
-
subjectEl.attr('href', subjectHref);
|
2772
|
-
}
|
2773
|
-
}, 0);
|
2774
|
-
},
|
2775
|
-
|
2776
|
-
|
2777
2520
|
// Utils
|
2778
2521
|
// -----------------------------------------------------------------------------------------------------------------
|
2779
2522
|
|
@@ -3259,11 +3002,11 @@ var MouseFollower = Class.extend(ListenerMixin, {
|
|
3259
3002
|
var _this = this;
|
3260
3003
|
var revertDuration = this.options.revertDuration;
|
3261
3004
|
|
3262
|
-
function complete() {
|
3263
|
-
|
3005
|
+
function complete() { // might be called by .animate(), which might change `this` context
|
3006
|
+
_this.isAnimating = false;
|
3264
3007
|
_this.removeElement();
|
3265
3008
|
|
3266
|
-
|
3009
|
+
_this.top0 = _this.left0 = null; // reset state for future updatePosition calls
|
3267
3010
|
|
3268
3011
|
if (callback) {
|
3269
3012
|
callback();
|
@@ -3297,7 +3040,6 @@ var MouseFollower = Class.extend(ListenerMixin, {
|
|
3297
3040
|
var el = this.el;
|
3298
3041
|
|
3299
3042
|
if (!el) {
|
3300
|
-
this.sourceEl.width(); // hack to force IE8 to compute correct bounding box
|
3301
3043
|
el = this.el = this.sourceEl.clone()
|
3302
3044
|
.addClass(this.options.additionalClass || '')
|
3303
3045
|
.css({
|
@@ -3342,7 +3084,6 @@ var MouseFollower = Class.extend(ListenerMixin, {
|
|
3342
3084
|
|
3343
3085
|
// make sure origin info was computed
|
3344
3086
|
if (this.top0 === null) {
|
3345
|
-
this.sourceEl.width(); // hack to force IE8 to compute correct bounding box
|
3346
3087
|
sourceOffset = this.sourceEl.offset();
|
3347
3088
|
origin = this.el.offsetParent().offset();
|
3348
3089
|
this.top0 = sourceOffset.top - origin.top;
|
@@ -3396,6 +3137,9 @@ var MouseFollower = Class.extend(ListenerMixin, {
|
|
3396
3137
|
|
3397
3138
|
var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
3398
3139
|
|
3140
|
+
// self-config, overridable by subclasses
|
3141
|
+
hasDayInteractions: true, // can user click/select ranges of time?
|
3142
|
+
|
3399
3143
|
view: null, // a View object
|
3400
3144
|
isRTL: null, // shortcut to the view's isRTL option
|
3401
3145
|
|
@@ -3563,10 +3307,13 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
|
3563
3307
|
// Does other DOM-related initializations.
|
3564
3308
|
setElement: function(el) {
|
3565
3309
|
this.el = el;
|
3566
|
-
preventSelection(el);
|
3567
3310
|
|
3568
|
-
|
3569
|
-
|
3311
|
+
if (this.hasDayInteractions) {
|
3312
|
+
preventSelection(el);
|
3313
|
+
|
3314
|
+
this.bindDayHandler('touchstart', this.dayTouchStart);
|
3315
|
+
this.bindDayHandler('mousedown', this.dayMousedown);
|
3316
|
+
}
|
3570
3317
|
|
3571
3318
|
// attach event-element-related handlers. in Grid.events
|
3572
3319
|
// same garbage collection note as above.
|
@@ -3583,8 +3330,12 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
|
3583
3330
|
// jQuery will take care of unregistering them when removeElement gets called.
|
3584
3331
|
this.el.on(name, function(ev) {
|
3585
3332
|
if (
|
3586
|
-
!$(ev.target).is(
|
3587
|
-
|
3333
|
+
!$(ev.target).is(
|
3334
|
+
_this.segSelector + ',' + // directly on an event element
|
3335
|
+
_this.segSelector + ' *,' + // within an event element
|
3336
|
+
'.fc-more,' + // a "more.." link
|
3337
|
+
'a[data-goto]' // a clickable nav link
|
3338
|
+
)
|
3588
3339
|
) {
|
3589
3340
|
return handler.call(_this, ev);
|
3590
3341
|
}
|
@@ -3683,6 +3434,7 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
|
3683
3434
|
scroll: view.opt('dragScroll'),
|
3684
3435
|
interactionStart: function() {
|
3685
3436
|
dayClickHit = dragListener.origHit; // for dayClick, where no dragging happens
|
3437
|
+
selectionSpan = null;
|
3686
3438
|
},
|
3687
3439
|
dragStart: function() {
|
3688
3440
|
view.unselect(); // since we could be rendering a new selection, we want to clear any old one
|
@@ -3709,10 +3461,12 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
|
3709
3461
|
}
|
3710
3462
|
}
|
3711
3463
|
},
|
3712
|
-
hitOut: function() {
|
3464
|
+
hitOut: function() { // called before mouse moves to a different hit OR moved out of all hits
|
3713
3465
|
dayClickHit = null;
|
3714
3466
|
selectionSpan = null;
|
3715
3467
|
_this.unrenderSelection();
|
3468
|
+
},
|
3469
|
+
hitDone: function() { // called after a hitOut OR before a dragEnd
|
3716
3470
|
enableCursor();
|
3717
3471
|
},
|
3718
3472
|
interactionEnd: function(ev, isCancelled) {
|
@@ -3731,7 +3485,6 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
|
3731
3485
|
// the selection will already have been rendered. just report it
|
3732
3486
|
view.reportSelection(selectionSpan, ev);
|
3733
3487
|
}
|
3734
|
-
enableCursor();
|
3735
3488
|
}
|
3736
3489
|
}
|
3737
3490
|
});
|
@@ -4034,6 +3787,9 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
|
4034
3787
|
|
4035
3788
|
Grid.mixin({
|
4036
3789
|
|
3790
|
+
// self-config, overridable by subclasses
|
3791
|
+
segSelector: '.fc-event-container > *', // what constitutes an event element?
|
3792
|
+
|
4037
3793
|
mousedOverSeg: null, // the segment object the user's mouse is over. null if over nothing
|
4038
3794
|
isDraggingSeg: false, // is a segment being dragged? boolean
|
4039
3795
|
isResizingSeg: false, // is a segment being resized? boolean
|
@@ -4172,7 +3928,7 @@ Grid.mixin({
|
|
4172
3928
|
|
4173
3929
|
|
4174
3930
|
// Generates an array of classNames to be used for the default rendering of a background event.
|
4175
|
-
// Called by
|
3931
|
+
// Called by fillSegHtml.
|
4176
3932
|
bgEventSegClasses: function(seg) {
|
4177
3933
|
var event = seg.event;
|
4178
3934
|
var source = event.source || {};
|
@@ -4185,7 +3941,7 @@ Grid.mixin({
|
|
4185
3941
|
|
4186
3942
|
|
4187
3943
|
// Generates a semicolon-separated CSS string to be used for the default rendering of a background event.
|
4188
|
-
// Called by
|
3944
|
+
// Called by fillSegHtml.
|
4189
3945
|
bgEventSegCss: function(seg) {
|
4190
3946
|
return {
|
4191
3947
|
'background-color': this.getSegSkinCss(seg)['background-color']
|
@@ -4194,32 +3950,68 @@ Grid.mixin({
|
|
4194
3950
|
|
4195
3951
|
|
4196
3952
|
// Generates an array of classNames to be used for the rendering business hours overlay. Called by the fill system.
|
3953
|
+
// Called by fillSegHtml.
|
4197
3954
|
businessHoursSegClasses: function(seg) {
|
4198
3955
|
return [ 'fc-nonbusiness', 'fc-bgevent' ];
|
4199
3956
|
},
|
4200
3957
|
|
4201
3958
|
|
3959
|
+
/* Business Hours
|
3960
|
+
------------------------------------------------------------------------------------------------------------------*/
|
3961
|
+
|
3962
|
+
|
3963
|
+
// Compute business hour segs for the grid's current date range.
|
3964
|
+
// Caller must ask if whole-day business hours are needed.
|
3965
|
+
buildBusinessHourSegs: function(wholeDay) {
|
3966
|
+
var events = this.view.calendar.getCurrentBusinessHourEvents(wholeDay);
|
3967
|
+
|
3968
|
+
// HACK. Eventually refactor business hours "events" system.
|
3969
|
+
// If no events are given, but businessHours is activated, this means the entire visible range should be
|
3970
|
+
// marked as *not* business-hours, via inverse-background rendering.
|
3971
|
+
if (
|
3972
|
+
!events.length &&
|
3973
|
+
this.view.calendar.options.businessHours // don't access view option. doesn't update with dynamic options
|
3974
|
+
) {
|
3975
|
+
events = [
|
3976
|
+
$.extend({}, BUSINESS_HOUR_EVENT_DEFAULTS, {
|
3977
|
+
start: this.view.end, // guaranteed out-of-range
|
3978
|
+
end: this.view.end, // "
|
3979
|
+
dow: null
|
3980
|
+
})
|
3981
|
+
];
|
3982
|
+
}
|
3983
|
+
|
3984
|
+
return this.eventsToSegs(events);
|
3985
|
+
},
|
3986
|
+
|
3987
|
+
|
4202
3988
|
/* Handlers
|
4203
3989
|
------------------------------------------------------------------------------------------------------------------*/
|
4204
3990
|
|
4205
3991
|
|
4206
|
-
// Attaches event-element-related handlers
|
3992
|
+
// Attaches event-element-related handlers for *all* rendered event segments of the view.
|
4207
3993
|
bindSegHandlers: function() {
|
4208
|
-
this.
|
4209
|
-
|
4210
|
-
|
4211
|
-
|
4212
|
-
|
4213
|
-
|
3994
|
+
this.bindSegHandlersToEl(this.el);
|
3995
|
+
},
|
3996
|
+
|
3997
|
+
|
3998
|
+
// Attaches event-element-related handlers to an arbitrary container element. leverages bubbling.
|
3999
|
+
bindSegHandlersToEl: function(el) {
|
4000
|
+
this.bindSegHandlerToEl(el, 'touchstart', this.handleSegTouchStart);
|
4001
|
+
this.bindSegHandlerToEl(el, 'touchend', this.handleSegTouchEnd);
|
4002
|
+
this.bindSegHandlerToEl(el, 'mouseenter', this.handleSegMouseover);
|
4003
|
+
this.bindSegHandlerToEl(el, 'mouseleave', this.handleSegMouseout);
|
4004
|
+
this.bindSegHandlerToEl(el, 'mousedown', this.handleSegMousedown);
|
4005
|
+
this.bindSegHandlerToEl(el, 'click', this.handleSegClick);
|
4214
4006
|
},
|
4215
4007
|
|
4216
4008
|
|
4217
4009
|
// Executes a handler for any a user-interaction on a segment.
|
4218
4010
|
// Handler gets called with (seg, ev), and with the `this` context of the Grid
|
4219
|
-
|
4011
|
+
bindSegHandlerToEl: function(el, name, handler) {
|
4220
4012
|
var _this = this;
|
4221
4013
|
|
4222
|
-
|
4014
|
+
el.on(name, this.segSelector, function(ev) {
|
4223
4015
|
var seg = $(this).data('fc-seg'); // grab segment data. put there by View::renderEvents
|
4224
4016
|
|
4225
4017
|
// only call the handlers if there is not a drag/resize in progress
|
@@ -4231,7 +4023,10 @@ Grid.mixin({
|
|
4231
4023
|
|
4232
4024
|
|
4233
4025
|
handleSegClick: function(seg, ev) {
|
4234
|
-
|
4026
|
+
var res = this.view.trigger('eventClick', seg.el[0], seg.event, ev); // can return `false` to cancel
|
4027
|
+
if (res === false) {
|
4028
|
+
ev.preventDefault();
|
4029
|
+
}
|
4235
4030
|
},
|
4236
4031
|
|
4237
4032
|
|
@@ -4242,7 +4037,9 @@ Grid.mixin({
|
|
4242
4037
|
!this.mousedOverSeg
|
4243
4038
|
) {
|
4244
4039
|
this.mousedOverSeg = seg;
|
4245
|
-
|
4040
|
+
if (this.view.isEventResizable(seg.event)) {
|
4041
|
+
seg.el.addClass('fc-allow-mouse-resize');
|
4042
|
+
}
|
4246
4043
|
this.view.trigger('eventMouseover', seg.el[0], seg.event, ev);
|
4247
4044
|
}
|
4248
4045
|
},
|
@@ -4256,7 +4053,9 @@ Grid.mixin({
|
|
4256
4053
|
if (this.mousedOverSeg) {
|
4257
4054
|
seg = seg || this.mousedOverSeg; // if given no args, use the currently moused-over segment
|
4258
4055
|
this.mousedOverSeg = null;
|
4259
|
-
|
4056
|
+
if (this.view.isEventResizable(seg.event)) {
|
4057
|
+
seg.el.removeClass('fc-allow-mouse-resize');
|
4058
|
+
}
|
4260
4059
|
this.view.trigger('eventMouseout', seg.el[0], seg.event, ev);
|
4261
4060
|
}
|
4262
4061
|
},
|
@@ -4353,6 +4152,7 @@ Grid.mixin({
|
|
4353
4152
|
subjectEl: el,
|
4354
4153
|
subjectCenter: true,
|
4355
4154
|
interactionStart: function(ev) {
|
4155
|
+
seg.component = _this; // for renderDrag
|
4356
4156
|
isDragging = false;
|
4357
4157
|
mouseFollower = new MouseFollower(seg.el, {
|
4358
4158
|
additionalClass: 'fc-dragging',
|
@@ -4421,6 +4221,8 @@ Grid.mixin({
|
|
4421
4221
|
enableCursor();
|
4422
4222
|
},
|
4423
4223
|
interactionEnd: function(ev) {
|
4224
|
+
delete seg.component; // prevent side effects
|
4225
|
+
|
4424
4226
|
// do revert animation if hasn't changed. calls a callback when finished (whether animation or not)
|
4425
4227
|
mouseFollower.stop(!dropLocation, function() {
|
4426
4228
|
if (isDragging) {
|
@@ -4508,11 +4310,7 @@ Grid.mixin({
|
|
4508
4310
|
}
|
4509
4311
|
// othewise, work off existing values
|
4510
4312
|
else {
|
4511
|
-
dropLocation =
|
4512
|
-
start: event.start.clone(),
|
4513
|
-
end: event.end ? event.end.clone() : null,
|
4514
|
-
allDay: event.allDay // keep it the same
|
4515
|
-
};
|
4313
|
+
dropLocation = pluckEventDateProps(event);
|
4516
4314
|
}
|
4517
4315
|
|
4518
4316
|
dropLocation.start.add(delta);
|
@@ -4538,11 +4336,7 @@ Grid.mixin({
|
|
4538
4336
|
var opacity = this.view.opt('dragOpacity');
|
4539
4337
|
|
4540
4338
|
if (opacity != null) {
|
4541
|
-
els.
|
4542
|
-
// Don't use jQuery (will set an IE filter), do it the old fashioned way.
|
4543
|
-
// In IE8, a helper element will disappears if there's a filter.
|
4544
|
-
node.style.opacity = opacity;
|
4545
|
-
});
|
4339
|
+
els.css('opacity', opacity);
|
4546
4340
|
}
|
4547
4341
|
},
|
4548
4342
|
|
@@ -4708,8 +4502,11 @@ Grid.mixin({
|
|
4708
4502
|
disableCursor();
|
4709
4503
|
resizeLocation = null;
|
4710
4504
|
}
|
4711
|
-
// no change? (
|
4712
|
-
else if (
|
4505
|
+
// no change? (FYI, event dates might have zones)
|
4506
|
+
else if (
|
4507
|
+
resizeLocation.start.isSame(event.start.clone().stripZone()) &&
|
4508
|
+
resizeLocation.end.isSame(eventEnd.clone().stripZone())
|
4509
|
+
) {
|
4713
4510
|
resizeLocation = null;
|
4714
4511
|
}
|
4715
4512
|
}
|
@@ -4862,15 +4659,11 @@ Grid.mixin({
|
|
4862
4659
|
// Generic utility for generating the HTML classNames for an event segment's element
|
4863
4660
|
getSegClasses: function(seg, isDraggable, isResizable) {
|
4864
4661
|
var view = this.view;
|
4865
|
-
var event = seg.event;
|
4866
4662
|
var classes = [
|
4867
4663
|
'fc-event',
|
4868
4664
|
seg.isStart ? 'fc-start' : 'fc-not-start',
|
4869
4665
|
seg.isEnd ? 'fc-end' : 'fc-not-end'
|
4870
|
-
].concat(
|
4871
|
-
event.className,
|
4872
|
-
event.source ? event.source.className : []
|
4873
|
-
);
|
4666
|
+
].concat(this.getSegCustomClasses(seg));
|
4874
4667
|
|
4875
4668
|
if (isDraggable) {
|
4876
4669
|
classes.push('fc-draggable');
|
@@ -4880,7 +4673,7 @@ Grid.mixin({
|
|
4880
4673
|
}
|
4881
4674
|
|
4882
4675
|
// event is currently selected? attach a className.
|
4883
|
-
if (view.isEventSelected(event)) {
|
4676
|
+
if (view.isEventSelected(seg.event)) {
|
4884
4677
|
classes.push('fc-selected');
|
4885
4678
|
}
|
4886
4679
|
|
@@ -4888,38 +4681,78 @@ Grid.mixin({
|
|
4888
4681
|
},
|
4889
4682
|
|
4890
4683
|
|
4891
|
-
//
|
4892
|
-
|
4684
|
+
// List of classes that were defined by the caller of the API in some way
|
4685
|
+
getSegCustomClasses: function(seg) {
|
4893
4686
|
var event = seg.event;
|
4894
|
-
var view = this.view;
|
4895
|
-
var source = event.source || {};
|
4896
|
-
var eventColor = event.color;
|
4897
|
-
var sourceColor = source.color;
|
4898
|
-
var optionColor = view.opt('eventColor');
|
4899
4687
|
|
4688
|
+
return [].concat(
|
4689
|
+
event.className, // guaranteed to be an array
|
4690
|
+
event.source ? event.source.className : []
|
4691
|
+
);
|
4692
|
+
},
|
4693
|
+
|
4694
|
+
|
4695
|
+
// Utility for generating event skin-related CSS properties
|
4696
|
+
getSegSkinCss: function(seg) {
|
4900
4697
|
return {
|
4901
|
-
'background-color':
|
4902
|
-
|
4903
|
-
|
4904
|
-
source.backgroundColor ||
|
4905
|
-
sourceColor ||
|
4906
|
-
view.opt('eventBackgroundColor') ||
|
4907
|
-
optionColor,
|
4908
|
-
'border-color':
|
4909
|
-
event.borderColor ||
|
4910
|
-
eventColor ||
|
4911
|
-
source.borderColor ||
|
4912
|
-
sourceColor ||
|
4913
|
-
view.opt('eventBorderColor') ||
|
4914
|
-
optionColor,
|
4915
|
-
color:
|
4916
|
-
event.textColor ||
|
4917
|
-
source.textColor ||
|
4918
|
-
view.opt('eventTextColor')
|
4698
|
+
'background-color': this.getSegBackgroundColor(seg),
|
4699
|
+
'border-color': this.getSegBorderColor(seg),
|
4700
|
+
color: this.getSegTextColor(seg)
|
4919
4701
|
};
|
4920
4702
|
},
|
4921
4703
|
|
4922
4704
|
|
4705
|
+
// Queries for caller-specified color, then falls back to default
|
4706
|
+
getSegBackgroundColor: function(seg) {
|
4707
|
+
return seg.event.backgroundColor ||
|
4708
|
+
seg.event.color ||
|
4709
|
+
this.getSegDefaultBackgroundColor(seg);
|
4710
|
+
},
|
4711
|
+
|
4712
|
+
|
4713
|
+
getSegDefaultBackgroundColor: function(seg) {
|
4714
|
+
var source = seg.event.source || {};
|
4715
|
+
|
4716
|
+
return source.backgroundColor ||
|
4717
|
+
source.color ||
|
4718
|
+
this.view.opt('eventBackgroundColor') ||
|
4719
|
+
this.view.opt('eventColor');
|
4720
|
+
},
|
4721
|
+
|
4722
|
+
|
4723
|
+
// Queries for caller-specified color, then falls back to default
|
4724
|
+
getSegBorderColor: function(seg) {
|
4725
|
+
return seg.event.borderColor ||
|
4726
|
+
seg.event.color ||
|
4727
|
+
this.getSegDefaultBorderColor(seg);
|
4728
|
+
},
|
4729
|
+
|
4730
|
+
|
4731
|
+
getSegDefaultBorderColor: function(seg) {
|
4732
|
+
var source = seg.event.source || {};
|
4733
|
+
|
4734
|
+
return source.borderColor ||
|
4735
|
+
source.color ||
|
4736
|
+
this.view.opt('eventBorderColor') ||
|
4737
|
+
this.view.opt('eventColor');
|
4738
|
+
},
|
4739
|
+
|
4740
|
+
|
4741
|
+
// Queries for caller-specified color, then falls back to default
|
4742
|
+
getSegTextColor: function(seg) {
|
4743
|
+
return seg.event.textColor ||
|
4744
|
+
this.getSegDefaultTextColor(seg);
|
4745
|
+
},
|
4746
|
+
|
4747
|
+
|
4748
|
+
getSegDefaultTextColor: function(seg) {
|
4749
|
+
var source = seg.event.source || {};
|
4750
|
+
|
4751
|
+
return source.textColor ||
|
4752
|
+
this.view.opt('eventTextColor');
|
4753
|
+
},
|
4754
|
+
|
4755
|
+
|
4923
4756
|
/* Converting events -> eventRange -> eventSpan -> eventSegs
|
4924
4757
|
------------------------------------------------------------------------------------------------------------------*/
|
4925
4758
|
|
@@ -4987,20 +4820,25 @@ Grid.mixin({
|
|
4987
4820
|
// Generates the unzoned start/end dates an event appears to occupy
|
4988
4821
|
// Can accept an event "location" as well (which only has start/end and no allDay)
|
4989
4822
|
eventToRange: function(event) {
|
4990
|
-
|
4991
|
-
|
4992
|
-
|
4823
|
+
var calendar = this.view.calendar;
|
4824
|
+
var start = event.start.clone().stripZone();
|
4825
|
+
var end = (
|
4993
4826
|
event.end ?
|
4994
4827
|
event.end.clone() :
|
4995
4828
|
// derive the end from the start and allDay. compute allDay if necessary
|
4996
|
-
|
4829
|
+
calendar.getDefaultEventEnd(
|
4997
4830
|
event.allDay != null ?
|
4998
4831
|
event.allDay :
|
4999
4832
|
!event.start.hasTime(),
|
5000
4833
|
event.start
|
5001
4834
|
)
|
5002
|
-
).stripZone()
|
5003
|
-
|
4835
|
+
).stripZone();
|
4836
|
+
|
4837
|
+
// hack: dynamic locale change forgets to upate stored event localed
|
4838
|
+
calendar.localizeMoment(start);
|
4839
|
+
calendar.localizeMoment(end);
|
4840
|
+
|
4841
|
+
return { start: start, end: end };
|
5004
4842
|
},
|
5005
4843
|
|
5006
4844
|
|
@@ -5103,6 +4941,16 @@ Grid.mixin({
|
|
5103
4941
|
----------------------------------------------------------------------------------------------------------------------*/
|
5104
4942
|
|
5105
4943
|
|
4944
|
+
function pluckEventDateProps(event) {
|
4945
|
+
return {
|
4946
|
+
start: event.start.clone(),
|
4947
|
+
end: event.end ? event.end.clone() : null,
|
4948
|
+
allDay: event.allDay // keep it the same
|
4949
|
+
};
|
4950
|
+
}
|
4951
|
+
FC.pluckEventDateProps = pluckEventDateProps;
|
4952
|
+
|
4953
|
+
|
5106
4954
|
function isBgEvent(event) { // returns true if background OR inverse-background
|
5107
4955
|
var rendering = getEventRendering(event);
|
5108
4956
|
return rendering === 'background' || rendering === 'inverse-background';
|
@@ -5493,7 +5341,7 @@ var DayTableMixin = FC.DayTableMixin = {
|
|
5493
5341
|
|
5494
5342
|
return '' +
|
5495
5343
|
'<th class="fc-day-header ' + view.widgetHeaderClass + ' fc-' + dayIDs[date.day()] + '"' +
|
5496
|
-
(this.rowCnt
|
5344
|
+
(this.rowCnt === 1 ?
|
5497
5345
|
' data-date="' + date.format('YYYY-MM-DD') + '"' :
|
5498
5346
|
'') +
|
5499
5347
|
(colspan > 1 ?
|
@@ -5502,8 +5350,12 @@ var DayTableMixin = FC.DayTableMixin = {
|
|
5502
5350
|
(otherAttrs ?
|
5503
5351
|
' ' + otherAttrs :
|
5504
5352
|
'') +
|
5505
|
-
|
5506
|
-
|
5353
|
+
'>' +
|
5354
|
+
// don't make a link if the heading could represent multiple days, or if there's only one day (forceOff)
|
5355
|
+
view.buildGotoAnchorHtml(
|
5356
|
+
{ date: date, forceOff: this.rowCnt > 1 || this.colCnt === 1 },
|
5357
|
+
htmlEscape(date.format(this.colHeadFormat)) // inner HTML
|
5358
|
+
) +
|
5507
5359
|
'</th>';
|
5508
5360
|
},
|
5509
5361
|
|
@@ -5656,13 +5508,16 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
5656
5508
|
|
5657
5509
|
|
5658
5510
|
renderBusinessHours: function() {
|
5659
|
-
var
|
5660
|
-
var segs = this.eventsToSegs(events);
|
5661
|
-
|
5511
|
+
var segs = this.buildBusinessHourSegs(true); // wholeDay=true
|
5662
5512
|
this.renderFill('businessHours', segs, 'bgevent');
|
5663
5513
|
},
|
5664
5514
|
|
5665
5515
|
|
5516
|
+
unrenderBusinessHours: function() {
|
5517
|
+
this.unrenderFill('businessHours');
|
5518
|
+
},
|
5519
|
+
|
5520
|
+
|
5666
5521
|
// Generates the HTML for a single row, which is a div that wraps a table.
|
5667
5522
|
// `row` is the row number.
|
5668
5523
|
renderDayRowHtml: function(row, isRigid) {
|
@@ -5729,23 +5584,57 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
5729
5584
|
// Generates the HTML for the <td>s of the "number" row in the DayGrid's content skeleton.
|
5730
5585
|
// The number row will only exist if either day numbers or week numbers are turned on.
|
5731
5586
|
renderNumberCellHtml: function(date) {
|
5587
|
+
var html = '';
|
5732
5588
|
var classes;
|
5589
|
+
var weekCalcFirstDoW;
|
5733
5590
|
|
5734
|
-
if (!this.view.dayNumbersVisible) {
|
5591
|
+
if (!this.view.dayNumbersVisible && !this.view.cellWeekNumbersVisible) {
|
5592
|
+
// no numbers in day cell (week number must be along the side)
|
5735
5593
|
return '<td/>'; // will create an empty space above events :(
|
5736
5594
|
}
|
5737
5595
|
|
5738
5596
|
classes = this.getDayClasses(date);
|
5739
|
-
classes.unshift('fc-day-
|
5740
|
-
|
5741
|
-
return '' +
|
5742
|
-
'<td class="' + classes.join(' ') + '" data-date="' + date.format() + '">' +
|
5743
|
-
date.date() +
|
5744
|
-
'</td>';
|
5745
|
-
},
|
5597
|
+
classes.unshift('fc-day-top');
|
5746
5598
|
|
5747
|
-
|
5748
|
-
|
5599
|
+
if (this.view.cellWeekNumbersVisible) {
|
5600
|
+
// To determine the day of week number change under ISO, we cannot
|
5601
|
+
// rely on moment.js methods such as firstDayOfWeek() or weekday(),
|
5602
|
+
// because they rely on the locale's dow (possibly overridden by
|
5603
|
+
// our firstDay option), which may not be Monday. We cannot change
|
5604
|
+
// dow, because that would affect the calendar start day as well.
|
5605
|
+
if (date._locale._fullCalendar_weekCalc === 'ISO') {
|
5606
|
+
weekCalcFirstDoW = 1; // Monday by ISO 8601 definition
|
5607
|
+
}
|
5608
|
+
else {
|
5609
|
+
weekCalcFirstDoW = date._locale.firstDayOfWeek();
|
5610
|
+
}
|
5611
|
+
}
|
5612
|
+
|
5613
|
+
html += '<td class="' + classes.join(' ') + '" data-date="' + date.format() + '">';
|
5614
|
+
|
5615
|
+
if (this.view.cellWeekNumbersVisible && (date.day() == weekCalcFirstDoW)) {
|
5616
|
+
html += this.view.buildGotoAnchorHtml(
|
5617
|
+
{ date: date, type: 'week' },
|
5618
|
+
{ 'class': 'fc-week-number' },
|
5619
|
+
date.format('w') // inner HTML
|
5620
|
+
);
|
5621
|
+
}
|
5622
|
+
|
5623
|
+
if (this.view.dayNumbersVisible) {
|
5624
|
+
html += this.view.buildGotoAnchorHtml(
|
5625
|
+
date,
|
5626
|
+
{ 'class': 'fc-day-number' },
|
5627
|
+
date.date() // inner HTML
|
5628
|
+
);
|
5629
|
+
}
|
5630
|
+
|
5631
|
+
html += '</td>';
|
5632
|
+
|
5633
|
+
return html;
|
5634
|
+
},
|
5635
|
+
|
5636
|
+
|
5637
|
+
/* Options
|
5749
5638
|
------------------------------------------------------------------------------------------------------------------*/
|
5750
5639
|
|
5751
5640
|
|
@@ -5809,11 +5698,13 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
5809
5698
|
|
5810
5699
|
|
5811
5700
|
queryHit: function(leftOffset, topOffset) {
|
5812
|
-
|
5813
|
-
|
5701
|
+
if (this.colCoordCache.isLeftInBounds(leftOffset) && this.rowCoordCache.isTopInBounds(topOffset)) {
|
5702
|
+
var col = this.colCoordCache.getHorizontalIndex(leftOffset);
|
5703
|
+
var row = this.rowCoordCache.getVerticalIndex(topOffset);
|
5814
5704
|
|
5815
|
-
|
5816
|
-
|
5705
|
+
if (row != null && col != null) {
|
5706
|
+
return this.getCellHit(row, col);
|
5707
|
+
}
|
5817
5708
|
}
|
5818
5709
|
},
|
5819
5710
|
|
@@ -5864,8 +5755,7 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
5864
5755
|
this.renderHighlight(this.eventToSpan(eventLocation));
|
5865
5756
|
|
5866
5757
|
// if a segment from the same calendar but another component is being dragged, render a helper event
|
5867
|
-
if (seg &&
|
5868
|
-
|
5758
|
+
if (seg && seg.component !== this) {
|
5869
5759
|
return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
|
5870
5760
|
}
|
5871
5761
|
},
|
@@ -6573,7 +6463,7 @@ DayGrid.mixin({
|
|
6573
6463
|
options = {
|
6574
6464
|
className: 'fc-more-popover',
|
6575
6465
|
content: this.renderSegPopoverContent(row, col, segs),
|
6576
|
-
parentEl: this.el,
|
6466
|
+
parentEl: this.view.el, // attach to root of view. guarantees outside of scrollbars.
|
6577
6467
|
top: topEl.offset().top,
|
6578
6468
|
autoHide: true, // when the user clicks elsewhere, hide the popover
|
6579
6469
|
viewportConstrain: view.opt('popoverViewportConstrain'),
|
@@ -6596,6 +6486,10 @@ DayGrid.mixin({
|
|
6596
6486
|
|
6597
6487
|
this.segPopover = new Popover(options);
|
6598
6488
|
this.segPopover.show();
|
6489
|
+
|
6490
|
+
// the popover doesn't live within the grid's container element, and thus won't get the event
|
6491
|
+
// delegated-handlers for free. attach event-related handlers to the popover.
|
6492
|
+
this.bindSegHandlersToEl(this.segPopover.el);
|
6599
6493
|
},
|
6600
6494
|
|
6601
6495
|
|
@@ -6844,7 +6738,6 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
6844
6738
|
|
6845
6739
|
this.labelFormat =
|
6846
6740
|
input ||
|
6847
|
-
view.opt('axisFormat') || // deprecated
|
6848
6741
|
view.opt('smallTimeFormat'); // the computed default
|
6849
6742
|
|
6850
6743
|
input = view.opt('slotLabelInterval');
|
@@ -6905,27 +6798,30 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
6905
6798
|
var snapsPerSlot = this.snapsPerSlot;
|
6906
6799
|
var colCoordCache = this.colCoordCache;
|
6907
6800
|
var slatCoordCache = this.slatCoordCache;
|
6908
|
-
|
6909
|
-
|
6910
|
-
|
6911
|
-
|
6912
|
-
|
6913
|
-
|
6914
|
-
|
6915
|
-
|
6916
|
-
|
6917
|
-
|
6918
|
-
|
6919
|
-
|
6920
|
-
|
6921
|
-
|
6922
|
-
|
6923
|
-
|
6924
|
-
|
6925
|
-
|
6926
|
-
|
6927
|
-
|
6928
|
-
|
6801
|
+
|
6802
|
+
if (colCoordCache.isLeftInBounds(leftOffset) && slatCoordCache.isTopInBounds(topOffset)) {
|
6803
|
+
var colIndex = colCoordCache.getHorizontalIndex(leftOffset);
|
6804
|
+
var slatIndex = slatCoordCache.getVerticalIndex(topOffset);
|
6805
|
+
|
6806
|
+
if (colIndex != null && slatIndex != null) {
|
6807
|
+
var slatTop = slatCoordCache.getTopOffset(slatIndex);
|
6808
|
+
var slatHeight = slatCoordCache.getHeight(slatIndex);
|
6809
|
+
var partial = (topOffset - slatTop) / slatHeight; // floating point number between 0 and 1
|
6810
|
+
var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
|
6811
|
+
var snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
|
6812
|
+
var snapTop = slatTop + (localSnapIndex / snapsPerSlot) * slatHeight;
|
6813
|
+
var snapBottom = slatTop + ((localSnapIndex + 1) / snapsPerSlot) * slatHeight;
|
6814
|
+
|
6815
|
+
return {
|
6816
|
+
col: colIndex,
|
6817
|
+
snap: snapIndex,
|
6818
|
+
component: this, // needed unfortunately :(
|
6819
|
+
left: colCoordCache.getLeftOffset(colIndex),
|
6820
|
+
right: colCoordCache.getRightOffset(colIndex),
|
6821
|
+
top: snapTop,
|
6822
|
+
bottom: snapBottom
|
6823
|
+
};
|
6824
|
+
}
|
6929
6825
|
}
|
6930
6826
|
},
|
6931
6827
|
|
@@ -7128,10 +7024,9 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7128
7024
|
|
7129
7025
|
|
7130
7026
|
renderBusinessHours: function() {
|
7131
|
-
|
7132
|
-
|
7133
|
-
|
7134
|
-
this.renderBusinessSegs(segs);
|
7027
|
+
this.renderBusinessSegs(
|
7028
|
+
this.buildBusinessHourSegs()
|
7029
|
+
);
|
7135
7030
|
},
|
7136
7031
|
|
7137
7032
|
|
@@ -8074,6 +7969,62 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8074
7969
|
},
|
8075
7970
|
|
8076
7971
|
|
7972
|
+
getAllDayHtml: function() {
|
7973
|
+
return this.opt('allDayHtml') || htmlEscape(this.opt('allDayText'));
|
7974
|
+
},
|
7975
|
+
|
7976
|
+
|
7977
|
+
/* Navigation
|
7978
|
+
------------------------------------------------------------------------------------------------------------------*/
|
7979
|
+
|
7980
|
+
|
7981
|
+
// Generates HTML for an anchor to another view into the calendar.
|
7982
|
+
// Will either generate an <a> tag or a non-clickable <span> tag, depending on enabled settings.
|
7983
|
+
// `gotoOptions` can either be a moment input, or an object with the form:
|
7984
|
+
// { date, type, forceOff }
|
7985
|
+
// `type` is a view-type like "day" or "week". default value is "day".
|
7986
|
+
// `attrs` and `innerHtml` are use to generate the rest of the HTML tag.
|
7987
|
+
buildGotoAnchorHtml: function(gotoOptions, attrs, innerHtml) {
|
7988
|
+
var date, type, forceOff;
|
7989
|
+
var finalOptions;
|
7990
|
+
|
7991
|
+
if ($.isPlainObject(gotoOptions)) {
|
7992
|
+
date = gotoOptions.date;
|
7993
|
+
type = gotoOptions.type;
|
7994
|
+
forceOff = gotoOptions.forceOff;
|
7995
|
+
}
|
7996
|
+
else {
|
7997
|
+
date = gotoOptions; // a single moment input
|
7998
|
+
}
|
7999
|
+
date = FC.moment(date); // if a string, parse it
|
8000
|
+
|
8001
|
+
finalOptions = { // for serialization into the link
|
8002
|
+
date: date.format('YYYY-MM-DD'),
|
8003
|
+
type: type || 'day'
|
8004
|
+
};
|
8005
|
+
|
8006
|
+
if (typeof attrs === 'string') {
|
8007
|
+
innerHtml = attrs;
|
8008
|
+
attrs = null;
|
8009
|
+
}
|
8010
|
+
|
8011
|
+
attrs = attrs ? ' ' + attrsToStr(attrs) : ''; // will have a leading space
|
8012
|
+
innerHtml = innerHtml || '';
|
8013
|
+
|
8014
|
+
if (!forceOff && this.opt('navLinks')) {
|
8015
|
+
return '<a' + attrs +
|
8016
|
+
' data-goto="' + htmlEscape(JSON.stringify(finalOptions)) + '">' +
|
8017
|
+
innerHtml +
|
8018
|
+
'</a>';
|
8019
|
+
}
|
8020
|
+
else {
|
8021
|
+
return '<span' + attrs + '>' +
|
8022
|
+
innerHtml +
|
8023
|
+
'</span>';
|
8024
|
+
}
|
8025
|
+
},
|
8026
|
+
|
8027
|
+
|
8077
8028
|
/* Rendering
|
8078
8029
|
------------------------------------------------------------------------------------------------------------------*/
|
8079
8030
|
|
@@ -8110,12 +8061,12 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8110
8061
|
// Does everything necessary to display the view centered around the given unzoned date.
|
8111
8062
|
// Does every type of rendering EXCEPT rendering events.
|
8112
8063
|
// Is asychronous and returns a promise.
|
8113
|
-
display: function(date) {
|
8064
|
+
display: function(date, explicitScrollState) {
|
8114
8065
|
var _this = this;
|
8115
|
-
var
|
8066
|
+
var prevScrollState = null;
|
8116
8067
|
|
8117
|
-
if (this.displaying) {
|
8118
|
-
|
8068
|
+
if (explicitScrollState != null && this.displaying) { // don't need prevScrollState if explicitScrollState
|
8069
|
+
prevScrollState = this.queryScroll();
|
8119
8070
|
}
|
8120
8071
|
|
8121
8072
|
this.calendar.freezeContentHeight();
|
@@ -8124,7 +8075,17 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8124
8075
|
return (
|
8125
8076
|
_this.displaying =
|
8126
8077
|
syncThen(_this.displayView(date), function() { // displayView might return a promise
|
8127
|
-
|
8078
|
+
|
8079
|
+
// caller of display() wants a specific scroll state?
|
8080
|
+
if (explicitScrollState != null) {
|
8081
|
+
// we make an assumption that this is NOT the initial render,
|
8082
|
+
// and thus don't need forceScroll (is inconveniently asynchronous)
|
8083
|
+
_this.setScroll(explicitScrollState);
|
8084
|
+
}
|
8085
|
+
else {
|
8086
|
+
_this.forceScroll(_this.computeInitialScroll(prevScrollState));
|
8087
|
+
}
|
8088
|
+
|
8128
8089
|
_this.calendar.unfreezeContentHeight();
|
8129
8090
|
_this.triggerRender();
|
8130
8091
|
})
|
@@ -8559,14 +8520,24 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8559
8520
|
|
8560
8521
|
// Computes if the given event is allowed to be dragged by the user
|
8561
8522
|
isEventDraggable: function(event) {
|
8562
|
-
|
8523
|
+
return this.isEventStartEditable(event);
|
8524
|
+
},
|
8563
8525
|
|
8526
|
+
|
8527
|
+
isEventStartEditable: function(event) {
|
8564
8528
|
return firstDefined(
|
8565
8529
|
event.startEditable,
|
8566
|
-
source.startEditable,
|
8530
|
+
(event.source || {}).startEditable,
|
8567
8531
|
this.opt('eventStartEditable'),
|
8532
|
+
this.isEventGenerallyEditable(event)
|
8533
|
+
);
|
8534
|
+
},
|
8535
|
+
|
8536
|
+
|
8537
|
+
isEventGenerallyEditable: function(event) {
|
8538
|
+
return firstDefined(
|
8568
8539
|
event.editable,
|
8569
|
-
source.editable,
|
8540
|
+
(event.source || {}).editable,
|
8570
8541
|
this.opt('editable')
|
8571
8542
|
);
|
8572
8543
|
},
|
@@ -9066,8 +9037,9 @@ var Scroller = FC.Scroller = Class.extend({
|
|
9066
9037
|
var Calendar = FC.Calendar = Class.extend({
|
9067
9038
|
|
9068
9039
|
dirDefaults: null, // option defaults related to LTR or RTL
|
9069
|
-
|
9040
|
+
localeDefaults: null, // option defaults related to current locale
|
9070
9041
|
overrides: null, // option overrides given to the fullCalendar constructor
|
9042
|
+
dynamicOverrides: null, // options set with dynamic setter method. higher precedence than view overrides.
|
9071
9043
|
options: null, // all defaults combined with overrides
|
9072
9044
|
viewSpecCache: null, // cache of view definitions
|
9073
9045
|
view: null, // current View object
|
@@ -9085,41 +9057,40 @@ var Calendar = FC.Calendar = Class.extend({
|
|
9085
9057
|
},
|
9086
9058
|
|
9087
9059
|
|
9088
|
-
//
|
9089
|
-
|
9090
|
-
|
9060
|
+
// Computes the flattened options hash for the calendar and assigns to `this.options`.
|
9061
|
+
// Assumes this.overrides and this.dynamicOverrides have already been initialized.
|
9062
|
+
populateOptionsHash: function() {
|
9063
|
+
var locale, localeDefaults;
|
9091
9064
|
var isRTL, dirDefaults;
|
9092
9065
|
|
9093
|
-
|
9094
|
-
|
9095
|
-
|
9096
|
-
|
9097
|
-
|
9098
|
-
|
9099
|
-
|
9100
|
-
|
9101
|
-
langDefaults = langOptionHash[lang] || {};
|
9066
|
+
locale = firstDefined( // explicit locale option given?
|
9067
|
+
this.dynamicOverrides.locale,
|
9068
|
+
this.overrides.locale
|
9069
|
+
);
|
9070
|
+
localeDefaults = localeOptionHash[locale];
|
9071
|
+
if (!localeDefaults) { // explicit locale option not given or invalid?
|
9072
|
+
locale = Calendar.defaults.locale;
|
9073
|
+
localeDefaults = localeOptionHash[locale] || {};
|
9102
9074
|
}
|
9103
9075
|
|
9104
|
-
isRTL = firstDefined(
|
9105
|
-
|
9106
|
-
|
9076
|
+
isRTL = firstDefined( // based on options computed so far, is direction RTL?
|
9077
|
+
this.dynamicOverrides.isRTL,
|
9078
|
+
this.overrides.isRTL,
|
9079
|
+
localeDefaults.isRTL,
|
9107
9080
|
Calendar.defaults.isRTL
|
9108
9081
|
);
|
9109
9082
|
dirDefaults = isRTL ? Calendar.rtlDefaults : {};
|
9110
9083
|
|
9111
9084
|
this.dirDefaults = dirDefaults;
|
9112
|
-
this.
|
9113
|
-
this.overrides = overrides;
|
9085
|
+
this.localeDefaults = localeDefaults;
|
9114
9086
|
this.options = mergeOptions([ // merge defaults and overrides. lowest to highest precedence
|
9115
9087
|
Calendar.defaults, // global defaults
|
9116
9088
|
dirDefaults,
|
9117
|
-
|
9118
|
-
overrides
|
9089
|
+
localeDefaults,
|
9090
|
+
this.overrides,
|
9091
|
+
this.dynamicOverrides
|
9119
9092
|
]);
|
9120
|
-
populateInstanceComputableOptions(this.options);
|
9121
|
-
|
9122
|
-
this.viewSpecCache = {}; // somewhat unrelated
|
9093
|
+
populateInstanceComputableOptions(this.options); // fill in gaps with computed options
|
9123
9094
|
},
|
9124
9095
|
|
9125
9096
|
|
@@ -9231,9 +9202,10 @@ var Calendar = FC.Calendar = Class.extend({
|
|
9231
9202
|
Calendar.defaults, // global defaults
|
9232
9203
|
spec.defaults, // view's defaults (from ViewSubclass.defaults)
|
9233
9204
|
this.dirDefaults,
|
9234
|
-
this.
|
9205
|
+
this.localeDefaults, // locale and dir take precedence over view's defaults!
|
9235
9206
|
this.overrides, // calendar's overrides (options given to constructor)
|
9236
|
-
spec.overrides // view's overrides (view-specific options)
|
9207
|
+
spec.overrides, // view's overrides (view-specific options)
|
9208
|
+
this.dynamicOverrides // dynamically set via setter. highest precedence
|
9237
9209
|
]);
|
9238
9210
|
populateInstanceComputableOptions(spec.options);
|
9239
9211
|
},
|
@@ -9247,17 +9219,21 @@ var Calendar = FC.Calendar = Class.extend({
|
|
9247
9219
|
function queryButtonText(options) {
|
9248
9220
|
var buttonText = options.buttonText || {};
|
9249
9221
|
return buttonText[requestedViewType] ||
|
9222
|
+
// view can decide to look up a certain key
|
9223
|
+
(spec.buttonTextKey ? buttonText[spec.buttonTextKey] : null) ||
|
9224
|
+
// a key like "month"
|
9250
9225
|
(spec.singleUnit ? buttonText[spec.singleUnit] : null);
|
9251
9226
|
}
|
9252
9227
|
|
9253
9228
|
// highest to lowest priority
|
9254
9229
|
spec.buttonTextOverride =
|
9230
|
+
queryButtonText(this.dynamicOverrides) ||
|
9255
9231
|
queryButtonText(this.overrides) || // constructor-specified buttonText lookup hash takes precedence
|
9256
9232
|
spec.overrides.buttonText; // `buttonText` for view-specific options is a string
|
9257
9233
|
|
9258
9234
|
// highest to lowest priority. mirrors buildViewSpecOptions
|
9259
9235
|
spec.buttonTextDefault =
|
9260
|
-
queryButtonText(this.
|
9236
|
+
queryButtonText(this.localeDefaults) ||
|
9261
9237
|
queryButtonText(this.dirDefaults) ||
|
9262
9238
|
spec.defaults.buttonText || // a single string. from ViewSubclass.defaults
|
9263
9239
|
queryButtonText(Calendar.defaults) ||
|
@@ -9324,10 +9300,6 @@ function Calendar_constructor(element, overrides) {
|
|
9324
9300
|
var t = this;
|
9325
9301
|
|
9326
9302
|
|
9327
|
-
t.initOptions(overrides || {});
|
9328
|
-
var options = this.options;
|
9329
|
-
|
9330
|
-
|
9331
9303
|
// Exports
|
9332
9304
|
// -----------------------------------------------------------------------------------
|
9333
9305
|
|
@@ -9352,67 +9324,95 @@ function Calendar_constructor(element, overrides) {
|
|
9352
9324
|
t.getDate = getDate;
|
9353
9325
|
t.getCalendar = getCalendar;
|
9354
9326
|
t.getView = getView;
|
9355
|
-
t.option = option;
|
9327
|
+
t.option = option; // getter/setter method
|
9356
9328
|
t.trigger = trigger;
|
9357
9329
|
|
9358
9330
|
|
9331
|
+
// Options
|
9332
|
+
// -----------------------------------------------------------------------------------
|
9333
|
+
|
9334
|
+
t.dynamicOverrides = {};
|
9335
|
+
t.viewSpecCache = {};
|
9336
|
+
t.optionHandlers = {}; // for Calendar.options.js
|
9337
|
+
t.overrides = $.extend({}, overrides); // make a copy
|
9338
|
+
|
9339
|
+
t.populateOptionsHash(); // sets this.options
|
9340
|
+
|
9359
9341
|
|
9360
|
-
|
9342
|
+
|
9343
|
+
// Locale-data Internals
|
9361
9344
|
// -----------------------------------------------------------------------------------
|
9362
|
-
// Apply overrides to the current
|
9345
|
+
// Apply overrides to the current locale's data
|
9363
9346
|
|
9347
|
+
var localeData;
|
9364
9348
|
|
9365
|
-
|
9366
|
-
|
9367
|
-
|
9349
|
+
// Called immediately, and when any of the options change.
|
9350
|
+
// Happens before any internal objects rebuild or rerender, because this is very core.
|
9351
|
+
t.bindOptions([
|
9352
|
+
'locale', 'monthNames', 'monthNamesShort', 'dayNames', 'dayNamesShort', 'firstDay', 'weekNumberCalculation'
|
9353
|
+
], function(locale, monthNames, monthNamesShort, dayNames, dayNamesShort, firstDay, weekNumberCalculation) {
|
9368
9354
|
|
9369
|
-
|
9370
|
-
|
9371
|
-
|
9372
|
-
|
9373
|
-
|
9374
|
-
|
9375
|
-
|
9376
|
-
|
9377
|
-
|
9378
|
-
|
9379
|
-
|
9380
|
-
|
9381
|
-
|
9382
|
-
|
9383
|
-
|
9384
|
-
|
9385
|
-
|
9355
|
+
// normalize
|
9356
|
+
if (weekNumberCalculation === 'iso') {
|
9357
|
+
weekNumberCalculation = 'ISO'; // normalize
|
9358
|
+
}
|
9359
|
+
|
9360
|
+
localeData = createObject( // make a cheap copy
|
9361
|
+
getMomentLocaleData(locale) // will fall back to en
|
9362
|
+
);
|
9363
|
+
|
9364
|
+
if (monthNames) {
|
9365
|
+
localeData._months = monthNames;
|
9366
|
+
}
|
9367
|
+
if (monthNamesShort) {
|
9368
|
+
localeData._monthsShort = monthNamesShort;
|
9369
|
+
}
|
9370
|
+
if (dayNames) {
|
9371
|
+
localeData._weekdays = dayNames;
|
9372
|
+
}
|
9373
|
+
if (dayNamesShort) {
|
9374
|
+
localeData._weekdaysShort = dayNamesShort;
|
9375
|
+
}
|
9386
9376
|
|
9387
|
-
|
9388
|
-
|
9389
|
-
if (typeof weekCalc === 'function') {
|
9390
|
-
return weekCalc;
|
9377
|
+
if (firstDay == null && weekNumberCalculation === 'ISO') {
|
9378
|
+
firstDay = 1;
|
9391
9379
|
}
|
9392
|
-
|
9393
|
-
|
9380
|
+
if (firstDay != null) {
|
9381
|
+
var _week = createObject(localeData._week); // _week: { dow: # }
|
9382
|
+
_week.dow = firstDay;
|
9383
|
+
localeData._week = _week;
|
9394
9384
|
}
|
9395
|
-
|
9396
|
-
|
9385
|
+
|
9386
|
+
if ( // whitelist certain kinds of input
|
9387
|
+
weekNumberCalculation === 'ISO' ||
|
9388
|
+
weekNumberCalculation === 'local' ||
|
9389
|
+
typeof weekNumberCalculation === 'function'
|
9390
|
+
) {
|
9391
|
+
localeData._fullCalendar_weekCalc = weekNumberCalculation; // moment-ext will know what to do with it
|
9397
9392
|
}
|
9398
|
-
})(options.weekNumberCalculation);
|
9399
9393
|
|
9394
|
+
// If the internal current date object already exists, move to new locale.
|
9395
|
+
// We do NOT need to do this technique for event dates, because this happens when converting to "segments".
|
9396
|
+
if (date) {
|
9397
|
+
localizeMoment(date); // sets to localeData
|
9398
|
+
}
|
9399
|
+
});
|
9400
9400
|
|
9401
9401
|
|
9402
9402
|
// Calendar-specific Date Utilities
|
9403
9403
|
// -----------------------------------------------------------------------------------
|
9404
9404
|
|
9405
9405
|
|
9406
|
-
t.defaultAllDayEventDuration = moment.duration(options.defaultAllDayEventDuration);
|
9407
|
-
t.defaultTimedEventDuration = moment.duration(options.defaultTimedEventDuration);
|
9406
|
+
t.defaultAllDayEventDuration = moment.duration(t.options.defaultAllDayEventDuration);
|
9407
|
+
t.defaultTimedEventDuration = moment.duration(t.options.defaultTimedEventDuration);
|
9408
9408
|
|
9409
9409
|
|
9410
|
-
// Builds a moment using the settings of the current calendar: timezone and
|
9410
|
+
// Builds a moment using the settings of the current calendar: timezone and locale.
|
9411
9411
|
// Accepts anything the vanilla moment() constructor accepts.
|
9412
9412
|
t.moment = function() {
|
9413
9413
|
var mom;
|
9414
9414
|
|
9415
|
-
if (options.timezone === 'local') {
|
9415
|
+
if (t.options.timezone === 'local') {
|
9416
9416
|
mom = FC.moment.apply(null, arguments);
|
9417
9417
|
|
9418
9418
|
// Force the moment to be local, because FC.moment doesn't guarantee it.
|
@@ -9420,28 +9420,30 @@ function Calendar_constructor(element, overrides) {
|
|
9420
9420
|
mom.local();
|
9421
9421
|
}
|
9422
9422
|
}
|
9423
|
-
else if (options.timezone === 'UTC') {
|
9423
|
+
else if (t.options.timezone === 'UTC') {
|
9424
9424
|
mom = FC.moment.utc.apply(null, arguments); // process as UTC
|
9425
9425
|
}
|
9426
9426
|
else {
|
9427
9427
|
mom = FC.moment.parseZone.apply(null, arguments); // let the input decide the zone
|
9428
9428
|
}
|
9429
9429
|
|
9430
|
-
|
9431
|
-
mom._locale = localeData;
|
9432
|
-
}
|
9433
|
-
else { // pre-moment-2.8
|
9434
|
-
mom._lang = localeData;
|
9435
|
-
}
|
9430
|
+
localizeMoment(mom);
|
9436
9431
|
|
9437
9432
|
return mom;
|
9438
9433
|
};
|
9439
9434
|
|
9440
9435
|
|
9436
|
+
// Updates the given moment's locale settings to the current calendar locale settings.
|
9437
|
+
function localizeMoment(mom) {
|
9438
|
+
mom._locale = localeData;
|
9439
|
+
}
|
9440
|
+
t.localizeMoment = localizeMoment;
|
9441
|
+
|
9442
|
+
|
9441
9443
|
// Returns a boolean about whether or not the calendar knows how to calculate
|
9442
9444
|
// the timezone offset of arbitrary dates in the current timezone.
|
9443
9445
|
t.getIsAmbigTimezone = function() {
|
9444
|
-
return options.timezone !== 'local' && options.timezone !== 'UTC';
|
9446
|
+
return t.options.timezone !== 'local' && t.options.timezone !== 'UTC';
|
9445
9447
|
};
|
9446
9448
|
|
9447
9449
|
|
@@ -9470,7 +9472,7 @@ function Calendar_constructor(element, overrides) {
|
|
9470
9472
|
// Returns a moment for the current date, as defined by the client's computer or from the `now` option.
|
9471
9473
|
// Will return an moment with an ambiguous timezone.
|
9472
9474
|
t.getNow = function() {
|
9473
|
-
var now = options.now;
|
9475
|
+
var now = t.options.now;
|
9474
9476
|
if (typeof now === 'function') {
|
9475
9477
|
now = now();
|
9476
9478
|
}
|
@@ -9512,8 +9514,7 @@ function Calendar_constructor(element, overrides) {
|
|
9512
9514
|
// Produces a human-readable string for the given duration.
|
9513
9515
|
// Side-effect: changes the locale of the given duration.
|
9514
9516
|
t.humanizeDuration = function(duration) {
|
9515
|
-
return
|
9516
|
-
.humanize();
|
9517
|
+
return duration.locale(t.options.locale).humanize();
|
9517
9518
|
};
|
9518
9519
|
|
9519
9520
|
|
@@ -9522,7 +9523,7 @@ function Calendar_constructor(element, overrides) {
|
|
9522
9523
|
// -----------------------------------------------------------------------------------
|
9523
9524
|
|
9524
9525
|
|
9525
|
-
EventManager.call(t
|
9526
|
+
EventManager.call(t);
|
9526
9527
|
var isFetchNeeded = t.isFetchNeeded;
|
9527
9528
|
var fetchEvents = t.fetchEvents;
|
9528
9529
|
var fetchEventSources = t.fetchEventSources;
|
@@ -9535,7 +9536,6 @@ function Calendar_constructor(element, overrides) {
|
|
9535
9536
|
|
9536
9537
|
var _element = element[0];
|
9537
9538
|
var header;
|
9538
|
-
var headerElement;
|
9539
9539
|
var content;
|
9540
9540
|
var tm; // for making theme classes
|
9541
9541
|
var currentView; // NOTE: keep this in sync with this.view
|
@@ -9553,8 +9553,8 @@ function Calendar_constructor(element, overrides) {
|
|
9553
9553
|
|
9554
9554
|
|
9555
9555
|
// compute the initial ambig-timezone date
|
9556
|
-
if (options.defaultDate != null) {
|
9557
|
-
date = t.moment(options.defaultDate).stripZone();
|
9556
|
+
if (t.options.defaultDate != null) {
|
9557
|
+
date = t.moment(t.options.defaultDate).stripZone();
|
9558
9558
|
}
|
9559
9559
|
else {
|
9560
9560
|
date = t.getNow(); // getNow already returns unzoned
|
@@ -9574,38 +9574,64 @@ function Calendar_constructor(element, overrides) {
|
|
9574
9574
|
|
9575
9575
|
|
9576
9576
|
function initialRender() {
|
9577
|
-
tm = options.theme ? 'ui' : 'fc';
|
9578
9577
|
element.addClass('fc');
|
9579
9578
|
|
9580
|
-
|
9581
|
-
|
9582
|
-
|
9583
|
-
|
9584
|
-
|
9585
|
-
|
9579
|
+
// event delegation for nav links
|
9580
|
+
element.on('click.fc', 'a[data-goto]', function(ev) {
|
9581
|
+
var anchorEl = $(this);
|
9582
|
+
var gotoOptions = anchorEl.data('goto'); // will automatically parse JSON
|
9583
|
+
var date = t.moment(gotoOptions.date);
|
9584
|
+
var viewType = gotoOptions.type;
|
9586
9585
|
|
9587
|
-
|
9588
|
-
|
9589
|
-
|
9590
|
-
|
9591
|
-
|
9592
|
-
|
9586
|
+
// property like "navLinkDayClick". might be a string or a function
|
9587
|
+
var customAction = currentView.opt('navLink' + capitaliseFirstLetter(viewType) + 'Click');
|
9588
|
+
|
9589
|
+
if (typeof customAction === 'function') {
|
9590
|
+
customAction(date, ev);
|
9591
|
+
}
|
9592
|
+
else {
|
9593
|
+
if (typeof customAction === 'string') {
|
9594
|
+
viewType = customAction;
|
9595
|
+
}
|
9596
|
+
zoomTo(date, viewType);
|
9597
|
+
}
|
9598
|
+
});
|
9599
|
+
|
9600
|
+
// called immediately, and upon option change
|
9601
|
+
t.bindOption('theme', function(theme) {
|
9602
|
+
tm = theme ? 'ui' : 'fc'; // affects a larger scope
|
9603
|
+
element.toggleClass('ui-widget', theme);
|
9604
|
+
element.toggleClass('fc-unthemed', !theme);
|
9605
|
+
});
|
9606
|
+
|
9607
|
+
// called immediately, and upon option change.
|
9608
|
+
// HACK: locale often affects isRTL, so we explicitly listen to that too.
|
9609
|
+
t.bindOptions([ 'isRTL', 'locale' ], function(isRTL) {
|
9610
|
+
element.toggleClass('fc-ltr', !isRTL);
|
9611
|
+
element.toggleClass('fc-rtl', isRTL);
|
9612
|
+
});
|
9593
9613
|
|
9594
9614
|
content = $("<div class='fc-view-container'/>").prependTo(element);
|
9595
9615
|
|
9596
|
-
header = t.header = new Header(t
|
9597
|
-
|
9598
|
-
if (headerElement) {
|
9599
|
-
element.prepend(headerElement);
|
9600
|
-
}
|
9616
|
+
header = t.header = new Header(t);
|
9617
|
+
renderHeader();
|
9601
9618
|
|
9602
|
-
renderView(options.defaultView);
|
9619
|
+
renderView(t.options.defaultView);
|
9603
9620
|
|
9604
|
-
if (options.handleWindowResize) {
|
9605
|
-
windowResizeProxy = debounce(windowResize, options.windowResizeDelay); // prevents rapid calls
|
9621
|
+
if (t.options.handleWindowResize) {
|
9622
|
+
windowResizeProxy = debounce(windowResize, t.options.windowResizeDelay); // prevents rapid calls
|
9606
9623
|
$(window).resize(windowResizeProxy);
|
9607
9624
|
}
|
9608
9625
|
}
|
9626
|
+
|
9627
|
+
|
9628
|
+
// can be called repeatedly and Header will rerender
|
9629
|
+
function renderHeader() {
|
9630
|
+
header.render();
|
9631
|
+
if (header.el) {
|
9632
|
+
element.prepend(header.el);
|
9633
|
+
}
|
9634
|
+
}
|
9609
9635
|
|
9610
9636
|
|
9611
9637
|
function destroy() {
|
@@ -9621,6 +9647,8 @@ function Calendar_constructor(element, overrides) {
|
|
9621
9647
|
content.remove();
|
9622
9648
|
element.removeClass('fc fc-ltr fc-rtl fc-unthemed ui-widget');
|
9623
9649
|
|
9650
|
+
element.off('.fc'); // unbind nav link handlers
|
9651
|
+
|
9624
9652
|
if (windowResizeProxy) {
|
9625
9653
|
$(window).unbind('resize', windowResizeProxy);
|
9626
9654
|
}
|
@@ -9639,15 +9667,14 @@ function Calendar_constructor(element, overrides) {
|
|
9639
9667
|
|
9640
9668
|
// Renders a view because of a date change, view-type change, or for the first time.
|
9641
9669
|
// If not given a viewType, keep the current view but render different dates.
|
9642
|
-
|
9670
|
+
// Accepts an optional scroll state to restore to.
|
9671
|
+
function renderView(viewType, explicitScrollState) {
|
9643
9672
|
ignoreWindowResize++;
|
9644
9673
|
|
9645
9674
|
// if viewType is changing, remove the old view's rendering
|
9646
9675
|
if (currentView && viewType && currentView.type !== viewType) {
|
9647
|
-
header.deactivateButton(currentView.type);
|
9648
9676
|
freezeContentHeight(); // prevent a scroll jump when view element is removed
|
9649
|
-
|
9650
|
-
currentView = t.view = null;
|
9677
|
+
clearView();
|
9651
9678
|
}
|
9652
9679
|
|
9653
9680
|
// if viewType changed, or the view was never created, create a fresh view
|
@@ -9670,11 +9697,14 @@ function Calendar_constructor(element, overrides) {
|
|
9670
9697
|
// render or rerender the view
|
9671
9698
|
if (
|
9672
9699
|
!currentView.displaying ||
|
9673
|
-
!
|
9700
|
+
!( // NOT within interval range signals an implicit date window change
|
9701
|
+
date >= currentView.intervalStart &&
|
9702
|
+
date < currentView.intervalEnd
|
9703
|
+
)
|
9674
9704
|
) {
|
9675
9705
|
if (elementVisible()) {
|
9676
9706
|
|
9677
|
-
currentView.display(date); // will call freezeContentHeight
|
9707
|
+
currentView.display(date, explicitScrollState); // will call freezeContentHeight
|
9678
9708
|
unfreezeContentHeight(); // immediately unfreeze regardless of whether display is async
|
9679
9709
|
|
9680
9710
|
// need to do this after View::render, so dates are calculated
|
@@ -9690,6 +9720,32 @@ function Calendar_constructor(element, overrides) {
|
|
9690
9720
|
ignoreWindowResize--;
|
9691
9721
|
}
|
9692
9722
|
|
9723
|
+
|
9724
|
+
// Unrenders the current view and reflects this change in the Header.
|
9725
|
+
// Unregsiters the `currentView`, but does not remove from viewByType hash.
|
9726
|
+
function clearView() {
|
9727
|
+
header.deactivateButton(currentView.type);
|
9728
|
+
currentView.removeElement();
|
9729
|
+
currentView = t.view = null;
|
9730
|
+
}
|
9731
|
+
|
9732
|
+
|
9733
|
+
// Destroys the view, including the view object. Then, re-instantiates it and renders it.
|
9734
|
+
// Maintains the same scroll state.
|
9735
|
+
// TODO: maintain any other user-manipulated state.
|
9736
|
+
function reinitView() {
|
9737
|
+
ignoreWindowResize++;
|
9738
|
+
freezeContentHeight();
|
9739
|
+
|
9740
|
+
var viewType = currentView.type;
|
9741
|
+
var scrollState = currentView.queryScroll();
|
9742
|
+
clearView();
|
9743
|
+
renderView(viewType, scrollState);
|
9744
|
+
|
9745
|
+
unfreezeContentHeight();
|
9746
|
+
ignoreWindowResize--;
|
9747
|
+
}
|
9748
|
+
|
9693
9749
|
|
9694
9750
|
|
9695
9751
|
// Resizing
|
@@ -9705,7 +9761,7 @@ function Calendar_constructor(element, overrides) {
|
|
9705
9761
|
|
9706
9762
|
|
9707
9763
|
t.isHeightAuto = function() {
|
9708
|
-
return options.contentHeight === 'auto' || options.height === 'auto';
|
9764
|
+
return t.options.contentHeight === 'auto' || t.options.height === 'auto';
|
9709
9765
|
};
|
9710
9766
|
|
9711
9767
|
|
@@ -9733,16 +9789,33 @@ function Calendar_constructor(element, overrides) {
|
|
9733
9789
|
|
9734
9790
|
|
9735
9791
|
function _calcSize() { // assumes elementVisible
|
9736
|
-
|
9737
|
-
|
9792
|
+
var contentHeightInput = t.options.contentHeight;
|
9793
|
+
var heightInput = t.options.height;
|
9794
|
+
|
9795
|
+
if (typeof contentHeightInput === 'number') { // exists and not 'auto'
|
9796
|
+
suggestedViewHeight = contentHeightInput;
|
9797
|
+
}
|
9798
|
+
else if (typeof contentHeightInput === 'function') { // exists and is a function
|
9799
|
+
suggestedViewHeight = contentHeightInput();
|
9738
9800
|
}
|
9739
|
-
else if (typeof
|
9740
|
-
suggestedViewHeight =
|
9801
|
+
else if (typeof heightInput === 'number') { // exists and not 'auto'
|
9802
|
+
suggestedViewHeight = heightInput - queryHeaderHeight();
|
9803
|
+
}
|
9804
|
+
else if (typeof heightInput === 'function') { // exists and is a function
|
9805
|
+
suggestedViewHeight = heightInput() - queryHeaderHeight();
|
9806
|
+
}
|
9807
|
+
else if (heightInput === 'parent') { // set to height of parent element
|
9808
|
+
suggestedViewHeight = element.parent().height() - queryHeaderHeight();
|
9741
9809
|
}
|
9742
9810
|
else {
|
9743
|
-
suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
|
9811
|
+
suggestedViewHeight = Math.round(content.width() / Math.max(t.options.aspectRatio, .5));
|
9744
9812
|
}
|
9745
9813
|
}
|
9814
|
+
|
9815
|
+
|
9816
|
+
function queryHeaderHeight() {
|
9817
|
+
return header.el ? header.el.outerHeight(true) : 0; // includes margin
|
9818
|
+
}
|
9746
9819
|
|
9747
9820
|
|
9748
9821
|
function windowResize(ev) {
|
@@ -9785,7 +9858,7 @@ function Calendar_constructor(element, overrides) {
|
|
9785
9858
|
|
9786
9859
|
|
9787
9860
|
function getAndRenderEvents() {
|
9788
|
-
if (!options.lazyFetching || isFetchNeeded(currentView.start, currentView.end)) {
|
9861
|
+
if (!t.options.lazyFetching || isFetchNeeded(currentView.start, currentView.end)) {
|
9789
9862
|
fetchAndRenderEvents();
|
9790
9863
|
}
|
9791
9864
|
else {
|
@@ -9826,7 +9899,8 @@ function Calendar_constructor(element, overrides) {
|
|
9826
9899
|
|
9827
9900
|
function updateTodayButton() {
|
9828
9901
|
var now = t.getNow();
|
9829
|
-
|
9902
|
+
|
9903
|
+
if (now >= currentView.intervalStart && now < currentView.intervalEnd) {
|
9830
9904
|
header.disableButton('today');
|
9831
9905
|
}
|
9832
9906
|
else {
|
@@ -9964,13 +10038,69 @@ function Calendar_constructor(element, overrides) {
|
|
9964
10038
|
|
9965
10039
|
|
9966
10040
|
function option(name, value) {
|
9967
|
-
|
9968
|
-
|
10041
|
+
var newOptionHash;
|
10042
|
+
|
10043
|
+
if (typeof name === 'string') {
|
10044
|
+
if (value === undefined) { // getter
|
10045
|
+
return t.options[name];
|
10046
|
+
}
|
10047
|
+
else { // setter for individual option
|
10048
|
+
newOptionHash = {};
|
10049
|
+
newOptionHash[name] = value;
|
10050
|
+
setOptions(newOptionHash);
|
10051
|
+
}
|
10052
|
+
}
|
10053
|
+
else if (typeof name === 'object') { // compound setter with object input
|
10054
|
+
setOptions(name);
|
10055
|
+
}
|
10056
|
+
}
|
10057
|
+
|
10058
|
+
|
10059
|
+
function setOptions(newOptionHash) {
|
10060
|
+
var optionCnt = 0;
|
10061
|
+
var optionName;
|
10062
|
+
|
10063
|
+
for (optionName in newOptionHash) {
|
10064
|
+
t.dynamicOverrides[optionName] = newOptionHash[optionName];
|
10065
|
+
}
|
10066
|
+
|
10067
|
+
t.viewSpecCache = {}; // the dynamic override invalidates the options in this cache, so just clear it
|
10068
|
+
t.populateOptionsHash(); // this.options needs to be recomputed after the dynamic override
|
10069
|
+
|
10070
|
+
// trigger handlers after this.options has been updated
|
10071
|
+
for (optionName in newOptionHash) {
|
10072
|
+
t.triggerOptionHandlers(optionName); // recall bindOption/bindOptions
|
10073
|
+
optionCnt++;
|
9969
10074
|
}
|
9970
|
-
|
9971
|
-
|
9972
|
-
|
10075
|
+
|
10076
|
+
// special-case handling of single option change.
|
10077
|
+
// if only one option change, `optionName` will be its name.
|
10078
|
+
if (optionCnt === 1) {
|
10079
|
+
if (optionName === 'height' || optionName === 'contentHeight' || optionName === 'aspectRatio') {
|
10080
|
+
updateSize(true); // true = allow recalculation of height
|
10081
|
+
return;
|
10082
|
+
}
|
10083
|
+
else if (optionName === 'defaultDate') {
|
10084
|
+
return; // can't change date this way. use gotoDate instead
|
10085
|
+
}
|
10086
|
+
else if (optionName === 'businessHours') {
|
10087
|
+
if (currentView) {
|
10088
|
+
currentView.unrenderBusinessHours();
|
10089
|
+
currentView.renderBusinessHours();
|
10090
|
+
}
|
10091
|
+
return;
|
10092
|
+
}
|
10093
|
+
else if (optionName === 'timezone') {
|
10094
|
+
t.rezoneArrayEventSources();
|
10095
|
+
refetchEvents();
|
10096
|
+
return;
|
10097
|
+
}
|
9973
10098
|
}
|
10099
|
+
|
10100
|
+
// catch-all. rerender the header and rebuild/rerender the current view
|
10101
|
+
renderHeader();
|
10102
|
+
viewsByType = {}; // even non-current views will be affected by this option change. do before rerender
|
10103
|
+
reinitView();
|
9974
10104
|
}
|
9975
10105
|
|
9976
10106
|
|
@@ -9980,20 +10110,84 @@ function Calendar_constructor(element, overrides) {
|
|
9980
10110
|
thisObj = thisObj || _element;
|
9981
10111
|
this.triggerWith(name, thisObj, args); // Emitter's method
|
9982
10112
|
|
9983
|
-
if (options[name]) {
|
9984
|
-
return options[name].apply(thisObj, args);
|
10113
|
+
if (t.options[name]) {
|
10114
|
+
return t.options[name].apply(thisObj, args);
|
9985
10115
|
}
|
9986
10116
|
}
|
9987
10117
|
|
9988
10118
|
t.initialize();
|
9989
10119
|
}
|
9990
10120
|
|
10121
|
+
;;
|
10122
|
+
/*
|
10123
|
+
Options binding/triggering system.
|
10124
|
+
*/
|
10125
|
+
Calendar.mixin({
|
10126
|
+
|
10127
|
+
// A map of option names to arrays of handler objects. Initialized to {} in Calendar.
|
10128
|
+
// Format for a handler object:
|
10129
|
+
// {
|
10130
|
+
// func // callback function to be called upon change
|
10131
|
+
// names // option names whose values should be given to func
|
10132
|
+
// }
|
10133
|
+
optionHandlers: null,
|
10134
|
+
|
10135
|
+
// Calls handlerFunc immediately, and when the given option has changed.
|
10136
|
+
// handlerFunc will be given the option value.
|
10137
|
+
bindOption: function(optionName, handlerFunc) {
|
10138
|
+
this.bindOptions([ optionName ], handlerFunc);
|
10139
|
+
},
|
10140
|
+
|
10141
|
+
// Calls handlerFunc immediately, and when any of the given options change.
|
10142
|
+
// handlerFunc will be given each option value as ordered function arguments.
|
10143
|
+
bindOptions: function(optionNames, handlerFunc) {
|
10144
|
+
var handlerObj = { func: handlerFunc, names: optionNames };
|
10145
|
+
var i;
|
10146
|
+
|
10147
|
+
for (i = 0; i < optionNames.length; i++) {
|
10148
|
+
this.registerOptionHandlerObj(optionNames[i], handlerObj);
|
10149
|
+
}
|
10150
|
+
|
10151
|
+
this.triggerOptionHandlerObj(handlerObj);
|
10152
|
+
},
|
10153
|
+
|
10154
|
+
// Puts the given handler object into the internal hash
|
10155
|
+
registerOptionHandlerObj: function(optionName, handlerObj) {
|
10156
|
+
(this.optionHandlers[optionName] || (this.optionHandlers[optionName] = []))
|
10157
|
+
.push(handlerObj);
|
10158
|
+
},
|
10159
|
+
|
10160
|
+
// Reports that the given option has changed, and calls all appropriate handlers.
|
10161
|
+
triggerOptionHandlers: function(optionName) {
|
10162
|
+
var handlerObjs = this.optionHandlers[optionName] || [];
|
10163
|
+
var i;
|
10164
|
+
|
10165
|
+
for (i = 0; i < handlerObjs.length; i++) {
|
10166
|
+
this.triggerOptionHandlerObj(handlerObjs[i]);
|
10167
|
+
}
|
10168
|
+
},
|
10169
|
+
|
10170
|
+
// Calls the callback for a specific handler object, passing in the appropriate arguments.
|
10171
|
+
triggerOptionHandlerObj: function(handlerObj) {
|
10172
|
+
var optionNames = handlerObj.names;
|
10173
|
+
var optionValues = [];
|
10174
|
+
var i;
|
10175
|
+
|
10176
|
+
for (i = 0; i < optionNames.length; i++) {
|
10177
|
+
optionValues.push(this.options[optionNames[i]]);
|
10178
|
+
}
|
10179
|
+
|
10180
|
+
handlerObj.func.apply(this, optionValues); // maintain the Calendar's `this` context
|
10181
|
+
}
|
10182
|
+
|
10183
|
+
});
|
10184
|
+
|
9991
10185
|
;;
|
9992
10186
|
|
9993
10187
|
Calendar.defaults = {
|
9994
10188
|
|
9995
10189
|
titleRangeSeparator: ' \u2013 ', // en dash
|
9996
|
-
monthYearFormat: 'MMMM YYYY', // required for en. other
|
10190
|
+
monthYearFormat: 'MMMM YYYY', // required for en. other locales rely on datepicker computable option
|
9997
10191
|
|
9998
10192
|
defaultTimedEventDuration: '02:00:00',
|
9999
10193
|
defaultAllDayEventDuration: { days: 1 },
|
@@ -10050,6 +10244,8 @@ Calendar.defaults = {
|
|
10050
10244
|
prevYear: 'left-double-arrow',
|
10051
10245
|
nextYear: 'right-double-arrow'
|
10052
10246
|
},
|
10247
|
+
|
10248
|
+
allDayText: 'all-day',
|
10053
10249
|
|
10054
10250
|
// jquery-ui theming
|
10055
10251
|
theme: false,
|
@@ -10078,14 +10274,14 @@ Calendar.defaults = {
|
|
10078
10274
|
dayPopoverFormat: 'LL',
|
10079
10275
|
|
10080
10276
|
handleWindowResize: true,
|
10081
|
-
windowResizeDelay:
|
10277
|
+
windowResizeDelay: 100, // milliseconds before an updateSize happens
|
10082
10278
|
|
10083
10279
|
longPressDelay: 1000
|
10084
10280
|
|
10085
10281
|
};
|
10086
10282
|
|
10087
10283
|
|
10088
|
-
Calendar.englishDefaults = { // used by
|
10284
|
+
Calendar.englishDefaults = { // used by locale.js
|
10089
10285
|
dayPopoverFormat: 'dddd, MMMM D'
|
10090
10286
|
};
|
10091
10287
|
|
@@ -10112,19 +10308,18 @@ Calendar.rtlDefaults = { // right-to-left defaults
|
|
10112
10308
|
|
10113
10309
|
;;
|
10114
10310
|
|
10115
|
-
var
|
10311
|
+
var localeOptionHash = FC.locales = {}; // initialize and expose
|
10116
10312
|
|
10117
10313
|
|
10118
|
-
// TODO: document the structure and ordering of a FullCalendar
|
10119
|
-
// TODO: rename everything "lang" to "locale", like what the moment project did
|
10314
|
+
// TODO: document the structure and ordering of a FullCalendar locale file
|
10120
10315
|
|
10121
10316
|
|
10122
10317
|
// Initialize jQuery UI datepicker translations while using some of the translations
|
10123
|
-
// Will set this as the default
|
10124
|
-
FC.
|
10318
|
+
// Will set this as the default locales for datepicker.
|
10319
|
+
FC.datepickerLocale = function(localeCode, dpLocaleCode, dpOptions) {
|
10125
10320
|
|
10126
|
-
// get the FullCalendar internal option hash for this
|
10127
|
-
var fcOptions =
|
10321
|
+
// get the FullCalendar internal option hash for this locale. create if necessary
|
10322
|
+
var fcOptions = localeOptionHash[localeCode] || (localeOptionHash[localeCode] = {});
|
10128
10323
|
|
10129
10324
|
// transfer some simple options from datepicker to fc
|
10130
10325
|
fcOptions.isRTL = dpOptions.isRTL;
|
@@ -10138,15 +10333,15 @@ FC.datepickerLang = function(langCode, dpLangCode, dpOptions) {
|
|
10138
10333
|
// is jQuery UI Datepicker is on the page?
|
10139
10334
|
if ($.datepicker) {
|
10140
10335
|
|
10141
|
-
// Register the
|
10142
|
-
// FullCalendar and MomentJS use
|
10143
|
-
// does it like "pt-BR" or if it doesn't have the
|
10144
|
-
// Make an alias so the
|
10145
|
-
$.datepicker.regional[
|
10146
|
-
$.datepicker.regional[
|
10336
|
+
// Register the locale data.
|
10337
|
+
// FullCalendar and MomentJS use locale codes like "pt-br" but Datepicker
|
10338
|
+
// does it like "pt-BR" or if it doesn't have the locale, maybe just "pt".
|
10339
|
+
// Make an alias so the locale can be referenced either way.
|
10340
|
+
$.datepicker.regional[dpLocaleCode] =
|
10341
|
+
$.datepicker.regional[localeCode] = // alias
|
10147
10342
|
dpOptions;
|
10148
10343
|
|
10149
|
-
// Alias 'en' to the default
|
10344
|
+
// Alias 'en' to the default locale data. Do this every time.
|
10150
10345
|
$.datepicker.regional.en = $.datepicker.regional[''];
|
10151
10346
|
|
10152
10347
|
// Set as Datepicker's global defaults.
|
@@ -10155,35 +10350,35 @@ FC.datepickerLang = function(langCode, dpLangCode, dpOptions) {
|
|
10155
10350
|
};
|
10156
10351
|
|
10157
10352
|
|
10158
|
-
// Sets FullCalendar-specific translations. Will set the
|
10159
|
-
FC.
|
10353
|
+
// Sets FullCalendar-specific translations. Will set the locales as the global default.
|
10354
|
+
FC.locale = function(localeCode, newFcOptions) {
|
10160
10355
|
var fcOptions;
|
10161
10356
|
var momOptions;
|
10162
10357
|
|
10163
|
-
// get the FullCalendar internal option hash for this
|
10164
|
-
fcOptions =
|
10358
|
+
// get the FullCalendar internal option hash for this locale. create if necessary
|
10359
|
+
fcOptions = localeOptionHash[localeCode] || (localeOptionHash[localeCode] = {});
|
10165
10360
|
|
10166
|
-
// provided new options for this
|
10361
|
+
// provided new options for this locales? merge them in
|
10167
10362
|
if (newFcOptions) {
|
10168
|
-
fcOptions =
|
10363
|
+
fcOptions = localeOptionHash[localeCode] = mergeOptions([ fcOptions, newFcOptions ]);
|
10169
10364
|
}
|
10170
10365
|
|
10171
|
-
// compute
|
10366
|
+
// compute locale options that weren't defined.
|
10172
10367
|
// always do this. newFcOptions can be undefined when initializing from i18n file,
|
10173
10368
|
// so no way to tell if this is an initialization or a default-setting.
|
10174
|
-
momOptions = getMomentLocaleData(
|
10369
|
+
momOptions = getMomentLocaleData(localeCode); // will fall back to en
|
10175
10370
|
$.each(momComputableOptions, function(name, func) {
|
10176
10371
|
if (fcOptions[name] == null) {
|
10177
10372
|
fcOptions[name] = func(momOptions, fcOptions);
|
10178
10373
|
}
|
10179
10374
|
});
|
10180
10375
|
|
10181
|
-
// set it as the default
|
10182
|
-
Calendar.defaults.
|
10376
|
+
// set it as the default locale for FullCalendar
|
10377
|
+
Calendar.defaults.locale = localeCode;
|
10183
10378
|
};
|
10184
10379
|
|
10185
10380
|
|
10186
|
-
// NOTE: can't guarantee any of these computations will run because not every
|
10381
|
+
// NOTE: can't guarantee any of these computations will run because not every locale has datepicker
|
10187
10382
|
// configs, so make sure there are English fallbacks for these in the defaults file.
|
10188
10383
|
var dpComputableOptions = {
|
10189
10384
|
|
@@ -10233,7 +10428,7 @@ var momComputableOptions = {
|
|
10233
10428
|
smallTimeFormat: function(momOptions) {
|
10234
10429
|
return momOptions.longDateFormat('LT')
|
10235
10430
|
.replace(':mm', '(:mm)')
|
10236
|
-
.replace(/(\Wmm)$/, '($1)') // like above, but for foreign
|
10431
|
+
.replace(/(\Wmm)$/, '($1)') // like above, but for foreign locales
|
10237
10432
|
.replace(/\s*a$/i, 'a'); // convert AM/PM/am/pm to lowercase. remove any spaces beforehand
|
10238
10433
|
},
|
10239
10434
|
|
@@ -10241,7 +10436,7 @@ var momComputableOptions = {
|
|
10241
10436
|
extraSmallTimeFormat: function(momOptions) {
|
10242
10437
|
return momOptions.longDateFormat('LT')
|
10243
10438
|
.replace(':mm', '(:mm)')
|
10244
|
-
.replace(/(\Wmm)$/, '($1)') // like above, but for foreign
|
10439
|
+
.replace(/(\Wmm)$/, '($1)') // like above, but for foreign locales
|
10245
10440
|
.replace(/\s*a$/i, 't'); // convert to AM/PM/am/pm to lowercase one-letter. remove any spaces beforehand
|
10246
10441
|
},
|
10247
10442
|
|
@@ -10249,7 +10444,7 @@ var momComputableOptions = {
|
|
10249
10444
|
hourFormat: function(momOptions) {
|
10250
10445
|
return momOptions.longDateFormat('LT')
|
10251
10446
|
.replace(':mm', '')
|
10252
|
-
.replace(/(\Wmm)$/, '') // like above, but for foreign
|
10447
|
+
.replace(/(\Wmm)$/, '') // like above, but for foreign locales
|
10253
10448
|
.replace(/\s*a$/i, 'a'); // convert AM/PM/am/pm to lowercase. remove any spaces beforehand
|
10254
10449
|
},
|
10255
10450
|
|
@@ -10263,7 +10458,7 @@ var momComputableOptions = {
|
|
10263
10458
|
|
10264
10459
|
|
10265
10460
|
// options that should be computed off live calendar options (considers override options)
|
10266
|
-
// TODO: best place for this? related to
|
10461
|
+
// TODO: best place for this? related to locale?
|
10267
10462
|
// TODO: flipping text based on isRTL is a bad idea because the CSS `direction` might want to handle it
|
10268
10463
|
var instanceComputableOptions = {
|
10269
10464
|
|
@@ -10300,17 +10495,14 @@ function populateInstanceComputableOptions(options) {
|
|
10300
10495
|
|
10301
10496
|
|
10302
10497
|
// Returns moment's internal locale data. If doesn't exist, returns English.
|
10303
|
-
|
10304
|
-
|
10305
|
-
var func = moment.localeData || moment.langData;
|
10306
|
-
return func.call(moment, langCode) ||
|
10307
|
-
func.call(moment, 'en'); // the newer localData could return null, so fall back to en
|
10498
|
+
function getMomentLocaleData(localeCode) {
|
10499
|
+
return moment.localeData(localeCode) || moment.localeData('en');
|
10308
10500
|
}
|
10309
10501
|
|
10310
10502
|
|
10311
10503
|
// Initialize English by forcing computation of moment-derived options.
|
10312
10504
|
// Also, sets it as the default.
|
10313
|
-
FC.
|
10505
|
+
FC.locale('en', Calendar.englishDefaults);
|
10314
10506
|
|
10315
10507
|
;;
|
10316
10508
|
|
@@ -10318,7 +10510,7 @@ FC.lang('en', Calendar.englishDefaults);
|
|
10318
10510
|
----------------------------------------------------------------------------------------------------------------------*/
|
10319
10511
|
// TODO: rename all header-related things to "toolbar"
|
10320
10512
|
|
10321
|
-
function Header(calendar
|
10513
|
+
function Header(calendar) {
|
10322
10514
|
var t = this;
|
10323
10515
|
|
10324
10516
|
// exports
|
@@ -10330,38 +10522,50 @@ function Header(calendar, options) {
|
|
10330
10522
|
t.disableButton = disableButton;
|
10331
10523
|
t.enableButton = enableButton;
|
10332
10524
|
t.getViewsWithButtons = getViewsWithButtons;
|
10525
|
+
t.el = null; // mirrors local `el`
|
10333
10526
|
|
10334
10527
|
// locals
|
10335
|
-
var el
|
10528
|
+
var el;
|
10336
10529
|
var viewsWithButtons = [];
|
10337
10530
|
var tm;
|
10338
10531
|
|
10339
10532
|
|
10533
|
+
// can be called repeatedly and will rerender
|
10340
10534
|
function render() {
|
10535
|
+
var options = calendar.options;
|
10341
10536
|
var sections = options.header;
|
10342
10537
|
|
10343
10538
|
tm = options.theme ? 'ui' : 'fc';
|
10344
10539
|
|
10345
10540
|
if (sections) {
|
10346
|
-
|
10347
|
-
.
|
10541
|
+
if (!el) {
|
10542
|
+
el = this.el = $("<div class='fc-toolbar'/>");
|
10543
|
+
}
|
10544
|
+
else {
|
10545
|
+
el.empty();
|
10546
|
+
}
|
10547
|
+
el.append(renderSection('left'))
|
10348
10548
|
.append(renderSection('right'))
|
10349
10549
|
.append(renderSection('center'))
|
10350
10550
|
.append('<div class="fc-clear"/>');
|
10351
|
-
|
10352
|
-
|
10551
|
+
}
|
10552
|
+
else {
|
10553
|
+
removeElement();
|
10353
10554
|
}
|
10354
10555
|
}
|
10355
10556
|
|
10356
10557
|
|
10357
10558
|
function removeElement() {
|
10358
|
-
el
|
10359
|
-
|
10559
|
+
if (el) {
|
10560
|
+
el.remove();
|
10561
|
+
el = t.el = null;
|
10562
|
+
}
|
10360
10563
|
}
|
10361
10564
|
|
10362
10565
|
|
10363
10566
|
function renderSection(position) {
|
10364
10567
|
var sectionEl = $('<div class="fc-' + position + '"/>');
|
10568
|
+
var options = calendar.options;
|
10365
10569
|
var buttonStr = options.header[position];
|
10366
10570
|
|
10367
10571
|
if (buttonStr) {
|
@@ -10387,7 +10591,7 @@ function Header(calendar, options) {
|
|
10387
10591
|
isOnlyButtons = false;
|
10388
10592
|
}
|
10389
10593
|
else {
|
10390
|
-
if ((customButtonProps = (
|
10594
|
+
if ((customButtonProps = (options.customButtons || {})[buttonName])) {
|
10391
10595
|
buttonClick = function(ev) {
|
10392
10596
|
if (customButtonProps.click) {
|
10393
10597
|
customButtonProps.click.call(button[0], ev);
|
@@ -10523,33 +10727,43 @@ function Header(calendar, options) {
|
|
10523
10727
|
|
10524
10728
|
|
10525
10729
|
function updateTitle(text) {
|
10526
|
-
el
|
10730
|
+
if (el) {
|
10731
|
+
el.find('h2').text(text);
|
10732
|
+
}
|
10527
10733
|
}
|
10528
10734
|
|
10529
10735
|
|
10530
10736
|
function activateButton(buttonName) {
|
10531
|
-
el
|
10532
|
-
.
|
10737
|
+
if (el) {
|
10738
|
+
el.find('.fc-' + buttonName + '-button')
|
10739
|
+
.addClass(tm + '-state-active');
|
10740
|
+
}
|
10533
10741
|
}
|
10534
10742
|
|
10535
10743
|
|
10536
10744
|
function deactivateButton(buttonName) {
|
10537
|
-
el
|
10538
|
-
.
|
10745
|
+
if (el) {
|
10746
|
+
el.find('.fc-' + buttonName + '-button')
|
10747
|
+
.removeClass(tm + '-state-active');
|
10748
|
+
}
|
10539
10749
|
}
|
10540
10750
|
|
10541
10751
|
|
10542
10752
|
function disableButton(buttonName) {
|
10543
|
-
el
|
10544
|
-
.
|
10545
|
-
|
10753
|
+
if (el) {
|
10754
|
+
el.find('.fc-' + buttonName + '-button')
|
10755
|
+
.prop('disabled', true)
|
10756
|
+
.addClass(tm + '-state-disabled');
|
10757
|
+
}
|
10546
10758
|
}
|
10547
10759
|
|
10548
10760
|
|
10549
10761
|
function enableButton(buttonName) {
|
10550
|
-
el
|
10551
|
-
.
|
10552
|
-
|
10762
|
+
if (el) {
|
10763
|
+
el.find('.fc-' + buttonName + '-button')
|
10764
|
+
.prop('disabled', false)
|
10765
|
+
.removeClass(tm + '-state-disabled');
|
10766
|
+
}
|
10553
10767
|
}
|
10554
10768
|
|
10555
10769
|
|
@@ -10572,7 +10786,7 @@ var ajaxDefaults = {
|
|
10572
10786
|
var eventGUID = 1;
|
10573
10787
|
|
10574
10788
|
|
10575
|
-
function EventManager(
|
10789
|
+
function EventManager() { // assumed to be a calendar
|
10576
10790
|
var t = this;
|
10577
10791
|
|
10578
10792
|
|
@@ -10609,7 +10823,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
10609
10823
|
|
10610
10824
|
|
10611
10825
|
$.each(
|
10612
|
-
(options.events ? [ options.events ] : []).concat(options.eventSources || []),
|
10826
|
+
(t.options.events ? [ t.options.events ] : []).concat(t.options.eventSources || []),
|
10613
10827
|
function(i, sourceInput) {
|
10614
10828
|
var source = buildEventSource(sourceInput);
|
10615
10829
|
if (source) {
|
@@ -10743,7 +10957,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
10743
10957
|
source,
|
10744
10958
|
rangeStart.clone(),
|
10745
10959
|
rangeEnd.clone(),
|
10746
|
-
options.timezone,
|
10960
|
+
t.options.timezone,
|
10747
10961
|
callback
|
10748
10962
|
);
|
10749
10963
|
|
@@ -10766,7 +10980,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
10766
10980
|
t, // this, the Calendar object
|
10767
10981
|
rangeStart.clone(),
|
10768
10982
|
rangeEnd.clone(),
|
10769
|
-
options.timezone,
|
10983
|
+
t.options.timezone,
|
10770
10984
|
function(events) {
|
10771
10985
|
callback(events);
|
10772
10986
|
t.popLoading();
|
@@ -10801,9 +11015,9 @@ function EventManager(options) { // assumed to be a calendar
|
|
10801
11015
|
// and not affect the passed-in object.
|
10802
11016
|
var data = $.extend({}, customData || {});
|
10803
11017
|
|
10804
|
-
var startParam = firstDefined(source.startParam, options.startParam);
|
10805
|
-
var endParam = firstDefined(source.endParam, options.endParam);
|
10806
|
-
var timezoneParam = firstDefined(source.timezoneParam, options.timezoneParam);
|
11018
|
+
var startParam = firstDefined(source.startParam, t.options.startParam);
|
11019
|
+
var endParam = firstDefined(source.endParam, t.options.endParam);
|
11020
|
+
var timezoneParam = firstDefined(source.timezoneParam, t.options.timezoneParam);
|
10807
11021
|
|
10808
11022
|
if (startParam) {
|
10809
11023
|
data[startParam] = rangeStart.format();
|
@@ -10811,8 +11025,8 @@ function EventManager(options) { // assumed to be a calendar
|
|
10811
11025
|
if (endParam) {
|
10812
11026
|
data[endParam] = rangeEnd.format();
|
10813
11027
|
}
|
10814
|
-
if (options.timezone && options.timezone != 'local') {
|
10815
|
-
data[timezoneParam] = options.timezone;
|
11028
|
+
if (t.options.timezone && t.options.timezone != 'local') {
|
11029
|
+
data[timezoneParam] = t.options.timezone;
|
10816
11030
|
}
|
10817
11031
|
|
10818
11032
|
t.pushLoading();
|
@@ -11144,7 +11358,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
11144
11358
|
|
11145
11359
|
reportEvents(cache);
|
11146
11360
|
}
|
11147
|
-
|
11361
|
+
|
11148
11362
|
|
11149
11363
|
function clientEvents(filter) {
|
11150
11364
|
if ($.isFunction(filter)) {
|
@@ -11158,7 +11372,33 @@ function EventManager(options) { // assumed to be a calendar
|
|
11158
11372
|
}
|
11159
11373
|
return cache; // else, return all
|
11160
11374
|
}
|
11161
|
-
|
11375
|
+
|
11376
|
+
|
11377
|
+
// Makes sure all array event sources have their internal event objects
|
11378
|
+
// converted over to the Calendar's current timezone.
|
11379
|
+
t.rezoneArrayEventSources = function() {
|
11380
|
+
var i;
|
11381
|
+
var events;
|
11382
|
+
var j;
|
11383
|
+
|
11384
|
+
for (i = 0; i < sources.length; i++) {
|
11385
|
+
events = sources[i].events;
|
11386
|
+
if ($.isArray(events)) {
|
11387
|
+
|
11388
|
+
for (j = 0; j < events.length; j++) {
|
11389
|
+
rezoneEventDates(events[j]);
|
11390
|
+
}
|
11391
|
+
}
|
11392
|
+
}
|
11393
|
+
};
|
11394
|
+
|
11395
|
+
function rezoneEventDates(event) {
|
11396
|
+
event.start = t.moment(event.start);
|
11397
|
+
if (event.end) {
|
11398
|
+
event.end = t.moment(event.end);
|
11399
|
+
}
|
11400
|
+
backupEventDates(event);
|
11401
|
+
}
|
11162
11402
|
|
11163
11403
|
|
11164
11404
|
/* Event Normalization
|
@@ -11174,8 +11414,8 @@ function EventManager(options) { // assumed to be a calendar
|
|
11174
11414
|
var start, end;
|
11175
11415
|
var allDay;
|
11176
11416
|
|
11177
|
-
if (options.eventDataTransform) {
|
11178
|
-
input = options.eventDataTransform(input);
|
11417
|
+
if (t.options.eventDataTransform) {
|
11418
|
+
input = t.options.eventDataTransform(input);
|
11179
11419
|
}
|
11180
11420
|
if (source && source.eventDataTransform) {
|
11181
11421
|
input = source.eventDataTransform(input);
|
@@ -11241,7 +11481,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
11241
11481
|
if (allDay === undefined) { // still undefined? fallback to default
|
11242
11482
|
allDay = firstDefined(
|
11243
11483
|
source ? source.allDayDefault : undefined,
|
11244
|
-
options.allDayDefault
|
11484
|
+
t.options.allDayDefault
|
11245
11485
|
);
|
11246
11486
|
// still undefined? normalizeEventDates will calculate it
|
11247
11487
|
}
|
@@ -11253,6 +11493,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
11253
11493
|
|
11254
11494
|
return out;
|
11255
11495
|
}
|
11496
|
+
t.buildEventFromInput = buildEventFromInput;
|
11256
11497
|
|
11257
11498
|
|
11258
11499
|
// Normalizes and assigns the given dates to the given partially-formed event object.
|
@@ -11277,7 +11518,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
11277
11518
|
}
|
11278
11519
|
|
11279
11520
|
if (!eventProps.end) {
|
11280
|
-
if (options.forceEventDuration) {
|
11521
|
+
if (t.options.forceEventDuration) {
|
11281
11522
|
eventProps.end = t.getDefaultEventEnd(eventProps.allDay, eventProps.start);
|
11282
11523
|
}
|
11283
11524
|
else {
|
@@ -11376,6 +11617,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
11376
11617
|
|
11377
11618
|
return events;
|
11378
11619
|
}
|
11620
|
+
t.expandEvent = expandEvent;
|
11379
11621
|
|
11380
11622
|
|
11381
11623
|
|
@@ -11569,125 +11811,134 @@ function EventManager(options) { // assumed to be a calendar
|
|
11569
11811
|
}
|
11570
11812
|
|
11571
11813
|
|
11572
|
-
|
11573
|
-
|
11574
|
-
|
11575
|
-
t.getBusinessHoursEvents = getBusinessHoursEvents;
|
11576
|
-
|
11577
|
-
|
11578
|
-
// Returns an array of events as to when the business hours occur in the given view.
|
11579
|
-
// Abuse of our event system :(
|
11580
|
-
function getBusinessHoursEvents(wholeDay) {
|
11581
|
-
var optionVal = options.businessHours;
|
11582
|
-
var defaultVal = {
|
11583
|
-
className: 'fc-nonbusiness',
|
11584
|
-
start: '09:00',
|
11585
|
-
end: '17:00',
|
11586
|
-
dow: [ 1, 2, 3, 4, 5 ], // monday - friday
|
11587
|
-
rendering: 'inverse-background'
|
11588
|
-
};
|
11589
|
-
var view = t.getView();
|
11590
|
-
var eventInput;
|
11814
|
+
t.getEventCache = function() {
|
11815
|
+
return cache;
|
11816
|
+
};
|
11591
11817
|
|
11592
|
-
|
11593
|
-
eventInput = $.extend(
|
11594
|
-
{}, // copy to a new object in either case
|
11595
|
-
defaultVal,
|
11596
|
-
typeof optionVal === 'object' ? optionVal : {} // override the defaults
|
11597
|
-
);
|
11598
|
-
}
|
11818
|
+
}
|
11599
11819
|
|
11600
|
-
if (eventInput) {
|
11601
11820
|
|
11602
|
-
|
11603
|
-
|
11604
|
-
|
11605
|
-
|
11606
|
-
}
|
11821
|
+
// hook for external libs to manipulate event properties upon creation.
|
11822
|
+
// should manipulate the event in-place.
|
11823
|
+
Calendar.prototype.normalizeEvent = function(event) {
|
11824
|
+
};
|
11607
11825
|
|
11608
|
-
return expandEvent(
|
11609
|
-
buildEventFromInput(eventInput),
|
11610
|
-
view.start,
|
11611
|
-
view.end
|
11612
|
-
);
|
11613
|
-
}
|
11614
11826
|
|
11615
|
-
|
11616
|
-
|
11827
|
+
// Does the given span (start, end, and other location information)
|
11828
|
+
// fully contain the other?
|
11829
|
+
Calendar.prototype.spanContainsSpan = function(outerSpan, innerSpan) {
|
11830
|
+
var eventStart = outerSpan.start.clone().stripZone();
|
11831
|
+
var eventEnd = this.getEventEnd(outerSpan).stripZone();
|
11617
11832
|
|
11833
|
+
return innerSpan.start >= eventStart && innerSpan.end <= eventEnd;
|
11834
|
+
};
|
11618
11835
|
|
11619
|
-
/* Overlapping / Constraining
|
11620
|
-
-----------------------------------------------------------------------------------------*/
|
11621
|
-
|
11622
|
-
t.isEventSpanAllowed = isEventSpanAllowed;
|
11623
|
-
t.isExternalSpanAllowed = isExternalSpanAllowed;
|
11624
|
-
t.isSelectionSpanAllowed = isSelectionSpanAllowed;
|
11625
11836
|
|
11837
|
+
// Returns a list of events that the given event should be compared against when being considered for a move to
|
11838
|
+
// the specified span. Attached to the Calendar's prototype because EventManager is a mixin for a Calendar.
|
11839
|
+
Calendar.prototype.getPeerEvents = function(span, event) {
|
11840
|
+
var cache = this.getEventCache();
|
11841
|
+
var peerEvents = [];
|
11842
|
+
var i, otherEvent;
|
11626
11843
|
|
11627
|
-
|
11628
|
-
|
11629
|
-
|
11630
|
-
|
11631
|
-
event.
|
11632
|
-
|
11633
|
-
|
11634
|
-
|
11635
|
-
var overlap = firstDefined(
|
11636
|
-
event.overlap,
|
11637
|
-
source.overlap,
|
11638
|
-
options.eventOverlap
|
11639
|
-
);
|
11640
|
-
return isSpanAllowed(span, constraint, overlap, event);
|
11844
|
+
for (i = 0; i < cache.length; i++) {
|
11845
|
+
otherEvent = cache[i];
|
11846
|
+
if (
|
11847
|
+
!event ||
|
11848
|
+
event._id !== otherEvent._id // don't compare the event to itself or other related [repeating] events
|
11849
|
+
) {
|
11850
|
+
peerEvents.push(otherEvent);
|
11851
|
+
}
|
11641
11852
|
}
|
11642
11853
|
|
11854
|
+
return peerEvents;
|
11855
|
+
};
|
11643
11856
|
|
11644
|
-
// Determines if an external event can be relocated to the given span (unzoned start/end with other misc data)
|
11645
|
-
function isExternalSpanAllowed(eventSpan, eventLocation, eventProps) {
|
11646
|
-
var eventInput;
|
11647
|
-
var event;
|
11648
11857
|
|
11649
|
-
|
11650
|
-
|
11651
|
-
|
11652
|
-
|
11653
|
-
|
11858
|
+
// updates the "backup" properties, which are preserved in order to compute diffs later on.
|
11859
|
+
function backupEventDates(event) {
|
11860
|
+
event._allDay = event.allDay;
|
11861
|
+
event._start = event.start.clone();
|
11862
|
+
event._end = event.end ? event.end.clone() : null;
|
11863
|
+
}
|
11654
11864
|
|
11655
|
-
if (event) {
|
11656
|
-
return isEventSpanAllowed(eventSpan, event);
|
11657
|
-
}
|
11658
|
-
else { // treat it as a selection
|
11659
11865
|
|
11660
|
-
|
11661
|
-
|
11866
|
+
/* Overlapping / Constraining
|
11867
|
+
-----------------------------------------------------------------------------------------*/
|
11868
|
+
|
11869
|
+
|
11870
|
+
// Determines if the given event can be relocated to the given span (unzoned start/end with other misc data)
|
11871
|
+
Calendar.prototype.isEventSpanAllowed = function(span, event) {
|
11872
|
+
var source = event.source || {};
|
11873
|
+
|
11874
|
+
var constraint = firstDefined(
|
11875
|
+
event.constraint,
|
11876
|
+
source.constraint,
|
11877
|
+
this.options.eventConstraint
|
11878
|
+
);
|
11879
|
+
|
11880
|
+
var overlap = firstDefined(
|
11881
|
+
event.overlap,
|
11882
|
+
source.overlap,
|
11883
|
+
this.options.eventOverlap
|
11884
|
+
);
|
11885
|
+
|
11886
|
+
return this.isSpanAllowed(span, constraint, overlap, event) &&
|
11887
|
+
(!this.options.eventAllow || this.options.eventAllow(span, event) !== false);
|
11888
|
+
};
|
11889
|
+
|
11890
|
+
|
11891
|
+
// Determines if an external event can be relocated to the given span (unzoned start/end with other misc data)
|
11892
|
+
Calendar.prototype.isExternalSpanAllowed = function(eventSpan, eventLocation, eventProps) {
|
11893
|
+
var eventInput;
|
11894
|
+
var event;
|
11895
|
+
|
11896
|
+
// note: very similar logic is in View's reportExternalDrop
|
11897
|
+
if (eventProps) {
|
11898
|
+
eventInput = $.extend({}, eventProps, eventLocation);
|
11899
|
+
event = this.expandEvent(
|
11900
|
+
this.buildEventFromInput(eventInput)
|
11901
|
+
)[0];
|
11662
11902
|
}
|
11663
11903
|
|
11904
|
+
if (event) {
|
11905
|
+
return this.isEventSpanAllowed(eventSpan, event);
|
11906
|
+
}
|
11907
|
+
else { // treat it as a selection
|
11664
11908
|
|
11665
|
-
|
11666
|
-
function isSelectionSpanAllowed(span) {
|
11667
|
-
return isSpanAllowed(span, options.selectConstraint, options.selectOverlap);
|
11909
|
+
return this.isSelectionSpanAllowed(eventSpan);
|
11668
11910
|
}
|
11911
|
+
};
|
11912
|
+
|
11669
11913
|
|
11914
|
+
// Determines the given span (unzoned start/end with other misc data) can be selected.
|
11915
|
+
Calendar.prototype.isSelectionSpanAllowed = function(span) {
|
11916
|
+
return this.isSpanAllowed(span, this.options.selectConstraint, this.options.selectOverlap) &&
|
11917
|
+
(!this.options.selectAllow || this.options.selectAllow(span) !== false);
|
11918
|
+
};
|
11670
11919
|
|
11671
|
-
// Returns true if the given span (caused by an event drop/resize or a selection) is allowed to exist
|
11672
|
-
// according to the constraint/overlap settings.
|
11673
|
-
// `event` is not required if checking a selection.
|
11674
|
-
function isSpanAllowed(span, constraint, overlap, event) {
|
11675
|
-
var constraintEvents;
|
11676
|
-
var anyContainment;
|
11677
|
-
var peerEvents;
|
11678
|
-
var i, peerEvent;
|
11679
|
-
var peerOverlap;
|
11680
11920
|
|
11681
|
-
|
11682
|
-
|
11921
|
+
// Returns true if the given span (caused by an event drop/resize or a selection) is allowed to exist
|
11922
|
+
// according to the constraint/overlap settings.
|
11923
|
+
// `event` is not required if checking a selection.
|
11924
|
+
Calendar.prototype.isSpanAllowed = function(span, constraint, overlap, event) {
|
11925
|
+
var constraintEvents;
|
11926
|
+
var anyContainment;
|
11927
|
+
var peerEvents;
|
11928
|
+
var i, peerEvent;
|
11929
|
+
var peerOverlap;
|
11683
11930
|
|
11684
|
-
|
11685
|
-
|
11686
|
-
|
11931
|
+
// the range must be fully contained by at least one of produced constraint events
|
11932
|
+
if (constraint != null) {
|
11933
|
+
|
11934
|
+
// not treated as an event! intermediate data structure
|
11935
|
+
// TODO: use ranges in the future
|
11936
|
+
constraintEvents = this.constraintToEvents(constraint);
|
11937
|
+
if (constraintEvents) { // not invalid
|
11687
11938
|
|
11688
11939
|
anyContainment = false;
|
11689
11940
|
for (i = 0; i < constraintEvents.length; i++) {
|
11690
|
-
if (
|
11941
|
+
if (this.spanContainsSpan(constraintEvents[i], span)) {
|
11691
11942
|
anyContainment = true;
|
11692
11943
|
break;
|
11693
11944
|
}
|
@@ -11697,126 +11948,150 @@ function EventManager(options) { // assumed to be a calendar
|
|
11697
11948
|
return false;
|
11698
11949
|
}
|
11699
11950
|
}
|
11951
|
+
}
|
11700
11952
|
|
11701
|
-
|
11953
|
+
peerEvents = this.getPeerEvents(span, event);
|
11702
11954
|
|
11703
|
-
|
11704
|
-
|
11955
|
+
for (i = 0; i < peerEvents.length; i++) {
|
11956
|
+
peerEvent = peerEvents[i];
|
11705
11957
|
|
11706
|
-
|
11707
|
-
|
11958
|
+
// there needs to be an actual intersection before disallowing anything
|
11959
|
+
if (this.eventIntersectsRange(peerEvent, span)) {
|
11960
|
+
|
11961
|
+
// evaluate overlap for the given range and short-circuit if necessary
|
11962
|
+
if (overlap === false) {
|
11963
|
+
return false;
|
11964
|
+
}
|
11965
|
+
// if the event's overlap is a test function, pass the peer event in question as the first param
|
11966
|
+
else if (typeof overlap === 'function' && !overlap(peerEvent, event)) {
|
11967
|
+
return false;
|
11968
|
+
}
|
11708
11969
|
|
11709
|
-
|
11710
|
-
|
11970
|
+
// if we are computing if the given range is allowable for an event, consider the other event's
|
11971
|
+
// EventObject-specific or Source-specific `overlap` property
|
11972
|
+
if (event) {
|
11973
|
+
peerOverlap = firstDefined(
|
11974
|
+
peerEvent.overlap,
|
11975
|
+
(peerEvent.source || {}).overlap
|
11976
|
+
// we already considered the global `eventOverlap`
|
11977
|
+
);
|
11978
|
+
if (peerOverlap === false) {
|
11711
11979
|
return false;
|
11712
11980
|
}
|
11713
|
-
// if the event's overlap is a test function, pass the
|
11714
|
-
|
11981
|
+
// if the peer event's overlap is a test function, pass the subject event as the first param
|
11982
|
+
if (typeof peerOverlap === 'function' && !peerOverlap(event, peerEvent)) {
|
11715
11983
|
return false;
|
11716
11984
|
}
|
11717
|
-
|
11718
|
-
// if we are computing if the given range is allowable for an event, consider the other event's
|
11719
|
-
// EventObject-specific or Source-specific `overlap` property
|
11720
|
-
if (event) {
|
11721
|
-
peerOverlap = firstDefined(
|
11722
|
-
peerEvent.overlap,
|
11723
|
-
(peerEvent.source || {}).overlap
|
11724
|
-
// we already considered the global `eventOverlap`
|
11725
|
-
);
|
11726
|
-
if (peerOverlap === false) {
|
11727
|
-
return false;
|
11728
|
-
}
|
11729
|
-
// if the peer event's overlap is a test function, pass the subject event as the first param
|
11730
|
-
if (typeof peerOverlap === 'function' && !peerOverlap(event, peerEvent)) {
|
11731
|
-
return false;
|
11732
|
-
}
|
11733
|
-
}
|
11734
11985
|
}
|
11735
11986
|
}
|
11736
|
-
|
11737
|
-
return true;
|
11738
11987
|
}
|
11739
11988
|
|
11989
|
+
return true;
|
11990
|
+
};
|
11740
11991
|
|
11741
|
-
// Given an event input from the API, produces an array of event objects. Possible event inputs:
|
11742
|
-
// 'businessHours'
|
11743
|
-
// An event ID (number or string)
|
11744
|
-
// An object with specific start/end dates or a recurring event (like what businessHours accepts)
|
11745
|
-
function constraintToEvents(constraintInput) {
|
11746
11992
|
|
11747
|
-
|
11748
|
-
|
11749
|
-
|
11993
|
+
// Given an event input from the API, produces an array of event objects. Possible event inputs:
|
11994
|
+
// 'businessHours'
|
11995
|
+
// An event ID (number or string)
|
11996
|
+
// An object with specific start/end dates or a recurring event (like what businessHours accepts)
|
11997
|
+
Calendar.prototype.constraintToEvents = function(constraintInput) {
|
11750
11998
|
|
11751
|
-
|
11752
|
-
|
11753
|
-
|
11999
|
+
if (constraintInput === 'businessHours') {
|
12000
|
+
return this.getCurrentBusinessHourEvents();
|
12001
|
+
}
|
11754
12002
|
|
11755
|
-
|
12003
|
+
if (typeof constraintInput === 'object') {
|
12004
|
+
if (constraintInput.start != null) { // needs to be event-like input
|
12005
|
+
return this.expandEvent(this.buildEventFromInput(constraintInput));
|
12006
|
+
}
|
12007
|
+
else {
|
12008
|
+
return null; // invalid
|
12009
|
+
}
|
11756
12010
|
}
|
11757
12011
|
|
12012
|
+
return this.clientEvents(constraintInput); // probably an ID
|
12013
|
+
};
|
11758
12014
|
|
11759
|
-
// Does the event's date range fully contain the given range?
|
11760
|
-
// start/end already assumed to have stripped zones :(
|
11761
|
-
function eventContainsRange(event, range) {
|
11762
|
-
var eventStart = event.start.clone().stripZone();
|
11763
|
-
var eventEnd = t.getEventEnd(event).stripZone();
|
11764
12015
|
|
11765
|
-
|
11766
|
-
|
12016
|
+
// Does the event's date range intersect with the given range?
|
12017
|
+
// start/end already assumed to have stripped zones :(
|
12018
|
+
Calendar.prototype.eventIntersectsRange = function(event, range) {
|
12019
|
+
var eventStart = event.start.clone().stripZone();
|
12020
|
+
var eventEnd = this.getEventEnd(event).stripZone();
|
11767
12021
|
|
12022
|
+
return range.start < eventEnd && range.end > eventStart;
|
12023
|
+
};
|
11768
12024
|
|
11769
|
-
// Does the event's date range intersect with the given range?
|
11770
|
-
// start/end already assumed to have stripped zones :(
|
11771
|
-
function eventIntersectsRange(event, range) {
|
11772
|
-
var eventStart = event.start.clone().stripZone();
|
11773
|
-
var eventEnd = t.getEventEnd(event).stripZone();
|
11774
12025
|
|
11775
|
-
|
11776
|
-
|
12026
|
+
/* Business Hours
|
12027
|
+
-----------------------------------------------------------------------------------------*/
|
11777
12028
|
|
12029
|
+
var BUSINESS_HOUR_EVENT_DEFAULTS = {
|
12030
|
+
id: '_fcBusinessHours', // will relate events from different calls to expandEvent
|
12031
|
+
start: '09:00',
|
12032
|
+
end: '17:00',
|
12033
|
+
dow: [ 1, 2, 3, 4, 5 ], // monday - friday
|
12034
|
+
rendering: 'inverse-background'
|
12035
|
+
// classNames are defined in businessHoursSegClasses
|
12036
|
+
};
|
11778
12037
|
|
11779
|
-
|
11780
|
-
|
11781
|
-
|
12038
|
+
// Return events objects for business hours within the current view.
|
12039
|
+
// Abuse of our event system :(
|
12040
|
+
Calendar.prototype.getCurrentBusinessHourEvents = function(wholeDay) {
|
12041
|
+
return this.computeBusinessHourEvents(wholeDay, this.options.businessHours);
|
12042
|
+
};
|
11782
12043
|
|
11783
|
-
|
12044
|
+
// Given a raw input value from options, return events objects for business hours within the current view.
|
12045
|
+
Calendar.prototype.computeBusinessHourEvents = function(wholeDay, input) {
|
12046
|
+
if (input === true) {
|
12047
|
+
return this.expandBusinessHourEvents(wholeDay, [ {} ]);
|
12048
|
+
}
|
12049
|
+
else if ($.isPlainObject(input)) {
|
12050
|
+
return this.expandBusinessHourEvents(wholeDay, [ input ]);
|
12051
|
+
}
|
12052
|
+
else if ($.isArray(input)) {
|
12053
|
+
return this.expandBusinessHourEvents(wholeDay, input, true);
|
12054
|
+
}
|
12055
|
+
else {
|
12056
|
+
return [];
|
12057
|
+
}
|
12058
|
+
};
|
11784
12059
|
|
12060
|
+
// inputs expected to be an array of objects.
|
12061
|
+
// if ignoreNoDow is true, will ignore entries that don't specify a day-of-week (dow) key.
|
12062
|
+
Calendar.prototype.expandBusinessHourEvents = function(wholeDay, inputs, ignoreNoDow) {
|
12063
|
+
var view = this.getView();
|
12064
|
+
var events = [];
|
12065
|
+
var i, input;
|
11785
12066
|
|
11786
|
-
|
11787
|
-
|
11788
|
-
Calendar.prototype.normalizeEvent = function(event) {
|
11789
|
-
};
|
12067
|
+
for (i = 0; i < inputs.length; i++) {
|
12068
|
+
input = inputs[i];
|
11790
12069
|
|
12070
|
+
if (ignoreNoDow && !input.dow) {
|
12071
|
+
continue;
|
12072
|
+
}
|
11791
12073
|
|
11792
|
-
//
|
11793
|
-
|
11794
|
-
Calendar.prototype.getPeerEvents = function(span, event) {
|
11795
|
-
var cache = this.getEventCache();
|
11796
|
-
var peerEvents = [];
|
11797
|
-
var i, otherEvent;
|
12074
|
+
// give defaults. will make a copy
|
12075
|
+
input = $.extend({}, BUSINESS_HOUR_EVENT_DEFAULTS, input);
|
11798
12076
|
|
11799
|
-
|
11800
|
-
|
11801
|
-
|
11802
|
-
|
11803
|
-
event._id !== otherEvent._id // don't compare the event to itself or other related [repeating] events
|
11804
|
-
) {
|
11805
|
-
peerEvents.push(otherEvent);
|
12077
|
+
// if a whole-day series is requested, clear the start/end times
|
12078
|
+
if (wholeDay) {
|
12079
|
+
input.start = null;
|
12080
|
+
input.end = null;
|
11806
12081
|
}
|
12082
|
+
|
12083
|
+
events.push.apply(events, // append
|
12084
|
+
this.expandEvent(
|
12085
|
+
this.buildEventFromInput(input),
|
12086
|
+
view.start,
|
12087
|
+
view.end
|
12088
|
+
)
|
12089
|
+
);
|
11807
12090
|
}
|
11808
12091
|
|
11809
|
-
return
|
12092
|
+
return events;
|
11810
12093
|
};
|
11811
12094
|
|
11812
|
-
|
11813
|
-
// updates the "backup" properties, which are preserved in order to compute diffs later on.
|
11814
|
-
function backupEventDates(event) {
|
11815
|
-
event._allDay = event.allDay;
|
11816
|
-
event._start = event.start.clone();
|
11817
|
-
event._end = event.end ? event.end.clone() : null;
|
11818
|
-
}
|
11819
|
-
|
11820
12095
|
;;
|
11821
12096
|
|
11822
12097
|
/* An abstract class for the "basic" views, as well as month view. Renders one or more rows of day cells.
|
@@ -11832,7 +12107,8 @@ var BasicView = FC.BasicView = View.extend({
|
|
11832
12107
|
dayGrid: null, // the main subcomponent that does most of the heavy lifting
|
11833
12108
|
|
11834
12109
|
dayNumbersVisible: false, // display day numbers on each day cell?
|
11835
|
-
|
12110
|
+
colWeekNumbersVisible: false, // display week numbers along the side?
|
12111
|
+
cellWeekNumbersVisible: false, // display week numbers in day cell?
|
11836
12112
|
|
11837
12113
|
weekNumberWidth: null, // width of all the week-number cells running down the side
|
11838
12114
|
|
@@ -11893,8 +12169,18 @@ var BasicView = FC.BasicView = View.extend({
|
|
11893
12169
|
renderDates: function() {
|
11894
12170
|
|
11895
12171
|
this.dayNumbersVisible = this.dayGrid.rowCnt > 1; // TODO: make grid responsible
|
11896
|
-
|
11897
|
-
|
12172
|
+
if (this.opt('weekNumbers')) {
|
12173
|
+
if (this.opt('weekNumbersWithinDays')) {
|
12174
|
+
this.cellWeekNumbersVisible = true;
|
12175
|
+
this.colWeekNumbersVisible = false;
|
12176
|
+
}
|
12177
|
+
else {
|
12178
|
+
this.cellWeekNumbersVisible = false;
|
12179
|
+
this.colWeekNumbersVisible = true;
|
12180
|
+
};
|
12181
|
+
}
|
12182
|
+
this.dayGrid.numbersVisible = this.dayNumbersVisible ||
|
12183
|
+
this.cellWeekNumbersVisible || this.colWeekNumbersVisible;
|
11898
12184
|
|
11899
12185
|
this.el.addClass('fc-basic-view').html(this.renderSkeletonHtml());
|
11900
12186
|
this.renderHead();
|
@@ -11932,6 +12218,11 @@ var BasicView = FC.BasicView = View.extend({
|
|
11932
12218
|
},
|
11933
12219
|
|
11934
12220
|
|
12221
|
+
unrenderBusinessHours: function() {
|
12222
|
+
this.dayGrid.unrenderBusinessHours();
|
12223
|
+
},
|
12224
|
+
|
12225
|
+
|
11935
12226
|
// Builds the HTML skeleton for the view.
|
11936
12227
|
// The day-grid component will render inside of a container defined by this HTML.
|
11937
12228
|
renderSkeletonHtml: function() {
|
@@ -11973,7 +12264,7 @@ var BasicView = FC.BasicView = View.extend({
|
|
11973
12264
|
|
11974
12265
|
// Refreshes the horizontal dimensions of the view
|
11975
12266
|
updateWidth: function() {
|
11976
|
-
if (this.
|
12267
|
+
if (this.colWeekNumbersVisible) {
|
11977
12268
|
// Make sure all week number cells running down the side have the same width.
|
11978
12269
|
// Record the width for cells created later.
|
11979
12270
|
this.weekNumberWidth = matchCellWidths(
|
@@ -12114,9 +12405,8 @@ var BasicView = FC.BasicView = View.extend({
|
|
12114
12405
|
unrenderEvents: function() {
|
12115
12406
|
this.dayGrid.unrenderEvents();
|
12116
12407
|
|
12117
|
-
// we DON'T need to call updateHeight() because
|
12118
|
-
//
|
12119
|
-
// B) in IE8, this causes a flash whenever events are rerendered
|
12408
|
+
// we DON'T need to call updateHeight() because
|
12409
|
+
// a renderEvents() call always happens after this, which will eventually call updateHeight()
|
12120
12410
|
},
|
12121
12411
|
|
12122
12412
|
|
@@ -12161,7 +12451,7 @@ var basicDayGridMethods = {
|
|
12161
12451
|
renderHeadIntroHtml: function() {
|
12162
12452
|
var view = this.view;
|
12163
12453
|
|
12164
|
-
if (view.
|
12454
|
+
if (view.colWeekNumbersVisible) {
|
12165
12455
|
return '' +
|
12166
12456
|
'<th class="fc-week-number ' + view.widgetHeaderClass + '" ' + view.weekNumberStyleAttr() + '>' +
|
12167
12457
|
'<span>' + // needed for matchCellWidths
|
@@ -12177,13 +12467,15 @@ var basicDayGridMethods = {
|
|
12177
12467
|
// Generates the HTML that will go before content-skeleton cells that display the day/week numbers
|
12178
12468
|
renderNumberIntroHtml: function(row) {
|
12179
12469
|
var view = this.view;
|
12470
|
+
var weekStart = this.getCellDate(row, 0);
|
12180
12471
|
|
12181
|
-
if (view.
|
12472
|
+
if (view.colWeekNumbersVisible) {
|
12182
12473
|
return '' +
|
12183
12474
|
'<td class="fc-week-number" ' + view.weekNumberStyleAttr() + '>' +
|
12184
|
-
|
12185
|
-
|
12186
|
-
|
12475
|
+
view.buildGotoAnchorHtml( // aside from link, important for matchCellWidths
|
12476
|
+
{ date: weekStart, type: 'week', forceOff: this.colCnt === 1 },
|
12477
|
+
weekStart.format('w') // inner HTML
|
12478
|
+
) +
|
12187
12479
|
'</td>';
|
12188
12480
|
}
|
12189
12481
|
|
@@ -12195,7 +12487,7 @@ var basicDayGridMethods = {
|
|
12195
12487
|
renderBgIntroHtml: function() {
|
12196
12488
|
var view = this.view;
|
12197
12489
|
|
12198
|
-
if (view.
|
12490
|
+
if (view.colWeekNumbersVisible) {
|
12199
12491
|
return '<td class="fc-week-number ' + view.widgetContentClass + '" ' +
|
12200
12492
|
view.weekNumberStyleAttr() + '></td>';
|
12201
12493
|
}
|
@@ -12209,7 +12501,7 @@ var basicDayGridMethods = {
|
|
12209
12501
|
renderIntroHtml: function() {
|
12210
12502
|
var view = this.view;
|
12211
12503
|
|
12212
|
-
if (view.
|
12504
|
+
if (view.colWeekNumbersVisible) {
|
12213
12505
|
return '<td class="fc-week-number" ' + view.weekNumberStyleAttr() + '></td>';
|
12214
12506
|
}
|
12215
12507
|
|
@@ -12243,8 +12535,6 @@ var MonthView = FC.MonthView = BasicView.extend({
|
|
12243
12535
|
// Overrides the default BasicView behavior to have special multi-week auto-height logic
|
12244
12536
|
setGridHeight: function(height, isAuto) {
|
12245
12537
|
|
12246
|
-
isAuto = isAuto || this.opt('weekMode') === 'variable'; // LEGACY: weekMode is deprecated
|
12247
|
-
|
12248
12538
|
// if auto, make the height of each row the height that it would be if there were 6 weeks
|
12249
12539
|
if (isAuto) {
|
12250
12540
|
height *= this.rowCnt / 6;
|
@@ -12255,11 +12545,6 @@ var MonthView = FC.MonthView = BasicView.extend({
|
|
12255
12545
|
|
12256
12546
|
|
12257
12547
|
isFixedWeeks: function() {
|
12258
|
-
var weekMode = this.opt('weekMode'); // LEGACY: weekMode is deprecated
|
12259
|
-
if (weekMode) {
|
12260
|
-
return weekMode === 'fixed'; // if any other type of weekMode, assume NOT fixed
|
12261
|
-
}
|
12262
|
-
|
12263
12548
|
return this.opt('fixedWeekCount');
|
12264
12549
|
}
|
12265
12550
|
|
@@ -12689,9 +12974,8 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
12689
12974
|
this.dayGrid.unrenderEvents();
|
12690
12975
|
}
|
12691
12976
|
|
12692
|
-
// we DON'T need to call updateHeight() because
|
12693
|
-
//
|
12694
|
-
// B) in IE8, this causes a flash whenever events are rerendered
|
12977
|
+
// we DON'T need to call updateHeight() because
|
12978
|
+
// a renderEvents() call always happens after this, which will eventually call updateHeight()
|
12695
12979
|
},
|
12696
12980
|
|
12697
12981
|
|
@@ -12759,9 +13043,10 @@ var agendaTimeGridMethods = {
|
|
12759
13043
|
|
12760
13044
|
return '' +
|
12761
13045
|
'<th class="fc-axis fc-week-number ' + view.widgetHeaderClass + '" ' + view.axisStyleAttr() + '>' +
|
12762
|
-
|
12763
|
-
|
12764
|
-
|
13046
|
+
view.buildGotoAnchorHtml( // aside from link, important for matchCellWidths
|
13047
|
+
{ date: this.start, type: 'week', forceOff: this.colCnt > 1 },
|
13048
|
+
htmlEscape(weekText) // inner HTML
|
13049
|
+
) +
|
12765
13050
|
'</th>';
|
12766
13051
|
}
|
12767
13052
|
else {
|
@@ -12800,7 +13085,7 @@ var agendaDayGridMethods = {
|
|
12800
13085
|
return '' +
|
12801
13086
|
'<td class="fc-axis ' + view.widgetContentClass + '" ' + view.axisStyleAttr() + '>' +
|
12802
13087
|
'<span>' + // needed for matchCellWidths
|
12803
|
-
|
13088
|
+
view.getAllDayHtml() +
|
12804
13089
|
'</span>' +
|
12805
13090
|
'</td>';
|
12806
13091
|
},
|
@@ -12834,7 +13119,6 @@ fcViews.agenda = {
|
|
12834
13119
|
'class': AgendaView,
|
12835
13120
|
defaults: {
|
12836
13121
|
allDaySlot: true,
|
12837
|
-
allDayText: 'all-day',
|
12838
13122
|
slotDuration: '00:30:00',
|
12839
13123
|
minTime: '00:00:00',
|
12840
13124
|
maxTime: '24:00:00',
|
@@ -12853,5 +13137,293 @@ fcViews.agendaWeek = {
|
|
12853
13137
|
};
|
12854
13138
|
;;
|
12855
13139
|
|
12856
|
-
|
13140
|
+
/*
|
13141
|
+
Responsible for the scroller, and forwarding event-related actions into the "grid"
|
13142
|
+
*/
|
13143
|
+
var ListView = View.extend({
|
13144
|
+
|
13145
|
+
grid: null,
|
13146
|
+
scroller: null,
|
13147
|
+
|
13148
|
+
initialize: function() {
|
13149
|
+
this.grid = new ListViewGrid(this);
|
13150
|
+
this.scroller = new Scroller({
|
13151
|
+
overflowX: 'hidden',
|
13152
|
+
overflowY: 'auto'
|
13153
|
+
});
|
13154
|
+
},
|
13155
|
+
|
13156
|
+
setRange: function(range) {
|
13157
|
+
View.prototype.setRange.call(this, range); // super
|
13158
|
+
|
13159
|
+
this.grid.setRange(range); // needs to process range-related options
|
13160
|
+
},
|
13161
|
+
|
13162
|
+
renderSkeleton: function() {
|
13163
|
+
this.el.addClass(
|
13164
|
+
'fc-list-view ' +
|
13165
|
+
this.widgetContentClass
|
13166
|
+
);
|
13167
|
+
|
13168
|
+
this.scroller.render();
|
13169
|
+
this.scroller.el.appendTo(this.el);
|
13170
|
+
|
13171
|
+
this.grid.setElement(this.scroller.scrollEl);
|
13172
|
+
},
|
13173
|
+
|
13174
|
+
unrenderSkeleton: function() {
|
13175
|
+
this.scroller.destroy(); // will remove the Grid too
|
13176
|
+
},
|
13177
|
+
|
13178
|
+
setHeight: function(totalHeight, isAuto) {
|
13179
|
+
this.scroller.setHeight(this.computeScrollerHeight(totalHeight));
|
13180
|
+
},
|
13181
|
+
|
13182
|
+
computeScrollerHeight: function(totalHeight) {
|
13183
|
+
return totalHeight -
|
13184
|
+
subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
|
13185
|
+
},
|
13186
|
+
|
13187
|
+
renderEvents: function(events) {
|
13188
|
+
this.grid.renderEvents(events);
|
13189
|
+
},
|
13190
|
+
|
13191
|
+
unrenderEvents: function() {
|
13192
|
+
this.grid.unrenderEvents();
|
13193
|
+
},
|
13194
|
+
|
13195
|
+
isEventResizable: function(event) {
|
13196
|
+
return false;
|
13197
|
+
},
|
13198
|
+
|
13199
|
+
isEventDraggable: function(event) {
|
13200
|
+
return false;
|
13201
|
+
}
|
13202
|
+
|
13203
|
+
});
|
13204
|
+
|
13205
|
+
/*
|
13206
|
+
Responsible for event rendering and user-interaction.
|
13207
|
+
Its "el" is the inner-content of the above view's scroller.
|
13208
|
+
*/
|
13209
|
+
var ListViewGrid = Grid.extend({
|
13210
|
+
|
13211
|
+
segSelector: '.fc-list-item', // which elements accept event actions
|
13212
|
+
hasDayInteractions: false, // no day selection or day clicking
|
13213
|
+
|
13214
|
+
// slices by day
|
13215
|
+
spanToSegs: function(span) {
|
13216
|
+
var view = this.view;
|
13217
|
+
var dayStart = view.start.clone();
|
13218
|
+
var dayEnd;
|
13219
|
+
var seg;
|
13220
|
+
var segs = [];
|
13221
|
+
|
13222
|
+
while (dayStart < view.end) {
|
13223
|
+
dayEnd = dayStart.clone().add(1, 'day');
|
13224
|
+
seg = intersectRanges(span, {
|
13225
|
+
start: dayStart,
|
13226
|
+
end: dayEnd
|
13227
|
+
});
|
13228
|
+
if (seg) {
|
13229
|
+
segs.push(seg);
|
13230
|
+
}
|
13231
|
+
dayStart = dayEnd;
|
13232
|
+
}
|
13233
|
+
|
13234
|
+
return segs;
|
13235
|
+
},
|
13236
|
+
|
13237
|
+
// like "4:00am"
|
13238
|
+
computeEventTimeFormat: function() {
|
13239
|
+
return this.view.opt('mediumTimeFormat');
|
13240
|
+
},
|
13241
|
+
|
13242
|
+
// for events with a url, the whole <tr> should be clickable,
|
13243
|
+
// but it's impossible to wrap with an <a> tag. simulate this.
|
13244
|
+
handleSegClick: function(seg, ev) {
|
13245
|
+
var url;
|
13246
|
+
|
13247
|
+
Grid.prototype.handleSegClick.apply(this, arguments); // super. might prevent the default action
|
13248
|
+
|
13249
|
+
// not clicking on or within an <a> with an href
|
13250
|
+
if (!$(ev.target).closest('a[href]').length) {
|
13251
|
+
url = seg.event.url;
|
13252
|
+
if (url && !ev.isDefaultPrevented()) { // jsEvent not cancelled in handler
|
13253
|
+
window.location.href = url; // simulate link click
|
13254
|
+
}
|
13255
|
+
}
|
13256
|
+
},
|
13257
|
+
|
13258
|
+
// returns list of foreground segs that were actually rendered
|
13259
|
+
renderFgSegs: function(segs) {
|
13260
|
+
segs = this.renderFgSegEls(segs); // might filter away hidden events
|
13261
|
+
|
13262
|
+
if (!segs.length) {
|
13263
|
+
this.renderEmptyMessage();
|
13264
|
+
return segs;
|
13265
|
+
}
|
13266
|
+
else {
|
13267
|
+
return this.renderSegList(segs);
|
13268
|
+
}
|
13269
|
+
},
|
13270
|
+
|
13271
|
+
renderEmptyMessage: function() {
|
13272
|
+
this.el.html(
|
13273
|
+
'<div class="fc-list-empty-wrap2">' + // TODO: try less wraps
|
13274
|
+
'<div class="fc-list-empty-wrap1">' +
|
13275
|
+
'<div class="fc-list-empty">' +
|
13276
|
+
htmlEscape(this.view.opt('noEventsMessage')) +
|
13277
|
+
'</div>' +
|
13278
|
+
'</div>' +
|
13279
|
+
'</div>'
|
13280
|
+
);
|
13281
|
+
},
|
13282
|
+
|
13283
|
+
// render the event segments in the view. returns the mutated array.
|
13284
|
+
renderSegList: function(segs) {
|
13285
|
+
var tableEl = $('<table class="fc-list-table"><tbody/></table>');
|
13286
|
+
var tbodyEl = tableEl.find('tbody');
|
13287
|
+
var i, seg;
|
13288
|
+
var dayDate;
|
13289
|
+
|
13290
|
+
this.sortEventSegs(segs);
|
13291
|
+
|
13292
|
+
for (i = 0; i < segs.length; i++) {
|
13293
|
+
seg = segs[i];
|
13294
|
+
|
13295
|
+
// append a day header
|
13296
|
+
if (!dayDate || !seg.start.isSame(dayDate, 'day')) {
|
13297
|
+
dayDate = seg.start.clone().stripTime();
|
13298
|
+
tbodyEl.append(this.dayHeaderHtml(dayDate));
|
13299
|
+
}
|
13300
|
+
|
13301
|
+
tbodyEl.append(seg.el); // append event row
|
13302
|
+
}
|
13303
|
+
|
13304
|
+
this.el.empty().append(tableEl);
|
13305
|
+
|
13306
|
+
return segs; // return the sorted list
|
13307
|
+
},
|
13308
|
+
|
13309
|
+
// generates the HTML for the day headers that live amongst the event rows
|
13310
|
+
dayHeaderHtml: function(dayDate) {
|
13311
|
+
var view = this.view;
|
13312
|
+
var mainFormat = view.opt('listDayFormat');
|
13313
|
+
var altFormat = view.opt('listDayAltFormat');
|
13314
|
+
|
13315
|
+
return '<tr class="fc-list-heading" data-date="' + dayDate.format('YYYY-MM-DD') + '">' +
|
13316
|
+
'<td class="' + view.widgetHeaderClass + '" colspan="3">' +
|
13317
|
+
(mainFormat ?
|
13318
|
+
view.buildGotoAnchorHtml(
|
13319
|
+
dayDate,
|
13320
|
+
{ 'class': 'fc-list-heading-main' },
|
13321
|
+
htmlEscape(dayDate.format(mainFormat)) // inner HTML
|
13322
|
+
) :
|
13323
|
+
'') +
|
13324
|
+
(altFormat ?
|
13325
|
+
view.buildGotoAnchorHtml(
|
13326
|
+
dayDate,
|
13327
|
+
{ 'class': 'fc-list-heading-alt' },
|
13328
|
+
htmlEscape(dayDate.format(altFormat)) // inner HTML
|
13329
|
+
) :
|
13330
|
+
'') +
|
13331
|
+
'</td>' +
|
13332
|
+
'</tr>';
|
13333
|
+
},
|
13334
|
+
|
13335
|
+
// generates the HTML for a single event row
|
13336
|
+
fgSegHtml: function(seg) {
|
13337
|
+
var view = this.view;
|
13338
|
+
var classes = [ 'fc-list-item' ].concat(this.getSegCustomClasses(seg));
|
13339
|
+
var bgColor = this.getSegBackgroundColor(seg);
|
13340
|
+
var event = seg.event;
|
13341
|
+
var url = event.url;
|
13342
|
+
var timeHtml;
|
13343
|
+
|
13344
|
+
if (!seg.start.hasTime()) {
|
13345
|
+
if (this.displayEventTime) {
|
13346
|
+
timeHtml = view.getAllDayHtml();
|
13347
|
+
}
|
13348
|
+
}
|
13349
|
+
else {
|
13350
|
+
timeHtml = htmlEscape(this.getEventTimeText(event)); // might return empty
|
13351
|
+
}
|
13352
|
+
|
13353
|
+
if (url) {
|
13354
|
+
classes.push('fc-has-url');
|
13355
|
+
}
|
13356
|
+
|
13357
|
+
return '<tr class="' + classes.join(' ') + '">' +
|
13358
|
+
(timeHtml ?
|
13359
|
+
'<td class="fc-list-item-time ' + view.widgetContentClass + '">' +
|
13360
|
+
timeHtml +
|
13361
|
+
'</td>' :
|
13362
|
+
'') +
|
13363
|
+
'<td class="fc-list-item-marker ' + view.widgetContentClass + '">' +
|
13364
|
+
'<span class="fc-event-dot"' +
|
13365
|
+
(bgColor ?
|
13366
|
+
' style="background-color:' + bgColor + '"' :
|
13367
|
+
'') +
|
13368
|
+
'></span>' +
|
13369
|
+
'</td>' +
|
13370
|
+
'<td class="fc-list-item-title ' + view.widgetContentClass + '">' +
|
13371
|
+
'<a' + (url ? ' href="' + htmlEscape(url) + '"' : '') + '>' +
|
13372
|
+
htmlEscape(seg.event.title) +
|
13373
|
+
'</a>' +
|
13374
|
+
'</td>' +
|
13375
|
+
'</tr>';
|
13376
|
+
}
|
13377
|
+
|
13378
|
+
});
|
13379
|
+
|
13380
|
+
;;
|
13381
|
+
|
13382
|
+
fcViews.list = {
|
13383
|
+
'class': ListView,
|
13384
|
+
buttonTextKey: 'list', // what to lookup in locale files
|
13385
|
+
defaults: {
|
13386
|
+
buttonText: 'list', // text to display for English
|
13387
|
+
listTime: true, // show the time column?
|
13388
|
+
listDayFormat: 'LL', // like "January 1, 2016"
|
13389
|
+
noEventsMessage: 'No events to display'
|
13390
|
+
}
|
13391
|
+
};
|
13392
|
+
|
13393
|
+
fcViews.listDay = {
|
13394
|
+
type: 'list',
|
13395
|
+
duration: { days: 1 },
|
13396
|
+
defaults: {
|
13397
|
+
listDayFormat: 'dddd' // day-of-week is all we need. full date is probably in header
|
13398
|
+
}
|
13399
|
+
};
|
13400
|
+
|
13401
|
+
fcViews.listWeek = {
|
13402
|
+
type: 'list',
|
13403
|
+
duration: { weeks: 1 },
|
13404
|
+
defaults: {
|
13405
|
+
listDayFormat: 'dddd', // day-of-week is more important
|
13406
|
+
listDayAltFormat: 'LL'
|
13407
|
+
}
|
13408
|
+
};
|
13409
|
+
|
13410
|
+
fcViews.listMonth = {
|
13411
|
+
type: 'list',
|
13412
|
+
duration: { month: 1 },
|
13413
|
+
defaults: {
|
13414
|
+
listDayAltFormat: 'dddd' // day-of-week is nice-to-have
|
13415
|
+
}
|
13416
|
+
};
|
13417
|
+
|
13418
|
+
fcViews.listYear = {
|
13419
|
+
type: 'list',
|
13420
|
+
duration: { year: 1 },
|
13421
|
+
defaults: {
|
13422
|
+
listDayAltFormat: 'dddd' // day-of-week is nice-to-have
|
13423
|
+
}
|
13424
|
+
};
|
13425
|
+
|
13426
|
+
;;
|
13427
|
+
|
13428
|
+
return FC; // export for Node/CommonJS
|
12857
13429
|
});
|