fullcalendar-rails 2.6.1.0 → 2.8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/fullcalendar-rails/version.rb +1 -1
- data/vendor/assets/javascripts/fullcalendar.js +1481 -522
- data/vendor/assets/javascripts/fullcalendar/gcal.js +4 -4
- data/vendor/assets/javascripts/fullcalendar/lang-all.js +4 -4
- 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 +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ca.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/cs.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/da.js +0 -0
- 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 +0 -0
- 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.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/eu.js +1 -0
- data/vendor/assets/javascripts/fullcalendar/lang/fa.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fi.js +0 -0
- 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 -0
- 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 +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/id.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/is.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/it.js +0 -0
- 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 -0
- 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/nb.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/nl.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/pl.js +0 -0
- 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 +0 -0
- 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 +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/uk.js +0 -0
- 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/stylesheets/fullcalendar.css +179 -42
- data/vendor/assets/stylesheets/fullcalendar.print.css +2 -2
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf346a46d7b099097273d5778484477c361d80d0
|
|
4
|
+
data.tar.gz: 1ef451e5d2a42a11c33174170e84394169ebf449
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 073212fe38be4290162ce05f714f9154979eafc35227132a311a8be3d260a2eecbb5e48987ef9cf3649e878f2b7c8e0c980963945f309b5e8602f78911930182
|
|
7
|
+
data.tar.gz: fc867f9f0a959c44749a5d2d0401725e235b379206537fc2cd2927b78c6d0a020a81b8cf6960b8229cd2c832f9bc16b60e309e1c501b498decc965e48602895a
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* FullCalendar v2.
|
|
2
|
+
* FullCalendar v2.8.0
|
|
3
3
|
* Docs & License: http://fullcalendar.io/
|
|
4
|
-
* (c)
|
|
4
|
+
* (c) 2016 Adam Shaw
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
(function(factory) {
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
;;
|
|
20
20
|
|
|
21
21
|
var FC = $.fullCalendar = {
|
|
22
|
-
version: "2.
|
|
23
|
-
internalApiVersion:
|
|
22
|
+
version: "2.8.0",
|
|
23
|
+
internalApiVersion: 4
|
|
24
24
|
};
|
|
25
25
|
var fcViews = FC.views = {};
|
|
26
26
|
|
|
@@ -262,29 +262,25 @@ function matchCellWidths(els) {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
// are scrollbars needed?
|
|
272
|
-
if (containerEl[0].scrollHeight - 1 > containerEl[0].clientHeight) { // !!! -1 because IE is often off-by-one :(
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
unsetScroller(containerEl); // undo
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
265
|
+
// Given one element that resides inside another,
|
|
266
|
+
// Subtracts the height of the inner element from the outer element.
|
|
267
|
+
function subtractInnerElHeight(outerEl, innerEl) {
|
|
268
|
+
var both = outerEl.add(innerEl);
|
|
269
|
+
var diff;
|
|
279
270
|
|
|
271
|
+
// effin' IE8/9/10/11 sometimes returns 0 for dimensions. this weird hack was the only thing that worked
|
|
272
|
+
both.css({
|
|
273
|
+
position: 'relative', // cause a reflow, which will force fresh dimension recalculation
|
|
274
|
+
left: -1 // ensure reflow in case the el was already relative. negative is less likely to cause new scroll
|
|
275
|
+
});
|
|
276
|
+
diff = outerEl.outerHeight() - innerEl.outerHeight(); // grab the dimensions
|
|
277
|
+
both.css({ position: '', left: '' }); // undo hack
|
|
280
278
|
|
|
281
|
-
|
|
282
|
-
function unsetScroller(containerEl) {
|
|
283
|
-
containerEl.height('').removeClass('fc-scroller');
|
|
279
|
+
return diff;
|
|
284
280
|
}
|
|
285
281
|
|
|
286
282
|
|
|
287
|
-
/*
|
|
283
|
+
/* Element Geom Utilities
|
|
288
284
|
----------------------------------------------------------------------------------------------------------------------*/
|
|
289
285
|
|
|
290
286
|
FC.getOuterRect = getOuterRect;
|
|
@@ -309,26 +305,30 @@ function getScrollParent(el) {
|
|
|
309
305
|
|
|
310
306
|
// Queries the outer bounding area of a jQuery element.
|
|
311
307
|
// Returns a rectangle with absolute coordinates: left, right (exclusive), top, bottom (exclusive).
|
|
312
|
-
|
|
308
|
+
// Origin is optional.
|
|
309
|
+
function getOuterRect(el, origin) {
|
|
313
310
|
var offset = el.offset();
|
|
311
|
+
var left = offset.left - (origin ? origin.left : 0);
|
|
312
|
+
var top = offset.top - (origin ? origin.top : 0);
|
|
314
313
|
|
|
315
314
|
return {
|
|
316
|
-
left:
|
|
317
|
-
right:
|
|
318
|
-
top:
|
|
319
|
-
bottom:
|
|
315
|
+
left: left,
|
|
316
|
+
right: left + el.outerWidth(),
|
|
317
|
+
top: top,
|
|
318
|
+
bottom: top + el.outerHeight()
|
|
320
319
|
};
|
|
321
320
|
}
|
|
322
321
|
|
|
323
322
|
|
|
324
323
|
// Queries the area within the margin/border/scrollbars of a jQuery element. Does not go within the padding.
|
|
325
324
|
// Returns a rectangle with absolute coordinates: left, right (exclusive), top, bottom (exclusive).
|
|
325
|
+
// Origin is optional.
|
|
326
326
|
// NOTE: should use clientLeft/clientTop, but very unreliable cross-browser.
|
|
327
|
-
function getClientRect(el) {
|
|
327
|
+
function getClientRect(el, origin) {
|
|
328
328
|
var offset = el.offset();
|
|
329
329
|
var scrollbarWidths = getScrollbarWidths(el);
|
|
330
|
-
var left = offset.left + getCssFloat(el, 'border-left-width') + scrollbarWidths.left;
|
|
331
|
-
var top = offset.top + getCssFloat(el, 'border-top-width') + scrollbarWidths.top;
|
|
330
|
+
var left = offset.left + getCssFloat(el, 'border-left-width') + scrollbarWidths.left - (origin ? origin.left : 0);
|
|
331
|
+
var top = offset.top + getCssFloat(el, 'border-top-width') + scrollbarWidths.top - (origin ? origin.top : 0);
|
|
332
332
|
|
|
333
333
|
return {
|
|
334
334
|
left: left,
|
|
@@ -341,10 +341,13 @@ function getClientRect(el) {
|
|
|
341
341
|
|
|
342
342
|
// Queries the area within the margin/border/padding of a jQuery element. Assumed not to have scrollbars.
|
|
343
343
|
// Returns a rectangle with absolute coordinates: left, right (exclusive), top, bottom (exclusive).
|
|
344
|
-
|
|
344
|
+
// Origin is optional.
|
|
345
|
+
function getContentRect(el, origin) {
|
|
345
346
|
var offset = el.offset(); // just outside of border, margin not included
|
|
346
|
-
var left = offset.left + getCssFloat(el, 'border-left-width') + getCssFloat(el, 'padding-left')
|
|
347
|
-
|
|
347
|
+
var left = offset.left + getCssFloat(el, 'border-left-width') + getCssFloat(el, 'padding-left') -
|
|
348
|
+
(origin ? origin.left : 0);
|
|
349
|
+
var top = offset.top + getCssFloat(el, 'border-top-width') + getCssFloat(el, 'padding-top') -
|
|
350
|
+
(origin ? origin.top : 0);
|
|
348
351
|
|
|
349
352
|
return {
|
|
350
353
|
left: left,
|
|
@@ -414,13 +417,82 @@ function getCssFloat(el, prop) {
|
|
|
414
417
|
}
|
|
415
418
|
|
|
416
419
|
|
|
420
|
+
/* Mouse / Touch Utilities
|
|
421
|
+
----------------------------------------------------------------------------------------------------------------------*/
|
|
422
|
+
|
|
423
|
+
FC.preventDefault = preventDefault;
|
|
424
|
+
|
|
425
|
+
|
|
417
426
|
// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
|
|
418
427
|
function isPrimaryMouseButton(ev) {
|
|
419
428
|
return ev.which == 1 && !ev.ctrlKey;
|
|
420
429
|
}
|
|
421
430
|
|
|
422
431
|
|
|
423
|
-
|
|
432
|
+
function getEvX(ev) {
|
|
433
|
+
if (ev.pageX !== undefined) {
|
|
434
|
+
return ev.pageX;
|
|
435
|
+
}
|
|
436
|
+
var touches = ev.originalEvent.touches;
|
|
437
|
+
if (touches) {
|
|
438
|
+
return touches[0].pageX;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
function getEvY(ev) {
|
|
444
|
+
if (ev.pageY !== undefined) {
|
|
445
|
+
return ev.pageY;
|
|
446
|
+
}
|
|
447
|
+
var touches = ev.originalEvent.touches;
|
|
448
|
+
if (touches) {
|
|
449
|
+
return touches[0].pageY;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
function getEvIsTouch(ev) {
|
|
455
|
+
return /^touch/.test(ev.type);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
function preventSelection(el) {
|
|
460
|
+
el.addClass('fc-unselectable')
|
|
461
|
+
.on('selectstart', preventDefault);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
// Stops a mouse/touch event from doing it's native browser action
|
|
466
|
+
function preventDefault(ev) {
|
|
467
|
+
ev.preventDefault();
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
// attach a handler to get called when ANY scroll action happens on the page.
|
|
472
|
+
// this was impossible to do with normal on/off because 'scroll' doesn't bubble.
|
|
473
|
+
// http://stackoverflow.com/a/32954565/96342
|
|
474
|
+
// returns `true` on success.
|
|
475
|
+
function bindAnyScroll(handler) {
|
|
476
|
+
if (window.addEventListener) {
|
|
477
|
+
window.addEventListener('scroll', handler, true); // useCapture=true
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
// undoes bindAnyScroll. must pass in the original function.
|
|
485
|
+
// returns `true` on success.
|
|
486
|
+
function unbindAnyScroll(handler) {
|
|
487
|
+
if (window.removeEventListener) {
|
|
488
|
+
window.removeEventListener('scroll', handler, true); // useCapture=true
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
/* General Geometry Utils
|
|
424
496
|
----------------------------------------------------------------------------------------------------------------------*/
|
|
425
497
|
|
|
426
498
|
FC.intersectRects = intersectRects;
|
|
@@ -946,22 +1018,21 @@ function proxy(obj, methodName) {
|
|
|
946
1018
|
|
|
947
1019
|
// Returns a function, that, as long as it continues to be invoked, will not
|
|
948
1020
|
// be triggered. The function will be called after it stops being called for
|
|
949
|
-
// N milliseconds.
|
|
1021
|
+
// N milliseconds. If `immediate` is passed, trigger the function on the
|
|
1022
|
+
// leading edge, instead of the trailing.
|
|
950
1023
|
// https://github.com/jashkenas/underscore/blob/1.6.0/underscore.js#L714
|
|
951
|
-
function debounce(func, wait) {
|
|
952
|
-
var
|
|
953
|
-
|
|
954
|
-
var context;
|
|
955
|
-
var timestamp; // of most recent call
|
|
1024
|
+
function debounce(func, wait, immediate) {
|
|
1025
|
+
var timeout, args, context, timestamp, result;
|
|
1026
|
+
|
|
956
1027
|
var later = function() {
|
|
957
1028
|
var last = +new Date() - timestamp;
|
|
958
|
-
if (last < wait
|
|
959
|
-
|
|
1029
|
+
if (last < wait) {
|
|
1030
|
+
timeout = setTimeout(later, wait - last);
|
|
960
1031
|
}
|
|
961
1032
|
else {
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1033
|
+
timeout = null;
|
|
1034
|
+
if (!immediate) {
|
|
1035
|
+
result = func.apply(context, args);
|
|
965
1036
|
context = args = null;
|
|
966
1037
|
}
|
|
967
1038
|
}
|
|
@@ -971,12 +1042,32 @@ function debounce(func, wait) {
|
|
|
971
1042
|
context = this;
|
|
972
1043
|
args = arguments;
|
|
973
1044
|
timestamp = +new Date();
|
|
974
|
-
|
|
975
|
-
|
|
1045
|
+
var callNow = immediate && !timeout;
|
|
1046
|
+
if (!timeout) {
|
|
1047
|
+
timeout = setTimeout(later, wait);
|
|
1048
|
+
}
|
|
1049
|
+
if (callNow) {
|
|
1050
|
+
result = func.apply(context, args);
|
|
1051
|
+
context = args = null;
|
|
976
1052
|
}
|
|
1053
|
+
return result;
|
|
977
1054
|
};
|
|
978
1055
|
}
|
|
979
1056
|
|
|
1057
|
+
|
|
1058
|
+
// HACK around jQuery's now A+ promises: execute callback synchronously if already resolved.
|
|
1059
|
+
// thenFunc shouldn't accept args.
|
|
1060
|
+
// similar to whenResources in Scheduler plugin.
|
|
1061
|
+
function syncThen(promise, thenFunc) {
|
|
1062
|
+
// not a promise, or an already-resolved promise?
|
|
1063
|
+
if (!promise || !promise.then || promise.state() === 'resolved') {
|
|
1064
|
+
return $.when(thenFunc()); // resolve immediately
|
|
1065
|
+
}
|
|
1066
|
+
else if (thenFunc) {
|
|
1067
|
+
return promise.then(thenFunc);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
980
1071
|
;;
|
|
981
1072
|
|
|
982
1073
|
var ambigDateOfMonthRegex = /^\s*\d{4}-\d\d$/;
|
|
@@ -1777,61 +1868,162 @@ function extendClass(superClass, members) {
|
|
|
1777
1868
|
|
|
1778
1869
|
|
|
1779
1870
|
function mixIntoClass(theClass, members) {
|
|
1780
|
-
copyOwnProps(members
|
|
1871
|
+
copyOwnProps(members, theClass.prototype); // TODO: copyNativeMethods?
|
|
1781
1872
|
}
|
|
1782
1873
|
;;
|
|
1783
1874
|
|
|
1784
|
-
var
|
|
1875
|
+
var EmitterMixin = FC.EmitterMixin = {
|
|
1876
|
+
|
|
1877
|
+
// jQuery-ification via $(this) allows a non-DOM object to have
|
|
1878
|
+
// the same event handling capabilities (including namespaces).
|
|
1785
1879
|
|
|
1786
|
-
callbackHash: null,
|
|
1787
1880
|
|
|
1881
|
+
on: function(types, handler) {
|
|
1882
|
+
|
|
1883
|
+
// handlers are always called with an "event" object as their first param.
|
|
1884
|
+
// sneak the `this` context and arguments into the extra parameter object
|
|
1885
|
+
// and forward them on to the original handler.
|
|
1886
|
+
var intercept = function(ev, extra) {
|
|
1887
|
+
return handler.apply(
|
|
1888
|
+
extra.context || this,
|
|
1889
|
+
extra.args || []
|
|
1890
|
+
);
|
|
1891
|
+
};
|
|
1892
|
+
|
|
1893
|
+
// mimick jQuery's internal "proxy" system (risky, I know)
|
|
1894
|
+
// causing all functions with the same .guid to appear to be the same.
|
|
1895
|
+
// https://github.com/jquery/jquery/blob/2.2.4/src/core.js#L448
|
|
1896
|
+
// this is needed for calling .off with the original non-intercept handler.
|
|
1897
|
+
if (!handler.guid) {
|
|
1898
|
+
handler.guid = $.guid++;
|
|
1899
|
+
}
|
|
1900
|
+
intercept.guid = handler.guid;
|
|
1901
|
+
|
|
1902
|
+
$(this).on(types, intercept);
|
|
1788
1903
|
|
|
1789
|
-
on: function(name, callback) {
|
|
1790
|
-
this.getCallbacks(name).add(callback);
|
|
1791
1904
|
return this; // for chaining
|
|
1792
1905
|
},
|
|
1793
1906
|
|
|
1794
1907
|
|
|
1795
|
-
off: function(
|
|
1796
|
-
this
|
|
1908
|
+
off: function(types, handler) {
|
|
1909
|
+
$(this).off(types, handler);
|
|
1910
|
+
|
|
1797
1911
|
return this; // for chaining
|
|
1798
1912
|
},
|
|
1799
1913
|
|
|
1800
1914
|
|
|
1801
|
-
trigger: function(
|
|
1802
|
-
var args = Array.prototype.slice.call(arguments, 1);
|
|
1915
|
+
trigger: function(types) {
|
|
1916
|
+
var args = Array.prototype.slice.call(arguments, 1); // arguments after the first
|
|
1803
1917
|
|
|
1804
|
-
|
|
1918
|
+
// pass in "extra" info to the intercept
|
|
1919
|
+
$(this).triggerHandler(types, { args: args });
|
|
1805
1920
|
|
|
1806
1921
|
return this; // for chaining
|
|
1807
1922
|
},
|
|
1808
1923
|
|
|
1809
1924
|
|
|
1810
|
-
triggerWith: function(
|
|
1811
|
-
var callbacks = this.getCallbacks(name);
|
|
1925
|
+
triggerWith: function(types, context, args) {
|
|
1812
1926
|
|
|
1813
|
-
|
|
1927
|
+
// `triggerHandler` is less reliant on the DOM compared to `trigger`.
|
|
1928
|
+
// pass in "extra" info to the intercept.
|
|
1929
|
+
$(this).triggerHandler(types, { context: context, args: args });
|
|
1814
1930
|
|
|
1815
1931
|
return this; // for chaining
|
|
1816
|
-
}
|
|
1932
|
+
}
|
|
1817
1933
|
|
|
1934
|
+
};
|
|
1818
1935
|
|
|
1819
|
-
|
|
1820
|
-
var callbacks;
|
|
1936
|
+
;;
|
|
1821
1937
|
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1938
|
+
/*
|
|
1939
|
+
Utility methods for easily listening to events on another object,
|
|
1940
|
+
and more importantly, easily unlistening from them.
|
|
1941
|
+
*/
|
|
1942
|
+
var ListenerMixin = FC.ListenerMixin = (function() {
|
|
1943
|
+
var guid = 0;
|
|
1944
|
+
var ListenerMixin = {
|
|
1945
|
+
|
|
1946
|
+
listenerId: null,
|
|
1947
|
+
|
|
1948
|
+
/*
|
|
1949
|
+
Given an `other` object that has on/off methods, bind the given `callback` to an event by the given name.
|
|
1950
|
+
The `callback` will be called with the `this` context of the object that .listenTo is being called on.
|
|
1951
|
+
Can be called:
|
|
1952
|
+
.listenTo(other, eventName, callback)
|
|
1953
|
+
OR
|
|
1954
|
+
.listenTo(other, {
|
|
1955
|
+
eventName1: callback1,
|
|
1956
|
+
eventName2: callback2
|
|
1957
|
+
})
|
|
1958
|
+
*/
|
|
1959
|
+
listenTo: function(other, arg, callback) {
|
|
1960
|
+
if (typeof arg === 'object') { // given dictionary of callbacks
|
|
1961
|
+
for (var eventName in arg) {
|
|
1962
|
+
if (arg.hasOwnProperty(eventName)) {
|
|
1963
|
+
this.listenTo(other, eventName, arg[eventName]);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
else if (typeof arg === 'string') {
|
|
1968
|
+
other.on(
|
|
1969
|
+
arg + '.' + this.getListenerNamespace(), // use event namespacing to identify this object
|
|
1970
|
+
$.proxy(callback, this) // always use `this` context
|
|
1971
|
+
// the usually-undesired jQuery guid behavior doesn't matter,
|
|
1972
|
+
// because we always unbind via namespace
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
},
|
|
1825
1976
|
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1977
|
+
/*
|
|
1978
|
+
Causes the current object to stop listening to events on the `other` object.
|
|
1979
|
+
`eventName` is optional. If omitted, will stop listening to ALL events on `other`.
|
|
1980
|
+
*/
|
|
1981
|
+
stopListeningTo: function(other, eventName) {
|
|
1982
|
+
other.off((eventName || '') + '.' + this.getListenerNamespace());
|
|
1983
|
+
},
|
|
1984
|
+
|
|
1985
|
+
/*
|
|
1986
|
+
Returns a string, unique to this object, to be used for event namespacing
|
|
1987
|
+
*/
|
|
1988
|
+
getListenerNamespace: function() {
|
|
1989
|
+
if (this.listenerId == null) {
|
|
1990
|
+
this.listenerId = guid++;
|
|
1991
|
+
}
|
|
1992
|
+
return '_listener' + this.listenerId;
|
|
1829
1993
|
}
|
|
1830
1994
|
|
|
1831
|
-
|
|
1995
|
+
};
|
|
1996
|
+
return ListenerMixin;
|
|
1997
|
+
})();
|
|
1998
|
+
;;
|
|
1999
|
+
|
|
2000
|
+
// simple class for toggle a `isIgnoringMouse` flag on delay
|
|
2001
|
+
// initMouseIgnoring must first be called, with a millisecond delay setting.
|
|
2002
|
+
var MouseIgnorerMixin = {
|
|
2003
|
+
|
|
2004
|
+
isIgnoringMouse: false, // bool
|
|
2005
|
+
delayUnignoreMouse: null, // method
|
|
2006
|
+
|
|
2007
|
+
|
|
2008
|
+
initMouseIgnoring: function(delay) {
|
|
2009
|
+
this.delayUnignoreMouse = debounce(proxy(this, 'unignoreMouse'), delay || 1000);
|
|
2010
|
+
},
|
|
2011
|
+
|
|
2012
|
+
|
|
2013
|
+
// temporarily ignore mouse actions on segments
|
|
2014
|
+
tempIgnoreMouse: function() {
|
|
2015
|
+
this.isIgnoringMouse = true;
|
|
2016
|
+
this.delayUnignoreMouse();
|
|
2017
|
+
},
|
|
2018
|
+
|
|
2019
|
+
|
|
2020
|
+
// delayUnignoreMouse eventually calls this
|
|
2021
|
+
unignoreMouse: function() {
|
|
2022
|
+
this.isIgnoringMouse = false;
|
|
1832
2023
|
}
|
|
1833
2024
|
|
|
1834
|
-
}
|
|
2025
|
+
};
|
|
2026
|
+
|
|
1835
2027
|
;;
|
|
1836
2028
|
|
|
1837
2029
|
/* A rectangular panel that is absolutely positioned over other content
|
|
@@ -1848,12 +2040,11 @@ Options:
|
|
|
1848
2040
|
- hide (callback)
|
|
1849
2041
|
*/
|
|
1850
2042
|
|
|
1851
|
-
var Popover = Class.extend({
|
|
2043
|
+
var Popover = Class.extend(ListenerMixin, {
|
|
1852
2044
|
|
|
1853
2045
|
isHidden: true,
|
|
1854
2046
|
options: null,
|
|
1855
2047
|
el: null, // the container element for the popover. generated by this object
|
|
1856
|
-
documentMousedownProxy: null, // document mousedown handler bound to `this`
|
|
1857
2048
|
margin: 10, // the space required between the popover and the edges of the scroll container
|
|
1858
2049
|
|
|
1859
2050
|
|
|
@@ -1907,7 +2098,7 @@ var Popover = Class.extend({
|
|
|
1907
2098
|
});
|
|
1908
2099
|
|
|
1909
2100
|
if (options.autoHide) {
|
|
1910
|
-
$(document)
|
|
2101
|
+
this.listenTo($(document), 'mousedown', this.documentMousedown);
|
|
1911
2102
|
}
|
|
1912
2103
|
},
|
|
1913
2104
|
|
|
@@ -1930,7 +2121,7 @@ var Popover = Class.extend({
|
|
|
1930
2121
|
this.el = null;
|
|
1931
2122
|
}
|
|
1932
2123
|
|
|
1933
|
-
$(document)
|
|
2124
|
+
this.stopListeningTo($(document), 'mousedown');
|
|
1934
2125
|
},
|
|
1935
2126
|
|
|
1936
2127
|
|
|
@@ -2243,257 +2434,421 @@ var CoordCache = FC.CoordCache = Class.extend({
|
|
|
2243
2434
|
----------------------------------------------------------------------------------------------------------------------*/
|
|
2244
2435
|
// TODO: use Emitter
|
|
2245
2436
|
|
|
2246
|
-
var DragListener = FC.DragListener = Class.extend({
|
|
2437
|
+
var DragListener = FC.DragListener = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
|
2247
2438
|
|
|
2248
2439
|
options: null,
|
|
2249
2440
|
|
|
2250
|
-
|
|
2251
|
-
|
|
2441
|
+
// for IE8 bug-fighting behavior
|
|
2442
|
+
subjectEl: null,
|
|
2443
|
+
subjectHref: null,
|
|
2252
2444
|
|
|
2253
2445
|
// coordinates of the initial mousedown
|
|
2254
2446
|
originX: null,
|
|
2255
2447
|
originY: null,
|
|
2256
2448
|
|
|
2257
|
-
//
|
|
2258
|
-
|
|
2259
|
-
|
|
2449
|
+
// the wrapping element that scrolls, or MIGHT scroll if there's overflow.
|
|
2450
|
+
// TODO: do this for wrappers that have overflow:hidden as well.
|
|
2451
|
+
scrollEl: null,
|
|
2260
2452
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2453
|
+
isInteracting: false,
|
|
2454
|
+
isDistanceSurpassed: false,
|
|
2455
|
+
isDelayEnded: false,
|
|
2456
|
+
isDragging: false,
|
|
2457
|
+
isTouch: false,
|
|
2264
2458
|
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
scrollLeftVel: null, // pixels per second
|
|
2269
|
-
scrollIntervalId: null, // ID of setTimeout for scrolling animation loop
|
|
2270
|
-
scrollHandlerProxy: null, // this-scoped function for handling when scrollEl is scrolled
|
|
2459
|
+
delay: null,
|
|
2460
|
+
delayTimeoutId: null,
|
|
2461
|
+
minDistance: null,
|
|
2271
2462
|
|
|
2272
|
-
|
|
2273
|
-
scrollSpeed: 200, // pixels per second, at maximum speed
|
|
2274
|
-
scrollIntervalMs: 50, // millisecond wait between scroll increment
|
|
2463
|
+
handleTouchScrollProxy: null, // calls handleTouchScroll, always bound to `this`
|
|
2275
2464
|
|
|
2276
2465
|
|
|
2277
2466
|
constructor: function(options) {
|
|
2278
|
-
options = options || {};
|
|
2279
|
-
this.
|
|
2280
|
-
this.
|
|
2467
|
+
this.options = options || {};
|
|
2468
|
+
this.handleTouchScrollProxy = proxy(this, 'handleTouchScroll');
|
|
2469
|
+
this.initMouseIgnoring(500);
|
|
2281
2470
|
},
|
|
2282
2471
|
|
|
2283
2472
|
|
|
2284
|
-
//
|
|
2285
|
-
|
|
2286
|
-
|
|
2473
|
+
// Interaction (high-level)
|
|
2474
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2475
|
+
|
|
2476
|
+
|
|
2477
|
+
startInteraction: function(ev, extraOptions) {
|
|
2478
|
+
var isTouch = getEvIsTouch(ev);
|
|
2479
|
+
|
|
2480
|
+
if (ev.type === 'mousedown') {
|
|
2481
|
+
if (this.isIgnoringMouse) {
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
else if (!isPrimaryMouseButton(ev)) {
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
else {
|
|
2488
|
+
ev.preventDefault(); // prevents native selection in most browsers
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2287
2491
|
|
|
2288
|
-
|
|
2492
|
+
if (!this.isInteracting) {
|
|
2289
2493
|
|
|
2290
|
-
|
|
2494
|
+
// process options
|
|
2495
|
+
extraOptions = extraOptions || {};
|
|
2496
|
+
this.delay = firstDefined(extraOptions.delay, this.options.delay, 0);
|
|
2497
|
+
this.minDistance = firstDefined(extraOptions.distance, this.options.distance, 0);
|
|
2498
|
+
this.subjectEl = this.options.subjectEl;
|
|
2291
2499
|
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2500
|
+
this.isInteracting = true;
|
|
2501
|
+
this.isTouch = isTouch;
|
|
2502
|
+
this.isDelayEnded = false;
|
|
2503
|
+
this.isDistanceSurpassed = false;
|
|
2504
|
+
|
|
2505
|
+
this.originX = getEvX(ev);
|
|
2506
|
+
this.originY = getEvY(ev);
|
|
2507
|
+
this.scrollEl = getScrollParent($(ev.target));
|
|
2508
|
+
|
|
2509
|
+
this.bindHandlers();
|
|
2510
|
+
this.initAutoScroll();
|
|
2511
|
+
this.handleInteractionStart(ev);
|
|
2512
|
+
this.startDelay(ev);
|
|
2513
|
+
|
|
2514
|
+
if (!this.minDistance) {
|
|
2515
|
+
this.handleDistanceSurpassed(ev);
|
|
2295
2516
|
}
|
|
2296
2517
|
}
|
|
2297
2518
|
},
|
|
2298
2519
|
|
|
2299
2520
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2521
|
+
handleInteractionStart: function(ev) {
|
|
2522
|
+
this.trigger('interactionStart', ev);
|
|
2523
|
+
},
|
|
2303
2524
|
|
|
2304
|
-
if (!this.isListening) {
|
|
2305
2525
|
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
if (!scrollParent.is(window) && !scrollParent.is(document)) {
|
|
2310
|
-
this.scrollEl = scrollParent;
|
|
2526
|
+
endInteraction: function(ev, isCancelled) {
|
|
2527
|
+
if (this.isInteracting) {
|
|
2528
|
+
this.endDrag(ev);
|
|
2311
2529
|
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
}
|
|
2530
|
+
if (this.delayTimeoutId) {
|
|
2531
|
+
clearTimeout(this.delayTimeoutId);
|
|
2532
|
+
this.delayTimeoutId = null;
|
|
2316
2533
|
}
|
|
2317
2534
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2535
|
+
this.destroyAutoScroll();
|
|
2536
|
+
this.unbindHandlers();
|
|
2537
|
+
|
|
2538
|
+
this.isInteracting = false;
|
|
2539
|
+
this.handleInteractionEnd(ev, isCancelled);
|
|
2322
2540
|
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2541
|
+
// a touchstart+touchend on the same element will result in the following addition simulated events:
|
|
2542
|
+
// mouseover + mouseout + click
|
|
2543
|
+
// let's ignore these bogus events
|
|
2544
|
+
if (this.isTouch) {
|
|
2545
|
+
this.tempIgnoreMouse();
|
|
2326
2546
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2547
|
+
}
|
|
2548
|
+
},
|
|
2549
|
+
|
|
2550
|
+
|
|
2551
|
+
handleInteractionEnd: function(ev, isCancelled) {
|
|
2552
|
+
this.trigger('interactionEnd', ev, isCancelled || false);
|
|
2553
|
+
},
|
|
2554
|
+
|
|
2555
|
+
|
|
2556
|
+
// Binding To DOM
|
|
2557
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2558
|
+
|
|
2559
|
+
|
|
2560
|
+
bindHandlers: function() {
|
|
2561
|
+
var _this = this;
|
|
2562
|
+
var touchStartIgnores = 1;
|
|
2563
|
+
|
|
2564
|
+
if (this.isTouch) {
|
|
2565
|
+
this.listenTo($(document), {
|
|
2566
|
+
touchmove: this.handleTouchMove,
|
|
2567
|
+
touchend: this.endInteraction,
|
|
2568
|
+
touchcancel: this.endInteraction,
|
|
2569
|
+
|
|
2570
|
+
// Sometimes touchend doesn't fire
|
|
2571
|
+
// (can't figure out why. touchcancel doesn't fire either. has to do with scrolling?)
|
|
2572
|
+
// If another touchstart happens, we know it's bogus, so cancel the drag.
|
|
2573
|
+
// touchend will continue to be broken until user does a shorttap/scroll, but this is best we can do.
|
|
2574
|
+
touchstart: function(ev) {
|
|
2575
|
+
if (touchStartIgnores) { // bindHandlers is called from within a touchstart,
|
|
2576
|
+
touchStartIgnores--; // and we don't want this to fire immediately, so ignore.
|
|
2577
|
+
}
|
|
2578
|
+
else {
|
|
2579
|
+
_this.endInteraction(ev, true); // isCancelled=true
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
});
|
|
2583
|
+
|
|
2584
|
+
// listen to ALL scroll actions on the page
|
|
2585
|
+
if (
|
|
2586
|
+
!bindAnyScroll(this.handleTouchScrollProxy) && // hopefully this works and short-circuits the rest
|
|
2587
|
+
this.scrollEl // otherwise, attach a single handler to this
|
|
2588
|
+
) {
|
|
2589
|
+
this.listenTo(this.scrollEl, 'scroll', this.handleTouchScroll);
|
|
2332
2590
|
}
|
|
2591
|
+
}
|
|
2592
|
+
else {
|
|
2593
|
+
this.listenTo($(document), {
|
|
2594
|
+
mousemove: this.handleMouseMove,
|
|
2595
|
+
mouseup: this.endInteraction
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
this.listenTo($(document), {
|
|
2600
|
+
selectstart: preventDefault, // don't allow selection while dragging
|
|
2601
|
+
contextmenu: preventDefault // long taps would open menu on Chrome dev tools
|
|
2602
|
+
});
|
|
2603
|
+
},
|
|
2604
|
+
|
|
2605
|
+
|
|
2606
|
+
unbindHandlers: function() {
|
|
2607
|
+
this.stopListeningTo($(document));
|
|
2608
|
+
|
|
2609
|
+
// unbind scroll listening
|
|
2610
|
+
unbindAnyScroll(this.handleTouchScrollProxy);
|
|
2611
|
+
if (this.scrollEl) {
|
|
2612
|
+
this.stopListeningTo(this.scrollEl, 'scroll');
|
|
2613
|
+
}
|
|
2614
|
+
},
|
|
2615
|
+
|
|
2333
2616
|
|
|
2334
|
-
|
|
2335
|
-
|
|
2617
|
+
// Drag (high-level)
|
|
2618
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2619
|
+
|
|
2620
|
+
|
|
2621
|
+
// extraOptions ignored if drag already started
|
|
2622
|
+
startDrag: function(ev, extraOptions) {
|
|
2623
|
+
this.startInteraction(ev, extraOptions); // ensure interaction began
|
|
2624
|
+
|
|
2625
|
+
if (!this.isDragging) {
|
|
2626
|
+
this.isDragging = true;
|
|
2627
|
+
this.handleDragStart(ev);
|
|
2336
2628
|
}
|
|
2337
2629
|
},
|
|
2338
2630
|
|
|
2339
2631
|
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
this.
|
|
2632
|
+
handleDragStart: function(ev) {
|
|
2633
|
+
this.trigger('dragStart', ev);
|
|
2634
|
+
this.initHrefHack();
|
|
2343
2635
|
},
|
|
2344
2636
|
|
|
2345
2637
|
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
var
|
|
2349
|
-
var
|
|
2350
|
-
var minDistance;
|
|
2638
|
+
handleMove: function(ev) {
|
|
2639
|
+
var dx = getEvX(ev) - this.originX;
|
|
2640
|
+
var dy = getEvY(ev) - this.originY;
|
|
2641
|
+
var minDistance = this.minDistance;
|
|
2351
2642
|
var distanceSq; // current distance from the origin, squared
|
|
2352
2643
|
|
|
2353
|
-
if (!this.
|
|
2354
|
-
// then start the drag if the minimum distance criteria is met
|
|
2355
|
-
minDistance = this.options.distance || 1;
|
|
2644
|
+
if (!this.isDistanceSurpassed) {
|
|
2356
2645
|
distanceSq = dx * dx + dy * dy;
|
|
2357
2646
|
if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem
|
|
2358
|
-
this.
|
|
2647
|
+
this.handleDistanceSurpassed(ev);
|
|
2359
2648
|
}
|
|
2360
2649
|
}
|
|
2361
2650
|
|
|
2362
2651
|
if (this.isDragging) {
|
|
2363
|
-
this.
|
|
2652
|
+
this.handleDrag(dx, dy, ev);
|
|
2364
2653
|
}
|
|
2365
2654
|
},
|
|
2366
2655
|
|
|
2367
2656
|
|
|
2368
|
-
//
|
|
2369
|
-
|
|
2370
|
-
|
|
2657
|
+
// Called while the mouse is being moved and when we know a legitimate drag is taking place
|
|
2658
|
+
handleDrag: function(dx, dy, ev) {
|
|
2659
|
+
this.trigger('drag', dx, dy, ev);
|
|
2660
|
+
this.updateAutoScroll(ev); // will possibly cause scrolling
|
|
2661
|
+
},
|
|
2371
2662
|
|
|
2372
|
-
if (!this.isListening) { // startDrag must have manually initiated
|
|
2373
|
-
this.startListening();
|
|
2374
|
-
}
|
|
2375
2663
|
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
this.
|
|
2664
|
+
endDrag: function(ev) {
|
|
2665
|
+
if (this.isDragging) {
|
|
2666
|
+
this.isDragging = false;
|
|
2667
|
+
this.handleDragEnd(ev);
|
|
2379
2668
|
}
|
|
2380
2669
|
},
|
|
2381
2670
|
|
|
2382
2671
|
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2672
|
+
handleDragEnd: function(ev) {
|
|
2673
|
+
this.trigger('dragEnd', ev);
|
|
2674
|
+
this.destroyHrefHack();
|
|
2675
|
+
},
|
|
2386
2676
|
|
|
2387
|
-
this.trigger('dragStart', ev);
|
|
2388
2677
|
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2678
|
+
// Delay
|
|
2679
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2680
|
+
|
|
2681
|
+
|
|
2682
|
+
startDelay: function(initialEv) {
|
|
2683
|
+
var _this = this;
|
|
2684
|
+
|
|
2685
|
+
if (this.delay) {
|
|
2686
|
+
this.delayTimeoutId = setTimeout(function() {
|
|
2687
|
+
_this.handleDelayEnd(initialEv);
|
|
2688
|
+
}, this.delay);
|
|
2689
|
+
}
|
|
2690
|
+
else {
|
|
2691
|
+
this.handleDelayEnd(initialEv);
|
|
2392
2692
|
}
|
|
2393
2693
|
},
|
|
2394
2694
|
|
|
2395
2695
|
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
this.
|
|
2696
|
+
handleDelayEnd: function(initialEv) {
|
|
2697
|
+
this.isDelayEnded = true;
|
|
2698
|
+
|
|
2699
|
+
if (this.isDistanceSurpassed) {
|
|
2700
|
+
this.startDrag(initialEv);
|
|
2701
|
+
}
|
|
2400
2702
|
},
|
|
2401
2703
|
|
|
2402
2704
|
|
|
2403
|
-
//
|
|
2404
|
-
|
|
2405
|
-
|
|
2705
|
+
// Distance
|
|
2706
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2707
|
+
|
|
2708
|
+
|
|
2709
|
+
handleDistanceSurpassed: function(ev) {
|
|
2710
|
+
this.isDistanceSurpassed = true;
|
|
2711
|
+
|
|
2712
|
+
if (this.isDelayEnded) {
|
|
2713
|
+
this.startDrag(ev);
|
|
2714
|
+
}
|
|
2406
2715
|
},
|
|
2407
2716
|
|
|
2408
2717
|
|
|
2409
|
-
//
|
|
2410
|
-
//
|
|
2411
|
-
|
|
2718
|
+
// Mouse / Touch
|
|
2719
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2720
|
+
|
|
2721
|
+
|
|
2722
|
+
handleTouchMove: function(ev) {
|
|
2723
|
+
// prevent inertia and touchmove-scrolling while dragging
|
|
2412
2724
|
if (this.isDragging) {
|
|
2413
|
-
|
|
2414
|
-
this.dragStop(ev);
|
|
2415
|
-
this.isDragging = false;
|
|
2725
|
+
ev.preventDefault();
|
|
2416
2726
|
}
|
|
2727
|
+
|
|
2728
|
+
this.handleMove(ev);
|
|
2417
2729
|
},
|
|
2418
2730
|
|
|
2419
2731
|
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2732
|
+
handleMouseMove: function(ev) {
|
|
2733
|
+
this.handleMove(ev);
|
|
2734
|
+
},
|
|
2423
2735
|
|
|
2424
|
-
this.trigger('dragStop', ev);
|
|
2425
2736
|
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
if (_this.subjectHref) {
|
|
2429
|
-
_this.subjectEl.attr('href', _this.subjectHref);
|
|
2430
|
-
}
|
|
2431
|
-
}, 0);
|
|
2432
|
-
},
|
|
2737
|
+
// Scrolling (unrelated to auto-scroll)
|
|
2738
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2433
2739
|
|
|
2434
2740
|
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2741
|
+
handleTouchScroll: function(ev) {
|
|
2742
|
+
// if the drag is being initiated by touch, but a scroll happens before
|
|
2743
|
+
// the drag-initiating delay is over, cancel the drag
|
|
2744
|
+
if (!this.isDragging) {
|
|
2745
|
+
this.endInteraction(ev, true); // isCancelled=true
|
|
2746
|
+
}
|
|
2747
|
+
},
|
|
2438
2748
|
|
|
2439
|
-
if (this.isListening) {
|
|
2440
2749
|
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
this.scrollEl.off('scroll', this.scrollHandlerProxy);
|
|
2444
|
-
this.scrollHandlerProxy = null;
|
|
2445
|
-
}
|
|
2750
|
+
// <A> HREF Hack
|
|
2751
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2446
2752
|
|
|
2447
|
-
$(document)
|
|
2448
|
-
.off('mousemove', this.mousemoveProxy)
|
|
2449
|
-
.off('mouseup', this.mouseupProxy)
|
|
2450
|
-
.off('selectstart', this.preventDefault);
|
|
2451
2753
|
|
|
2452
|
-
|
|
2453
|
-
|
|
2754
|
+
initHrefHack: function() {
|
|
2755
|
+
var subjectEl = this.subjectEl;
|
|
2454
2756
|
|
|
2455
|
-
|
|
2456
|
-
|
|
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');
|
|
2457
2760
|
}
|
|
2458
2761
|
},
|
|
2459
2762
|
|
|
2460
2763
|
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
this.
|
|
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);
|
|
2464
2774
|
},
|
|
2465
2775
|
|
|
2466
2776
|
|
|
2777
|
+
// Utils
|
|
2778
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
2779
|
+
|
|
2780
|
+
|
|
2467
2781
|
// Triggers a callback. Calls a function in the option hash of the same name.
|
|
2468
2782
|
// Arguments beyond the first `name` are forwarded on.
|
|
2469
2783
|
trigger: function(name) {
|
|
2470
2784
|
if (this.options[name]) {
|
|
2471
2785
|
this.options[name].apply(this, Array.prototype.slice.call(arguments, 1));
|
|
2472
2786
|
}
|
|
2473
|
-
|
|
2787
|
+
// makes _methods callable by event name. TODO: kill this
|
|
2788
|
+
if (this['_' + name]) {
|
|
2789
|
+
this['_' + name].apply(this, Array.prototype.slice.call(arguments, 1));
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
|
|
2794
|
+
});
|
|
2795
|
+
|
|
2796
|
+
;;
|
|
2797
|
+
/*
|
|
2798
|
+
this.scrollEl is set in DragListener
|
|
2799
|
+
*/
|
|
2800
|
+
DragListener.mixin({
|
|
2801
|
+
|
|
2802
|
+
isAutoScroll: false,
|
|
2803
|
+
|
|
2804
|
+
scrollBounds: null, // { top, bottom, left, right }
|
|
2805
|
+
scrollTopVel: null, // pixels per second
|
|
2806
|
+
scrollLeftVel: null, // pixels per second
|
|
2807
|
+
scrollIntervalId: null, // ID of setTimeout for scrolling animation loop
|
|
2808
|
+
|
|
2809
|
+
// defaults
|
|
2810
|
+
scrollSensitivity: 30, // pixels from edge for scrolling to start
|
|
2811
|
+
scrollSpeed: 200, // pixels per second, at maximum speed
|
|
2812
|
+
scrollIntervalMs: 50, // millisecond wait between scroll increment
|
|
2813
|
+
|
|
2814
|
+
|
|
2815
|
+
initAutoScroll: function() {
|
|
2816
|
+
var scrollEl = this.scrollEl;
|
|
2474
2817
|
|
|
2818
|
+
this.isAutoScroll =
|
|
2819
|
+
this.options.scroll &&
|
|
2820
|
+
scrollEl &&
|
|
2821
|
+
!scrollEl.is(window) &&
|
|
2822
|
+
!scrollEl.is(document);
|
|
2475
2823
|
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2824
|
+
if (this.isAutoScroll) {
|
|
2825
|
+
// debounce makes sure rapid calls don't happen
|
|
2826
|
+
this.listenTo(scrollEl, 'scroll', debounce(this.handleDebouncedScroll, 100));
|
|
2827
|
+
}
|
|
2479
2828
|
},
|
|
2480
2829
|
|
|
2481
2830
|
|
|
2482
|
-
|
|
2483
|
-
|
|
2831
|
+
destroyAutoScroll: function() {
|
|
2832
|
+
this.endAutoScroll(); // kill any animation loop
|
|
2833
|
+
|
|
2834
|
+
// remove the scroll handler if there is a scrollEl
|
|
2835
|
+
if (this.isAutoScroll) {
|
|
2836
|
+
this.stopListeningTo(this.scrollEl, 'scroll'); // will probably get removed by unbindHandlers too :(
|
|
2837
|
+
}
|
|
2838
|
+
},
|
|
2484
2839
|
|
|
2485
2840
|
|
|
2486
2841
|
// Computes and stores the bounding rectangle of scrollEl
|
|
2487
2842
|
computeScrollBounds: function() {
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
this.scrollBounds = el ? getOuterRect(el) : null;
|
|
2843
|
+
if (this.isAutoScroll) {
|
|
2844
|
+
this.scrollBounds = getOuterRect(this.scrollEl);
|
|
2491
2845
|
// TODO: use getClientRect in future. but prevents auto scrolling when on top of scrollbars
|
|
2846
|
+
}
|
|
2492
2847
|
},
|
|
2493
2848
|
|
|
2494
2849
|
|
|
2495
2850
|
// Called when the dragging is in progress and scrolling should be updated
|
|
2496
|
-
|
|
2851
|
+
updateAutoScroll: function(ev) {
|
|
2497
2852
|
var sensitivity = this.scrollSensitivity;
|
|
2498
2853
|
var bounds = this.scrollBounds;
|
|
2499
2854
|
var topCloseness, bottomCloseness;
|
|
@@ -2504,10 +2859,10 @@ var DragListener = FC.DragListener = Class.extend({
|
|
|
2504
2859
|
if (bounds) { // only scroll if scrollEl exists
|
|
2505
2860
|
|
|
2506
2861
|
// compute closeness to edges. valid range is from 0.0 - 1.0
|
|
2507
|
-
topCloseness = (sensitivity - (ev
|
|
2508
|
-
bottomCloseness = (sensitivity - (bounds.bottom - ev
|
|
2509
|
-
leftCloseness = (sensitivity - (ev
|
|
2510
|
-
rightCloseness = (sensitivity - (bounds.right - ev
|
|
2862
|
+
topCloseness = (sensitivity - (getEvY(ev) - bounds.top)) / sensitivity;
|
|
2863
|
+
bottomCloseness = (sensitivity - (bounds.bottom - getEvY(ev))) / sensitivity;
|
|
2864
|
+
leftCloseness = (sensitivity - (getEvX(ev) - bounds.left)) / sensitivity;
|
|
2865
|
+
rightCloseness = (sensitivity - (bounds.right - getEvX(ev))) / sensitivity;
|
|
2511
2866
|
|
|
2512
2867
|
// translate vertical closeness into velocity.
|
|
2513
2868
|
// mouse must be completely in bounds for velocity to happen.
|
|
@@ -2594,38 +2949,36 @@ var DragListener = FC.DragListener = Class.extend({
|
|
|
2594
2949
|
|
|
2595
2950
|
// if scrolled all the way, which causes the vels to be zero, stop the animation loop
|
|
2596
2951
|
if (!this.scrollTopVel && !this.scrollLeftVel) {
|
|
2597
|
-
this.
|
|
2952
|
+
this.endAutoScroll();
|
|
2598
2953
|
}
|
|
2599
2954
|
},
|
|
2600
2955
|
|
|
2601
2956
|
|
|
2602
2957
|
// Kills any existing scrolling animation loop
|
|
2603
|
-
|
|
2958
|
+
endAutoScroll: function() {
|
|
2604
2959
|
if (this.scrollIntervalId) {
|
|
2605
2960
|
clearInterval(this.scrollIntervalId);
|
|
2606
2961
|
this.scrollIntervalId = null;
|
|
2607
2962
|
|
|
2608
|
-
|
|
2609
|
-
this.scrollStop();
|
|
2963
|
+
this.handleScrollEnd();
|
|
2610
2964
|
}
|
|
2611
2965
|
},
|
|
2612
2966
|
|
|
2613
2967
|
|
|
2614
2968
|
// Get called when the scrollEl is scrolled (NOTE: this is delayed via debounce)
|
|
2615
|
-
|
|
2969
|
+
handleDebouncedScroll: function() {
|
|
2616
2970
|
// recompute all coordinates, but *only* if this is *not* part of our scrolling animation
|
|
2617
2971
|
if (!this.scrollIntervalId) {
|
|
2618
|
-
this.
|
|
2972
|
+
this.handleScrollEnd();
|
|
2619
2973
|
}
|
|
2620
2974
|
},
|
|
2621
2975
|
|
|
2622
2976
|
|
|
2623
2977
|
// Called when scrolling has stopped, whether through auto scroll, or the user scrolling
|
|
2624
|
-
|
|
2978
|
+
handleScrollEnd: function() {
|
|
2625
2979
|
}
|
|
2626
2980
|
|
|
2627
2981
|
});
|
|
2628
|
-
|
|
2629
2982
|
;;
|
|
2630
2983
|
|
|
2631
2984
|
/* Tracks mouse movements over a component and raises events about which hit the mouse is over.
|
|
@@ -2654,18 +3007,16 @@ var HitDragListener = DragListener.extend({
|
|
|
2654
3007
|
|
|
2655
3008
|
// Called when drag listening starts (but a real drag has not necessarily began).
|
|
2656
3009
|
// ev might be undefined if dragging was started manually.
|
|
2657
|
-
|
|
3010
|
+
handleInteractionStart: function(ev) {
|
|
2658
3011
|
var subjectEl = this.subjectEl;
|
|
2659
3012
|
var subjectRect;
|
|
2660
3013
|
var origPoint;
|
|
2661
3014
|
var point;
|
|
2662
3015
|
|
|
2663
|
-
DragListener.prototype.listenStart.apply(this, arguments); // call the super-method
|
|
2664
|
-
|
|
2665
3016
|
this.computeCoords();
|
|
2666
3017
|
|
|
2667
3018
|
if (ev) {
|
|
2668
|
-
origPoint = { left: ev
|
|
3019
|
+
origPoint = { left: getEvX(ev), top: getEvY(ev) };
|
|
2669
3020
|
point = origPoint;
|
|
2670
3021
|
|
|
2671
3022
|
// constrain the point to bounds of the element being dragged
|
|
@@ -2695,61 +3046,64 @@ var HitDragListener = DragListener.extend({
|
|
|
2695
3046
|
this.origHit = null;
|
|
2696
3047
|
this.coordAdjust = null;
|
|
2697
3048
|
}
|
|
3049
|
+
|
|
3050
|
+
// call the super-method. do it after origHit has been computed
|
|
3051
|
+
DragListener.prototype.handleInteractionStart.apply(this, arguments);
|
|
2698
3052
|
},
|
|
2699
3053
|
|
|
2700
3054
|
|
|
2701
3055
|
// Recomputes the drag-critical positions of elements
|
|
2702
3056
|
computeCoords: function() {
|
|
2703
3057
|
this.component.prepareHits();
|
|
2704
|
-
this.computeScrollBounds(); // why is this here
|
|
3058
|
+
this.computeScrollBounds(); // why is this here??????
|
|
2705
3059
|
},
|
|
2706
3060
|
|
|
2707
3061
|
|
|
2708
3062
|
// Called when the actual drag has started
|
|
2709
|
-
|
|
3063
|
+
handleDragStart: function(ev) {
|
|
2710
3064
|
var hit;
|
|
2711
3065
|
|
|
2712
|
-
DragListener.prototype.
|
|
3066
|
+
DragListener.prototype.handleDragStart.apply(this, arguments); // call the super-method
|
|
2713
3067
|
|
|
2714
3068
|
// might be different from this.origHit if the min-distance is large
|
|
2715
|
-
hit = this.queryHit(ev
|
|
3069
|
+
hit = this.queryHit(getEvX(ev), getEvY(ev));
|
|
2716
3070
|
|
|
2717
3071
|
// report the initial hit the mouse is over
|
|
2718
3072
|
// especially important if no min-distance and drag starts immediately
|
|
2719
3073
|
if (hit) {
|
|
2720
|
-
this.
|
|
3074
|
+
this.handleHitOver(hit);
|
|
2721
3075
|
}
|
|
2722
3076
|
},
|
|
2723
3077
|
|
|
2724
3078
|
|
|
2725
3079
|
// Called when the drag moves
|
|
2726
|
-
|
|
3080
|
+
handleDrag: function(dx, dy, ev) {
|
|
2727
3081
|
var hit;
|
|
2728
3082
|
|
|
2729
|
-
DragListener.prototype.
|
|
3083
|
+
DragListener.prototype.handleDrag.apply(this, arguments); // call the super-method
|
|
2730
3084
|
|
|
2731
|
-
hit = this.queryHit(ev
|
|
3085
|
+
hit = this.queryHit(getEvX(ev), getEvY(ev));
|
|
2732
3086
|
|
|
2733
3087
|
if (!isHitsEqual(hit, this.hit)) { // a different hit than before?
|
|
2734
3088
|
if (this.hit) {
|
|
2735
|
-
this.
|
|
3089
|
+
this.handleHitOut();
|
|
2736
3090
|
}
|
|
2737
3091
|
if (hit) {
|
|
2738
|
-
this.
|
|
3092
|
+
this.handleHitOver(hit);
|
|
2739
3093
|
}
|
|
2740
3094
|
}
|
|
2741
3095
|
},
|
|
2742
3096
|
|
|
2743
3097
|
|
|
2744
3098
|
// Called when dragging has been stopped
|
|
2745
|
-
|
|
2746
|
-
this.
|
|
2747
|
-
DragListener.prototype.
|
|
3099
|
+
handleDragEnd: function() {
|
|
3100
|
+
this.handleHitDone();
|
|
3101
|
+
DragListener.prototype.handleDragEnd.apply(this, arguments); // call the super-method
|
|
2748
3102
|
},
|
|
2749
3103
|
|
|
2750
3104
|
|
|
2751
3105
|
// Called when a the mouse has just moved over a new hit
|
|
2752
|
-
|
|
3106
|
+
handleHitOver: function(hit) {
|
|
2753
3107
|
var isOrig = isHitsEqual(hit, this.origHit);
|
|
2754
3108
|
|
|
2755
3109
|
this.hit = hit;
|
|
@@ -2759,26 +3113,26 @@ var HitDragListener = DragListener.extend({
|
|
|
2759
3113
|
|
|
2760
3114
|
|
|
2761
3115
|
// Called when the mouse has just moved out of a hit
|
|
2762
|
-
|
|
3116
|
+
handleHitOut: function() {
|
|
2763
3117
|
if (this.hit) {
|
|
2764
3118
|
this.trigger('hitOut', this.hit);
|
|
2765
|
-
this.
|
|
3119
|
+
this.handleHitDone();
|
|
2766
3120
|
this.hit = null;
|
|
2767
3121
|
}
|
|
2768
3122
|
},
|
|
2769
3123
|
|
|
2770
3124
|
|
|
2771
3125
|
// Called after a hitOut. Also called before a dragStop
|
|
2772
|
-
|
|
3126
|
+
handleHitDone: function() {
|
|
2773
3127
|
if (this.hit) {
|
|
2774
3128
|
this.trigger('hitDone', this.hit);
|
|
2775
3129
|
}
|
|
2776
3130
|
},
|
|
2777
3131
|
|
|
2778
3132
|
|
|
2779
|
-
// Called when drag
|
|
2780
|
-
|
|
2781
|
-
DragListener.prototype.
|
|
3133
|
+
// Called when the interaction ends, whether there was a real drag or not
|
|
3134
|
+
handleInteractionEnd: function() {
|
|
3135
|
+
DragListener.prototype.handleInteractionEnd.apply(this, arguments); // call the super-method
|
|
2782
3136
|
|
|
2783
3137
|
this.origHit = null;
|
|
2784
3138
|
this.hit = null;
|
|
@@ -2788,8 +3142,8 @@ var HitDragListener = DragListener.extend({
|
|
|
2788
3142
|
|
|
2789
3143
|
|
|
2790
3144
|
// Called when scrolling has stopped, whether through auto scroll, or the user scrolling
|
|
2791
|
-
|
|
2792
|
-
DragListener.prototype.
|
|
3145
|
+
handleScrollEnd: function() {
|
|
3146
|
+
DragListener.prototype.handleScrollEnd.apply(this, arguments); // call the super-method
|
|
2793
3147
|
|
|
2794
3148
|
this.computeCoords(); // hits' absolute positions will be in new places. recompute
|
|
2795
3149
|
},
|
|
@@ -2844,7 +3198,7 @@ function isHitPropsWithin(subHit, superHit) {
|
|
|
2844
3198
|
/* Creates a clone of an element and lets it track the mouse as it moves
|
|
2845
3199
|
----------------------------------------------------------------------------------------------------------------------*/
|
|
2846
3200
|
|
|
2847
|
-
var MouseFollower = Class.extend({
|
|
3201
|
+
var MouseFollower = Class.extend(ListenerMixin, {
|
|
2848
3202
|
|
|
2849
3203
|
options: null,
|
|
2850
3204
|
|
|
@@ -2856,16 +3210,14 @@ var MouseFollower = Class.extend({
|
|
|
2856
3210
|
top0: null,
|
|
2857
3211
|
left0: null,
|
|
2858
3212
|
|
|
2859
|
-
// the
|
|
2860
|
-
|
|
2861
|
-
|
|
3213
|
+
// the absolute coordinates of the initiating touch/mouse action
|
|
3214
|
+
y0: null,
|
|
3215
|
+
x0: null,
|
|
2862
3216
|
|
|
2863
3217
|
// the number of pixels the mouse has moved from its initial position
|
|
2864
3218
|
topDelta: null,
|
|
2865
3219
|
leftDelta: null,
|
|
2866
3220
|
|
|
2867
|
-
mousemoveProxy: null, // document mousemove handler, bound to the MouseFollower's `this`
|
|
2868
|
-
|
|
2869
3221
|
isFollowing: false,
|
|
2870
3222
|
isHidden: false,
|
|
2871
3223
|
isAnimating: false, // doing the revert animation?
|
|
@@ -2882,8 +3234,8 @@ var MouseFollower = Class.extend({
|
|
|
2882
3234
|
if (!this.isFollowing) {
|
|
2883
3235
|
this.isFollowing = true;
|
|
2884
3236
|
|
|
2885
|
-
this.
|
|
2886
|
-
this.
|
|
3237
|
+
this.y0 = getEvY(ev);
|
|
3238
|
+
this.x0 = getEvX(ev);
|
|
2887
3239
|
this.topDelta = 0;
|
|
2888
3240
|
this.leftDelta = 0;
|
|
2889
3241
|
|
|
@@ -2891,7 +3243,12 @@ var MouseFollower = Class.extend({
|
|
|
2891
3243
|
this.updatePosition();
|
|
2892
3244
|
}
|
|
2893
3245
|
|
|
2894
|
-
|
|
3246
|
+
if (getEvIsTouch(ev)) {
|
|
3247
|
+
this.listenTo($(document), 'touchmove', this.handleMove);
|
|
3248
|
+
}
|
|
3249
|
+
else {
|
|
3250
|
+
this.listenTo($(document), 'mousemove', this.handleMove);
|
|
3251
|
+
}
|
|
2895
3252
|
}
|
|
2896
3253
|
},
|
|
2897
3254
|
|
|
@@ -2916,7 +3273,7 @@ var MouseFollower = Class.extend({
|
|
|
2916
3273
|
if (this.isFollowing && !this.isAnimating) { // disallow more than one stop animation at a time
|
|
2917
3274
|
this.isFollowing = false;
|
|
2918
3275
|
|
|
2919
|
-
$(document)
|
|
3276
|
+
this.stopListeningTo($(document));
|
|
2920
3277
|
|
|
2921
3278
|
if (shouldRevert && revertDuration && !this.isHidden) { // do a revert animation?
|
|
2922
3279
|
this.isAnimating = true;
|
|
@@ -2942,6 +3299,7 @@ var MouseFollower = Class.extend({
|
|
|
2942
3299
|
if (!el) {
|
|
2943
3300
|
this.sourceEl.width(); // hack to force IE8 to compute correct bounding box
|
|
2944
3301
|
el = this.el = this.sourceEl.clone()
|
|
3302
|
+
.addClass(this.options.additionalClass || '')
|
|
2945
3303
|
.css({
|
|
2946
3304
|
position: 'absolute',
|
|
2947
3305
|
visibility: '', // in case original element was hidden (commonly through hideEvents())
|
|
@@ -2953,8 +3311,13 @@ var MouseFollower = Class.extend({
|
|
|
2953
3311
|
height: this.sourceEl.height(), // explicit width in case there was a 'bottom' value
|
|
2954
3312
|
opacity: this.options.opacity || '',
|
|
2955
3313
|
zIndex: this.options.zIndex
|
|
2956
|
-
})
|
|
2957
|
-
|
|
3314
|
+
});
|
|
3315
|
+
|
|
3316
|
+
// we don't want long taps or any mouse interaction causing selection/menus.
|
|
3317
|
+
// would use preventSelection(), but that prevents selectstart, causing problems.
|
|
3318
|
+
el.addClass('fc-unselectable');
|
|
3319
|
+
|
|
3320
|
+
el.appendTo(this.parentEl);
|
|
2958
3321
|
}
|
|
2959
3322
|
|
|
2960
3323
|
return el;
|
|
@@ -2994,9 +3357,9 @@ var MouseFollower = Class.extend({
|
|
|
2994
3357
|
|
|
2995
3358
|
|
|
2996
3359
|
// Gets called when the user moves the mouse
|
|
2997
|
-
|
|
2998
|
-
this.topDelta = ev
|
|
2999
|
-
this.leftDelta = ev
|
|
3360
|
+
handleMove: function(ev) {
|
|
3361
|
+
this.topDelta = getEvY(ev) - this.y0;
|
|
3362
|
+
this.leftDelta = getEvX(ev) - this.x0;
|
|
3000
3363
|
|
|
3001
3364
|
if (!this.isHidden) {
|
|
3002
3365
|
this.updatePosition();
|
|
@@ -3031,7 +3394,7 @@ var MouseFollower = Class.extend({
|
|
|
3031
3394
|
/* An abstract class comprised of a "grid" of areas that each represent a specific datetime
|
|
3032
3395
|
----------------------------------------------------------------------------------------------------------------------*/
|
|
3033
3396
|
|
|
3034
|
-
var Grid = FC.Grid = Class.extend({
|
|
3397
|
+
var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
|
|
3035
3398
|
|
|
3036
3399
|
view: null, // a View object
|
|
3037
3400
|
isRTL: null, // shortcut to the view's isRTL option
|
|
@@ -3042,8 +3405,6 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3042
3405
|
el: null, // the containing element
|
|
3043
3406
|
elsByFill: null, // a hash of jQuery element sets used for rendering each fill. Keyed by fill name.
|
|
3044
3407
|
|
|
3045
|
-
externalDragStartProxy: null, // binds the Grid's scope to externalDragStart (in DayGrid.events)
|
|
3046
|
-
|
|
3047
3408
|
// derived from options
|
|
3048
3409
|
eventTimeFormat: null,
|
|
3049
3410
|
displayEventTime: null,
|
|
@@ -3056,13 +3417,19 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3056
3417
|
// TODO: port isTimeScale into same system?
|
|
3057
3418
|
largeUnit: null,
|
|
3058
3419
|
|
|
3420
|
+
dayDragListener: null,
|
|
3421
|
+
segDragListener: null,
|
|
3422
|
+
segResizeListener: null,
|
|
3423
|
+
externalDragListener: null,
|
|
3424
|
+
|
|
3059
3425
|
|
|
3060
3426
|
constructor: function(view) {
|
|
3061
3427
|
this.view = view;
|
|
3062
3428
|
this.isRTL = view.opt('isRTL');
|
|
3063
|
-
|
|
3064
3429
|
this.elsByFill = {};
|
|
3065
|
-
|
|
3430
|
+
|
|
3431
|
+
this.dayDragListener = this.buildDayDragListener();
|
|
3432
|
+
this.initMouseIgnoring();
|
|
3066
3433
|
},
|
|
3067
3434
|
|
|
3068
3435
|
|
|
@@ -3195,26 +3562,33 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3195
3562
|
// Sets the container element that the grid should render inside of.
|
|
3196
3563
|
// Does other DOM-related initializations.
|
|
3197
3564
|
setElement: function(el) {
|
|
3198
|
-
var _this = this;
|
|
3199
|
-
|
|
3200
3565
|
this.el = el;
|
|
3566
|
+
preventSelection(el);
|
|
3567
|
+
|
|
3568
|
+
this.bindDayHandler('touchstart', this.dayTouchStart);
|
|
3569
|
+
this.bindDayHandler('mousedown', this.dayMousedown);
|
|
3570
|
+
|
|
3571
|
+
// attach event-element-related handlers. in Grid.events
|
|
3572
|
+
// same garbage collection note as above.
|
|
3573
|
+
this.bindSegHandlers();
|
|
3574
|
+
|
|
3575
|
+
this.bindGlobalHandlers();
|
|
3576
|
+
},
|
|
3577
|
+
|
|
3578
|
+
|
|
3579
|
+
bindDayHandler: function(name, handler) {
|
|
3580
|
+
var _this = this;
|
|
3201
3581
|
|
|
3202
3582
|
// attach a handler to the grid's root element.
|
|
3203
3583
|
// jQuery will take care of unregistering them when removeElement gets called.
|
|
3204
|
-
el.on(
|
|
3584
|
+
this.el.on(name, function(ev) {
|
|
3205
3585
|
if (
|
|
3206
3586
|
!$(ev.target).is('.fc-event-container *, .fc-more') && // not an an event element, or "more.." link
|
|
3207
3587
|
!$(ev.target).closest('.fc-popover').length // not on a popover (like the "more.." events one)
|
|
3208
3588
|
) {
|
|
3209
|
-
|
|
3589
|
+
return handler.call(_this, ev);
|
|
3210
3590
|
}
|
|
3211
3591
|
});
|
|
3212
|
-
|
|
3213
|
-
// attach event-element-related handlers. in Grid.events
|
|
3214
|
-
// same garbage collection note as above.
|
|
3215
|
-
this.bindSegHandlers();
|
|
3216
|
-
|
|
3217
|
-
this.bindGlobalHandlers();
|
|
3218
3592
|
},
|
|
3219
3593
|
|
|
3220
3594
|
|
|
@@ -3222,6 +3596,7 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3222
3596
|
// DOES NOT remove any content beforehand (doesn't clear events or call unrenderDates), unlike View
|
|
3223
3597
|
removeElement: function() {
|
|
3224
3598
|
this.unbindGlobalHandlers();
|
|
3599
|
+
this.clearDragListeners();
|
|
3225
3600
|
|
|
3226
3601
|
this.el.remove();
|
|
3227
3602
|
|
|
@@ -3254,18 +3629,47 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3254
3629
|
|
|
3255
3630
|
// Binds DOM handlers to elements that reside outside the grid, such as the document
|
|
3256
3631
|
bindGlobalHandlers: function() {
|
|
3257
|
-
$(document)
|
|
3632
|
+
this.listenTo($(document), {
|
|
3633
|
+
dragstart: this.externalDragStart, // jqui
|
|
3634
|
+
sortstart: this.externalDragStart // jqui
|
|
3635
|
+
});
|
|
3258
3636
|
},
|
|
3259
3637
|
|
|
3260
3638
|
|
|
3261
3639
|
// Unbinds DOM handlers from elements that reside outside the grid
|
|
3262
3640
|
unbindGlobalHandlers: function() {
|
|
3263
|
-
$(document)
|
|
3641
|
+
this.stopListeningTo($(document));
|
|
3264
3642
|
},
|
|
3265
3643
|
|
|
3266
3644
|
|
|
3267
3645
|
// Process a mousedown on an element that represents a day. For day clicking and selecting.
|
|
3268
3646
|
dayMousedown: function(ev) {
|
|
3647
|
+
if (!this.isIgnoringMouse) {
|
|
3648
|
+
this.dayDragListener.startInteraction(ev, {
|
|
3649
|
+
//distance: 5, // needs more work if we want dayClick to fire correctly
|
|
3650
|
+
});
|
|
3651
|
+
}
|
|
3652
|
+
},
|
|
3653
|
+
|
|
3654
|
+
|
|
3655
|
+
dayTouchStart: function(ev) {
|
|
3656
|
+
var view = this.view;
|
|
3657
|
+
|
|
3658
|
+
// HACK to prevent a user's clickaway for unselecting a range or an event
|
|
3659
|
+
// from causing a dayClick.
|
|
3660
|
+
if (view.isSelected || view.selectedEvent) {
|
|
3661
|
+
this.tempIgnoreMouse();
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
this.dayDragListener.startInteraction(ev, {
|
|
3665
|
+
delay: this.view.opt('longPressDelay')
|
|
3666
|
+
});
|
|
3667
|
+
},
|
|
3668
|
+
|
|
3669
|
+
|
|
3670
|
+
// Creates a listener that tracks the user's drag across day elements.
|
|
3671
|
+
// For day clicking and selecting.
|
|
3672
|
+
buildDayDragListener: function() {
|
|
3269
3673
|
var _this = this;
|
|
3270
3674
|
var view = this.view;
|
|
3271
3675
|
var isSelectable = view.opt('selectable');
|
|
@@ -3276,14 +3680,21 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3276
3680
|
// if the drag ends on the same day, it is a 'dayClick'.
|
|
3277
3681
|
// if 'selectable' is enabled, this listener also detects selections.
|
|
3278
3682
|
var dragListener = new HitDragListener(this, {
|
|
3279
|
-
//distance: 5, // needs more work if we want dayClick to fire correctly
|
|
3280
3683
|
scroll: view.opt('dragScroll'),
|
|
3684
|
+
interactionStart: function() {
|
|
3685
|
+
dayClickHit = dragListener.origHit; // for dayClick, where no dragging happens
|
|
3686
|
+
},
|
|
3281
3687
|
dragStart: function() {
|
|
3282
3688
|
view.unselect(); // since we could be rendering a new selection, we want to clear any old one
|
|
3283
3689
|
},
|
|
3284
3690
|
hitOver: function(hit, isOrig, origHit) {
|
|
3285
3691
|
if (origHit) { // click needs to have started on a hit
|
|
3286
|
-
|
|
3692
|
+
|
|
3693
|
+
// if user dragged to another cell at any point, it can no longer be a dayClick
|
|
3694
|
+
if (!isOrig) {
|
|
3695
|
+
dayClickHit = null;
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3287
3698
|
if (isSelectable) {
|
|
3288
3699
|
selectionSpan = _this.computeSelection(
|
|
3289
3700
|
_this.getHitSpan(origHit),
|
|
@@ -3304,23 +3715,46 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3304
3715
|
_this.unrenderSelection();
|
|
3305
3716
|
enableCursor();
|
|
3306
3717
|
},
|
|
3307
|
-
|
|
3308
|
-
if (
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
_this.
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3718
|
+
interactionEnd: function(ev, isCancelled) {
|
|
3719
|
+
if (!isCancelled) {
|
|
3720
|
+
if (
|
|
3721
|
+
dayClickHit &&
|
|
3722
|
+
!_this.isIgnoringMouse // see hack in dayTouchStart
|
|
3723
|
+
) {
|
|
3724
|
+
view.triggerDayClick(
|
|
3725
|
+
_this.getHitSpan(dayClickHit),
|
|
3726
|
+
_this.getHitEl(dayClickHit),
|
|
3727
|
+
ev
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3730
|
+
if (selectionSpan) {
|
|
3731
|
+
// the selection will already have been rendered. just report it
|
|
3732
|
+
view.reportSelection(selectionSpan, ev);
|
|
3733
|
+
}
|
|
3734
|
+
enableCursor();
|
|
3318
3735
|
}
|
|
3319
|
-
enableCursor();
|
|
3320
3736
|
}
|
|
3321
3737
|
});
|
|
3322
3738
|
|
|
3323
|
-
dragListener
|
|
3739
|
+
return dragListener;
|
|
3740
|
+
},
|
|
3741
|
+
|
|
3742
|
+
|
|
3743
|
+
// Kills all in-progress dragging.
|
|
3744
|
+
// Useful for when public API methods that result in re-rendering are invoked during a drag.
|
|
3745
|
+
// Also useful for when touch devices misbehave and don't fire their touchend.
|
|
3746
|
+
clearDragListeners: function() {
|
|
3747
|
+
this.dayDragListener.endInteraction();
|
|
3748
|
+
|
|
3749
|
+
if (this.segDragListener) {
|
|
3750
|
+
this.segDragListener.endInteraction(); // will clear this.segDragListener
|
|
3751
|
+
}
|
|
3752
|
+
if (this.segResizeListener) {
|
|
3753
|
+
this.segResizeListener.endInteraction(); // will clear this.segResizeListener
|
|
3754
|
+
}
|
|
3755
|
+
if (this.externalDragListener) {
|
|
3756
|
+
this.externalDragListener.endInteraction(); // will clear this.externalDragListener
|
|
3757
|
+
}
|
|
3324
3758
|
},
|
|
3325
3759
|
|
|
3326
3760
|
|
|
@@ -3330,10 +3764,11 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3330
3764
|
|
|
3331
3765
|
|
|
3332
3766
|
// Renders a mock event at the given event location, which contains zoned start/end properties.
|
|
3767
|
+
// Returns all mock event elements.
|
|
3333
3768
|
renderEventLocationHelper: function(eventLocation, sourceSeg) {
|
|
3334
3769
|
var fakeEvent = this.fabricateHelperEvent(eventLocation, sourceSeg);
|
|
3335
3770
|
|
|
3336
|
-
this.renderHelper(fakeEvent, sourceSeg); // do the actual rendering
|
|
3771
|
+
return this.renderHelper(fakeEvent, sourceSeg); // do the actual rendering
|
|
3337
3772
|
},
|
|
3338
3773
|
|
|
3339
3774
|
|
|
@@ -3361,6 +3796,7 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3361
3796
|
|
|
3362
3797
|
|
|
3363
3798
|
// Renders a mock event. Given zoned event date properties.
|
|
3799
|
+
// Must return all mock event elements.
|
|
3364
3800
|
renderHelper: function(eventLocation, sourceSeg) {
|
|
3365
3801
|
// subclasses must implement
|
|
3366
3802
|
},
|
|
@@ -3538,7 +3974,7 @@ var Grid = FC.Grid = Class.extend({
|
|
|
3538
3974
|
fillSegTag: 'div', // subclasses can override
|
|
3539
3975
|
|
|
3540
3976
|
|
|
3541
|
-
// Builds the HTML needed for one fill segment. Generic
|
|
3977
|
+
// Builds the HTML needed for one fill segment. Generic enough to work with different types.
|
|
3542
3978
|
fillSegHtml: function(type, seg) {
|
|
3543
3979
|
|
|
3544
3980
|
// custom hooks per-type
|
|
@@ -3640,7 +4076,8 @@ Grid.mixin({
|
|
|
3640
4076
|
|
|
3641
4077
|
// Unrenders all events currently rendered on the grid
|
|
3642
4078
|
unrenderEvents: function() {
|
|
3643
|
-
this.
|
|
4079
|
+
this.handleSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
|
|
4080
|
+
this.clearDragListeners();
|
|
3644
4081
|
|
|
3645
4082
|
this.unrenderFgSegs();
|
|
3646
4083
|
this.unrenderBgSegs();
|
|
@@ -3768,48 +4205,44 @@ Grid.mixin({
|
|
|
3768
4205
|
|
|
3769
4206
|
// Attaches event-element-related handlers to the container element and leverage bubbling
|
|
3770
4207
|
bindSegHandlers: function() {
|
|
4208
|
+
this.bindSegHandler('touchstart', this.handleSegTouchStart);
|
|
4209
|
+
this.bindSegHandler('touchend', this.handleSegTouchEnd);
|
|
4210
|
+
this.bindSegHandler('mouseenter', this.handleSegMouseover);
|
|
4211
|
+
this.bindSegHandler('mouseleave', this.handleSegMouseout);
|
|
4212
|
+
this.bindSegHandler('mousedown', this.handleSegMousedown);
|
|
4213
|
+
this.bindSegHandler('click', this.handleSegClick);
|
|
4214
|
+
},
|
|
4215
|
+
|
|
4216
|
+
|
|
4217
|
+
// Executes a handler for any a user-interaction on a segment.
|
|
4218
|
+
// Handler gets called with (seg, ev), and with the `this` context of the Grid
|
|
4219
|
+
bindSegHandler: function(name, handler) {
|
|
3771
4220
|
var _this = this;
|
|
3772
|
-
var view = this.view;
|
|
3773
4221
|
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
_this.triggerSegMouseout(seg, ev);
|
|
3781
|
-
},
|
|
3782
|
-
click: function(seg, ev) {
|
|
3783
|
-
return view.trigger('eventClick', this, seg.event, ev); // can return `false` to cancel
|
|
3784
|
-
},
|
|
3785
|
-
mousedown: function(seg, ev) {
|
|
3786
|
-
if ($(ev.target).is('.fc-resizer') && view.isEventResizable(seg.event)) {
|
|
3787
|
-
_this.segResizeMousedown(seg, ev, $(ev.target).is('.fc-start-resizer'));
|
|
3788
|
-
}
|
|
3789
|
-
else if (view.isEventDraggable(seg.event)) {
|
|
3790
|
-
_this.segDragMousedown(seg, ev);
|
|
3791
|
-
}
|
|
3792
|
-
}
|
|
3793
|
-
},
|
|
3794
|
-
function(name, func) {
|
|
3795
|
-
// attach the handler to the container element and only listen for real event elements via bubbling
|
|
3796
|
-
_this.el.on(name, '.fc-event-container > *', function(ev) {
|
|
3797
|
-
var seg = $(this).data('fc-seg'); // grab segment data. put there by View::renderEvents
|
|
3798
|
-
|
|
3799
|
-
// only call the handlers if there is not a drag/resize in progress
|
|
3800
|
-
if (seg && !_this.isDraggingSeg && !_this.isResizingSeg) {
|
|
3801
|
-
return func.call(this, seg, ev); // `this` will be the event element
|
|
3802
|
-
}
|
|
3803
|
-
});
|
|
4222
|
+
this.el.on(name, '.fc-event-container > *', function(ev) {
|
|
4223
|
+
var seg = $(this).data('fc-seg'); // grab segment data. put there by View::renderEvents
|
|
4224
|
+
|
|
4225
|
+
// only call the handlers if there is not a drag/resize in progress
|
|
4226
|
+
if (seg && !_this.isDraggingSeg && !_this.isResizingSeg) {
|
|
4227
|
+
return handler.call(_this, seg, ev); // context will be the Grid
|
|
3804
4228
|
}
|
|
3805
|
-
);
|
|
4229
|
+
});
|
|
4230
|
+
},
|
|
4231
|
+
|
|
4232
|
+
|
|
4233
|
+
handleSegClick: function(seg, ev) {
|
|
4234
|
+
return this.view.trigger('eventClick', seg.el[0], seg.event, ev); // can return `false` to cancel
|
|
3806
4235
|
},
|
|
3807
4236
|
|
|
3808
4237
|
|
|
3809
4238
|
// Updates internal state and triggers handlers for when an event element is moused over
|
|
3810
|
-
|
|
3811
|
-
if (
|
|
4239
|
+
handleSegMouseover: function(seg, ev) {
|
|
4240
|
+
if (
|
|
4241
|
+
!this.isIgnoringMouse &&
|
|
4242
|
+
!this.mousedOverSeg
|
|
4243
|
+
) {
|
|
3812
4244
|
this.mousedOverSeg = seg;
|
|
4245
|
+
seg.el.addClass('fc-allow-mouse-resize');
|
|
3813
4246
|
this.view.trigger('eventMouseover', seg.el[0], seg.event, ev);
|
|
3814
4247
|
}
|
|
3815
4248
|
},
|
|
@@ -3817,56 +4250,132 @@ Grid.mixin({
|
|
|
3817
4250
|
|
|
3818
4251
|
// Updates internal state and triggers handlers for when an event element is moused out.
|
|
3819
4252
|
// Can be given no arguments, in which case it will mouseout the segment that was previously moused over.
|
|
3820
|
-
|
|
4253
|
+
handleSegMouseout: function(seg, ev) {
|
|
3821
4254
|
ev = ev || {}; // if given no args, make a mock mouse event
|
|
3822
4255
|
|
|
3823
4256
|
if (this.mousedOverSeg) {
|
|
3824
4257
|
seg = seg || this.mousedOverSeg; // if given no args, use the currently moused-over segment
|
|
3825
4258
|
this.mousedOverSeg = null;
|
|
4259
|
+
seg.el.removeClass('fc-allow-mouse-resize');
|
|
3826
4260
|
this.view.trigger('eventMouseout', seg.el[0], seg.event, ev);
|
|
3827
4261
|
}
|
|
3828
4262
|
},
|
|
3829
4263
|
|
|
3830
4264
|
|
|
4265
|
+
handleSegMousedown: function(seg, ev) {
|
|
4266
|
+
var isResizing = this.startSegResize(seg, ev, { distance: 5 });
|
|
4267
|
+
|
|
4268
|
+
if (!isResizing && this.view.isEventDraggable(seg.event)) {
|
|
4269
|
+
this.buildSegDragListener(seg)
|
|
4270
|
+
.startInteraction(ev, {
|
|
4271
|
+
distance: 5
|
|
4272
|
+
});
|
|
4273
|
+
}
|
|
4274
|
+
},
|
|
4275
|
+
|
|
4276
|
+
|
|
4277
|
+
handleSegTouchStart: function(seg, ev) {
|
|
4278
|
+
var view = this.view;
|
|
4279
|
+
var event = seg.event;
|
|
4280
|
+
var isSelected = view.isEventSelected(event);
|
|
4281
|
+
var isDraggable = view.isEventDraggable(event);
|
|
4282
|
+
var isResizable = view.isEventResizable(event);
|
|
4283
|
+
var isResizing = false;
|
|
4284
|
+
var dragListener;
|
|
4285
|
+
|
|
4286
|
+
if (isSelected && isResizable) {
|
|
4287
|
+
// only allow resizing of the event is selected
|
|
4288
|
+
isResizing = this.startSegResize(seg, ev);
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
if (!isResizing && (isDraggable || isResizable)) { // allowed to be selected?
|
|
4292
|
+
|
|
4293
|
+
dragListener = isDraggable ?
|
|
4294
|
+
this.buildSegDragListener(seg) :
|
|
4295
|
+
this.buildSegSelectListener(seg); // seg isn't draggable, but still needs to be selected
|
|
4296
|
+
|
|
4297
|
+
dragListener.startInteraction(ev, { // won't start if already started
|
|
4298
|
+
delay: isSelected ? 0 : this.view.opt('longPressDelay') // do delay if not already selected
|
|
4299
|
+
});
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
// a long tap simulates a mouseover. ignore this bogus mouseover.
|
|
4303
|
+
this.tempIgnoreMouse();
|
|
4304
|
+
},
|
|
4305
|
+
|
|
4306
|
+
|
|
4307
|
+
handleSegTouchEnd: function(seg, ev) {
|
|
4308
|
+
// touchstart+touchend = click, which simulates a mouseover.
|
|
4309
|
+
// ignore this bogus mouseover.
|
|
4310
|
+
this.tempIgnoreMouse();
|
|
4311
|
+
},
|
|
4312
|
+
|
|
4313
|
+
|
|
4314
|
+
// returns boolean whether resizing actually started or not.
|
|
4315
|
+
// assumes the seg allows resizing.
|
|
4316
|
+
// `dragOptions` are optional.
|
|
4317
|
+
startSegResize: function(seg, ev, dragOptions) {
|
|
4318
|
+
if ($(ev.target).is('.fc-resizer')) {
|
|
4319
|
+
this.buildSegResizeListener(seg, $(ev.target).is('.fc-start-resizer'))
|
|
4320
|
+
.startInteraction(ev, dragOptions);
|
|
4321
|
+
return true;
|
|
4322
|
+
}
|
|
4323
|
+
return false;
|
|
4324
|
+
},
|
|
4325
|
+
|
|
4326
|
+
|
|
4327
|
+
|
|
3831
4328
|
/* Event Dragging
|
|
3832
4329
|
------------------------------------------------------------------------------------------------------------------*/
|
|
3833
4330
|
|
|
3834
4331
|
|
|
3835
|
-
//
|
|
4332
|
+
// Builds a listener that will track user-dragging on an event segment.
|
|
3836
4333
|
// Generic enough to work with any type of Grid.
|
|
3837
|
-
|
|
4334
|
+
// Has side effect of setting/unsetting `segDragListener`
|
|
4335
|
+
buildSegDragListener: function(seg) {
|
|
3838
4336
|
var _this = this;
|
|
3839
4337
|
var view = this.view;
|
|
3840
4338
|
var calendar = view.calendar;
|
|
3841
4339
|
var el = seg.el;
|
|
3842
4340
|
var event = seg.event;
|
|
4341
|
+
var isDragging;
|
|
4342
|
+
var mouseFollower; // A clone of the original element that will move with the mouse
|
|
3843
4343
|
var dropLocation; // zoned event date properties
|
|
3844
4344
|
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
opacity: view.opt('dragOpacity'),
|
|
3849
|
-
revertDuration: view.opt('dragRevertDuration'),
|
|
3850
|
-
zIndex: 2 // one above the .fc-view
|
|
3851
|
-
});
|
|
4345
|
+
if (this.segDragListener) {
|
|
4346
|
+
return this.segDragListener;
|
|
4347
|
+
}
|
|
3852
4348
|
|
|
3853
4349
|
// Tracks mouse movement over the *view's* coordinate map. Allows dragging and dropping between subcomponents
|
|
3854
4350
|
// of the view.
|
|
3855
|
-
var dragListener = new HitDragListener(view, {
|
|
3856
|
-
distance: 5,
|
|
4351
|
+
var dragListener = this.segDragListener = new HitDragListener(view, {
|
|
3857
4352
|
scroll: view.opt('dragScroll'),
|
|
3858
4353
|
subjectEl: el,
|
|
3859
4354
|
subjectCenter: true,
|
|
3860
|
-
|
|
4355
|
+
interactionStart: function(ev) {
|
|
4356
|
+
isDragging = false;
|
|
4357
|
+
mouseFollower = new MouseFollower(seg.el, {
|
|
4358
|
+
additionalClass: 'fc-dragging',
|
|
4359
|
+
parentEl: view.el,
|
|
4360
|
+
opacity: dragListener.isTouch ? null : view.opt('dragOpacity'),
|
|
4361
|
+
revertDuration: view.opt('dragRevertDuration'),
|
|
4362
|
+
zIndex: 2 // one above the .fc-view
|
|
4363
|
+
});
|
|
3861
4364
|
mouseFollower.hide(); // don't show until we know this is a real drag
|
|
3862
4365
|
mouseFollower.start(ev);
|
|
3863
4366
|
},
|
|
3864
4367
|
dragStart: function(ev) {
|
|
3865
|
-
|
|
4368
|
+
if (dragListener.isTouch && !view.isEventSelected(event)) {
|
|
4369
|
+
// if not previously selected, will fire after a delay. then, select the event
|
|
4370
|
+
view.selectEvent(event);
|
|
4371
|
+
}
|
|
4372
|
+
isDragging = true;
|
|
4373
|
+
_this.handleSegMouseout(seg, ev); // ensure a mouseout on the manipulated event has been reported
|
|
3866
4374
|
_this.segDragStart(seg, ev);
|
|
3867
4375
|
view.hideEvent(event); // hide all event segments. our mouseFollower will take over
|
|
3868
4376
|
},
|
|
3869
4377
|
hitOver: function(hit, isOrig, origHit) {
|
|
4378
|
+
var dragHelperEls;
|
|
3870
4379
|
|
|
3871
4380
|
// starting hit could be forced (DayGrid.limit)
|
|
3872
4381
|
if (seg.hit) {
|
|
@@ -3886,7 +4395,13 @@ Grid.mixin({
|
|
|
3886
4395
|
}
|
|
3887
4396
|
|
|
3888
4397
|
// if a valid drop location, have the subclass render a visual indication
|
|
3889
|
-
if (dropLocation && view.renderDrag(dropLocation, seg)) {
|
|
4398
|
+
if (dropLocation && (dragHelperEls = view.renderDrag(dropLocation, seg))) {
|
|
4399
|
+
|
|
4400
|
+
dragHelperEls.addClass('fc-dragging');
|
|
4401
|
+
if (!dragListener.isTouch) {
|
|
4402
|
+
_this.applyDragOpacity(dragHelperEls);
|
|
4403
|
+
}
|
|
4404
|
+
|
|
3890
4405
|
mouseFollower.hide(); // if the subclass is already using a mock event "helper", hide our own
|
|
3891
4406
|
}
|
|
3892
4407
|
else {
|
|
@@ -3902,27 +4417,54 @@ Grid.mixin({
|
|
|
3902
4417
|
mouseFollower.show(); // show in case we are moving out of all hits
|
|
3903
4418
|
dropLocation = null;
|
|
3904
4419
|
},
|
|
3905
|
-
hitDone: function() { // Called after a hitOut OR before a
|
|
4420
|
+
hitDone: function() { // Called after a hitOut OR before a dragEnd
|
|
3906
4421
|
enableCursor();
|
|
3907
4422
|
},
|
|
3908
|
-
|
|
4423
|
+
interactionEnd: function(ev) {
|
|
3909
4424
|
// do revert animation if hasn't changed. calls a callback when finished (whether animation or not)
|
|
3910
4425
|
mouseFollower.stop(!dropLocation, function() {
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
4426
|
+
if (isDragging) {
|
|
4427
|
+
view.unrenderDrag();
|
|
4428
|
+
view.showEvent(event);
|
|
4429
|
+
_this.segDragStop(seg, ev);
|
|
4430
|
+
}
|
|
3915
4431
|
if (dropLocation) {
|
|
3916
4432
|
view.reportEventDrop(event, dropLocation, this.largeUnit, el, ev);
|
|
3917
4433
|
}
|
|
3918
4434
|
});
|
|
4435
|
+
_this.segDragListener = null;
|
|
4436
|
+
}
|
|
4437
|
+
});
|
|
4438
|
+
|
|
4439
|
+
return dragListener;
|
|
4440
|
+
},
|
|
4441
|
+
|
|
4442
|
+
|
|
4443
|
+
// seg isn't draggable, but let's use a generic DragListener
|
|
4444
|
+
// simply for the delay, so it can be selected.
|
|
4445
|
+
// Has side effect of setting/unsetting `segDragListener`
|
|
4446
|
+
buildSegSelectListener: function(seg) {
|
|
4447
|
+
var _this = this;
|
|
4448
|
+
var view = this.view;
|
|
4449
|
+
var event = seg.event;
|
|
4450
|
+
|
|
4451
|
+
if (this.segDragListener) {
|
|
4452
|
+
return this.segDragListener;
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
var dragListener = this.segDragListener = new DragListener({
|
|
4456
|
+
dragStart: function(ev) {
|
|
4457
|
+
if (dragListener.isTouch && !view.isEventSelected(event)) {
|
|
4458
|
+
// if not previously selected, will fire after a delay. then, select the event
|
|
4459
|
+
view.selectEvent(event);
|
|
4460
|
+
}
|
|
3919
4461
|
},
|
|
3920
|
-
|
|
3921
|
-
|
|
4462
|
+
interactionEnd: function(ev) {
|
|
4463
|
+
_this.segDragListener = null;
|
|
3922
4464
|
}
|
|
3923
4465
|
});
|
|
3924
4466
|
|
|
3925
|
-
dragListener
|
|
4467
|
+
return dragListener;
|
|
3926
4468
|
},
|
|
3927
4469
|
|
|
3928
4470
|
|
|
@@ -4038,8 +4580,8 @@ Grid.mixin({
|
|
|
4038
4580
|
var dropLocation; // a null value signals an unsuccessful drag
|
|
4039
4581
|
|
|
4040
4582
|
// listener that tracks mouse movement over date-associated pixel regions
|
|
4041
|
-
var dragListener = new HitDragListener(this, {
|
|
4042
|
-
|
|
4583
|
+
var dragListener = _this.externalDragListener = new HitDragListener(this, {
|
|
4584
|
+
interactionStart: function() {
|
|
4043
4585
|
_this.isDraggingExternal = true;
|
|
4044
4586
|
},
|
|
4045
4587
|
hitOver: function(hit) {
|
|
@@ -4063,17 +4605,16 @@ Grid.mixin({
|
|
|
4063
4605
|
hitOut: function() {
|
|
4064
4606
|
dropLocation = null; // signal unsuccessful
|
|
4065
4607
|
},
|
|
4066
|
-
hitDone: function() { // Called after a hitOut OR before a
|
|
4608
|
+
hitDone: function() { // Called after a hitOut OR before a dragEnd
|
|
4067
4609
|
enableCursor();
|
|
4068
4610
|
_this.unrenderDrag();
|
|
4069
4611
|
},
|
|
4070
|
-
|
|
4612
|
+
interactionEnd: function(ev) {
|
|
4071
4613
|
if (dropLocation) { // element was dropped on a valid hit
|
|
4072
4614
|
_this.view.reportExternalDrop(meta, dropLocation, el, ev, ui);
|
|
4073
4615
|
}
|
|
4074
|
-
},
|
|
4075
|
-
listenStop: function() {
|
|
4076
4616
|
_this.isDraggingExternal = false;
|
|
4617
|
+
_this.externalDragListener = null;
|
|
4077
4618
|
}
|
|
4078
4619
|
});
|
|
4079
4620
|
|
|
@@ -4114,6 +4655,7 @@ Grid.mixin({
|
|
|
4114
4655
|
// `dropLocation` contains hypothetical start/end/allDay values the event would have if dropped. end can be null.
|
|
4115
4656
|
// `seg` is the internal segment object that is being dragged. If dragging an external element, `seg` is null.
|
|
4116
4657
|
// A truthy returned value indicates this method has rendered a helper element.
|
|
4658
|
+
// Must return elements used for any mock events.
|
|
4117
4659
|
renderDrag: function(dropLocation, seg) {
|
|
4118
4660
|
// subclasses must implement
|
|
4119
4661
|
},
|
|
@@ -4129,24 +4671,28 @@ Grid.mixin({
|
|
|
4129
4671
|
------------------------------------------------------------------------------------------------------------------*/
|
|
4130
4672
|
|
|
4131
4673
|
|
|
4132
|
-
//
|
|
4674
|
+
// Creates a listener that tracks the user as they resize an event segment.
|
|
4133
4675
|
// Generic enough to work with any type of Grid.
|
|
4134
|
-
|
|
4676
|
+
buildSegResizeListener: function(seg, isStart) {
|
|
4135
4677
|
var _this = this;
|
|
4136
4678
|
var view = this.view;
|
|
4137
4679
|
var calendar = view.calendar;
|
|
4138
4680
|
var el = seg.el;
|
|
4139
4681
|
var event = seg.event;
|
|
4140
4682
|
var eventEnd = calendar.getEventEnd(event);
|
|
4683
|
+
var isDragging;
|
|
4141
4684
|
var resizeLocation; // zoned event date properties. falsy if invalid resize
|
|
4142
4685
|
|
|
4143
4686
|
// Tracks mouse movement over the *grid's* coordinate map
|
|
4144
|
-
var dragListener = new HitDragListener(this, {
|
|
4145
|
-
distance: 5,
|
|
4687
|
+
var dragListener = this.segResizeListener = new HitDragListener(this, {
|
|
4146
4688
|
scroll: view.opt('dragScroll'),
|
|
4147
4689
|
subjectEl: el,
|
|
4690
|
+
interactionStart: function() {
|
|
4691
|
+
isDragging = false;
|
|
4692
|
+
},
|
|
4148
4693
|
dragStart: function(ev) {
|
|
4149
|
-
|
|
4694
|
+
isDragging = true;
|
|
4695
|
+
_this.handleSegMouseout(seg, ev); // ensure a mouseout on the manipulated event has been reported
|
|
4150
4696
|
_this.segResizeStart(seg, ev);
|
|
4151
4697
|
},
|
|
4152
4698
|
hitOver: function(hit, isOrig, origHit) {
|
|
@@ -4181,16 +4727,18 @@ Grid.mixin({
|
|
|
4181
4727
|
view.showEvent(event);
|
|
4182
4728
|
enableCursor();
|
|
4183
4729
|
},
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4730
|
+
interactionEnd: function(ev) {
|
|
4731
|
+
if (isDragging) {
|
|
4732
|
+
_this.segResizeStop(seg, ev);
|
|
4733
|
+
}
|
|
4187
4734
|
if (resizeLocation) { // valid date to resize to?
|
|
4188
4735
|
view.reportEventResize(event, resizeLocation, this.largeUnit, el, ev);
|
|
4189
4736
|
}
|
|
4737
|
+
_this.segResizeListener = null;
|
|
4190
4738
|
}
|
|
4191
4739
|
});
|
|
4192
4740
|
|
|
4193
|
-
dragListener
|
|
4741
|
+
return dragListener;
|
|
4194
4742
|
},
|
|
4195
4743
|
|
|
4196
4744
|
|
|
@@ -4267,6 +4815,7 @@ Grid.mixin({
|
|
|
4267
4815
|
|
|
4268
4816
|
// Renders a visual indication of an event being resized.
|
|
4269
4817
|
// `range` has the updated dates of the event. `seg` is the original segment object involved in the drag.
|
|
4818
|
+
// Must return elements used for any mock events.
|
|
4270
4819
|
renderEventResize: function(range, seg) {
|
|
4271
4820
|
// subclasses must implement
|
|
4272
4821
|
},
|
|
@@ -4312,6 +4861,7 @@ Grid.mixin({
|
|
|
4312
4861
|
|
|
4313
4862
|
// Generic utility for generating the HTML classNames for an event segment's element
|
|
4314
4863
|
getSegClasses: function(seg, isDraggable, isResizable) {
|
|
4864
|
+
var view = this.view;
|
|
4315
4865
|
var event = seg.event;
|
|
4316
4866
|
var classes = [
|
|
4317
4867
|
'fc-event',
|
|
@@ -4329,6 +4879,11 @@ Grid.mixin({
|
|
|
4329
4879
|
classes.push('fc-resizable');
|
|
4330
4880
|
}
|
|
4331
4881
|
|
|
4882
|
+
// event is currently selected? attach a className.
|
|
4883
|
+
if (view.isEventSelected(event)) {
|
|
4884
|
+
classes.push('fc-selected');
|
|
4885
|
+
}
|
|
4886
|
+
|
|
4332
4887
|
return classes;
|
|
4333
4888
|
},
|
|
4334
4889
|
|
|
@@ -5311,10 +5866,7 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
|
5311
5866
|
// if a segment from the same calendar but another component is being dragged, render a helper event
|
|
5312
5867
|
if (seg && !seg.el.closest(this.el).length) {
|
|
5313
5868
|
|
|
5314
|
-
this.renderEventLocationHelper(eventLocation, seg);
|
|
5315
|
-
this.applyDragOpacity(this.helperEls);
|
|
5316
|
-
|
|
5317
|
-
return true; // a helper has been rendered
|
|
5869
|
+
return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
|
|
5318
5870
|
}
|
|
5319
5871
|
},
|
|
5320
5872
|
|
|
@@ -5333,7 +5885,7 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
|
5333
5885
|
// Renders a visual indication of an event being resized
|
|
5334
5886
|
renderEventResize: function(eventLocation, seg) {
|
|
5335
5887
|
this.renderHighlight(this.eventToSpan(eventLocation));
|
|
5336
|
-
this.renderEventLocationHelper(eventLocation, seg);
|
|
5888
|
+
return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
|
|
5337
5889
|
},
|
|
5338
5890
|
|
|
5339
5891
|
|
|
@@ -5379,7 +5931,9 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
|
5379
5931
|
helperNodes.push(skeletonEl[0]);
|
|
5380
5932
|
});
|
|
5381
5933
|
|
|
5382
|
-
|
|
5934
|
+
return ( // must return the elements rendered
|
|
5935
|
+
this.helperEls = $(helperNodes) // array -> jQuery set
|
|
5936
|
+
);
|
|
5383
5937
|
},
|
|
5384
5938
|
|
|
5385
5939
|
|
|
@@ -6165,6 +6719,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
|
6165
6719
|
labelInterval: null, // duration of how often a label should be displayed for a slot
|
|
6166
6720
|
|
|
6167
6721
|
colEls: null, // cells elements in the day-row background
|
|
6722
|
+
slatContainerEl: null, // div that wraps all the slat rows
|
|
6168
6723
|
slatEls: null, // elements running horizontally across all columns
|
|
6169
6724
|
nowIndicatorEls: null,
|
|
6170
6725
|
|
|
@@ -6184,7 +6739,8 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
|
6184
6739
|
renderDates: function() {
|
|
6185
6740
|
this.el.html(this.renderHtml());
|
|
6186
6741
|
this.colEls = this.el.find('.fc-day');
|
|
6187
|
-
this.
|
|
6742
|
+
this.slatContainerEl = this.el.find('.fc-slats');
|
|
6743
|
+
this.slatEls = this.slatContainerEl.find('tr');
|
|
6188
6744
|
|
|
6189
6745
|
this.colCoordCache = new CoordCache({
|
|
6190
6746
|
els: this.colEls,
|
|
@@ -6463,6 +7019,11 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
|
6463
7019
|
},
|
|
6464
7020
|
|
|
6465
7021
|
|
|
7022
|
+
getTotalSlatHeight: function() {
|
|
7023
|
+
return this.slatContainerEl.outerHeight();
|
|
7024
|
+
},
|
|
7025
|
+
|
|
7026
|
+
|
|
6466
7027
|
// Computes the top coordinate, relative to the bounds of the grid, of the given date.
|
|
6467
7028
|
// A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
|
|
6468
7029
|
computeDateTop: function(date, startOfDayDate) {
|
|
@@ -6511,13 +7072,10 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
|
6511
7072
|
renderDrag: function(eventLocation, seg) {
|
|
6512
7073
|
|
|
6513
7074
|
if (seg) { // if there is event information for this drag, render a helper event
|
|
6514
|
-
this.renderEventLocationHelper(eventLocation, seg);
|
|
6515
7075
|
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
return true; // signal that a helper has been rendered
|
|
7076
|
+
// returns mock event elements
|
|
7077
|
+
// signal that a helper has been rendered
|
|
7078
|
+
return this.renderEventLocationHelper(eventLocation, seg);
|
|
6521
7079
|
}
|
|
6522
7080
|
else {
|
|
6523
7081
|
// otherwise, just render a highlight
|
|
@@ -6539,7 +7097,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
|
6539
7097
|
|
|
6540
7098
|
// Renders a visual indication of an event being resized
|
|
6541
7099
|
renderEventResize: function(eventLocation, seg) {
|
|
6542
|
-
this.renderEventLocationHelper(eventLocation, seg);
|
|
7100
|
+
return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
|
|
6543
7101
|
},
|
|
6544
7102
|
|
|
6545
7103
|
|
|
@@ -6555,7 +7113,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
|
6555
7113
|
|
|
6556
7114
|
// Renders a mock "helper" event. `sourceSeg` is the original segment object and might be null (an external drag)
|
|
6557
7115
|
renderHelper: function(event, sourceSeg) {
|
|
6558
|
-
this.renderHelperSegs(this.eventToSegs(event), sourceSeg);
|
|
7116
|
+
return this.renderHelperSegs(this.eventToSegs(event), sourceSeg); // returns mock event elements
|
|
6559
7117
|
},
|
|
6560
7118
|
|
|
6561
7119
|
|
|
@@ -6749,6 +7307,7 @@ TimeGrid.mixin({
|
|
|
6749
7307
|
|
|
6750
7308
|
|
|
6751
7309
|
renderHelperSegs: function(segs, sourceSeg) {
|
|
7310
|
+
var helperEls = [];
|
|
6752
7311
|
var i, seg;
|
|
6753
7312
|
var sourceEl;
|
|
6754
7313
|
|
|
@@ -6766,9 +7325,12 @@ TimeGrid.mixin({
|
|
|
6766
7325
|
'margin-right': sourceEl.css('margin-right')
|
|
6767
7326
|
});
|
|
6768
7327
|
}
|
|
7328
|
+
helperEls.push(seg.el[0]);
|
|
6769
7329
|
}
|
|
6770
7330
|
|
|
6771
7331
|
this.helperSegs = segs;
|
|
7332
|
+
|
|
7333
|
+
return $(helperEls); // must return rendered helpers
|
|
6772
7334
|
},
|
|
6773
7335
|
|
|
6774
7336
|
|
|
@@ -7279,7 +7841,7 @@ function isSlotSegCollision(seg1, seg2) {
|
|
|
7279
7841
|
/* An abstract class from which other views inherit from
|
|
7280
7842
|
----------------------------------------------------------------------------------------------------------------------*/
|
|
7281
7843
|
|
|
7282
|
-
var View = FC.View = Class.extend({
|
|
7844
|
+
var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
7283
7845
|
|
|
7284
7846
|
type: null, // subclass' view name (string)
|
|
7285
7847
|
name: null, // deprecated. use `type` instead
|
|
@@ -7306,13 +7868,10 @@ var View = FC.View = Class.extend({
|
|
|
7306
7868
|
|
|
7307
7869
|
isRTL: false,
|
|
7308
7870
|
isSelected: false, // boolean whether a range of time is user-selected or not
|
|
7871
|
+
selectedEvent: null,
|
|
7309
7872
|
|
|
7310
7873
|
eventOrderSpecs: null, // criteria for ordering events when they have same date/time
|
|
7311
7874
|
|
|
7312
|
-
// subclasses can optionally use a scroll container
|
|
7313
|
-
scrollerEl: null, // the element that will most likely scroll when content is too tall
|
|
7314
|
-
scrollTop: null, // cached vertical scroll value
|
|
7315
|
-
|
|
7316
7875
|
// classNames styled by jqui themes
|
|
7317
7876
|
widgetHeaderClass: null,
|
|
7318
7877
|
widgetContentClass: null,
|
|
@@ -7322,9 +7881,6 @@ var View = FC.View = Class.extend({
|
|
|
7322
7881
|
nextDayThreshold: null,
|
|
7323
7882
|
isHiddenDayHash: null,
|
|
7324
7883
|
|
|
7325
|
-
// document handlers, bound to `this` object
|
|
7326
|
-
documentMousedownProxy: null, // TODO: doesn't work with touch
|
|
7327
|
-
|
|
7328
7884
|
// now indicator
|
|
7329
7885
|
isNowIndicatorRendered: null,
|
|
7330
7886
|
initialNowDate: null, // result first getNow call
|
|
@@ -7347,8 +7903,6 @@ var View = FC.View = Class.extend({
|
|
|
7347
7903
|
|
|
7348
7904
|
this.eventOrderSpecs = parseFieldSpecs(this.opt('eventOrder'));
|
|
7349
7905
|
|
|
7350
|
-
this.documentMousedownProxy = proxy(this, 'documentMousedown');
|
|
7351
|
-
|
|
7352
7906
|
this.initialize();
|
|
7353
7907
|
},
|
|
7354
7908
|
|
|
@@ -7566,15 +8120,14 @@ var View = FC.View = Class.extend({
|
|
|
7566
8120
|
|
|
7567
8121
|
this.calendar.freezeContentHeight();
|
|
7568
8122
|
|
|
7569
|
-
return this.clear()
|
|
8123
|
+
return syncThen(this.clear(), function() { // clear the content first
|
|
7570
8124
|
return (
|
|
7571
8125
|
_this.displaying =
|
|
7572
|
-
|
|
7573
|
-
.
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
})
|
|
8126
|
+
syncThen(_this.displayView(date), function() { // displayView might return a promise
|
|
8127
|
+
_this.forceScroll(_this.computeInitialScroll(scrollState));
|
|
8128
|
+
_this.calendar.unfreezeContentHeight();
|
|
8129
|
+
_this.triggerRender();
|
|
8130
|
+
})
|
|
7578
8131
|
);
|
|
7579
8132
|
});
|
|
7580
8133
|
},
|
|
@@ -7588,7 +8141,7 @@ var View = FC.View = Class.extend({
|
|
|
7588
8141
|
var displaying = this.displaying;
|
|
7589
8142
|
|
|
7590
8143
|
if (displaying) { // previously displayed, or in the process of being displayed?
|
|
7591
|
-
return displaying
|
|
8144
|
+
return syncThen(displaying, function() { // wait for the display to finish
|
|
7592
8145
|
_this.displaying = null;
|
|
7593
8146
|
_this.clearEvents();
|
|
7594
8147
|
return _this.clearView(); // might return a promise. chain it
|
|
@@ -7674,13 +8227,14 @@ var View = FC.View = Class.extend({
|
|
|
7674
8227
|
|
|
7675
8228
|
// Binds DOM handlers to elements that reside outside the view container, such as the document
|
|
7676
8229
|
bindGlobalHandlers: function() {
|
|
7677
|
-
$(document)
|
|
8230
|
+
this.listenTo($(document), 'mousedown', this.handleDocumentMousedown);
|
|
8231
|
+
this.listenTo($(document), 'touchstart', this.processUnselect);
|
|
7678
8232
|
},
|
|
7679
8233
|
|
|
7680
8234
|
|
|
7681
8235
|
// Unbinds DOM handlers from elements that reside outside the view container
|
|
7682
8236
|
unbindGlobalHandlers: function() {
|
|
7683
|
-
$(document)
|
|
8237
|
+
this.stopListeningTo($(document));
|
|
7684
8238
|
},
|
|
7685
8239
|
|
|
7686
8240
|
|
|
@@ -7845,28 +8399,7 @@ var View = FC.View = Class.extend({
|
|
|
7845
8399
|
|
|
7846
8400
|
|
|
7847
8401
|
/* Scroller
|
|
7848
|
-
------------------------------------------------------------------------------------------------------------------*/
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
// Given the total height of the view, return the number of pixels that should be used for the scroller.
|
|
7852
|
-
// Utility for subclasses.
|
|
7853
|
-
computeScrollerHeight: function(totalHeight) {
|
|
7854
|
-
var scrollerEl = this.scrollerEl;
|
|
7855
|
-
var both;
|
|
7856
|
-
var otherHeight; // cumulative height of everything that is not the scrollerEl in the view (header+borders)
|
|
7857
|
-
|
|
7858
|
-
both = this.el.add(scrollerEl);
|
|
7859
|
-
|
|
7860
|
-
// fuckin IE8/9/10/11 sometimes returns 0 for dimensions. this weird hack was the only thing that worked
|
|
7861
|
-
both.css({
|
|
7862
|
-
position: 'relative', // cause a reflow, which will force fresh dimension recalculation
|
|
7863
|
-
left: -1 // ensure reflow in case the el was already relative. negative is less likely to cause new scroll
|
|
7864
|
-
});
|
|
7865
|
-
otherHeight = this.el.outerHeight() - scrollerEl.height(); // grab the dimensions
|
|
7866
|
-
both.css({ position: '', left: '' }); // undo hack
|
|
7867
|
-
|
|
7868
|
-
return totalHeight - otherHeight;
|
|
7869
|
-
},
|
|
8402
|
+
------------------------------------------------------------------------------------------------------------------*/
|
|
7870
8403
|
|
|
7871
8404
|
|
|
7872
8405
|
// Computes the initial pre-configured scroll state prior to allowing the user to change it.
|
|
@@ -7878,17 +8411,13 @@ var View = FC.View = Class.extend({
|
|
|
7878
8411
|
|
|
7879
8412
|
// Retrieves the view's current natural scroll state. Can return an arbitrary format.
|
|
7880
8413
|
queryScroll: function() {
|
|
7881
|
-
|
|
7882
|
-
return this.scrollerEl.scrollTop(); // operates on scrollerEl by default
|
|
7883
|
-
}
|
|
8414
|
+
// subclasses must implement
|
|
7884
8415
|
},
|
|
7885
8416
|
|
|
7886
8417
|
|
|
7887
8418
|
// Sets the view's scroll state. Will accept the same format computeInitialScroll and queryScroll produce.
|
|
7888
8419
|
setScroll: function(scrollState) {
|
|
7889
|
-
|
|
7890
|
-
return this.scrollerEl.scrollTop(scrollState); // operates on scrollerEl by default
|
|
7891
|
-
}
|
|
8420
|
+
// subclasses must implement
|
|
7892
8421
|
},
|
|
7893
8422
|
|
|
7894
8423
|
|
|
@@ -8103,7 +8632,8 @@ var View = FC.View = Class.extend({
|
|
|
8103
8632
|
|
|
8104
8633
|
|
|
8105
8634
|
// Renders a visual indication of a event or external-element drag over the given drop zone.
|
|
8106
|
-
// If an external-element, seg will be `null
|
|
8635
|
+
// If an external-element, seg will be `null`.
|
|
8636
|
+
// Must return elements used for any mock events.
|
|
8107
8637
|
renderDrag: function(dropLocation, seg) {
|
|
8108
8638
|
// subclasses must implement
|
|
8109
8639
|
},
|
|
@@ -8166,7 +8696,7 @@ var View = FC.View = Class.extend({
|
|
|
8166
8696
|
},
|
|
8167
8697
|
|
|
8168
8698
|
|
|
8169
|
-
/* Selection
|
|
8699
|
+
/* Selection (time range)
|
|
8170
8700
|
------------------------------------------------------------------------------------------------------------------*/
|
|
8171
8701
|
|
|
8172
8702
|
|
|
@@ -8224,13 +8754,62 @@ var View = FC.View = Class.extend({
|
|
|
8224
8754
|
},
|
|
8225
8755
|
|
|
8226
8756
|
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8757
|
+
/* Event Selection
|
|
8758
|
+
------------------------------------------------------------------------------------------------------------------*/
|
|
8759
|
+
|
|
8760
|
+
|
|
8761
|
+
selectEvent: function(event) {
|
|
8762
|
+
if (!this.selectedEvent || this.selectedEvent !== event) {
|
|
8763
|
+
this.unselectEvent();
|
|
8764
|
+
this.renderedEventSegEach(function(seg) {
|
|
8765
|
+
seg.el.addClass('fc-selected');
|
|
8766
|
+
}, event);
|
|
8767
|
+
this.selectedEvent = event;
|
|
8768
|
+
}
|
|
8769
|
+
},
|
|
8770
|
+
|
|
8771
|
+
|
|
8772
|
+
unselectEvent: function() {
|
|
8773
|
+
if (this.selectedEvent) {
|
|
8774
|
+
this.renderedEventSegEach(function(seg) {
|
|
8775
|
+
seg.el.removeClass('fc-selected');
|
|
8776
|
+
}, this.selectedEvent);
|
|
8777
|
+
this.selectedEvent = null;
|
|
8778
|
+
}
|
|
8779
|
+
},
|
|
8780
|
+
|
|
8781
|
+
|
|
8782
|
+
isEventSelected: function(event) {
|
|
8783
|
+
// event references might change on refetchEvents(), while selectedEvent doesn't,
|
|
8784
|
+
// so compare IDs
|
|
8785
|
+
return this.selectedEvent && this.selectedEvent._id === event._id;
|
|
8786
|
+
},
|
|
8787
|
+
|
|
8788
|
+
|
|
8789
|
+
/* Mouse / Touch Unselecting (time range & event unselection)
|
|
8790
|
+
------------------------------------------------------------------------------------------------------------------*/
|
|
8791
|
+
// TODO: move consistently to down/start or up/end?
|
|
8792
|
+
// TODO: don't kill previous selection if touch scrolling
|
|
8793
|
+
|
|
8794
|
+
|
|
8795
|
+
handleDocumentMousedown: function(ev) {
|
|
8796
|
+
if (isPrimaryMouseButton(ev)) {
|
|
8797
|
+
this.processUnselect(ev);
|
|
8798
|
+
}
|
|
8799
|
+
},
|
|
8800
|
+
|
|
8801
|
+
|
|
8802
|
+
processUnselect: function(ev) {
|
|
8803
|
+
this.processRangeUnselect(ev);
|
|
8804
|
+
this.processEventUnselect(ev);
|
|
8805
|
+
},
|
|
8806
|
+
|
|
8230
8807
|
|
|
8231
|
-
|
|
8232
|
-
|
|
8808
|
+
processRangeUnselect: function(ev) {
|
|
8809
|
+
var ignore;
|
|
8233
8810
|
|
|
8811
|
+
// is there a time-range selection?
|
|
8812
|
+
if (this.isSelected && this.opt('unselectAuto')) {
|
|
8234
8813
|
// only unselect if the clicked element is not identical to or inside of an 'unselectCancel' element
|
|
8235
8814
|
ignore = this.opt('unselectCancel');
|
|
8236
8815
|
if (!ignore || !$(ev.target).closest(ignore).length) {
|
|
@@ -8240,6 +8819,15 @@ var View = FC.View = Class.extend({
|
|
|
8240
8819
|
},
|
|
8241
8820
|
|
|
8242
8821
|
|
|
8822
|
+
processEventUnselect: function(ev) {
|
|
8823
|
+
if (this.selectedEvent) {
|
|
8824
|
+
if (!$(ev.target).closest('.fc-selected').length) {
|
|
8825
|
+
this.unselectEvent();
|
|
8826
|
+
}
|
|
8827
|
+
}
|
|
8828
|
+
},
|
|
8829
|
+
|
|
8830
|
+
|
|
8243
8831
|
/* Day Click
|
|
8244
8832
|
------------------------------------------------------------------------------------------------------------------*/
|
|
8245
8833
|
|
|
@@ -8354,6 +8942,127 @@ var View = FC.View = Class.extend({
|
|
|
8354
8942
|
|
|
8355
8943
|
;;
|
|
8356
8944
|
|
|
8945
|
+
/*
|
|
8946
|
+
Embodies a div that has potential scrollbars
|
|
8947
|
+
*/
|
|
8948
|
+
var Scroller = FC.Scroller = Class.extend({
|
|
8949
|
+
|
|
8950
|
+
el: null, // the guaranteed outer element
|
|
8951
|
+
scrollEl: null, // the element with the scrollbars
|
|
8952
|
+
overflowX: null,
|
|
8953
|
+
overflowY: null,
|
|
8954
|
+
|
|
8955
|
+
|
|
8956
|
+
constructor: function(options) {
|
|
8957
|
+
options = options || {};
|
|
8958
|
+
this.overflowX = options.overflowX || options.overflow || 'auto';
|
|
8959
|
+
this.overflowY = options.overflowY || options.overflow || 'auto';
|
|
8960
|
+
},
|
|
8961
|
+
|
|
8962
|
+
|
|
8963
|
+
render: function() {
|
|
8964
|
+
this.el = this.renderEl();
|
|
8965
|
+
this.applyOverflow();
|
|
8966
|
+
},
|
|
8967
|
+
|
|
8968
|
+
|
|
8969
|
+
renderEl: function() {
|
|
8970
|
+
return (this.scrollEl = $('<div class="fc-scroller"></div>'));
|
|
8971
|
+
},
|
|
8972
|
+
|
|
8973
|
+
|
|
8974
|
+
// sets to natural height, unlocks overflow
|
|
8975
|
+
clear: function() {
|
|
8976
|
+
this.setHeight('auto');
|
|
8977
|
+
this.applyOverflow();
|
|
8978
|
+
},
|
|
8979
|
+
|
|
8980
|
+
|
|
8981
|
+
destroy: function() {
|
|
8982
|
+
this.el.remove();
|
|
8983
|
+
},
|
|
8984
|
+
|
|
8985
|
+
|
|
8986
|
+
// Overflow
|
|
8987
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
8988
|
+
|
|
8989
|
+
|
|
8990
|
+
applyOverflow: function() {
|
|
8991
|
+
this.scrollEl.css({
|
|
8992
|
+
'overflow-x': this.overflowX,
|
|
8993
|
+
'overflow-y': this.overflowY
|
|
8994
|
+
});
|
|
8995
|
+
},
|
|
8996
|
+
|
|
8997
|
+
|
|
8998
|
+
// Causes any 'auto' overflow values to resolves to 'scroll' or 'hidden'.
|
|
8999
|
+
// Useful for preserving scrollbar widths regardless of future resizes.
|
|
9000
|
+
// Can pass in scrollbarWidths for optimization.
|
|
9001
|
+
lockOverflow: function(scrollbarWidths) {
|
|
9002
|
+
var overflowX = this.overflowX;
|
|
9003
|
+
var overflowY = this.overflowY;
|
|
9004
|
+
|
|
9005
|
+
scrollbarWidths = scrollbarWidths || this.getScrollbarWidths();
|
|
9006
|
+
|
|
9007
|
+
if (overflowX === 'auto') {
|
|
9008
|
+
overflowX = (
|
|
9009
|
+
scrollbarWidths.top || scrollbarWidths.bottom || // horizontal scrollbars?
|
|
9010
|
+
// OR scrolling pane with massless scrollbars?
|
|
9011
|
+
this.scrollEl[0].scrollWidth - 1 > this.scrollEl[0].clientWidth
|
|
9012
|
+
// subtract 1 because of IE off-by-one issue
|
|
9013
|
+
) ? 'scroll' : 'hidden';
|
|
9014
|
+
}
|
|
9015
|
+
|
|
9016
|
+
if (overflowY === 'auto') {
|
|
9017
|
+
overflowY = (
|
|
9018
|
+
scrollbarWidths.left || scrollbarWidths.right || // vertical scrollbars?
|
|
9019
|
+
// OR scrolling pane with massless scrollbars?
|
|
9020
|
+
this.scrollEl[0].scrollHeight - 1 > this.scrollEl[0].clientHeight
|
|
9021
|
+
// subtract 1 because of IE off-by-one issue
|
|
9022
|
+
) ? 'scroll' : 'hidden';
|
|
9023
|
+
}
|
|
9024
|
+
|
|
9025
|
+
this.scrollEl.css({ 'overflow-x': overflowX, 'overflow-y': overflowY });
|
|
9026
|
+
},
|
|
9027
|
+
|
|
9028
|
+
|
|
9029
|
+
// Getters / Setters
|
|
9030
|
+
// -----------------------------------------------------------------------------------------------------------------
|
|
9031
|
+
|
|
9032
|
+
|
|
9033
|
+
setHeight: function(height) {
|
|
9034
|
+
this.scrollEl.height(height);
|
|
9035
|
+
},
|
|
9036
|
+
|
|
9037
|
+
|
|
9038
|
+
getScrollTop: function() {
|
|
9039
|
+
return this.scrollEl.scrollTop();
|
|
9040
|
+
},
|
|
9041
|
+
|
|
9042
|
+
|
|
9043
|
+
setScrollTop: function(top) {
|
|
9044
|
+
this.scrollEl.scrollTop(top);
|
|
9045
|
+
},
|
|
9046
|
+
|
|
9047
|
+
|
|
9048
|
+
getClientWidth: function() {
|
|
9049
|
+
return this.scrollEl[0].clientWidth;
|
|
9050
|
+
},
|
|
9051
|
+
|
|
9052
|
+
|
|
9053
|
+
getClientHeight: function() {
|
|
9054
|
+
return this.scrollEl[0].clientHeight;
|
|
9055
|
+
},
|
|
9056
|
+
|
|
9057
|
+
|
|
9058
|
+
getScrollbarWidths: function() {
|
|
9059
|
+
return getScrollbarWidths(this.scrollEl);
|
|
9060
|
+
}
|
|
9061
|
+
|
|
9062
|
+
});
|
|
9063
|
+
|
|
9064
|
+
;;
|
|
9065
|
+
|
|
8357
9066
|
var Calendar = FC.Calendar = Class.extend({
|
|
8358
9067
|
|
|
8359
9068
|
dirDefaults: null, // option defaults related to LTR or RTL
|
|
@@ -8608,7 +9317,7 @@ var Calendar = FC.Calendar = Class.extend({
|
|
|
8608
9317
|
});
|
|
8609
9318
|
|
|
8610
9319
|
|
|
8611
|
-
Calendar.mixin(
|
|
9320
|
+
Calendar.mixin(EmitterMixin);
|
|
8612
9321
|
|
|
8613
9322
|
|
|
8614
9323
|
function Calendar_constructor(element, overrides) {
|
|
@@ -8625,6 +9334,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
8625
9334
|
t.render = render;
|
|
8626
9335
|
t.destroy = destroy;
|
|
8627
9336
|
t.refetchEvents = refetchEvents;
|
|
9337
|
+
t.refetchEventSources = refetchEventSources;
|
|
8628
9338
|
t.reportEvents = reportEvents;
|
|
8629
9339
|
t.reportEventChange = reportEventChange;
|
|
8630
9340
|
t.rerenderEvents = renderEvents; // `renderEvents` serves as a rerender. an API method
|
|
@@ -8815,6 +9525,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
8815
9525
|
EventManager.call(t, options);
|
|
8816
9526
|
var isFetchNeeded = t.isFetchNeeded;
|
|
8817
9527
|
var fetchEvents = t.fetchEvents;
|
|
9528
|
+
var fetchEventSources = t.fetchEventSources;
|
|
8818
9529
|
|
|
8819
9530
|
|
|
8820
9531
|
|
|
@@ -9054,11 +9765,16 @@ function Calendar_constructor(element, overrides) {
|
|
|
9054
9765
|
|
|
9055
9766
|
|
|
9056
9767
|
function refetchEvents() { // can be called as an API method
|
|
9057
|
-
destroyEvents(); // so that events are cleared before user starts waiting for AJAX
|
|
9058
9768
|
fetchAndRenderEvents();
|
|
9059
9769
|
}
|
|
9060
9770
|
|
|
9061
9771
|
|
|
9772
|
+
// TODO: move this into EventManager?
|
|
9773
|
+
function refetchEventSources(matchInputs) {
|
|
9774
|
+
fetchEventSources(t.getEventSourcesByMatchArray(matchInputs));
|
|
9775
|
+
}
|
|
9776
|
+
|
|
9777
|
+
|
|
9062
9778
|
function renderEvents() { // destroys old events if previously rendered
|
|
9063
9779
|
if (elementVisible()) {
|
|
9064
9780
|
freezeContentHeight();
|
|
@@ -9066,13 +9782,6 @@ function Calendar_constructor(element, overrides) {
|
|
|
9066
9782
|
unfreezeContentHeight();
|
|
9067
9783
|
}
|
|
9068
9784
|
}
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
function destroyEvents() {
|
|
9072
|
-
freezeContentHeight();
|
|
9073
|
-
currentView.clearEvents();
|
|
9074
|
-
unfreezeContentHeight();
|
|
9075
|
-
}
|
|
9076
9785
|
|
|
9077
9786
|
|
|
9078
9787
|
function getAndRenderEvents() {
|
|
@@ -9283,7 +9992,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
9283
9992
|
|
|
9284
9993
|
Calendar.defaults = {
|
|
9285
9994
|
|
|
9286
|
-
titleRangeSeparator: ' \
|
|
9995
|
+
titleRangeSeparator: ' \u2013 ', // en dash
|
|
9287
9996
|
monthYearFormat: 'MMMM YYYY', // required for en. other languages rely on datepicker computable option
|
|
9288
9997
|
|
|
9289
9998
|
defaultTimedEventDuration: '02:00:00',
|
|
@@ -9369,7 +10078,9 @@ Calendar.defaults = {
|
|
|
9369
10078
|
dayPopoverFormat: 'LL',
|
|
9370
10079
|
|
|
9371
10080
|
handleWindowResize: true,
|
|
9372
|
-
windowResizeDelay: 200 // milliseconds before an updateSize happens
|
|
10081
|
+
windowResizeDelay: 200, // milliseconds before an updateSize happens
|
|
10082
|
+
|
|
10083
|
+
longPressDelay: 1000
|
|
9373
10084
|
|
|
9374
10085
|
};
|
|
9375
10086
|
|
|
@@ -9830,14 +10541,14 @@ function Header(calendar, options) {
|
|
|
9830
10541
|
|
|
9831
10542
|
function disableButton(buttonName) {
|
|
9832
10543
|
el.find('.fc-' + buttonName + '-button')
|
|
9833
|
-
.
|
|
10544
|
+
.prop('disabled', true)
|
|
9834
10545
|
.addClass(tm + '-state-disabled');
|
|
9835
10546
|
}
|
|
9836
10547
|
|
|
9837
10548
|
|
|
9838
10549
|
function enableButton(buttonName) {
|
|
9839
10550
|
el.find('.fc-' + buttonName + '-button')
|
|
9840
|
-
.
|
|
10551
|
+
.prop('disabled', false)
|
|
9841
10552
|
.removeClass(tm + '-state-disabled');
|
|
9842
10553
|
}
|
|
9843
10554
|
|
|
@@ -9868,8 +10579,14 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
9868
10579
|
// exports
|
|
9869
10580
|
t.isFetchNeeded = isFetchNeeded;
|
|
9870
10581
|
t.fetchEvents = fetchEvents;
|
|
10582
|
+
t.fetchEventSources = fetchEventSources;
|
|
10583
|
+
t.getEventSources = getEventSources;
|
|
10584
|
+
t.getEventSourceById = getEventSourceById;
|
|
10585
|
+
t.getEventSourcesByMatchArray = getEventSourcesByMatchArray;
|
|
10586
|
+
t.getEventSourcesByMatch = getEventSourcesByMatch;
|
|
9871
10587
|
t.addEventSource = addEventSource;
|
|
9872
10588
|
t.removeEventSource = removeEventSource;
|
|
10589
|
+
t.removeEventSources = removeEventSources;
|
|
9873
10590
|
t.updateEvent = updateEvent;
|
|
9874
10591
|
t.renderEvent = renderEvent;
|
|
9875
10592
|
t.removeEvents = removeEvents;
|
|
@@ -9887,8 +10604,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
9887
10604
|
var stickySource = { events: [] };
|
|
9888
10605
|
var sources = [ stickySource ];
|
|
9889
10606
|
var rangeStart, rangeEnd;
|
|
9890
|
-
var
|
|
9891
|
-
var pendingSourceCnt = 0;
|
|
10607
|
+
var pendingSourceCnt = 0; // outstanding fetch requests, max one per source
|
|
9892
10608
|
var cache = []; // holds events that have already been expanded
|
|
9893
10609
|
|
|
9894
10610
|
|
|
@@ -9918,23 +10634,58 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
9918
10634
|
function fetchEvents(start, end) {
|
|
9919
10635
|
rangeStart = start;
|
|
9920
10636
|
rangeEnd = end;
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
10637
|
+
fetchEventSources(sources, 'reset');
|
|
10638
|
+
}
|
|
10639
|
+
|
|
10640
|
+
|
|
10641
|
+
// expects an array of event source objects (the originals, not copies)
|
|
10642
|
+
// `specialFetchType` is an optimization parameter that affects purging of the event cache.
|
|
10643
|
+
function fetchEventSources(specificSources, specialFetchType) {
|
|
10644
|
+
var i, source;
|
|
10645
|
+
|
|
10646
|
+
if (specialFetchType === 'reset') {
|
|
10647
|
+
cache = [];
|
|
10648
|
+
}
|
|
10649
|
+
else if (specialFetchType !== 'add') {
|
|
10650
|
+
cache = excludeEventsBySources(cache, specificSources);
|
|
10651
|
+
}
|
|
10652
|
+
|
|
10653
|
+
for (i = 0; i < specificSources.length; i++) {
|
|
10654
|
+
source = specificSources[i];
|
|
10655
|
+
|
|
10656
|
+
// already-pending sources have already been accounted for in pendingSourceCnt
|
|
10657
|
+
if (source._status !== 'pending') {
|
|
10658
|
+
pendingSourceCnt++;
|
|
10659
|
+
}
|
|
10660
|
+
|
|
10661
|
+
source._fetchId = (source._fetchId || 0) + 1;
|
|
10662
|
+
source._status = 'pending';
|
|
10663
|
+
}
|
|
10664
|
+
|
|
10665
|
+
for (i = 0; i < specificSources.length; i++) {
|
|
10666
|
+
source = specificSources[i];
|
|
10667
|
+
|
|
10668
|
+
tryFetchEventSource(source, source._fetchId);
|
|
9927
10669
|
}
|
|
9928
10670
|
}
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
10671
|
+
|
|
10672
|
+
|
|
10673
|
+
// fetches an event source and processes its result ONLY if it is still the current fetch.
|
|
10674
|
+
// caller is responsible for incrementing pendingSourceCnt first.
|
|
10675
|
+
function tryFetchEventSource(source, fetchId) {
|
|
9932
10676
|
_fetchEventSource(source, function(eventInputs) {
|
|
9933
10677
|
var isArraySource = $.isArray(source.events);
|
|
9934
10678
|
var i, eventInput;
|
|
9935
10679
|
var abstractEvent;
|
|
9936
10680
|
|
|
9937
|
-
if (
|
|
10681
|
+
if (
|
|
10682
|
+
// is this the source's most recent fetch?
|
|
10683
|
+
// if not, rely on an upcoming fetch of this source to decrement pendingSourceCnt
|
|
10684
|
+
fetchId === source._fetchId &&
|
|
10685
|
+
// event source no longer valid?
|
|
10686
|
+
source._status !== 'rejected'
|
|
10687
|
+
) {
|
|
10688
|
+
source._status = 'resolved';
|
|
9938
10689
|
|
|
9939
10690
|
if (eventInputs) {
|
|
9940
10691
|
for (i = 0; i < eventInputs.length; i++) {
|
|
@@ -9956,13 +10707,29 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
9956
10707
|
}
|
|
9957
10708
|
}
|
|
9958
10709
|
|
|
9959
|
-
|
|
9960
|
-
if (!pendingSourceCnt) {
|
|
9961
|
-
reportEvents(cache);
|
|
9962
|
-
}
|
|
10710
|
+
decrementPendingSourceCnt();
|
|
9963
10711
|
}
|
|
9964
10712
|
});
|
|
9965
10713
|
}
|
|
10714
|
+
|
|
10715
|
+
|
|
10716
|
+
function rejectEventSource(source) {
|
|
10717
|
+
var wasPending = source._status === 'pending';
|
|
10718
|
+
|
|
10719
|
+
source._status = 'rejected';
|
|
10720
|
+
|
|
10721
|
+
if (wasPending) {
|
|
10722
|
+
decrementPendingSourceCnt();
|
|
10723
|
+
}
|
|
10724
|
+
}
|
|
10725
|
+
|
|
10726
|
+
|
|
10727
|
+
function decrementPendingSourceCnt() {
|
|
10728
|
+
pendingSourceCnt--;
|
|
10729
|
+
if (!pendingSourceCnt) {
|
|
10730
|
+
reportEvents(cache);
|
|
10731
|
+
}
|
|
10732
|
+
}
|
|
9966
10733
|
|
|
9967
10734
|
|
|
9968
10735
|
function _fetchEventSource(source, callback) {
|
|
@@ -10078,14 +10845,13 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
10078
10845
|
|
|
10079
10846
|
/* Sources
|
|
10080
10847
|
-----------------------------------------------------------------------------*/
|
|
10081
|
-
|
|
10848
|
+
|
|
10082
10849
|
|
|
10083
10850
|
function addEventSource(sourceInput) {
|
|
10084
10851
|
var source = buildEventSource(sourceInput);
|
|
10085
10852
|
if (source) {
|
|
10086
10853
|
sources.push(source);
|
|
10087
|
-
|
|
10088
|
-
fetchEventSource(source, currentFetchID); // will eventually call reportEvents
|
|
10854
|
+
fetchEventSources([ source ], 'add'); // will eventually call reportEvents
|
|
10089
10855
|
}
|
|
10090
10856
|
}
|
|
10091
10857
|
|
|
@@ -10135,19 +10901,120 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
10135
10901
|
}
|
|
10136
10902
|
|
|
10137
10903
|
|
|
10138
|
-
function removeEventSource(
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10904
|
+
function removeEventSource(matchInput) {
|
|
10905
|
+
removeSpecificEventSources(
|
|
10906
|
+
getEventSourcesByMatch(matchInput)
|
|
10907
|
+
);
|
|
10908
|
+
}
|
|
10909
|
+
|
|
10910
|
+
|
|
10911
|
+
// if called with no arguments, removes all.
|
|
10912
|
+
function removeEventSources(matchInputs) {
|
|
10913
|
+
if (matchInputs == null) {
|
|
10914
|
+
removeSpecificEventSources(sources, true); // isAll=true
|
|
10915
|
+
}
|
|
10916
|
+
else {
|
|
10917
|
+
removeSpecificEventSources(
|
|
10918
|
+
getEventSourcesByMatchArray(matchInputs)
|
|
10919
|
+
);
|
|
10920
|
+
}
|
|
10921
|
+
}
|
|
10922
|
+
|
|
10923
|
+
|
|
10924
|
+
function removeSpecificEventSources(targetSources, isAll) {
|
|
10925
|
+
var i;
|
|
10926
|
+
|
|
10927
|
+
// cancel pending requests
|
|
10928
|
+
for (i = 0; i < targetSources.length; i++) {
|
|
10929
|
+
rejectEventSource(targetSources[i]);
|
|
10930
|
+
}
|
|
10931
|
+
|
|
10932
|
+
if (isAll) { // an optimization
|
|
10933
|
+
sources = [];
|
|
10934
|
+
cache = [];
|
|
10935
|
+
}
|
|
10936
|
+
else {
|
|
10937
|
+
// remove from persisted source list
|
|
10938
|
+
sources = $.grep(sources, function(source) {
|
|
10939
|
+
for (i = 0; i < targetSources.length; i++) {
|
|
10940
|
+
if (source === targetSources[i]) {
|
|
10941
|
+
return false; // exclude
|
|
10942
|
+
}
|
|
10943
|
+
}
|
|
10944
|
+
return true; // include
|
|
10945
|
+
});
|
|
10946
|
+
|
|
10947
|
+
cache = excludeEventsBySources(cache, targetSources);
|
|
10948
|
+
}
|
|
10949
|
+
|
|
10146
10950
|
reportEvents(cache);
|
|
10147
10951
|
}
|
|
10148
10952
|
|
|
10149
10953
|
|
|
10150
|
-
function
|
|
10954
|
+
function getEventSources() {
|
|
10955
|
+
return sources.slice(1); // returns a shallow copy of sources with stickySource removed
|
|
10956
|
+
}
|
|
10957
|
+
|
|
10958
|
+
|
|
10959
|
+
function getEventSourceById(id) {
|
|
10960
|
+
return $.grep(sources, function(source) {
|
|
10961
|
+
return source.id && source.id === id;
|
|
10962
|
+
})[0];
|
|
10963
|
+
}
|
|
10964
|
+
|
|
10965
|
+
|
|
10966
|
+
// like getEventSourcesByMatch, but accepts multple match criteria (like multiple IDs)
|
|
10967
|
+
function getEventSourcesByMatchArray(matchInputs) {
|
|
10968
|
+
|
|
10969
|
+
// coerce into an array
|
|
10970
|
+
if (!matchInputs) {
|
|
10971
|
+
matchInputs = [];
|
|
10972
|
+
}
|
|
10973
|
+
else if (!$.isArray(matchInputs)) {
|
|
10974
|
+
matchInputs = [ matchInputs ];
|
|
10975
|
+
}
|
|
10976
|
+
|
|
10977
|
+
var matchingSources = [];
|
|
10978
|
+
var i;
|
|
10979
|
+
|
|
10980
|
+
// resolve raw inputs to real event source objects
|
|
10981
|
+
for (i = 0; i < matchInputs.length; i++) {
|
|
10982
|
+
matchingSources.push.apply( // append
|
|
10983
|
+
matchingSources,
|
|
10984
|
+
getEventSourcesByMatch(matchInputs[i])
|
|
10985
|
+
);
|
|
10986
|
+
}
|
|
10987
|
+
|
|
10988
|
+
return matchingSources;
|
|
10989
|
+
}
|
|
10990
|
+
|
|
10991
|
+
|
|
10992
|
+
// matchInput can either by a real event source object, an ID, or the function/URL for the source.
|
|
10993
|
+
// returns an array of matching source objects.
|
|
10994
|
+
function getEventSourcesByMatch(matchInput) {
|
|
10995
|
+
var i, source;
|
|
10996
|
+
|
|
10997
|
+
// given an proper event source object
|
|
10998
|
+
for (i = 0; i < sources.length; i++) {
|
|
10999
|
+
source = sources[i];
|
|
11000
|
+
if (source === matchInput) {
|
|
11001
|
+
return [ source ];
|
|
11002
|
+
}
|
|
11003
|
+
}
|
|
11004
|
+
|
|
11005
|
+
// an ID match
|
|
11006
|
+
source = getEventSourceById(matchInput);
|
|
11007
|
+
if (source) {
|
|
11008
|
+
return [ source ];
|
|
11009
|
+
}
|
|
11010
|
+
|
|
11011
|
+
return $.grep(sources, function(source) {
|
|
11012
|
+
return isSourcesEquivalent(matchInput, source);
|
|
11013
|
+
});
|
|
11014
|
+
}
|
|
11015
|
+
|
|
11016
|
+
|
|
11017
|
+
function isSourcesEquivalent(source1, source2) {
|
|
10151
11018
|
return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
|
|
10152
11019
|
}
|
|
10153
11020
|
|
|
@@ -10160,6 +11027,20 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
10160
11027
|
) ||
|
|
10161
11028
|
source; // the given argument *is* the primitive
|
|
10162
11029
|
}
|
|
11030
|
+
|
|
11031
|
+
|
|
11032
|
+
// util
|
|
11033
|
+
// returns a filtered array without events that are part of any of the given sources
|
|
11034
|
+
function excludeEventsBySources(specificEvents, specificSources) {
|
|
11035
|
+
return $.grep(specificEvents, function(event) {
|
|
11036
|
+
for (var i = 0; i < specificSources.length; i++) {
|
|
11037
|
+
if (event.source === specificSources[i]) {
|
|
11038
|
+
return false; // exclude
|
|
11039
|
+
}
|
|
11040
|
+
}
|
|
11041
|
+
return true; // keep
|
|
11042
|
+
});
|
|
11043
|
+
}
|
|
10163
11044
|
|
|
10164
11045
|
|
|
10165
11046
|
|
|
@@ -10368,6 +11249,8 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
10368
11249
|
assignDatesToEvent(start, end, allDay, out);
|
|
10369
11250
|
}
|
|
10370
11251
|
|
|
11252
|
+
t.normalizeEvent(out); // hook for external use. a prototype method
|
|
11253
|
+
|
|
10371
11254
|
return out;
|
|
10372
11255
|
}
|
|
10373
11256
|
|
|
@@ -10900,6 +11783,12 @@ function EventManager(options) { // assumed to be a calendar
|
|
|
10900
11783
|
}
|
|
10901
11784
|
|
|
10902
11785
|
|
|
11786
|
+
// hook for external libs to manipulate event properties upon creation.
|
|
11787
|
+
// should manipulate the event in-place.
|
|
11788
|
+
Calendar.prototype.normalizeEvent = function(event) {
|
|
11789
|
+
};
|
|
11790
|
+
|
|
11791
|
+
|
|
10903
11792
|
// Returns a list of events that the given event should be compared against when being considered for a move to
|
|
10904
11793
|
// the specified span. Attached to the Calendar's prototype because EventManager is a mixin for a Calendar.
|
|
10905
11794
|
Calendar.prototype.getPeerEvents = function(span, event) {
|
|
@@ -10937,6 +11826,8 @@ function backupEventDates(event) {
|
|
|
10937
11826
|
|
|
10938
11827
|
var BasicView = FC.BasicView = View.extend({
|
|
10939
11828
|
|
|
11829
|
+
scroller: null,
|
|
11830
|
+
|
|
10940
11831
|
dayGridClass: DayGrid, // class the dayGrid will be instantiated from (overridable by subclasses)
|
|
10941
11832
|
dayGrid: null, // the main subcomponent that does most of the heavy lifting
|
|
10942
11833
|
|
|
@@ -10951,6 +11842,11 @@ var BasicView = FC.BasicView = View.extend({
|
|
|
10951
11842
|
|
|
10952
11843
|
initialize: function() {
|
|
10953
11844
|
this.dayGrid = this.instantiateDayGrid();
|
|
11845
|
+
|
|
11846
|
+
this.scroller = new Scroller({
|
|
11847
|
+
overflowX: 'hidden',
|
|
11848
|
+
overflowY: 'auto'
|
|
11849
|
+
});
|
|
10954
11850
|
},
|
|
10955
11851
|
|
|
10956
11852
|
|
|
@@ -11003,9 +11899,12 @@ var BasicView = FC.BasicView = View.extend({
|
|
|
11003
11899
|
this.el.addClass('fc-basic-view').html(this.renderSkeletonHtml());
|
|
11004
11900
|
this.renderHead();
|
|
11005
11901
|
|
|
11006
|
-
this.
|
|
11902
|
+
this.scroller.render();
|
|
11903
|
+
var dayGridContainerEl = this.scroller.el.addClass('fc-day-grid-container');
|
|
11904
|
+
var dayGridEl = $('<div class="fc-day-grid" />').appendTo(dayGridContainerEl);
|
|
11905
|
+
this.el.find('.fc-body > tr > td').append(dayGridContainerEl);
|
|
11007
11906
|
|
|
11008
|
-
this.dayGrid.setElement(
|
|
11907
|
+
this.dayGrid.setElement(dayGridEl);
|
|
11009
11908
|
this.dayGrid.renderDates(this.hasRigidRows());
|
|
11010
11909
|
},
|
|
11011
11910
|
|
|
@@ -11024,6 +11923,7 @@ var BasicView = FC.BasicView = View.extend({
|
|
|
11024
11923
|
unrenderDates: function() {
|
|
11025
11924
|
this.dayGrid.unrenderDates();
|
|
11026
11925
|
this.dayGrid.removeElement();
|
|
11926
|
+
this.scroller.destroy();
|
|
11027
11927
|
},
|
|
11028
11928
|
|
|
11029
11929
|
|
|
@@ -11044,11 +11944,7 @@ var BasicView = FC.BasicView = View.extend({
|
|
|
11044
11944
|
'</thead>' +
|
|
11045
11945
|
'<tbody class="fc-body">' +
|
|
11046
11946
|
'<tr>' +
|
|
11047
|
-
'<td class="' + this.widgetContentClass + '">' +
|
|
11048
|
-
'<div class="fc-day-grid-container">' +
|
|
11049
|
-
'<div class="fc-day-grid"/>' +
|
|
11050
|
-
'</div>' +
|
|
11051
|
-
'</td>' +
|
|
11947
|
+
'<td class="' + this.widgetContentClass + '"></td>' +
|
|
11052
11948
|
'</tr>' +
|
|
11053
11949
|
'</tbody>' +
|
|
11054
11950
|
'</table>';
|
|
@@ -11091,9 +11987,10 @@ var BasicView = FC.BasicView = View.extend({
|
|
|
11091
11987
|
setHeight: function(totalHeight, isAuto) {
|
|
11092
11988
|
var eventLimit = this.opt('eventLimit');
|
|
11093
11989
|
var scrollerHeight;
|
|
11990
|
+
var scrollbarWidths;
|
|
11094
11991
|
|
|
11095
11992
|
// reset all heights to be natural
|
|
11096
|
-
|
|
11993
|
+
this.scroller.clear();
|
|
11097
11994
|
uncompensateScroll(this.headRowEl);
|
|
11098
11995
|
|
|
11099
11996
|
this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
|
|
@@ -11103,6 +12000,8 @@ var BasicView = FC.BasicView = View.extend({
|
|
|
11103
12000
|
this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
|
|
11104
12001
|
}
|
|
11105
12002
|
|
|
12003
|
+
// distribute the height to the rows
|
|
12004
|
+
// (totalHeight is a "recommended" value if isAuto)
|
|
11106
12005
|
scrollerHeight = this.computeScrollerHeight(totalHeight);
|
|
11107
12006
|
this.setGridHeight(scrollerHeight, isAuto);
|
|
11108
12007
|
|
|
@@ -11111,17 +12010,33 @@ var BasicView = FC.BasicView = View.extend({
|
|
|
11111
12010
|
this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
|
|
11112
12011
|
}
|
|
11113
12012
|
|
|
11114
|
-
if (!isAuto
|
|
12013
|
+
if (!isAuto) { // should we force dimensions of the scroll container?
|
|
11115
12014
|
|
|
11116
|
-
|
|
12015
|
+
this.scroller.setHeight(scrollerHeight);
|
|
12016
|
+
scrollbarWidths = this.scroller.getScrollbarWidths();
|
|
11117
12017
|
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
12018
|
+
if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
|
|
12019
|
+
|
|
12020
|
+
compensateScroll(this.headRowEl, scrollbarWidths);
|
|
12021
|
+
|
|
12022
|
+
// doing the scrollbar compensation might have created text overflow which created more height. redo
|
|
12023
|
+
scrollerHeight = this.computeScrollerHeight(totalHeight);
|
|
12024
|
+
this.scroller.setHeight(scrollerHeight);
|
|
12025
|
+
}
|
|
12026
|
+
|
|
12027
|
+
// guarantees the same scrollbar widths
|
|
12028
|
+
this.scroller.lockOverflow(scrollbarWidths);
|
|
11121
12029
|
}
|
|
11122
12030
|
},
|
|
11123
12031
|
|
|
11124
12032
|
|
|
12033
|
+
// given a desired total height of the view, returns what the height of the scroller should be
|
|
12034
|
+
computeScrollerHeight: function(totalHeight) {
|
|
12035
|
+
return totalHeight -
|
|
12036
|
+
subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
|
|
12037
|
+
},
|
|
12038
|
+
|
|
12039
|
+
|
|
11125
12040
|
// Sets the height of just the DayGrid component in this view
|
|
11126
12041
|
setGridHeight: function(height, isAuto) {
|
|
11127
12042
|
if (isAuto) {
|
|
@@ -11133,6 +12048,20 @@ var BasicView = FC.BasicView = View.extend({
|
|
|
11133
12048
|
},
|
|
11134
12049
|
|
|
11135
12050
|
|
|
12051
|
+
/* Scroll
|
|
12052
|
+
------------------------------------------------------------------------------------------------------------------*/
|
|
12053
|
+
|
|
12054
|
+
|
|
12055
|
+
queryScroll: function() {
|
|
12056
|
+
return this.scroller.getScrollTop();
|
|
12057
|
+
},
|
|
12058
|
+
|
|
12059
|
+
|
|
12060
|
+
setScroll: function(top) {
|
|
12061
|
+
this.scroller.setScrollTop(top);
|
|
12062
|
+
},
|
|
12063
|
+
|
|
12064
|
+
|
|
11136
12065
|
/* Hit Areas
|
|
11137
12066
|
------------------------------------------------------------------------------------------------------------------*/
|
|
11138
12067
|
// forward all hit-related method calls to dayGrid
|
|
@@ -11368,6 +12297,8 @@ fcViews.month = {
|
|
|
11368
12297
|
|
|
11369
12298
|
var AgendaView = FC.AgendaView = View.extend({
|
|
11370
12299
|
|
|
12300
|
+
scroller: null,
|
|
12301
|
+
|
|
11371
12302
|
timeGridClass: TimeGrid, // class used to instantiate the timeGrid. subclasses can override
|
|
11372
12303
|
timeGrid: null, // the main time-grid subcomponent of this view
|
|
11373
12304
|
|
|
@@ -11377,11 +12308,10 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
|
11377
12308
|
axisWidth: null, // the width of the time axis running down the side
|
|
11378
12309
|
|
|
11379
12310
|
headContainerEl: null, // div that hold's the timeGrid's rendered date header
|
|
11380
|
-
noScrollRowEls: null, // set of fake row elements that must compensate when
|
|
12311
|
+
noScrollRowEls: null, // set of fake row elements that must compensate when scroller has scrollbars
|
|
11381
12312
|
|
|
11382
12313
|
// when the time-grid isn't tall enough to occupy the given height, we render an <hr> underneath
|
|
11383
12314
|
bottomRuleEl: null,
|
|
11384
|
-
bottomRuleHeight: null,
|
|
11385
12315
|
|
|
11386
12316
|
|
|
11387
12317
|
initialize: function() {
|
|
@@ -11390,6 +12320,11 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
|
11390
12320
|
if (this.opt('allDaySlot')) { // should we display the "all-day" area?
|
|
11391
12321
|
this.dayGrid = this.instantiateDayGrid(); // the all-day subcomponent of this view
|
|
11392
12322
|
}
|
|
12323
|
+
|
|
12324
|
+
this.scroller = new Scroller({
|
|
12325
|
+
overflowX: 'hidden',
|
|
12326
|
+
overflowY: 'auto'
|
|
12327
|
+
});
|
|
11393
12328
|
},
|
|
11394
12329
|
|
|
11395
12330
|
|
|
@@ -11430,10 +12365,12 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
|
11430
12365
|
this.el.addClass('fc-agenda-view').html(this.renderSkeletonHtml());
|
|
11431
12366
|
this.renderHead();
|
|
11432
12367
|
|
|
11433
|
-
|
|
11434
|
-
|
|
12368
|
+
this.scroller.render();
|
|
12369
|
+
var timeGridWrapEl = this.scroller.el.addClass('fc-time-grid-container');
|
|
12370
|
+
var timeGridEl = $('<div class="fc-time-grid" />').appendTo(timeGridWrapEl);
|
|
12371
|
+
this.el.find('.fc-body > tr > td').append(timeGridWrapEl);
|
|
11435
12372
|
|
|
11436
|
-
this.timeGrid.setElement(
|
|
12373
|
+
this.timeGrid.setElement(timeGridEl);
|
|
11437
12374
|
this.timeGrid.renderDates();
|
|
11438
12375
|
|
|
11439
12376
|
// the <hr> that sometimes displays under the time-grid
|
|
@@ -11470,6 +12407,8 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
|
11470
12407
|
this.dayGrid.unrenderDates();
|
|
11471
12408
|
this.dayGrid.removeElement();
|
|
11472
12409
|
}
|
|
12410
|
+
|
|
12411
|
+
this.scroller.destroy();
|
|
11473
12412
|
},
|
|
11474
12413
|
|
|
11475
12414
|
|
|
@@ -11491,9 +12430,6 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
|
11491
12430
|
'<hr class="fc-divider ' + this.widgetHeaderClass + '"/>' :
|
|
11492
12431
|
''
|
|
11493
12432
|
) +
|
|
11494
|
-
'<div class="fc-time-grid-container">' +
|
|
11495
|
-
'<div class="fc-time-grid"/>' +
|
|
11496
|
-
'</div>' +
|
|
11497
12433
|
'</td>' +
|
|
11498
12434
|
'</tr>' +
|
|
11499
12435
|
'</tbody>' +
|
|
@@ -11573,16 +12509,11 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
|
11573
12509
|
setHeight: function(totalHeight, isAuto) {
|
|
11574
12510
|
var eventLimit;
|
|
11575
12511
|
var scrollerHeight;
|
|
11576
|
-
|
|
11577
|
-
if (this.bottomRuleHeight === null) {
|
|
11578
|
-
// calculate the height of the rule the very first time
|
|
11579
|
-
this.bottomRuleHeight = this.bottomRuleEl.outerHeight();
|
|
11580
|
-
}
|
|
11581
|
-
this.bottomRuleEl.hide(); // .show() will be called later if this <hr> is necessary
|
|
12512
|
+
var scrollbarWidths;
|
|
11582
12513
|
|
|
11583
12514
|
// reset all dimensions back to the original state
|
|
11584
|
-
this.
|
|
11585
|
-
|
|
12515
|
+
this.bottomRuleEl.hide(); // .show() will be called later if this <hr> is necessary
|
|
12516
|
+
this.scroller.clear(); // sets height to 'auto' and clears overflow
|
|
11586
12517
|
uncompensateScroll(this.noScrollRowEls);
|
|
11587
12518
|
|
|
11588
12519
|
// limit number of events in the all-day area
|
|
@@ -11598,28 +12529,46 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
|
11598
12529
|
}
|
|
11599
12530
|
}
|
|
11600
12531
|
|
|
11601
|
-
if (!isAuto) { // should we force dimensions of the scroll container
|
|
12532
|
+
if (!isAuto) { // should we force dimensions of the scroll container?
|
|
11602
12533
|
|
|
11603
12534
|
scrollerHeight = this.computeScrollerHeight(totalHeight);
|
|
11604
|
-
|
|
12535
|
+
this.scroller.setHeight(scrollerHeight);
|
|
12536
|
+
scrollbarWidths = this.scroller.getScrollbarWidths();
|
|
12537
|
+
|
|
12538
|
+
if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
|
|
11605
12539
|
|
|
11606
12540
|
// make the all-day and header rows lines up
|
|
11607
|
-
compensateScroll(this.noScrollRowEls,
|
|
12541
|
+
compensateScroll(this.noScrollRowEls, scrollbarWidths);
|
|
11608
12542
|
|
|
11609
12543
|
// the scrollbar compensation might have changed text flow, which might affect height, so recalculate
|
|
11610
12544
|
// and reapply the desired height to the scroller.
|
|
11611
12545
|
scrollerHeight = this.computeScrollerHeight(totalHeight);
|
|
11612
|
-
this.
|
|
12546
|
+
this.scroller.setHeight(scrollerHeight);
|
|
11613
12547
|
}
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
12548
|
+
|
|
12549
|
+
// guarantees the same scrollbar widths
|
|
12550
|
+
this.scroller.lockOverflow(scrollbarWidths);
|
|
12551
|
+
|
|
12552
|
+
// if there's any space below the slats, show the horizontal rule.
|
|
12553
|
+
// this won't cause any new overflow, because lockOverflow already called.
|
|
12554
|
+
if (this.timeGrid.getTotalSlatHeight() < scrollerHeight) {
|
|
11617
12555
|
this.bottomRuleEl.show();
|
|
11618
12556
|
}
|
|
11619
12557
|
}
|
|
11620
12558
|
},
|
|
11621
12559
|
|
|
11622
12560
|
|
|
12561
|
+
// given a desired total height of the view, returns what the height of the scroller should be
|
|
12562
|
+
computeScrollerHeight: function(totalHeight) {
|
|
12563
|
+
return totalHeight -
|
|
12564
|
+
subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
|
|
12565
|
+
},
|
|
12566
|
+
|
|
12567
|
+
|
|
12568
|
+
/* Scroll
|
|
12569
|
+
------------------------------------------------------------------------------------------------------------------*/
|
|
12570
|
+
|
|
12571
|
+
|
|
11623
12572
|
// Computes the initial pre-configured scroll state prior to allowing the user to change it
|
|
11624
12573
|
computeInitialScroll: function() {
|
|
11625
12574
|
var scrollTime = moment.duration(this.opt('scrollTime'));
|
|
@@ -11636,6 +12585,16 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
|
11636
12585
|
},
|
|
11637
12586
|
|
|
11638
12587
|
|
|
12588
|
+
queryScroll: function() {
|
|
12589
|
+
return this.scroller.getScrollTop();
|
|
12590
|
+
},
|
|
12591
|
+
|
|
12592
|
+
|
|
12593
|
+
setScroll: function(top) {
|
|
12594
|
+
this.scroller.setScrollTop(top);
|
|
12595
|
+
},
|
|
12596
|
+
|
|
12597
|
+
|
|
11639
12598
|
/* Hit Areas
|
|
11640
12599
|
------------------------------------------------------------------------------------------------------------------*/
|
|
11641
12600
|
// forward all hit-related method calls to the grids (dayGrid might not be defined)
|