fullcalendar.io-rails 2.6.1 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/fullcalendar.io-rails.gemspec +1 -1
- data/lib/fullcalendar.io/rails/version.rb +1 -1
- data/vendor/assets/javascripts/fullcalendar.js +1116 -455
- data/vendor/assets/javascripts/fullcalendar/fullcalendar.js +12559 -0
- data/vendor/assets/javascripts/fullcalendar/gcal.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang-all.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ar-ma.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ar-sa.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ar-tn.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ar.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/bg.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ca.js +0 -0
- 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 +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/de.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/el.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/en-au.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/en-ca.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/en-gb.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/en-ie.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/en-nz.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/es.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/fa.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/fi.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/fr-ca.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/fr-ch.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/fr.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/he.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/hi.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/hr.js +0 -0
- 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 +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/it.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ja.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ko.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/lt.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/lv.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/nb.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/nl.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/pl.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/pt-br.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/pt.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ro.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/ru.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/sk.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/sl.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/sr-cyrl.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/sr.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/sv.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/th.js +0 -0
- 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 +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/zh-cn.js +0 -0
- data/vendor/assets/javascripts/fullcalendar/lang/zh-tw.js +0 -0
- data/vendor/assets/stylesheets/fullcalendar.css +182 -35
- data/vendor/assets/stylesheets/fullcalendar.print.css +1 -1
- metadata +54 -125
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b543ff2553f46e50d402c15b667d16db0ccaefa4
|
4
|
+
data.tar.gz: 8d4383e4ac10f442751447595ac1b8095afe3494
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec4a8cf82f3ca63f5c8c86570f158c89c70c427a390c6bdc368e249d44cfa341cacbc441b88c4af39bfe35ace12618fe828b415ab88275974d20e923c025c73f
|
7
|
+
data.tar.gz: a056689056efb450c9107c8931c45f685c0b6fd5336c5e6d4018176da0f4d4fcf22469969b3b802a966650e74411ae5b7ae6cc025f417570b2e9b190e1344fed
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.test_files = Dir["spec/**/*"]
|
21
21
|
|
22
22
|
spec.add_runtime_dependency 'jquery-rails', '>= 3.1.0', '< 5.0'
|
23
|
-
spec.add_runtime_dependency 'momentjs-rails', '~> 2.
|
23
|
+
spec.add_runtime_dependency 'momentjs-rails', '~> 2.11', '>= 2.11.0'
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.7"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
26
|
spec.add_development_dependency 'rails', '4.2.5'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
* FullCalendar v2.
|
2
|
+
* FullCalendar v2.7.0
|
3
3
|
* Docs & License: http://fullcalendar.io/
|
4
4
|
* (c) 2015 Adam Shaw
|
5
5
|
*/
|
@@ -19,12 +19,15 @@
|
|
19
19
|
;;
|
20
20
|
|
21
21
|
var FC = $.fullCalendar = {
|
22
|
-
version: "2.
|
22
|
+
version: "2.7.0",
|
23
23
|
internalApiVersion: 3
|
24
24
|
};
|
25
25
|
var fcViews = FC.views = {};
|
26
26
|
|
27
27
|
|
28
|
+
FC.isTouch = 'ontouchstart' in document;
|
29
|
+
|
30
|
+
|
28
31
|
$.fn.fullCalendar = function(options) {
|
29
32
|
var args = Array.prototype.slice.call(arguments, 1); // for a possible method call
|
30
33
|
var res = this; // what this function will return (this jQuery object by default)
|
@@ -262,29 +265,25 @@ function matchCellWidths(els) {
|
|
262
265
|
}
|
263
266
|
|
264
267
|
|
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
|
-
}
|
268
|
+
// Given one element that resides inside another,
|
269
|
+
// Subtracts the height of the inner element from the outer element.
|
270
|
+
function subtractInnerElHeight(outerEl, innerEl) {
|
271
|
+
var both = outerEl.add(innerEl);
|
272
|
+
var diff;
|
279
273
|
|
274
|
+
// fuckin IE8/9/10/11 sometimes returns 0 for dimensions. this weird hack was the only thing that worked
|
275
|
+
both.css({
|
276
|
+
position: 'relative', // cause a reflow, which will force fresh dimension recalculation
|
277
|
+
left: -1 // ensure reflow in case the el was already relative. negative is less likely to cause new scroll
|
278
|
+
});
|
279
|
+
diff = outerEl.outerHeight() - innerEl.outerHeight(); // grab the dimensions
|
280
|
+
both.css({ position: '', left: '' }); // undo hack
|
280
281
|
|
281
|
-
|
282
|
-
function unsetScroller(containerEl) {
|
283
|
-
containerEl.height('').removeClass('fc-scroller');
|
282
|
+
return diff;
|
284
283
|
}
|
285
284
|
|
286
285
|
|
287
|
-
/*
|
286
|
+
/* Element Geom Utilities
|
288
287
|
----------------------------------------------------------------------------------------------------------------------*/
|
289
288
|
|
290
289
|
FC.getOuterRect = getOuterRect;
|
@@ -309,26 +308,30 @@ function getScrollParent(el) {
|
|
309
308
|
|
310
309
|
// Queries the outer bounding area of a jQuery element.
|
311
310
|
// Returns a rectangle with absolute coordinates: left, right (exclusive), top, bottom (exclusive).
|
312
|
-
|
311
|
+
// Origin is optional.
|
312
|
+
function getOuterRect(el, origin) {
|
313
313
|
var offset = el.offset();
|
314
|
+
var left = offset.left - (origin ? origin.left : 0);
|
315
|
+
var top = offset.top - (origin ? origin.top : 0);
|
314
316
|
|
315
317
|
return {
|
316
|
-
left:
|
317
|
-
right:
|
318
|
-
top:
|
319
|
-
bottom:
|
318
|
+
left: left,
|
319
|
+
right: left + el.outerWidth(),
|
320
|
+
top: top,
|
321
|
+
bottom: top + el.outerHeight()
|
320
322
|
};
|
321
323
|
}
|
322
324
|
|
323
325
|
|
324
326
|
// Queries the area within the margin/border/scrollbars of a jQuery element. Does not go within the padding.
|
325
327
|
// Returns a rectangle with absolute coordinates: left, right (exclusive), top, bottom (exclusive).
|
328
|
+
// Origin is optional.
|
326
329
|
// NOTE: should use clientLeft/clientTop, but very unreliable cross-browser.
|
327
|
-
function getClientRect(el) {
|
330
|
+
function getClientRect(el, origin) {
|
328
331
|
var offset = el.offset();
|
329
332
|
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;
|
333
|
+
var left = offset.left + getCssFloat(el, 'border-left-width') + scrollbarWidths.left - (origin ? origin.left : 0);
|
334
|
+
var top = offset.top + getCssFloat(el, 'border-top-width') + scrollbarWidths.top - (origin ? origin.top : 0);
|
332
335
|
|
333
336
|
return {
|
334
337
|
left: left,
|
@@ -341,10 +344,13 @@ function getClientRect(el) {
|
|
341
344
|
|
342
345
|
// Queries the area within the margin/border/padding of a jQuery element. Assumed not to have scrollbars.
|
343
346
|
// Returns a rectangle with absolute coordinates: left, right (exclusive), top, bottom (exclusive).
|
344
|
-
|
347
|
+
// Origin is optional.
|
348
|
+
function getContentRect(el, origin) {
|
345
349
|
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
|
-
|
350
|
+
var left = offset.left + getCssFloat(el, 'border-left-width') + getCssFloat(el, 'padding-left') -
|
351
|
+
(origin ? origin.left : 0);
|
352
|
+
var top = offset.top + getCssFloat(el, 'border-top-width') + getCssFloat(el, 'padding-top') -
|
353
|
+
(origin ? origin.top : 0);
|
348
354
|
|
349
355
|
return {
|
350
356
|
left: left,
|
@@ -414,13 +420,58 @@ function getCssFloat(el, prop) {
|
|
414
420
|
}
|
415
421
|
|
416
422
|
|
423
|
+
/* Mouse / Touch Utilities
|
424
|
+
----------------------------------------------------------------------------------------------------------------------*/
|
425
|
+
|
426
|
+
FC.preventDefault = preventDefault;
|
427
|
+
|
428
|
+
|
417
429
|
// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
|
418
430
|
function isPrimaryMouseButton(ev) {
|
419
431
|
return ev.which == 1 && !ev.ctrlKey;
|
420
432
|
}
|
421
433
|
|
422
434
|
|
423
|
-
|
435
|
+
function getEvX(ev) {
|
436
|
+
if (ev.pageX !== undefined) {
|
437
|
+
return ev.pageX;
|
438
|
+
}
|
439
|
+
var touches = ev.originalEvent.touches;
|
440
|
+
if (touches) {
|
441
|
+
return touches[0].pageX;
|
442
|
+
}
|
443
|
+
}
|
444
|
+
|
445
|
+
|
446
|
+
function getEvY(ev) {
|
447
|
+
if (ev.pageY !== undefined) {
|
448
|
+
return ev.pageY;
|
449
|
+
}
|
450
|
+
var touches = ev.originalEvent.touches;
|
451
|
+
if (touches) {
|
452
|
+
return touches[0].pageY;
|
453
|
+
}
|
454
|
+
}
|
455
|
+
|
456
|
+
|
457
|
+
function getEvIsTouch(ev) {
|
458
|
+
return /^touch/.test(ev.type);
|
459
|
+
}
|
460
|
+
|
461
|
+
|
462
|
+
function preventSelection(el) {
|
463
|
+
el.addClass('fc-unselectable')
|
464
|
+
.on('selectstart', preventDefault);
|
465
|
+
}
|
466
|
+
|
467
|
+
|
468
|
+
// Stops a mouse/touch event from doing it's native browser action
|
469
|
+
function preventDefault(ev) {
|
470
|
+
ev.preventDefault();
|
471
|
+
}
|
472
|
+
|
473
|
+
|
474
|
+
/* General Geometry Utils
|
424
475
|
----------------------------------------------------------------------------------------------------------------------*/
|
425
476
|
|
426
477
|
FC.intersectRects = intersectRects;
|
@@ -946,22 +997,21 @@ function proxy(obj, methodName) {
|
|
946
997
|
|
947
998
|
// Returns a function, that, as long as it continues to be invoked, will not
|
948
999
|
// be triggered. The function will be called after it stops being called for
|
949
|
-
// N milliseconds.
|
1000
|
+
// N milliseconds. If `immediate` is passed, trigger the function on the
|
1001
|
+
// leading edge, instead of the trailing.
|
950
1002
|
// 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
|
1003
|
+
function debounce(func, wait, immediate) {
|
1004
|
+
var timeout, args, context, timestamp, result;
|
1005
|
+
|
956
1006
|
var later = function() {
|
957
1007
|
var last = +new Date() - timestamp;
|
958
|
-
if (last < wait
|
959
|
-
|
1008
|
+
if (last < wait) {
|
1009
|
+
timeout = setTimeout(later, wait - last);
|
960
1010
|
}
|
961
1011
|
else {
|
962
|
-
|
963
|
-
|
964
|
-
|
1012
|
+
timeout = null;
|
1013
|
+
if (!immediate) {
|
1014
|
+
result = func.apply(context, args);
|
965
1015
|
context = args = null;
|
966
1016
|
}
|
967
1017
|
}
|
@@ -971,9 +1021,15 @@ function debounce(func, wait) {
|
|
971
1021
|
context = this;
|
972
1022
|
args = arguments;
|
973
1023
|
timestamp = +new Date();
|
974
|
-
|
975
|
-
|
1024
|
+
var callNow = immediate && !timeout;
|
1025
|
+
if (!timeout) {
|
1026
|
+
timeout = setTimeout(later, wait);
|
1027
|
+
}
|
1028
|
+
if (callNow) {
|
1029
|
+
result = func.apply(context, args);
|
1030
|
+
context = args = null;
|
976
1031
|
}
|
1032
|
+
return result;
|
977
1033
|
};
|
978
1034
|
}
|
979
1035
|
|
@@ -1777,23 +1833,25 @@ function extendClass(superClass, members) {
|
|
1777
1833
|
|
1778
1834
|
|
1779
1835
|
function mixIntoClass(theClass, members) {
|
1780
|
-
copyOwnProps(members
|
1836
|
+
copyOwnProps(members, theClass.prototype); // TODO: copyNativeMethods?
|
1781
1837
|
}
|
1782
1838
|
;;
|
1783
1839
|
|
1784
|
-
var
|
1840
|
+
var EmitterMixin = FC.EmitterMixin = {
|
1785
1841
|
|
1786
1842
|
callbackHash: null,
|
1787
1843
|
|
1788
1844
|
|
1789
1845
|
on: function(name, callback) {
|
1790
|
-
this.
|
1846
|
+
this.loopCallbacks(name, 'add', [ callback ]);
|
1847
|
+
|
1791
1848
|
return this; // for chaining
|
1792
1849
|
},
|
1793
1850
|
|
1794
1851
|
|
1795
1852
|
off: function(name, callback) {
|
1796
|
-
this.
|
1853
|
+
this.loopCallbacks(name, 'remove', [ callback ]);
|
1854
|
+
|
1797
1855
|
return this; // for chaining
|
1798
1856
|
},
|
1799
1857
|
|
@@ -1808,30 +1866,104 @@ var Emitter = FC.Emitter = Class.extend({
|
|
1808
1866
|
|
1809
1867
|
|
1810
1868
|
triggerWith: function(name, context, args) {
|
1811
|
-
|
1812
|
-
|
1813
|
-
callbacks.fireWith(context, args);
|
1869
|
+
this.loopCallbacks(name, 'fireWith', [ context, args ]);
|
1814
1870
|
|
1815
1871
|
return this; // for chaining
|
1816
1872
|
},
|
1817
1873
|
|
1818
1874
|
|
1819
|
-
|
1820
|
-
|
1875
|
+
/*
|
1876
|
+
Given an event name string with possible namespaces,
|
1877
|
+
call the given methodName on all the internal Callback object with the given arguments.
|
1878
|
+
*/
|
1879
|
+
loopCallbacks: function(name, methodName, args) {
|
1880
|
+
var parts = name.split('.'); // "click.namespace" -> [ "click", "namespace" ]
|
1881
|
+
var i, part;
|
1882
|
+
var callbackObj;
|
1883
|
+
|
1884
|
+
for (i = 0; i < parts.length; i++) {
|
1885
|
+
part = parts[i];
|
1886
|
+
if (part) { // in case no event name like "click"
|
1887
|
+
callbackObj = this.ensureCallbackObj((i ? '.' : '') + part); // put periods in front of namespaces
|
1888
|
+
callbackObj[methodName].apply(callbackObj, args);
|
1889
|
+
}
|
1890
|
+
}
|
1891
|
+
},
|
1892
|
+
|
1821
1893
|
|
1894
|
+
ensureCallbackObj: function(name) {
|
1822
1895
|
if (!this.callbackHash) {
|
1823
1896
|
this.callbackHash = {};
|
1824
1897
|
}
|
1825
|
-
|
1826
|
-
|
1827
|
-
if (!callbacks) {
|
1828
|
-
callbacks = this.callbackHash[name] = $.Callbacks();
|
1898
|
+
if (!this.callbackHash[name]) {
|
1899
|
+
this.callbackHash[name] = $.Callbacks();
|
1829
1900
|
}
|
1830
|
-
|
1831
|
-
return callbacks;
|
1901
|
+
return this.callbackHash[name];
|
1832
1902
|
}
|
1833
1903
|
|
1834
|
-
}
|
1904
|
+
};
|
1905
|
+
;;
|
1906
|
+
|
1907
|
+
/*
|
1908
|
+
Utility methods for easily listening to events on another object,
|
1909
|
+
and more importantly, easily unlistening from them.
|
1910
|
+
*/
|
1911
|
+
var ListenerMixin = FC.ListenerMixin = (function() {
|
1912
|
+
var guid = 0;
|
1913
|
+
var ListenerMixin = {
|
1914
|
+
|
1915
|
+
listenerId: null,
|
1916
|
+
|
1917
|
+
/*
|
1918
|
+
Given an `other` object that has on/off methods, bind the given `callback` to an event by the given name.
|
1919
|
+
The `callback` will be called with the `this` context of the object that .listenTo is being called on.
|
1920
|
+
Can be called:
|
1921
|
+
.listenTo(other, eventName, callback)
|
1922
|
+
OR
|
1923
|
+
.listenTo(other, {
|
1924
|
+
eventName1: callback1,
|
1925
|
+
eventName2: callback2
|
1926
|
+
})
|
1927
|
+
*/
|
1928
|
+
listenTo: function(other, arg, callback) {
|
1929
|
+
if (typeof arg === 'object') { // given dictionary of callbacks
|
1930
|
+
for (var eventName in arg) {
|
1931
|
+
if (arg.hasOwnProperty(eventName)) {
|
1932
|
+
this.listenTo(other, eventName, arg[eventName]);
|
1933
|
+
}
|
1934
|
+
}
|
1935
|
+
}
|
1936
|
+
else if (typeof arg === 'string') {
|
1937
|
+
other.on(
|
1938
|
+
arg + '.' + this.getListenerNamespace(), // use event namespacing to identify this object
|
1939
|
+
$.proxy(callback, this) // always use `this` context
|
1940
|
+
// the usually-undesired jQuery guid behavior doesn't matter,
|
1941
|
+
// because we always unbind via namespace
|
1942
|
+
);
|
1943
|
+
}
|
1944
|
+
},
|
1945
|
+
|
1946
|
+
/*
|
1947
|
+
Causes the current object to stop listening to events on the `other` object.
|
1948
|
+
`eventName` is optional. If omitted, will stop listening to ALL events on `other`.
|
1949
|
+
*/
|
1950
|
+
stopListeningTo: function(other, eventName) {
|
1951
|
+
other.off((eventName || '') + '.' + this.getListenerNamespace());
|
1952
|
+
},
|
1953
|
+
|
1954
|
+
/*
|
1955
|
+
Returns a string, unique to this object, to be used for event namespacing
|
1956
|
+
*/
|
1957
|
+
getListenerNamespace: function() {
|
1958
|
+
if (this.listenerId == null) {
|
1959
|
+
this.listenerId = guid++;
|
1960
|
+
}
|
1961
|
+
return '_listener' + this.listenerId;
|
1962
|
+
}
|
1963
|
+
|
1964
|
+
};
|
1965
|
+
return ListenerMixin;
|
1966
|
+
})();
|
1835
1967
|
;;
|
1836
1968
|
|
1837
1969
|
/* A rectangular panel that is absolutely positioned over other content
|
@@ -1848,12 +1980,11 @@ Options:
|
|
1848
1980
|
- hide (callback)
|
1849
1981
|
*/
|
1850
1982
|
|
1851
|
-
var Popover = Class.extend({
|
1983
|
+
var Popover = Class.extend(ListenerMixin, {
|
1852
1984
|
|
1853
1985
|
isHidden: true,
|
1854
1986
|
options: null,
|
1855
1987
|
el: null, // the container element for the popover. generated by this object
|
1856
|
-
documentMousedownProxy: null, // document mousedown handler bound to `this`
|
1857
1988
|
margin: 10, // the space required between the popover and the edges of the scroll container
|
1858
1989
|
|
1859
1990
|
|
@@ -1907,7 +2038,7 @@ var Popover = Class.extend({
|
|
1907
2038
|
});
|
1908
2039
|
|
1909
2040
|
if (options.autoHide) {
|
1910
|
-
$(document)
|
2041
|
+
this.listenTo($(document), 'mousedown', this.documentMousedown);
|
1911
2042
|
}
|
1912
2043
|
},
|
1913
2044
|
|
@@ -1930,7 +2061,7 @@ var Popover = Class.extend({
|
|
1930
2061
|
this.el = null;
|
1931
2062
|
}
|
1932
2063
|
|
1933
|
-
$(document)
|
2064
|
+
this.stopListeningTo($(document), 'mousedown');
|
1934
2065
|
},
|
1935
2066
|
|
1936
2067
|
|
@@ -2243,257 +2374,399 @@ var CoordCache = FC.CoordCache = Class.extend({
|
|
2243
2374
|
----------------------------------------------------------------------------------------------------------------------*/
|
2244
2375
|
// TODO: use Emitter
|
2245
2376
|
|
2246
|
-
var DragListener = FC.DragListener = Class.extend({
|
2377
|
+
var DragListener = FC.DragListener = Class.extend(ListenerMixin, {
|
2247
2378
|
|
2248
2379
|
options: null,
|
2249
2380
|
|
2250
|
-
|
2251
|
-
|
2381
|
+
// for IE8 bug-fighting behavior
|
2382
|
+
subjectEl: null,
|
2383
|
+
subjectHref: null,
|
2252
2384
|
|
2253
2385
|
// coordinates of the initial mousedown
|
2254
2386
|
originX: null,
|
2255
2387
|
originY: null,
|
2256
2388
|
|
2257
|
-
// handler attached to the document, bound to the DragListener's `this`
|
2258
|
-
mousemoveProxy: null,
|
2259
|
-
mouseupProxy: null,
|
2260
|
-
|
2261
|
-
// for IE8 bug-fighting behavior, for now
|
2262
|
-
subjectEl: null, // the element being draged. optional
|
2263
|
-
subjectHref: null,
|
2264
|
-
|
2265
2389
|
scrollEl: null,
|
2266
|
-
scrollBounds: null, // { top, bottom, left, right }
|
2267
|
-
scrollTopVel: null, // pixels per second
|
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
|
2271
2390
|
|
2272
|
-
|
2273
|
-
|
2274
|
-
|
2391
|
+
isInteracting: false,
|
2392
|
+
isDistanceSurpassed: false,
|
2393
|
+
isDelayEnded: false,
|
2394
|
+
isDragging: false,
|
2395
|
+
isTouch: false,
|
2396
|
+
|
2397
|
+
delay: null,
|
2398
|
+
delayTimeoutId: null,
|
2399
|
+
minDistance: null,
|
2275
2400
|
|
2276
2401
|
|
2277
2402
|
constructor: function(options) {
|
2278
|
-
options = options || {};
|
2279
|
-
this.options = options;
|
2280
|
-
this.subjectEl = options.subjectEl;
|
2403
|
+
this.options = options || {};
|
2281
2404
|
},
|
2282
2405
|
|
2283
2406
|
|
2284
|
-
//
|
2285
|
-
|
2286
|
-
if (isPrimaryMouseButton(ev)) {
|
2407
|
+
// Interaction (high-level)
|
2408
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2287
2409
|
|
2288
|
-
ev.preventDefault(); // prevents native selection in most browsers
|
2289
2410
|
|
2290
|
-
|
2411
|
+
startInteraction: function(ev, extraOptions) {
|
2412
|
+
var isTouch = getEvIsTouch(ev);
|
2291
2413
|
|
2292
|
-
|
2293
|
-
if (!
|
2294
|
-
|
2414
|
+
if (ev.type === 'mousedown') {
|
2415
|
+
if (!isPrimaryMouseButton(ev)) {
|
2416
|
+
return;
|
2417
|
+
}
|
2418
|
+
else {
|
2419
|
+
ev.preventDefault(); // prevents native selection in most browsers
|
2295
2420
|
}
|
2296
2421
|
}
|
2297
|
-
},
|
2298
2422
|
|
2423
|
+
if (!this.isInteracting) {
|
2299
2424
|
|
2300
|
-
|
2301
|
-
|
2302
|
-
|
2425
|
+
// process options
|
2426
|
+
extraOptions = extraOptions || {};
|
2427
|
+
this.delay = firstDefined(extraOptions.delay, this.options.delay, 0);
|
2428
|
+
this.minDistance = firstDefined(extraOptions.distance, this.options.distance, 0);
|
2429
|
+
this.subjectEl = this.options.subjectEl;
|
2303
2430
|
|
2304
|
-
|
2431
|
+
this.isInteracting = true;
|
2432
|
+
this.isTouch = isTouch;
|
2433
|
+
this.isDelayEnded = false;
|
2434
|
+
this.isDistanceSurpassed = false;
|
2305
2435
|
|
2306
|
-
|
2307
|
-
|
2308
|
-
|
2309
|
-
if (!scrollParent.is(window) && !scrollParent.is(document)) {
|
2310
|
-
this.scrollEl = scrollParent;
|
2436
|
+
this.originX = getEvX(ev);
|
2437
|
+
this.originY = getEvY(ev);
|
2438
|
+
this.scrollEl = getScrollParent($(ev.target));
|
2311
2439
|
|
2312
|
-
|
2313
|
-
|
2314
|
-
|
2315
|
-
|
2440
|
+
this.bindHandlers();
|
2441
|
+
this.initAutoScroll();
|
2442
|
+
this.handleInteractionStart(ev);
|
2443
|
+
this.startDelay(ev);
|
2444
|
+
|
2445
|
+
if (!this.minDistance) {
|
2446
|
+
this.handleDistanceSurpassed(ev);
|
2316
2447
|
}
|
2448
|
+
}
|
2449
|
+
},
|
2450
|
+
|
2451
|
+
|
2452
|
+
handleInteractionStart: function(ev) {
|
2453
|
+
this.trigger('interactionStart', ev);
|
2454
|
+
},
|
2455
|
+
|
2317
2456
|
|
2318
|
-
|
2319
|
-
|
2320
|
-
|
2321
|
-
.on('selectstart', this.preventDefault); // prevents native selection in IE<=8
|
2457
|
+
endInteraction: function(ev) {
|
2458
|
+
if (this.isInteracting) {
|
2459
|
+
this.endDrag(ev);
|
2322
2460
|
|
2323
|
-
if (
|
2324
|
-
this.
|
2325
|
-
this.
|
2461
|
+
if (this.delayTimeoutId) {
|
2462
|
+
clearTimeout(this.delayTimeoutId);
|
2463
|
+
this.delayTimeoutId = null;
|
2326
2464
|
}
|
2327
|
-
|
2328
|
-
|
2329
|
-
|
2330
|
-
|
2331
|
-
|
2465
|
+
|
2466
|
+
this.destroyAutoScroll();
|
2467
|
+
this.unbindHandlers();
|
2468
|
+
|
2469
|
+
this.isInteracting = false;
|
2470
|
+
this.handleInteractionEnd(ev);
|
2471
|
+
}
|
2472
|
+
},
|
2473
|
+
|
2474
|
+
|
2475
|
+
handleInteractionEnd: function(ev) {
|
2476
|
+
this.trigger('interactionEnd', ev);
|
2477
|
+
},
|
2478
|
+
|
2479
|
+
|
2480
|
+
// Binding To DOM
|
2481
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2482
|
+
|
2483
|
+
|
2484
|
+
bindHandlers: function() {
|
2485
|
+
var _this = this;
|
2486
|
+
var touchStartIgnores = 1;
|
2487
|
+
|
2488
|
+
if (this.isTouch) {
|
2489
|
+
this.listenTo($(document), {
|
2490
|
+
touchmove: this.handleTouchMove,
|
2491
|
+
touchend: this.endInteraction,
|
2492
|
+
touchcancel: this.endInteraction,
|
2493
|
+
|
2494
|
+
// Sometimes touchend doesn't fire
|
2495
|
+
// (can't figure out why. touchcancel doesn't fire either. has to do with scrolling?)
|
2496
|
+
// If another touchstart happens, we know it's bogus, so cancel the drag.
|
2497
|
+
// touchend will continue to be broken until user does a shorttap/scroll, but this is best we can do.
|
2498
|
+
touchstart: function(ev) {
|
2499
|
+
if (touchStartIgnores) { // bindHandlers is called from within a touchstart,
|
2500
|
+
touchStartIgnores--; // and we don't want this to fire immediately, so ignore.
|
2501
|
+
}
|
2502
|
+
else {
|
2503
|
+
_this.endInteraction(ev);
|
2504
|
+
}
|
2505
|
+
}
|
2506
|
+
});
|
2507
|
+
|
2508
|
+
if (this.scrollEl) {
|
2509
|
+
this.listenTo(this.scrollEl, 'scroll', this.handleTouchScroll);
|
2332
2510
|
}
|
2511
|
+
}
|
2512
|
+
else {
|
2513
|
+
this.listenTo($(document), {
|
2514
|
+
mousemove: this.handleMouseMove,
|
2515
|
+
mouseup: this.endInteraction
|
2516
|
+
});
|
2517
|
+
}
|
2518
|
+
|
2519
|
+
this.listenTo($(document), {
|
2520
|
+
selectstart: preventDefault, // don't allow selection while dragging
|
2521
|
+
contextmenu: preventDefault // long taps would open menu on Chrome dev tools
|
2522
|
+
});
|
2523
|
+
},
|
2524
|
+
|
2525
|
+
|
2526
|
+
unbindHandlers: function() {
|
2527
|
+
this.stopListeningTo($(document));
|
2528
|
+
|
2529
|
+
if (this.scrollEl) {
|
2530
|
+
this.stopListeningTo(this.scrollEl);
|
2531
|
+
}
|
2532
|
+
},
|
2533
|
+
|
2534
|
+
|
2535
|
+
// Drag (high-level)
|
2536
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2333
2537
|
|
2334
|
-
|
2335
|
-
|
2538
|
+
|
2539
|
+
// extraOptions ignored if drag already started
|
2540
|
+
startDrag: function(ev, extraOptions) {
|
2541
|
+
this.startInteraction(ev, extraOptions); // ensure interaction began
|
2542
|
+
|
2543
|
+
if (!this.isDragging) {
|
2544
|
+
this.isDragging = true;
|
2545
|
+
this.handleDragStart(ev);
|
2336
2546
|
}
|
2337
2547
|
},
|
2338
2548
|
|
2339
2549
|
|
2340
|
-
|
2341
|
-
|
2342
|
-
this.
|
2550
|
+
handleDragStart: function(ev) {
|
2551
|
+
this.trigger('dragStart', ev);
|
2552
|
+
this.initHrefHack();
|
2343
2553
|
},
|
2344
2554
|
|
2345
2555
|
|
2346
|
-
|
2347
|
-
|
2348
|
-
var
|
2349
|
-
var
|
2350
|
-
var minDistance;
|
2556
|
+
handleMove: function(ev) {
|
2557
|
+
var dx = getEvX(ev) - this.originX;
|
2558
|
+
var dy = getEvY(ev) - this.originY;
|
2559
|
+
var minDistance = this.minDistance;
|
2351
2560
|
var distanceSq; // current distance from the origin, squared
|
2352
2561
|
|
2353
|
-
if (!this.
|
2354
|
-
// then start the drag if the minimum distance criteria is met
|
2355
|
-
minDistance = this.options.distance || 1;
|
2562
|
+
if (!this.isDistanceSurpassed) {
|
2356
2563
|
distanceSq = dx * dx + dy * dy;
|
2357
2564
|
if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem
|
2358
|
-
this.
|
2565
|
+
this.handleDistanceSurpassed(ev);
|
2359
2566
|
}
|
2360
2567
|
}
|
2361
2568
|
|
2362
2569
|
if (this.isDragging) {
|
2363
|
-
this.
|
2570
|
+
this.handleDrag(dx, dy, ev);
|
2364
2571
|
}
|
2365
2572
|
},
|
2366
2573
|
|
2367
2574
|
|
2368
|
-
//
|
2369
|
-
|
2370
|
-
|
2575
|
+
// Called while the mouse is being moved and when we know a legitimate drag is taking place
|
2576
|
+
handleDrag: function(dx, dy, ev) {
|
2577
|
+
this.trigger('drag', dx, dy, ev);
|
2578
|
+
this.updateAutoScroll(ev); // will possibly cause scrolling
|
2579
|
+
},
|
2371
2580
|
|
2372
|
-
if (!this.isListening) { // startDrag must have manually initiated
|
2373
|
-
this.startListening();
|
2374
|
-
}
|
2375
2581
|
|
2376
|
-
|
2377
|
-
|
2378
|
-
this.
|
2582
|
+
endDrag: function(ev) {
|
2583
|
+
if (this.isDragging) {
|
2584
|
+
this.isDragging = false;
|
2585
|
+
this.handleDragEnd(ev);
|
2379
2586
|
}
|
2380
2587
|
},
|
2381
2588
|
|
2382
2589
|
|
2383
|
-
|
2384
|
-
|
2385
|
-
|
2590
|
+
handleDragEnd: function(ev) {
|
2591
|
+
this.trigger('dragEnd', ev);
|
2592
|
+
this.destroyHrefHack();
|
2593
|
+
},
|
2386
2594
|
|
2387
|
-
this.trigger('dragStart', ev);
|
2388
2595
|
|
2389
|
-
|
2390
|
-
|
2391
|
-
|
2596
|
+
// Delay
|
2597
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2598
|
+
|
2599
|
+
|
2600
|
+
startDelay: function(initialEv) {
|
2601
|
+
var _this = this;
|
2602
|
+
|
2603
|
+
if (this.delay) {
|
2604
|
+
this.delayTimeoutId = setTimeout(function() {
|
2605
|
+
_this.handleDelayEnd(initialEv);
|
2606
|
+
}, this.delay);
|
2607
|
+
}
|
2608
|
+
else {
|
2609
|
+
this.handleDelayEnd(initialEv);
|
2392
2610
|
}
|
2393
2611
|
},
|
2394
2612
|
|
2395
2613
|
|
2396
|
-
|
2397
|
-
|
2398
|
-
|
2399
|
-
this.
|
2614
|
+
handleDelayEnd: function(initialEv) {
|
2615
|
+
this.isDelayEnded = true;
|
2616
|
+
|
2617
|
+
if (this.isDistanceSurpassed) {
|
2618
|
+
this.startDrag(initialEv);
|
2619
|
+
}
|
2400
2620
|
},
|
2401
2621
|
|
2402
2622
|
|
2403
|
-
//
|
2404
|
-
|
2405
|
-
|
2623
|
+
// Distance
|
2624
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2625
|
+
|
2626
|
+
|
2627
|
+
handleDistanceSurpassed: function(ev) {
|
2628
|
+
this.isDistanceSurpassed = true;
|
2629
|
+
|
2630
|
+
if (this.isDelayEnded) {
|
2631
|
+
this.startDrag(ev);
|
2632
|
+
}
|
2406
2633
|
},
|
2407
2634
|
|
2408
2635
|
|
2409
|
-
//
|
2410
|
-
//
|
2411
|
-
|
2636
|
+
// Mouse / Touch
|
2637
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2638
|
+
|
2639
|
+
|
2640
|
+
handleTouchMove: function(ev) {
|
2641
|
+
// prevent inertia and touchmove-scrolling while dragging
|
2412
2642
|
if (this.isDragging) {
|
2413
|
-
|
2414
|
-
this.dragStop(ev);
|
2415
|
-
this.isDragging = false;
|
2643
|
+
ev.preventDefault();
|
2416
2644
|
}
|
2645
|
+
|
2646
|
+
this.handleMove(ev);
|
2417
2647
|
},
|
2418
2648
|
|
2419
2649
|
|
2420
|
-
|
2421
|
-
|
2422
|
-
|
2650
|
+
handleMouseMove: function(ev) {
|
2651
|
+
this.handleMove(ev);
|
2652
|
+
},
|
2423
2653
|
|
2424
|
-
this.trigger('dragStop', ev);
|
2425
2654
|
|
2426
|
-
|
2427
|
-
|
2428
|
-
if (_this.subjectHref) {
|
2429
|
-
_this.subjectEl.attr('href', _this.subjectHref);
|
2430
|
-
}
|
2431
|
-
}, 0);
|
2432
|
-
},
|
2655
|
+
// Scrolling (unrelated to auto-scroll)
|
2656
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2433
2657
|
|
2434
2658
|
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2659
|
+
handleTouchScroll: function(ev) {
|
2660
|
+
// if the drag is being initiated by touch, but a scroll happens before
|
2661
|
+
// the drag-initiating delay is over, cancel the drag
|
2662
|
+
if (!this.isDragging) {
|
2663
|
+
this.endInteraction(ev);
|
2664
|
+
}
|
2665
|
+
},
|
2438
2666
|
|
2439
|
-
if (this.isListening) {
|
2440
2667
|
|
2441
|
-
|
2442
|
-
|
2443
|
-
this.scrollEl.off('scroll', this.scrollHandlerProxy);
|
2444
|
-
this.scrollHandlerProxy = null;
|
2445
|
-
}
|
2668
|
+
// <A> HREF Hack
|
2669
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2446
2670
|
|
2447
|
-
$(document)
|
2448
|
-
.off('mousemove', this.mousemoveProxy)
|
2449
|
-
.off('mouseup', this.mouseupProxy)
|
2450
|
-
.off('selectstart', this.preventDefault);
|
2451
2671
|
|
2452
|
-
|
2453
|
-
|
2672
|
+
initHrefHack: function() {
|
2673
|
+
var subjectEl = this.subjectEl;
|
2454
2674
|
|
2455
|
-
|
2456
|
-
|
2675
|
+
// remove a mousedown'd <a>'s href so it is not visited (IE8 bug)
|
2676
|
+
if ((this.subjectHref = subjectEl ? subjectEl.attr('href') : null)) {
|
2677
|
+
subjectEl.removeAttr('href');
|
2457
2678
|
}
|
2458
2679
|
},
|
2459
2680
|
|
2460
2681
|
|
2461
|
-
|
2462
|
-
|
2463
|
-
this.
|
2682
|
+
destroyHrefHack: function() {
|
2683
|
+
var subjectEl = this.subjectEl;
|
2684
|
+
var subjectHref = this.subjectHref;
|
2685
|
+
|
2686
|
+
// restore a mousedown'd <a>'s href (for IE8 bug)
|
2687
|
+
setTimeout(function() { // must be outside of the click's execution
|
2688
|
+
if (subjectHref) {
|
2689
|
+
subjectEl.attr('href', subjectHref);
|
2690
|
+
}
|
2691
|
+
}, 0);
|
2464
2692
|
},
|
2465
2693
|
|
2466
2694
|
|
2695
|
+
// Utils
|
2696
|
+
// -----------------------------------------------------------------------------------------------------------------
|
2697
|
+
|
2698
|
+
|
2467
2699
|
// Triggers a callback. Calls a function in the option hash of the same name.
|
2468
2700
|
// Arguments beyond the first `name` are forwarded on.
|
2469
2701
|
trigger: function(name) {
|
2470
2702
|
if (this.options[name]) {
|
2471
2703
|
this.options[name].apply(this, Array.prototype.slice.call(arguments, 1));
|
2472
2704
|
}
|
2473
|
-
|
2705
|
+
// makes _methods callable by event name. TODO: kill this
|
2706
|
+
if (this['_' + name]) {
|
2707
|
+
this['_' + name].apply(this, Array.prototype.slice.call(arguments, 1));
|
2708
|
+
}
|
2709
|
+
}
|
2710
|
+
|
2711
|
+
|
2712
|
+
});
|
2713
|
+
|
2714
|
+
;;
|
2715
|
+
/*
|
2716
|
+
this.scrollEl is set in DragListener
|
2717
|
+
*/
|
2718
|
+
DragListener.mixin({
|
2719
|
+
|
2720
|
+
isAutoScroll: false,
|
2721
|
+
|
2722
|
+
scrollBounds: null, // { top, bottom, left, right }
|
2723
|
+
scrollTopVel: null, // pixels per second
|
2724
|
+
scrollLeftVel: null, // pixels per second
|
2725
|
+
scrollIntervalId: null, // ID of setTimeout for scrolling animation loop
|
2726
|
+
|
2727
|
+
// defaults
|
2728
|
+
scrollSensitivity: 30, // pixels from edge for scrolling to start
|
2729
|
+
scrollSpeed: 200, // pixels per second, at maximum speed
|
2730
|
+
scrollIntervalMs: 50, // millisecond wait between scroll increment
|
2731
|
+
|
2732
|
+
|
2733
|
+
initAutoScroll: function() {
|
2734
|
+
var scrollEl = this.scrollEl;
|
2474
2735
|
|
2736
|
+
this.isAutoScroll =
|
2737
|
+
this.options.scroll &&
|
2738
|
+
scrollEl &&
|
2739
|
+
!scrollEl.is(window) &&
|
2740
|
+
!scrollEl.is(document);
|
2475
2741
|
|
2476
|
-
|
2477
|
-
|
2478
|
-
|
2742
|
+
if (this.isAutoScroll) {
|
2743
|
+
// debounce makes sure rapid calls don't happen
|
2744
|
+
this.listenTo(scrollEl, 'scroll', debounce(this.handleDebouncedScroll, 100));
|
2745
|
+
}
|
2479
2746
|
},
|
2480
2747
|
|
2481
2748
|
|
2482
|
-
|
2483
|
-
|
2749
|
+
destroyAutoScroll: function() {
|
2750
|
+
this.endAutoScroll(); // kill any animation loop
|
2751
|
+
|
2752
|
+
// remove the scroll handler if there is a scrollEl
|
2753
|
+
if (this.isAutoScroll) {
|
2754
|
+
this.stopListeningTo(this.scrollEl, 'scroll'); // will probably get removed by unbindHandlers too :(
|
2755
|
+
}
|
2756
|
+
},
|
2484
2757
|
|
2485
2758
|
|
2486
2759
|
// Computes and stores the bounding rectangle of scrollEl
|
2487
2760
|
computeScrollBounds: function() {
|
2488
|
-
|
2489
|
-
|
2490
|
-
this.scrollBounds = el ? getOuterRect(el) : null;
|
2761
|
+
if (this.isAutoScroll) {
|
2762
|
+
this.scrollBounds = getOuterRect(this.scrollEl);
|
2491
2763
|
// TODO: use getClientRect in future. but prevents auto scrolling when on top of scrollbars
|
2764
|
+
}
|
2492
2765
|
},
|
2493
2766
|
|
2494
2767
|
|
2495
2768
|
// Called when the dragging is in progress and scrolling should be updated
|
2496
|
-
|
2769
|
+
updateAutoScroll: function(ev) {
|
2497
2770
|
var sensitivity = this.scrollSensitivity;
|
2498
2771
|
var bounds = this.scrollBounds;
|
2499
2772
|
var topCloseness, bottomCloseness;
|
@@ -2504,10 +2777,10 @@ var DragListener = FC.DragListener = Class.extend({
|
|
2504
2777
|
if (bounds) { // only scroll if scrollEl exists
|
2505
2778
|
|
2506
2779
|
// 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
|
2780
|
+
topCloseness = (sensitivity - (getEvY(ev) - bounds.top)) / sensitivity;
|
2781
|
+
bottomCloseness = (sensitivity - (bounds.bottom - getEvY(ev))) / sensitivity;
|
2782
|
+
leftCloseness = (sensitivity - (getEvX(ev) - bounds.left)) / sensitivity;
|
2783
|
+
rightCloseness = (sensitivity - (bounds.right - getEvX(ev))) / sensitivity;
|
2511
2784
|
|
2512
2785
|
// translate vertical closeness into velocity.
|
2513
2786
|
// mouse must be completely in bounds for velocity to happen.
|
@@ -2594,38 +2867,36 @@ var DragListener = FC.DragListener = Class.extend({
|
|
2594
2867
|
|
2595
2868
|
// if scrolled all the way, which causes the vels to be zero, stop the animation loop
|
2596
2869
|
if (!this.scrollTopVel && !this.scrollLeftVel) {
|
2597
|
-
this.
|
2870
|
+
this.endAutoScroll();
|
2598
2871
|
}
|
2599
2872
|
},
|
2600
2873
|
|
2601
2874
|
|
2602
2875
|
// Kills any existing scrolling animation loop
|
2603
|
-
|
2876
|
+
endAutoScroll: function() {
|
2604
2877
|
if (this.scrollIntervalId) {
|
2605
2878
|
clearInterval(this.scrollIntervalId);
|
2606
2879
|
this.scrollIntervalId = null;
|
2607
2880
|
|
2608
|
-
|
2609
|
-
this.scrollStop();
|
2881
|
+
this.handleScrollEnd();
|
2610
2882
|
}
|
2611
2883
|
},
|
2612
2884
|
|
2613
2885
|
|
2614
2886
|
// Get called when the scrollEl is scrolled (NOTE: this is delayed via debounce)
|
2615
|
-
|
2887
|
+
handleDebouncedScroll: function() {
|
2616
2888
|
// recompute all coordinates, but *only* if this is *not* part of our scrolling animation
|
2617
2889
|
if (!this.scrollIntervalId) {
|
2618
|
-
this.
|
2890
|
+
this.handleScrollEnd();
|
2619
2891
|
}
|
2620
2892
|
},
|
2621
2893
|
|
2622
2894
|
|
2623
2895
|
// Called when scrolling has stopped, whether through auto scroll, or the user scrolling
|
2624
|
-
|
2896
|
+
handleScrollEnd: function() {
|
2625
2897
|
}
|
2626
2898
|
|
2627
2899
|
});
|
2628
|
-
|
2629
2900
|
;;
|
2630
2901
|
|
2631
2902
|
/* Tracks mouse movements over a component and raises events about which hit the mouse is over.
|
@@ -2654,18 +2925,18 @@ var HitDragListener = DragListener.extend({
|
|
2654
2925
|
|
2655
2926
|
// Called when drag listening starts (but a real drag has not necessarily began).
|
2656
2927
|
// ev might be undefined if dragging was started manually.
|
2657
|
-
|
2928
|
+
handleInteractionStart: function(ev) {
|
2658
2929
|
var subjectEl = this.subjectEl;
|
2659
2930
|
var subjectRect;
|
2660
2931
|
var origPoint;
|
2661
2932
|
var point;
|
2662
2933
|
|
2663
|
-
DragListener.prototype.
|
2934
|
+
DragListener.prototype.handleInteractionStart.apply(this, arguments); // call the super-method
|
2664
2935
|
|
2665
2936
|
this.computeCoords();
|
2666
2937
|
|
2667
2938
|
if (ev) {
|
2668
|
-
origPoint = { left: ev
|
2939
|
+
origPoint = { left: getEvX(ev), top: getEvY(ev) };
|
2669
2940
|
point = origPoint;
|
2670
2941
|
|
2671
2942
|
// constrain the point to bounds of the element being dragged
|
@@ -2701,55 +2972,55 @@ var HitDragListener = DragListener.extend({
|
|
2701
2972
|
// Recomputes the drag-critical positions of elements
|
2702
2973
|
computeCoords: function() {
|
2703
2974
|
this.component.prepareHits();
|
2704
|
-
this.computeScrollBounds(); // why is this here
|
2975
|
+
this.computeScrollBounds(); // why is this here??????
|
2705
2976
|
},
|
2706
2977
|
|
2707
2978
|
|
2708
2979
|
// Called when the actual drag has started
|
2709
|
-
|
2980
|
+
handleDragStart: function(ev) {
|
2710
2981
|
var hit;
|
2711
2982
|
|
2712
|
-
DragListener.prototype.
|
2983
|
+
DragListener.prototype.handleDragStart.apply(this, arguments); // call the super-method
|
2713
2984
|
|
2714
2985
|
// might be different from this.origHit if the min-distance is large
|
2715
|
-
hit = this.queryHit(ev
|
2986
|
+
hit = this.queryHit(getEvX(ev), getEvY(ev));
|
2716
2987
|
|
2717
2988
|
// report the initial hit the mouse is over
|
2718
2989
|
// especially important if no min-distance and drag starts immediately
|
2719
2990
|
if (hit) {
|
2720
|
-
this.
|
2991
|
+
this.handleHitOver(hit);
|
2721
2992
|
}
|
2722
2993
|
},
|
2723
2994
|
|
2724
2995
|
|
2725
2996
|
// Called when the drag moves
|
2726
|
-
|
2997
|
+
handleDrag: function(dx, dy, ev) {
|
2727
2998
|
var hit;
|
2728
2999
|
|
2729
|
-
DragListener.prototype.
|
3000
|
+
DragListener.prototype.handleDrag.apply(this, arguments); // call the super-method
|
2730
3001
|
|
2731
|
-
hit = this.queryHit(ev
|
3002
|
+
hit = this.queryHit(getEvX(ev), getEvY(ev));
|
2732
3003
|
|
2733
3004
|
if (!isHitsEqual(hit, this.hit)) { // a different hit than before?
|
2734
3005
|
if (this.hit) {
|
2735
|
-
this.
|
3006
|
+
this.handleHitOut();
|
2736
3007
|
}
|
2737
3008
|
if (hit) {
|
2738
|
-
this.
|
3009
|
+
this.handleHitOver(hit);
|
2739
3010
|
}
|
2740
3011
|
}
|
2741
3012
|
},
|
2742
3013
|
|
2743
3014
|
|
2744
3015
|
// Called when dragging has been stopped
|
2745
|
-
|
2746
|
-
this.
|
2747
|
-
DragListener.prototype.
|
3016
|
+
handleDragEnd: function() {
|
3017
|
+
this.handleHitDone();
|
3018
|
+
DragListener.prototype.handleDragEnd.apply(this, arguments); // call the super-method
|
2748
3019
|
},
|
2749
3020
|
|
2750
3021
|
|
2751
3022
|
// Called when a the mouse has just moved over a new hit
|
2752
|
-
|
3023
|
+
handleHitOver: function(hit) {
|
2753
3024
|
var isOrig = isHitsEqual(hit, this.origHit);
|
2754
3025
|
|
2755
3026
|
this.hit = hit;
|
@@ -2759,26 +3030,26 @@ var HitDragListener = DragListener.extend({
|
|
2759
3030
|
|
2760
3031
|
|
2761
3032
|
// Called when the mouse has just moved out of a hit
|
2762
|
-
|
3033
|
+
handleHitOut: function() {
|
2763
3034
|
if (this.hit) {
|
2764
3035
|
this.trigger('hitOut', this.hit);
|
2765
|
-
this.
|
3036
|
+
this.handleHitDone();
|
2766
3037
|
this.hit = null;
|
2767
3038
|
}
|
2768
3039
|
},
|
2769
3040
|
|
2770
3041
|
|
2771
3042
|
// Called after a hitOut. Also called before a dragStop
|
2772
|
-
|
3043
|
+
handleHitDone: function() {
|
2773
3044
|
if (this.hit) {
|
2774
3045
|
this.trigger('hitDone', this.hit);
|
2775
3046
|
}
|
2776
3047
|
},
|
2777
3048
|
|
2778
3049
|
|
2779
|
-
// Called when drag
|
2780
|
-
|
2781
|
-
DragListener.prototype.
|
3050
|
+
// Called when the interaction ends, whether there was a real drag or not
|
3051
|
+
handleInteractionEnd: function() {
|
3052
|
+
DragListener.prototype.handleInteractionEnd.apply(this, arguments); // call the super-method
|
2782
3053
|
|
2783
3054
|
this.origHit = null;
|
2784
3055
|
this.hit = null;
|
@@ -2788,8 +3059,8 @@ var HitDragListener = DragListener.extend({
|
|
2788
3059
|
|
2789
3060
|
|
2790
3061
|
// Called when scrolling has stopped, whether through auto scroll, or the user scrolling
|
2791
|
-
|
2792
|
-
DragListener.prototype.
|
3062
|
+
handleScrollEnd: function() {
|
3063
|
+
DragListener.prototype.handleScrollEnd.apply(this, arguments); // call the super-method
|
2793
3064
|
|
2794
3065
|
this.computeCoords(); // hits' absolute positions will be in new places. recompute
|
2795
3066
|
},
|
@@ -2844,7 +3115,7 @@ function isHitPropsWithin(subHit, superHit) {
|
|
2844
3115
|
/* Creates a clone of an element and lets it track the mouse as it moves
|
2845
3116
|
----------------------------------------------------------------------------------------------------------------------*/
|
2846
3117
|
|
2847
|
-
var MouseFollower = Class.extend({
|
3118
|
+
var MouseFollower = Class.extend(ListenerMixin, {
|
2848
3119
|
|
2849
3120
|
options: null,
|
2850
3121
|
|
@@ -2856,16 +3127,14 @@ var MouseFollower = Class.extend({
|
|
2856
3127
|
top0: null,
|
2857
3128
|
left0: null,
|
2858
3129
|
|
2859
|
-
// the
|
2860
|
-
|
2861
|
-
|
3130
|
+
// the absolute coordinates of the initiating touch/mouse action
|
3131
|
+
y0: null,
|
3132
|
+
x0: null,
|
2862
3133
|
|
2863
3134
|
// the number of pixels the mouse has moved from its initial position
|
2864
3135
|
topDelta: null,
|
2865
3136
|
leftDelta: null,
|
2866
3137
|
|
2867
|
-
mousemoveProxy: null, // document mousemove handler, bound to the MouseFollower's `this`
|
2868
|
-
|
2869
3138
|
isFollowing: false,
|
2870
3139
|
isHidden: false,
|
2871
3140
|
isAnimating: false, // doing the revert animation?
|
@@ -2882,8 +3151,8 @@ var MouseFollower = Class.extend({
|
|
2882
3151
|
if (!this.isFollowing) {
|
2883
3152
|
this.isFollowing = true;
|
2884
3153
|
|
2885
|
-
this.
|
2886
|
-
this.
|
3154
|
+
this.y0 = getEvY(ev);
|
3155
|
+
this.x0 = getEvX(ev);
|
2887
3156
|
this.topDelta = 0;
|
2888
3157
|
this.leftDelta = 0;
|
2889
3158
|
|
@@ -2891,7 +3160,12 @@ var MouseFollower = Class.extend({
|
|
2891
3160
|
this.updatePosition();
|
2892
3161
|
}
|
2893
3162
|
|
2894
|
-
|
3163
|
+
if (getEvIsTouch(ev)) {
|
3164
|
+
this.listenTo($(document), 'touchmove', this.handleMove);
|
3165
|
+
}
|
3166
|
+
else {
|
3167
|
+
this.listenTo($(document), 'mousemove', this.handleMove);
|
3168
|
+
}
|
2895
3169
|
}
|
2896
3170
|
},
|
2897
3171
|
|
@@ -2916,7 +3190,7 @@ var MouseFollower = Class.extend({
|
|
2916
3190
|
if (this.isFollowing && !this.isAnimating) { // disallow more than one stop animation at a time
|
2917
3191
|
this.isFollowing = false;
|
2918
3192
|
|
2919
|
-
$(document)
|
3193
|
+
this.stopListeningTo($(document));
|
2920
3194
|
|
2921
3195
|
if (shouldRevert && revertDuration && !this.isHidden) { // do a revert animation?
|
2922
3196
|
this.isAnimating = true;
|
@@ -2942,6 +3216,7 @@ var MouseFollower = Class.extend({
|
|
2942
3216
|
if (!el) {
|
2943
3217
|
this.sourceEl.width(); // hack to force IE8 to compute correct bounding box
|
2944
3218
|
el = this.el = this.sourceEl.clone()
|
3219
|
+
.addClass(this.options.additionalClass || '')
|
2945
3220
|
.css({
|
2946
3221
|
position: 'absolute',
|
2947
3222
|
visibility: '', // in case original element was hidden (commonly through hideEvents())
|
@@ -2953,8 +3228,13 @@ var MouseFollower = Class.extend({
|
|
2953
3228
|
height: this.sourceEl.height(), // explicit width in case there was a 'bottom' value
|
2954
3229
|
opacity: this.options.opacity || '',
|
2955
3230
|
zIndex: this.options.zIndex
|
2956
|
-
})
|
2957
|
-
|
3231
|
+
});
|
3232
|
+
|
3233
|
+
// we don't want long taps or any mouse interaction causing selection/menus.
|
3234
|
+
// would use preventSelection(), but that prevents selectstart, causing problems.
|
3235
|
+
el.addClass('fc-unselectable');
|
3236
|
+
|
3237
|
+
el.appendTo(this.parentEl);
|
2958
3238
|
}
|
2959
3239
|
|
2960
3240
|
return el;
|
@@ -2994,9 +3274,9 @@ var MouseFollower = Class.extend({
|
|
2994
3274
|
|
2995
3275
|
|
2996
3276
|
// Gets called when the user moves the mouse
|
2997
|
-
|
2998
|
-
this.topDelta = ev
|
2999
|
-
this.leftDelta = ev
|
3277
|
+
handleMove: function(ev) {
|
3278
|
+
this.topDelta = getEvY(ev) - this.y0;
|
3279
|
+
this.leftDelta = getEvX(ev) - this.x0;
|
3000
3280
|
|
3001
3281
|
if (!this.isHidden) {
|
3002
3282
|
this.updatePosition();
|
@@ -3031,7 +3311,7 @@ var MouseFollower = Class.extend({
|
|
3031
3311
|
/* An abstract class comprised of a "grid" of areas that each represent a specific datetime
|
3032
3312
|
----------------------------------------------------------------------------------------------------------------------*/
|
3033
3313
|
|
3034
|
-
var Grid = FC.Grid = Class.extend({
|
3314
|
+
var Grid = FC.Grid = Class.extend(ListenerMixin, {
|
3035
3315
|
|
3036
3316
|
view: null, // a View object
|
3037
3317
|
isRTL: null, // shortcut to the view's isRTL option
|
@@ -3042,8 +3322,6 @@ var Grid = FC.Grid = Class.extend({
|
|
3042
3322
|
el: null, // the containing element
|
3043
3323
|
elsByFill: null, // a hash of jQuery element sets used for rendering each fill. Keyed by fill name.
|
3044
3324
|
|
3045
|
-
externalDragStartProxy: null, // binds the Grid's scope to externalDragStart (in DayGrid.events)
|
3046
|
-
|
3047
3325
|
// derived from options
|
3048
3326
|
eventTimeFormat: null,
|
3049
3327
|
displayEventTime: null,
|
@@ -3056,13 +3334,16 @@ var Grid = FC.Grid = Class.extend({
|
|
3056
3334
|
// TODO: port isTimeScale into same system?
|
3057
3335
|
largeUnit: null,
|
3058
3336
|
|
3337
|
+
dayDragListener: null,
|
3338
|
+
segDragListener: null,
|
3339
|
+
segResizeListener: null,
|
3340
|
+
externalDragListener: null,
|
3341
|
+
|
3059
3342
|
|
3060
3343
|
constructor: function(view) {
|
3061
3344
|
this.view = view;
|
3062
3345
|
this.isRTL = view.opt('isRTL');
|
3063
|
-
|
3064
3346
|
this.elsByFill = {};
|
3065
|
-
this.externalDragStartProxy = proxy(this, 'externalDragStart');
|
3066
3347
|
},
|
3067
3348
|
|
3068
3349
|
|
@@ -3195,26 +3476,37 @@ var Grid = FC.Grid = Class.extend({
|
|
3195
3476
|
// Sets the container element that the grid should render inside of.
|
3196
3477
|
// Does other DOM-related initializations.
|
3197
3478
|
setElement: function(el) {
|
3198
|
-
var _this = this;
|
3199
|
-
|
3200
3479
|
this.el = el;
|
3480
|
+
preventSelection(el);
|
3481
|
+
|
3482
|
+
if (this.view.calendar.isTouch) {
|
3483
|
+
this.bindDayHandler('touchstart', this.dayTouchStart);
|
3484
|
+
}
|
3485
|
+
else {
|
3486
|
+
this.bindDayHandler('mousedown', this.dayMousedown);
|
3487
|
+
}
|
3488
|
+
|
3489
|
+
// attach event-element-related handlers. in Grid.events
|
3490
|
+
// same garbage collection note as above.
|
3491
|
+
this.bindSegHandlers();
|
3492
|
+
|
3493
|
+
this.bindGlobalHandlers();
|
3494
|
+
},
|
3495
|
+
|
3496
|
+
|
3497
|
+
bindDayHandler: function(name, handler) {
|
3498
|
+
var _this = this;
|
3201
3499
|
|
3202
3500
|
// attach a handler to the grid's root element.
|
3203
3501
|
// jQuery will take care of unregistering them when removeElement gets called.
|
3204
|
-
el.on(
|
3502
|
+
this.el.on(name, function(ev) {
|
3205
3503
|
if (
|
3206
3504
|
!$(ev.target).is('.fc-event-container *, .fc-more') && // not an an event element, or "more.." link
|
3207
3505
|
!$(ev.target).closest('.fc-popover').length // not on a popover (like the "more.." events one)
|
3208
3506
|
) {
|
3209
|
-
|
3507
|
+
return handler.call(_this, ev);
|
3210
3508
|
}
|
3211
3509
|
});
|
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
3510
|
},
|
3219
3511
|
|
3220
3512
|
|
@@ -3222,6 +3514,7 @@ var Grid = FC.Grid = Class.extend({
|
|
3222
3514
|
// DOES NOT remove any content beforehand (doesn't clear events or call unrenderDates), unlike View
|
3223
3515
|
removeElement: function() {
|
3224
3516
|
this.unbindGlobalHandlers();
|
3517
|
+
this.clearDragListeners();
|
3225
3518
|
|
3226
3519
|
this.el.remove();
|
3227
3520
|
|
@@ -3254,18 +3547,39 @@ var Grid = FC.Grid = Class.extend({
|
|
3254
3547
|
|
3255
3548
|
// Binds DOM handlers to elements that reside outside the grid, such as the document
|
3256
3549
|
bindGlobalHandlers: function() {
|
3257
|
-
$(document)
|
3550
|
+
this.listenTo($(document), {
|
3551
|
+
dragstart: this.externalDragStart, // jqui
|
3552
|
+
sortstart: this.externalDragStart // jqui
|
3553
|
+
});
|
3258
3554
|
},
|
3259
3555
|
|
3260
3556
|
|
3261
3557
|
// Unbinds DOM handlers from elements that reside outside the grid
|
3262
3558
|
unbindGlobalHandlers: function() {
|
3263
|
-
$(document)
|
3559
|
+
this.stopListeningTo($(document));
|
3264
3560
|
},
|
3265
3561
|
|
3266
3562
|
|
3267
3563
|
// Process a mousedown on an element that represents a day. For day clicking and selecting.
|
3268
3564
|
dayMousedown: function(ev) {
|
3565
|
+
this.clearDragListeners();
|
3566
|
+
this.buildDayDragListener().startInteraction(ev, {
|
3567
|
+
//distance: 5, // needs more work if we want dayClick to fire correctly
|
3568
|
+
});
|
3569
|
+
},
|
3570
|
+
|
3571
|
+
|
3572
|
+
dayTouchStart: function(ev) {
|
3573
|
+
this.clearDragListeners();
|
3574
|
+
this.buildDayDragListener().startInteraction(ev, {
|
3575
|
+
delay: this.view.opt('longPressDelay')
|
3576
|
+
});
|
3577
|
+
},
|
3578
|
+
|
3579
|
+
|
3580
|
+
// Creates a listener that tracks the user's drag across day elements.
|
3581
|
+
// For day clicking and selecting.
|
3582
|
+
buildDayDragListener: function() {
|
3269
3583
|
var _this = this;
|
3270
3584
|
var view = this.view;
|
3271
3585
|
var isSelectable = view.opt('selectable');
|
@@ -3275,8 +3589,7 @@ var Grid = FC.Grid = Class.extend({
|
|
3275
3589
|
// this listener tracks a mousedown on a day element, and a subsequent drag.
|
3276
3590
|
// if the drag ends on the same day, it is a 'dayClick'.
|
3277
3591
|
// if 'selectable' is enabled, this listener also detects selections.
|
3278
|
-
var dragListener = new HitDragListener(this, {
|
3279
|
-
//distance: 5, // needs more work if we want dayClick to fire correctly
|
3592
|
+
var dragListener = this.dayDragListener = new HitDragListener(this, {
|
3280
3593
|
scroll: view.opt('dragScroll'),
|
3281
3594
|
dragStart: function() {
|
3282
3595
|
view.unselect(); // since we could be rendering a new selection, we want to clear any old one
|
@@ -3304,7 +3617,7 @@ var Grid = FC.Grid = Class.extend({
|
|
3304
3617
|
_this.unrenderSelection();
|
3305
3618
|
enableCursor();
|
3306
3619
|
},
|
3307
|
-
|
3620
|
+
interactionEnd: function(ev) {
|
3308
3621
|
if (dayClickHit) {
|
3309
3622
|
view.triggerDayClick(
|
3310
3623
|
_this.getHitSpan(dayClickHit),
|
@@ -3317,10 +3630,30 @@ var Grid = FC.Grid = Class.extend({
|
|
3317
3630
|
view.reportSelection(selectionSpan, ev);
|
3318
3631
|
}
|
3319
3632
|
enableCursor();
|
3633
|
+
_this.dayDragListener = null;
|
3320
3634
|
}
|
3321
3635
|
});
|
3322
3636
|
|
3323
|
-
dragListener
|
3637
|
+
return dragListener;
|
3638
|
+
},
|
3639
|
+
|
3640
|
+
|
3641
|
+
// Kills all in-progress dragging.
|
3642
|
+
// Useful for when public API methods that result in re-rendering are invoked during a drag.
|
3643
|
+
// Also useful for when touch devices misbehave and don't fire their touchend.
|
3644
|
+
clearDragListeners: function() {
|
3645
|
+
if (this.dayDragListener) {
|
3646
|
+
this.dayDragListener.endInteraction(); // will clear this.dayDragListener
|
3647
|
+
}
|
3648
|
+
if (this.segDragListener) {
|
3649
|
+
this.segDragListener.endInteraction(); // will clear this.segDragListener
|
3650
|
+
}
|
3651
|
+
if (this.segResizeListener) {
|
3652
|
+
this.segResizeListener.endInteraction(); // will clear this.segResizeListener
|
3653
|
+
}
|
3654
|
+
if (this.externalDragListener) {
|
3655
|
+
this.externalDragListener.endInteraction(); // will clear this.externalDragListener
|
3656
|
+
}
|
3324
3657
|
},
|
3325
3658
|
|
3326
3659
|
|
@@ -3330,10 +3663,11 @@ var Grid = FC.Grid = Class.extend({
|
|
3330
3663
|
|
3331
3664
|
|
3332
3665
|
// Renders a mock event at the given event location, which contains zoned start/end properties.
|
3666
|
+
// Returns all mock event elements.
|
3333
3667
|
renderEventLocationHelper: function(eventLocation, sourceSeg) {
|
3334
3668
|
var fakeEvent = this.fabricateHelperEvent(eventLocation, sourceSeg);
|
3335
3669
|
|
3336
|
-
this.renderHelper(fakeEvent, sourceSeg); // do the actual rendering
|
3670
|
+
return this.renderHelper(fakeEvent, sourceSeg); // do the actual rendering
|
3337
3671
|
},
|
3338
3672
|
|
3339
3673
|
|
@@ -3361,6 +3695,7 @@ var Grid = FC.Grid = Class.extend({
|
|
3361
3695
|
|
3362
3696
|
|
3363
3697
|
// Renders a mock event. Given zoned event date properties.
|
3698
|
+
// Must return all mock event elements.
|
3364
3699
|
renderHelper: function(eventLocation, sourceSeg) {
|
3365
3700
|
// subclasses must implement
|
3366
3701
|
},
|
@@ -3640,7 +3975,8 @@ Grid.mixin({
|
|
3640
3975
|
|
3641
3976
|
// Unrenders all events currently rendered on the grid
|
3642
3977
|
unrenderEvents: function() {
|
3643
|
-
this.
|
3978
|
+
this.handleSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
|
3979
|
+
this.clearDragListeners();
|
3644
3980
|
|
3645
3981
|
this.unrenderFgSegs();
|
3646
3982
|
this.unrenderBgSegs();
|
@@ -3768,46 +4104,42 @@ Grid.mixin({
|
|
3768
4104
|
|
3769
4105
|
// Attaches event-element-related handlers to the container element and leverage bubbling
|
3770
4106
|
bindSegHandlers: function() {
|
4107
|
+
if (this.view.calendar.isTouch) {
|
4108
|
+
this.bindSegHandler('touchstart', this.handleSegTouchStart);
|
4109
|
+
}
|
4110
|
+
else {
|
4111
|
+
this.bindSegHandler('mouseenter', this.handleSegMouseover);
|
4112
|
+
this.bindSegHandler('mouseleave', this.handleSegMouseout);
|
4113
|
+
this.bindSegHandler('mousedown', this.handleSegMousedown);
|
4114
|
+
}
|
4115
|
+
|
4116
|
+
this.bindSegHandler('click', this.handleSegClick);
|
4117
|
+
},
|
4118
|
+
|
4119
|
+
|
4120
|
+
// Executes a handler for any a user-interaction on a segment.
|
4121
|
+
// Handler gets called with (seg, ev), and with the `this` context of the Grid
|
4122
|
+
bindSegHandler: function(name, handler) {
|
3771
4123
|
var _this = this;
|
3772
|
-
var view = this.view;
|
3773
4124
|
|
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
|
-
});
|
4125
|
+
this.el.on(name, '.fc-event-container > *', function(ev) {
|
4126
|
+
var seg = $(this).data('fc-seg'); // grab segment data. put there by View::renderEvents
|
4127
|
+
|
4128
|
+
// only call the handlers if there is not a drag/resize in progress
|
4129
|
+
if (seg && !_this.isDraggingSeg && !_this.isResizingSeg) {
|
4130
|
+
return handler.call(_this, seg, ev); // context will be the Grid
|
3804
4131
|
}
|
3805
|
-
);
|
4132
|
+
});
|
4133
|
+
},
|
4134
|
+
|
4135
|
+
|
4136
|
+
handleSegClick: function(seg, ev) {
|
4137
|
+
return this.view.trigger('eventClick', seg.el[0], seg.event, ev); // can return `false` to cancel
|
3806
4138
|
},
|
3807
4139
|
|
3808
4140
|
|
3809
4141
|
// Updates internal state and triggers handlers for when an event element is moused over
|
3810
|
-
|
4142
|
+
handleSegMouseover: function(seg, ev) {
|
3811
4143
|
if (!this.mousedOverSeg) {
|
3812
4144
|
this.mousedOverSeg = seg;
|
3813
4145
|
this.view.trigger('eventMouseover', seg.el[0], seg.event, ev);
|
@@ -3817,7 +4149,7 @@ Grid.mixin({
|
|
3817
4149
|
|
3818
4150
|
// Updates internal state and triggers handlers for when an event element is moused out.
|
3819
4151
|
// Can be given no arguments, in which case it will mouseout the segment that was previously moused over.
|
3820
|
-
|
4152
|
+
handleSegMouseout: function(seg, ev) {
|
3821
4153
|
ev = ev || {}; // if given no args, make a mock mouse event
|
3822
4154
|
|
3823
4155
|
if (this.mousedOverSeg) {
|
@@ -3828,45 +4160,112 @@ Grid.mixin({
|
|
3828
4160
|
},
|
3829
4161
|
|
3830
4162
|
|
4163
|
+
handleSegTouchStart: function(seg, ev) {
|
4164
|
+
var view = this.view;
|
4165
|
+
var event = seg.event;
|
4166
|
+
var isSelected = view.isEventSelected(event);
|
4167
|
+
var isDraggable = view.isEventDraggable(event);
|
4168
|
+
var isResizable = view.isEventResizable(event);
|
4169
|
+
var isResizing = false;
|
4170
|
+
var dragListener;
|
4171
|
+
|
4172
|
+
if (isSelected && isResizable) {
|
4173
|
+
// only allow resizing of the event is selected
|
4174
|
+
isResizing = this.startSegResize(seg, ev);
|
4175
|
+
}
|
4176
|
+
|
4177
|
+
if (!isResizing && (isDraggable || isResizable)) { // allowed to be selected?
|
4178
|
+
this.clearDragListeners();
|
4179
|
+
|
4180
|
+
dragListener = isDraggable ?
|
4181
|
+
this.buildSegDragListener(seg) :
|
4182
|
+
new DragListener(); // seg isn't draggable, but let's use a generic DragListener
|
4183
|
+
// simply for the delay, so it can be selected.
|
4184
|
+
|
4185
|
+
dragListener._dragStart = function() { // TODO: better way of binding
|
4186
|
+
// if not previously selected, will fire after a delay. then, select the event
|
4187
|
+
if (!isSelected) {
|
4188
|
+
view.selectEvent(event);
|
4189
|
+
}
|
4190
|
+
};
|
4191
|
+
|
4192
|
+
dragListener.startInteraction(ev, {
|
4193
|
+
delay: isSelected ? 0 : this.view.opt('longPressDelay') // do delay if not already selected
|
4194
|
+
});
|
4195
|
+
}
|
4196
|
+
},
|
4197
|
+
|
4198
|
+
|
4199
|
+
handleSegMousedown: function(seg, ev) {
|
4200
|
+
var isResizing = this.startSegResize(seg, ev, { distance: 5 });
|
4201
|
+
|
4202
|
+
if (!isResizing && this.view.isEventDraggable(seg.event)) {
|
4203
|
+
this.clearDragListeners();
|
4204
|
+
this.buildSegDragListener(seg)
|
4205
|
+
.startInteraction(ev, {
|
4206
|
+
distance: 5
|
4207
|
+
});
|
4208
|
+
}
|
4209
|
+
},
|
4210
|
+
|
4211
|
+
|
4212
|
+
// returns boolean whether resizing actually started or not.
|
4213
|
+
// assumes the seg allows resizing.
|
4214
|
+
// `dragOptions` are optional.
|
4215
|
+
startSegResize: function(seg, ev, dragOptions) {
|
4216
|
+
if ($(ev.target).is('.fc-resizer')) {
|
4217
|
+
this.clearDragListeners();
|
4218
|
+
this.buildSegResizeListener(seg, $(ev.target).is('.fc-start-resizer'))
|
4219
|
+
.startInteraction(ev, dragOptions);
|
4220
|
+
return true;
|
4221
|
+
}
|
4222
|
+
return false;
|
4223
|
+
},
|
4224
|
+
|
4225
|
+
|
4226
|
+
|
3831
4227
|
/* Event Dragging
|
3832
4228
|
------------------------------------------------------------------------------------------------------------------*/
|
3833
4229
|
|
3834
4230
|
|
3835
|
-
//
|
4231
|
+
// Builds a listener that will track user-dragging on an event segment.
|
3836
4232
|
// Generic enough to work with any type of Grid.
|
3837
|
-
|
4233
|
+
buildSegDragListener: function(seg) {
|
3838
4234
|
var _this = this;
|
3839
4235
|
var view = this.view;
|
3840
4236
|
var calendar = view.calendar;
|
3841
4237
|
var el = seg.el;
|
3842
4238
|
var event = seg.event;
|
4239
|
+
var isDragging;
|
4240
|
+
var mouseFollower; // A clone of the original element that will move with the mouse
|
3843
4241
|
var dropLocation; // zoned event date properties
|
3844
4242
|
|
3845
|
-
// A clone of the original element that will move with the mouse
|
3846
|
-
var mouseFollower = new MouseFollower(seg.el, {
|
3847
|
-
parentEl: view.el,
|
3848
|
-
opacity: view.opt('dragOpacity'),
|
3849
|
-
revertDuration: view.opt('dragRevertDuration'),
|
3850
|
-
zIndex: 2 // one above the .fc-view
|
3851
|
-
});
|
3852
|
-
|
3853
4243
|
// Tracks mouse movement over the *view's* coordinate map. Allows dragging and dropping between subcomponents
|
3854
4244
|
// of the view.
|
3855
|
-
var dragListener = new HitDragListener(view, {
|
3856
|
-
distance: 5,
|
4245
|
+
var dragListener = this.segDragListener = new HitDragListener(view, {
|
3857
4246
|
scroll: view.opt('dragScroll'),
|
3858
4247
|
subjectEl: el,
|
3859
4248
|
subjectCenter: true,
|
3860
|
-
|
4249
|
+
interactionStart: function(ev) {
|
4250
|
+
isDragging = false;
|
4251
|
+
mouseFollower = new MouseFollower(seg.el, {
|
4252
|
+
additionalClass: 'fc-dragging',
|
4253
|
+
parentEl: view.el,
|
4254
|
+
opacity: dragListener.isTouch ? null : view.opt('dragOpacity'),
|
4255
|
+
revertDuration: view.opt('dragRevertDuration'),
|
4256
|
+
zIndex: 2 // one above the .fc-view
|
4257
|
+
});
|
3861
4258
|
mouseFollower.hide(); // don't show until we know this is a real drag
|
3862
4259
|
mouseFollower.start(ev);
|
3863
4260
|
},
|
3864
4261
|
dragStart: function(ev) {
|
3865
|
-
|
4262
|
+
isDragging = true;
|
4263
|
+
_this.handleSegMouseout(seg, ev); // ensure a mouseout on the manipulated event has been reported
|
3866
4264
|
_this.segDragStart(seg, ev);
|
3867
4265
|
view.hideEvent(event); // hide all event segments. our mouseFollower will take over
|
3868
4266
|
},
|
3869
4267
|
hitOver: function(hit, isOrig, origHit) {
|
4268
|
+
var dragHelperEls;
|
3870
4269
|
|
3871
4270
|
// starting hit could be forced (DayGrid.limit)
|
3872
4271
|
if (seg.hit) {
|
@@ -3886,7 +4285,13 @@ Grid.mixin({
|
|
3886
4285
|
}
|
3887
4286
|
|
3888
4287
|
// if a valid drop location, have the subclass render a visual indication
|
3889
|
-
if (dropLocation && view.renderDrag(dropLocation, seg)) {
|
4288
|
+
if (dropLocation && (dragHelperEls = view.renderDrag(dropLocation, seg))) {
|
4289
|
+
|
4290
|
+
dragHelperEls.addClass('fc-dragging');
|
4291
|
+
if (!dragListener.isTouch) {
|
4292
|
+
_this.applyDragOpacity(dragHelperEls);
|
4293
|
+
}
|
4294
|
+
|
3890
4295
|
mouseFollower.hide(); // if the subclass is already using a mock event "helper", hide our own
|
3891
4296
|
}
|
3892
4297
|
else {
|
@@ -3902,27 +4307,26 @@ Grid.mixin({
|
|
3902
4307
|
mouseFollower.show(); // show in case we are moving out of all hits
|
3903
4308
|
dropLocation = null;
|
3904
4309
|
},
|
3905
|
-
hitDone: function() { // Called after a hitOut OR before a
|
4310
|
+
hitDone: function() { // Called after a hitOut OR before a dragEnd
|
3906
4311
|
enableCursor();
|
3907
4312
|
},
|
3908
|
-
|
4313
|
+
interactionEnd: function(ev) {
|
3909
4314
|
// do revert animation if hasn't changed. calls a callback when finished (whether animation or not)
|
3910
4315
|
mouseFollower.stop(!dropLocation, function() {
|
3911
|
-
|
3912
|
-
|
3913
|
-
|
3914
|
-
|
4316
|
+
if (isDragging) {
|
4317
|
+
view.unrenderDrag();
|
4318
|
+
view.showEvent(event);
|
4319
|
+
_this.segDragStop(seg, ev);
|
4320
|
+
}
|
3915
4321
|
if (dropLocation) {
|
3916
4322
|
view.reportEventDrop(event, dropLocation, this.largeUnit, el, ev);
|
3917
4323
|
}
|
3918
4324
|
});
|
3919
|
-
|
3920
|
-
listenStop: function() {
|
3921
|
-
mouseFollower.stop(); // put in listenStop in case there was a mousedown but the drag never started
|
4325
|
+
_this.segDragListener = null;
|
3922
4326
|
}
|
3923
4327
|
});
|
3924
4328
|
|
3925
|
-
dragListener
|
4329
|
+
return dragListener;
|
3926
4330
|
},
|
3927
4331
|
|
3928
4332
|
|
@@ -4038,8 +4442,8 @@ Grid.mixin({
|
|
4038
4442
|
var dropLocation; // a null value signals an unsuccessful drag
|
4039
4443
|
|
4040
4444
|
// listener that tracks mouse movement over date-associated pixel regions
|
4041
|
-
var dragListener = new HitDragListener(this, {
|
4042
|
-
|
4445
|
+
var dragListener = _this.externalDragListener = new HitDragListener(this, {
|
4446
|
+
interactionStart: function() {
|
4043
4447
|
_this.isDraggingExternal = true;
|
4044
4448
|
},
|
4045
4449
|
hitOver: function(hit) {
|
@@ -4063,17 +4467,16 @@ Grid.mixin({
|
|
4063
4467
|
hitOut: function() {
|
4064
4468
|
dropLocation = null; // signal unsuccessful
|
4065
4469
|
},
|
4066
|
-
hitDone: function() { // Called after a hitOut OR before a
|
4470
|
+
hitDone: function() { // Called after a hitOut OR before a dragEnd
|
4067
4471
|
enableCursor();
|
4068
4472
|
_this.unrenderDrag();
|
4069
4473
|
},
|
4070
|
-
|
4474
|
+
interactionEnd: function(ev) {
|
4071
4475
|
if (dropLocation) { // element was dropped on a valid hit
|
4072
4476
|
_this.view.reportExternalDrop(meta, dropLocation, el, ev, ui);
|
4073
4477
|
}
|
4074
|
-
},
|
4075
|
-
listenStop: function() {
|
4076
4478
|
_this.isDraggingExternal = false;
|
4479
|
+
_this.externalDragListener = null;
|
4077
4480
|
}
|
4078
4481
|
});
|
4079
4482
|
|
@@ -4114,6 +4517,7 @@ Grid.mixin({
|
|
4114
4517
|
// `dropLocation` contains hypothetical start/end/allDay values the event would have if dropped. end can be null.
|
4115
4518
|
// `seg` is the internal segment object that is being dragged. If dragging an external element, `seg` is null.
|
4116
4519
|
// A truthy returned value indicates this method has rendered a helper element.
|
4520
|
+
// Must return elements used for any mock events.
|
4117
4521
|
renderDrag: function(dropLocation, seg) {
|
4118
4522
|
// subclasses must implement
|
4119
4523
|
},
|
@@ -4129,24 +4533,28 @@ Grid.mixin({
|
|
4129
4533
|
------------------------------------------------------------------------------------------------------------------*/
|
4130
4534
|
|
4131
4535
|
|
4132
|
-
//
|
4536
|
+
// Creates a listener that tracks the user as they resize an event segment.
|
4133
4537
|
// Generic enough to work with any type of Grid.
|
4134
|
-
|
4538
|
+
buildSegResizeListener: function(seg, isStart) {
|
4135
4539
|
var _this = this;
|
4136
4540
|
var view = this.view;
|
4137
4541
|
var calendar = view.calendar;
|
4138
4542
|
var el = seg.el;
|
4139
4543
|
var event = seg.event;
|
4140
4544
|
var eventEnd = calendar.getEventEnd(event);
|
4545
|
+
var isDragging;
|
4141
4546
|
var resizeLocation; // zoned event date properties. falsy if invalid resize
|
4142
4547
|
|
4143
4548
|
// Tracks mouse movement over the *grid's* coordinate map
|
4144
|
-
var dragListener = new HitDragListener(this, {
|
4145
|
-
distance: 5,
|
4549
|
+
var dragListener = this.segResizeListener = new HitDragListener(this, {
|
4146
4550
|
scroll: view.opt('dragScroll'),
|
4147
4551
|
subjectEl: el,
|
4552
|
+
interactionStart: function() {
|
4553
|
+
isDragging = false;
|
4554
|
+
},
|
4148
4555
|
dragStart: function(ev) {
|
4149
|
-
|
4556
|
+
isDragging = true;
|
4557
|
+
_this.handleSegMouseout(seg, ev); // ensure a mouseout on the manipulated event has been reported
|
4150
4558
|
_this.segResizeStart(seg, ev);
|
4151
4559
|
},
|
4152
4560
|
hitOver: function(hit, isOrig, origHit) {
|
@@ -4181,16 +4589,18 @@ Grid.mixin({
|
|
4181
4589
|
view.showEvent(event);
|
4182
4590
|
enableCursor();
|
4183
4591
|
},
|
4184
|
-
|
4185
|
-
|
4186
|
-
|
4592
|
+
interactionEnd: function(ev) {
|
4593
|
+
if (isDragging) {
|
4594
|
+
_this.segResizeStop(seg, ev);
|
4595
|
+
}
|
4187
4596
|
if (resizeLocation) { // valid date to resize to?
|
4188
4597
|
view.reportEventResize(event, resizeLocation, this.largeUnit, el, ev);
|
4189
4598
|
}
|
4599
|
+
_this.segResizeListener = null;
|
4190
4600
|
}
|
4191
4601
|
});
|
4192
4602
|
|
4193
|
-
dragListener
|
4603
|
+
return dragListener;
|
4194
4604
|
},
|
4195
4605
|
|
4196
4606
|
|
@@ -4267,6 +4677,7 @@ Grid.mixin({
|
|
4267
4677
|
|
4268
4678
|
// Renders a visual indication of an event being resized.
|
4269
4679
|
// `range` has the updated dates of the event. `seg` is the original segment object involved in the drag.
|
4680
|
+
// Must return elements used for any mock events.
|
4270
4681
|
renderEventResize: function(range, seg) {
|
4271
4682
|
// subclasses must implement
|
4272
4683
|
},
|
@@ -4312,6 +4723,7 @@ Grid.mixin({
|
|
4312
4723
|
|
4313
4724
|
// Generic utility for generating the HTML classNames for an event segment's element
|
4314
4725
|
getSegClasses: function(seg, isDraggable, isResizable) {
|
4726
|
+
var view = this.view;
|
4315
4727
|
var event = seg.event;
|
4316
4728
|
var classes = [
|
4317
4729
|
'fc-event',
|
@@ -4329,6 +4741,11 @@ Grid.mixin({
|
|
4329
4741
|
classes.push('fc-resizable');
|
4330
4742
|
}
|
4331
4743
|
|
4744
|
+
// event is currently selected? attach a className.
|
4745
|
+
if (view.isEventSelected(event)) {
|
4746
|
+
classes.push('fc-selected');
|
4747
|
+
}
|
4748
|
+
|
4332
4749
|
return classes;
|
4333
4750
|
},
|
4334
4751
|
|
@@ -5311,10 +5728,7 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
5311
5728
|
// if a segment from the same calendar but another component is being dragged, render a helper event
|
5312
5729
|
if (seg && !seg.el.closest(this.el).length) {
|
5313
5730
|
|
5314
|
-
this.renderEventLocationHelper(eventLocation, seg);
|
5315
|
-
this.applyDragOpacity(this.helperEls);
|
5316
|
-
|
5317
|
-
return true; // a helper has been rendered
|
5731
|
+
return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
|
5318
5732
|
}
|
5319
5733
|
},
|
5320
5734
|
|
@@ -5333,7 +5747,7 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
5333
5747
|
// Renders a visual indication of an event being resized
|
5334
5748
|
renderEventResize: function(eventLocation, seg) {
|
5335
5749
|
this.renderHighlight(this.eventToSpan(eventLocation));
|
5336
|
-
this.renderEventLocationHelper(eventLocation, seg);
|
5750
|
+
return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
|
5337
5751
|
},
|
5338
5752
|
|
5339
5753
|
|
@@ -5379,7 +5793,9 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
5379
5793
|
helperNodes.push(skeletonEl[0]);
|
5380
5794
|
});
|
5381
5795
|
|
5382
|
-
|
5796
|
+
return ( // must return the elements rendered
|
5797
|
+
this.helperEls = $(helperNodes) // array -> jQuery set
|
5798
|
+
);
|
5383
5799
|
},
|
5384
5800
|
|
5385
5801
|
|
@@ -6165,6 +6581,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
6165
6581
|
labelInterval: null, // duration of how often a label should be displayed for a slot
|
6166
6582
|
|
6167
6583
|
colEls: null, // cells elements in the day-row background
|
6584
|
+
slatContainerEl: null, // div that wraps all the slat rows
|
6168
6585
|
slatEls: null, // elements running horizontally across all columns
|
6169
6586
|
nowIndicatorEls: null,
|
6170
6587
|
|
@@ -6184,7 +6601,8 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
6184
6601
|
renderDates: function() {
|
6185
6602
|
this.el.html(this.renderHtml());
|
6186
6603
|
this.colEls = this.el.find('.fc-day');
|
6187
|
-
this.
|
6604
|
+
this.slatContainerEl = this.el.find('.fc-slats');
|
6605
|
+
this.slatEls = this.slatContainerEl.find('tr');
|
6188
6606
|
|
6189
6607
|
this.colCoordCache = new CoordCache({
|
6190
6608
|
els: this.colEls,
|
@@ -6463,6 +6881,11 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
6463
6881
|
},
|
6464
6882
|
|
6465
6883
|
|
6884
|
+
getTotalSlatHeight: function() {
|
6885
|
+
return this.slatContainerEl.outerHeight();
|
6886
|
+
},
|
6887
|
+
|
6888
|
+
|
6466
6889
|
// Computes the top coordinate, relative to the bounds of the grid, of the given date.
|
6467
6890
|
// A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
|
6468
6891
|
computeDateTop: function(date, startOfDayDate) {
|
@@ -6511,13 +6934,10 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
6511
6934
|
renderDrag: function(eventLocation, seg) {
|
6512
6935
|
|
6513
6936
|
if (seg) { // if there is event information for this drag, render a helper event
|
6514
|
-
this.renderEventLocationHelper(eventLocation, seg);
|
6515
|
-
|
6516
|
-
for (var i = 0; i < this.helperSegs.length; i++) {
|
6517
|
-
this.applyDragOpacity(this.helperSegs[i].el);
|
6518
|
-
}
|
6519
6937
|
|
6520
|
-
|
6938
|
+
// returns mock event elements
|
6939
|
+
// signal that a helper has been rendered
|
6940
|
+
return this.renderEventLocationHelper(eventLocation, seg);
|
6521
6941
|
}
|
6522
6942
|
else {
|
6523
6943
|
// otherwise, just render a highlight
|
@@ -6539,7 +6959,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
6539
6959
|
|
6540
6960
|
// Renders a visual indication of an event being resized
|
6541
6961
|
renderEventResize: function(eventLocation, seg) {
|
6542
|
-
this.renderEventLocationHelper(eventLocation, seg);
|
6962
|
+
return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
|
6543
6963
|
},
|
6544
6964
|
|
6545
6965
|
|
@@ -6555,7 +6975,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
6555
6975
|
|
6556
6976
|
// Renders a mock "helper" event. `sourceSeg` is the original segment object and might be null (an external drag)
|
6557
6977
|
renderHelper: function(event, sourceSeg) {
|
6558
|
-
this.renderHelperSegs(this.eventToSegs(event), sourceSeg);
|
6978
|
+
return this.renderHelperSegs(this.eventToSegs(event), sourceSeg); // returns mock event elements
|
6559
6979
|
},
|
6560
6980
|
|
6561
6981
|
|
@@ -6749,6 +7169,7 @@ TimeGrid.mixin({
|
|
6749
7169
|
|
6750
7170
|
|
6751
7171
|
renderHelperSegs: function(segs, sourceSeg) {
|
7172
|
+
var helperEls = [];
|
6752
7173
|
var i, seg;
|
6753
7174
|
var sourceEl;
|
6754
7175
|
|
@@ -6766,9 +7187,12 @@ TimeGrid.mixin({
|
|
6766
7187
|
'margin-right': sourceEl.css('margin-right')
|
6767
7188
|
});
|
6768
7189
|
}
|
7190
|
+
helperEls.push(seg.el[0]);
|
6769
7191
|
}
|
6770
7192
|
|
6771
7193
|
this.helperSegs = segs;
|
7194
|
+
|
7195
|
+
return $(helperEls); // must return rendered helpers
|
6772
7196
|
},
|
6773
7197
|
|
6774
7198
|
|
@@ -7279,7 +7703,7 @@ function isSlotSegCollision(seg1, seg2) {
|
|
7279
7703
|
/* An abstract class from which other views inherit from
|
7280
7704
|
----------------------------------------------------------------------------------------------------------------------*/
|
7281
7705
|
|
7282
|
-
var View = FC.View = Class.extend({
|
7706
|
+
var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
7283
7707
|
|
7284
7708
|
type: null, // subclass' view name (string)
|
7285
7709
|
name: null, // deprecated. use `type` instead
|
@@ -7306,13 +7730,10 @@ var View = FC.View = Class.extend({
|
|
7306
7730
|
|
7307
7731
|
isRTL: false,
|
7308
7732
|
isSelected: false, // boolean whether a range of time is user-selected or not
|
7733
|
+
selectedEvent: null,
|
7309
7734
|
|
7310
7735
|
eventOrderSpecs: null, // criteria for ordering events when they have same date/time
|
7311
7736
|
|
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
7737
|
// classNames styled by jqui themes
|
7317
7738
|
widgetHeaderClass: null,
|
7318
7739
|
widgetContentClass: null,
|
@@ -7322,9 +7743,6 @@ var View = FC.View = Class.extend({
|
|
7322
7743
|
nextDayThreshold: null,
|
7323
7744
|
isHiddenDayHash: null,
|
7324
7745
|
|
7325
|
-
// document handlers, bound to `this` object
|
7326
|
-
documentMousedownProxy: null, // TODO: doesn't work with touch
|
7327
|
-
|
7328
7746
|
// now indicator
|
7329
7747
|
isNowIndicatorRendered: null,
|
7330
7748
|
initialNowDate: null, // result first getNow call
|
@@ -7347,8 +7765,6 @@ var View = FC.View = Class.extend({
|
|
7347
7765
|
|
7348
7766
|
this.eventOrderSpecs = parseFieldSpecs(this.opt('eventOrder'));
|
7349
7767
|
|
7350
|
-
this.documentMousedownProxy = proxy(this, 'documentMousedown');
|
7351
|
-
|
7352
7768
|
this.initialize();
|
7353
7769
|
},
|
7354
7770
|
|
@@ -7674,13 +8090,15 @@ var View = FC.View = Class.extend({
|
|
7674
8090
|
|
7675
8091
|
// Binds DOM handlers to elements that reside outside the view container, such as the document
|
7676
8092
|
bindGlobalHandlers: function() {
|
7677
|
-
$(document)
|
8093
|
+
this.listenTo($(document), 'mousedown', this.handleDocumentMousedown);
|
8094
|
+
this.listenTo($(document), 'touchstart', this.handleDocumentTouchStart);
|
8095
|
+
this.listenTo($(document), 'touchend', this.handleDocumentTouchEnd);
|
7678
8096
|
},
|
7679
8097
|
|
7680
8098
|
|
7681
8099
|
// Unbinds DOM handlers from elements that reside outside the view container
|
7682
8100
|
unbindGlobalHandlers: function() {
|
7683
|
-
$(document)
|
8101
|
+
this.stopListeningTo($(document));
|
7684
8102
|
},
|
7685
8103
|
|
7686
8104
|
|
@@ -7848,27 +8266,6 @@ var View = FC.View = Class.extend({
|
|
7848
8266
|
------------------------------------------------------------------------------------------------------------------*/
|
7849
8267
|
|
7850
8268
|
|
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
|
-
},
|
7870
|
-
|
7871
|
-
|
7872
8269
|
// Computes the initial pre-configured scroll state prior to allowing the user to change it.
|
7873
8270
|
// Given the scroll state from the previous rendering. If first time rendering, given null.
|
7874
8271
|
computeInitialScroll: function(previousScrollState) {
|
@@ -7878,17 +8275,13 @@ var View = FC.View = Class.extend({
|
|
7878
8275
|
|
7879
8276
|
// Retrieves the view's current natural scroll state. Can return an arbitrary format.
|
7880
8277
|
queryScroll: function() {
|
7881
|
-
|
7882
|
-
return this.scrollerEl.scrollTop(); // operates on scrollerEl by default
|
7883
|
-
}
|
8278
|
+
// subclasses must implement
|
7884
8279
|
},
|
7885
8280
|
|
7886
8281
|
|
7887
8282
|
// Sets the view's scroll state. Will accept the same format computeInitialScroll and queryScroll produce.
|
7888
8283
|
setScroll: function(scrollState) {
|
7889
|
-
|
7890
|
-
return this.scrollerEl.scrollTop(scrollState); // operates on scrollerEl by default
|
7891
|
-
}
|
8284
|
+
// subclasses must implement
|
7892
8285
|
},
|
7893
8286
|
|
7894
8287
|
|
@@ -8103,7 +8496,8 @@ var View = FC.View = Class.extend({
|
|
8103
8496
|
|
8104
8497
|
|
8105
8498
|
// 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
|
8499
|
+
// If an external-element, seg will be `null`.
|
8500
|
+
// Must return elements used for any mock events.
|
8107
8501
|
renderDrag: function(dropLocation, seg) {
|
8108
8502
|
// subclasses must implement
|
8109
8503
|
},
|
@@ -8166,7 +8560,7 @@ var View = FC.View = Class.extend({
|
|
8166
8560
|
},
|
8167
8561
|
|
8168
8562
|
|
8169
|
-
/* Selection
|
8563
|
+
/* Selection (time range)
|
8170
8564
|
------------------------------------------------------------------------------------------------------------------*/
|
8171
8565
|
|
8172
8566
|
|
@@ -8224,13 +8618,69 @@ var View = FC.View = Class.extend({
|
|
8224
8618
|
},
|
8225
8619
|
|
8226
8620
|
|
8227
|
-
|
8228
|
-
|
8229
|
-
|
8621
|
+
/* Event Selection
|
8622
|
+
------------------------------------------------------------------------------------------------------------------*/
|
8623
|
+
|
8624
|
+
|
8625
|
+
selectEvent: function(event) {
|
8626
|
+
if (!this.selectedEvent || this.selectedEvent !== event) {
|
8627
|
+
this.unselectEvent();
|
8628
|
+
this.renderedEventSegEach(function(seg) {
|
8629
|
+
seg.el.addClass('fc-selected');
|
8630
|
+
}, event);
|
8631
|
+
this.selectedEvent = event;
|
8632
|
+
}
|
8633
|
+
},
|
8634
|
+
|
8635
|
+
|
8636
|
+
unselectEvent: function() {
|
8637
|
+
if (this.selectedEvent) {
|
8638
|
+
this.renderedEventSegEach(function(seg) {
|
8639
|
+
seg.el.removeClass('fc-selected');
|
8640
|
+
}, this.selectedEvent);
|
8641
|
+
this.selectedEvent = null;
|
8642
|
+
}
|
8643
|
+
},
|
8644
|
+
|
8645
|
+
|
8646
|
+
isEventSelected: function(event) {
|
8647
|
+
// event references might change on refetchEvents(), while selectedEvent doesn't,
|
8648
|
+
// so compare IDs
|
8649
|
+
return this.selectedEvent && this.selectedEvent._id === event._id;
|
8650
|
+
},
|
8651
|
+
|
8652
|
+
|
8653
|
+
/* Mouse / Touch Unselecting (time range & event unselection)
|
8654
|
+
------------------------------------------------------------------------------------------------------------------*/
|
8655
|
+
// TODO: move consistently to down/start or up/end?
|
8656
|
+
|
8657
|
+
|
8658
|
+
handleDocumentMousedown: function(ev) {
|
8659
|
+
// touch devices fire simulated mouse events on a "click".
|
8660
|
+
// only process mousedown if we know this isn't a touch device.
|
8661
|
+
if (!this.calendar.isTouch && isPrimaryMouseButton(ev)) {
|
8662
|
+
this.processRangeUnselect(ev);
|
8663
|
+
this.processEventUnselect(ev);
|
8664
|
+
}
|
8665
|
+
},
|
8666
|
+
|
8667
|
+
|
8668
|
+
handleDocumentTouchStart: function(ev) {
|
8669
|
+
this.processRangeUnselect(ev);
|
8670
|
+
},
|
8230
8671
|
|
8231
|
-
// is there a selection, and has the user made a proper left click?
|
8232
|
-
if (this.isSelected && this.opt('unselectAuto') && isPrimaryMouseButton(ev)) {
|
8233
8672
|
|
8673
|
+
handleDocumentTouchEnd: function(ev) {
|
8674
|
+
// TODO: don't do this if because of touch-scrolling
|
8675
|
+
this.processEventUnselect(ev);
|
8676
|
+
},
|
8677
|
+
|
8678
|
+
|
8679
|
+
processRangeUnselect: function(ev) {
|
8680
|
+
var ignore;
|
8681
|
+
|
8682
|
+
// is there a time-range selection?
|
8683
|
+
if (this.isSelected && this.opt('unselectAuto')) {
|
8234
8684
|
// only unselect if the clicked element is not identical to or inside of an 'unselectCancel' element
|
8235
8685
|
ignore = this.opt('unselectCancel');
|
8236
8686
|
if (!ignore || !$(ev.target).closest(ignore).length) {
|
@@ -8240,6 +8690,15 @@ var View = FC.View = Class.extend({
|
|
8240
8690
|
},
|
8241
8691
|
|
8242
8692
|
|
8693
|
+
processEventUnselect: function(ev) {
|
8694
|
+
if (this.selectedEvent) {
|
8695
|
+
if (!$(ev.target).closest('.fc-selected').length) {
|
8696
|
+
this.unselectEvent();
|
8697
|
+
}
|
8698
|
+
}
|
8699
|
+
},
|
8700
|
+
|
8701
|
+
|
8243
8702
|
/* Day Click
|
8244
8703
|
------------------------------------------------------------------------------------------------------------------*/
|
8245
8704
|
|
@@ -8354,6 +8813,127 @@ var View = FC.View = Class.extend({
|
|
8354
8813
|
|
8355
8814
|
;;
|
8356
8815
|
|
8816
|
+
/*
|
8817
|
+
Embodies a div that has potential scrollbars
|
8818
|
+
*/
|
8819
|
+
var Scroller = FC.Scroller = Class.extend({
|
8820
|
+
|
8821
|
+
el: null, // the guaranteed outer element
|
8822
|
+
scrollEl: null, // the element with the scrollbars
|
8823
|
+
overflowX: null,
|
8824
|
+
overflowY: null,
|
8825
|
+
|
8826
|
+
|
8827
|
+
constructor: function(options) {
|
8828
|
+
options = options || {};
|
8829
|
+
this.overflowX = options.overflowX || options.overflow || 'auto';
|
8830
|
+
this.overflowY = options.overflowY || options.overflow || 'auto';
|
8831
|
+
},
|
8832
|
+
|
8833
|
+
|
8834
|
+
render: function() {
|
8835
|
+
this.el = this.renderEl();
|
8836
|
+
this.applyOverflow();
|
8837
|
+
},
|
8838
|
+
|
8839
|
+
|
8840
|
+
renderEl: function() {
|
8841
|
+
return (this.scrollEl = $('<div class="fc-scroller"></div>'));
|
8842
|
+
},
|
8843
|
+
|
8844
|
+
|
8845
|
+
// sets to natural height, unlocks overflow
|
8846
|
+
clear: function() {
|
8847
|
+
this.setHeight('auto');
|
8848
|
+
this.applyOverflow();
|
8849
|
+
},
|
8850
|
+
|
8851
|
+
|
8852
|
+
destroy: function() {
|
8853
|
+
this.el.remove();
|
8854
|
+
},
|
8855
|
+
|
8856
|
+
|
8857
|
+
// Overflow
|
8858
|
+
// -----------------------------------------------------------------------------------------------------------------
|
8859
|
+
|
8860
|
+
|
8861
|
+
applyOverflow: function() {
|
8862
|
+
this.scrollEl.css({
|
8863
|
+
'overflow-x': this.overflowX,
|
8864
|
+
'overflow-y': this.overflowY
|
8865
|
+
});
|
8866
|
+
},
|
8867
|
+
|
8868
|
+
|
8869
|
+
// Causes any 'auto' overflow values to resolves to 'scroll' or 'hidden'.
|
8870
|
+
// Useful for preserving scrollbar widths regardless of future resizes.
|
8871
|
+
// Can pass in scrollbarWidths for optimization.
|
8872
|
+
lockOverflow: function(scrollbarWidths) {
|
8873
|
+
var overflowX = this.overflowX;
|
8874
|
+
var overflowY = this.overflowY;
|
8875
|
+
|
8876
|
+
scrollbarWidths = scrollbarWidths || this.getScrollbarWidths();
|
8877
|
+
|
8878
|
+
if (overflowX === 'auto') {
|
8879
|
+
overflowX = (
|
8880
|
+
scrollbarWidths.top || scrollbarWidths.bottom || // horizontal scrollbars?
|
8881
|
+
// OR scrolling pane with massless scrollbars?
|
8882
|
+
this.scrollEl[0].scrollWidth - 1 > this.scrollEl[0].clientWidth
|
8883
|
+
// subtract 1 because of IE off-by-one issue
|
8884
|
+
) ? 'scroll' : 'hidden';
|
8885
|
+
}
|
8886
|
+
|
8887
|
+
if (overflowY === 'auto') {
|
8888
|
+
overflowY = (
|
8889
|
+
scrollbarWidths.left || scrollbarWidths.right || // vertical scrollbars?
|
8890
|
+
// OR scrolling pane with massless scrollbars?
|
8891
|
+
this.scrollEl[0].scrollHeight - 1 > this.scrollEl[0].clientHeight
|
8892
|
+
// subtract 1 because of IE off-by-one issue
|
8893
|
+
) ? 'scroll' : 'hidden';
|
8894
|
+
}
|
8895
|
+
|
8896
|
+
this.scrollEl.css({ 'overflow-x': overflowX, 'overflow-y': overflowY });
|
8897
|
+
},
|
8898
|
+
|
8899
|
+
|
8900
|
+
// Getters / Setters
|
8901
|
+
// -----------------------------------------------------------------------------------------------------------------
|
8902
|
+
|
8903
|
+
|
8904
|
+
setHeight: function(height) {
|
8905
|
+
this.scrollEl.height(height);
|
8906
|
+
},
|
8907
|
+
|
8908
|
+
|
8909
|
+
getScrollTop: function() {
|
8910
|
+
return this.scrollEl.scrollTop();
|
8911
|
+
},
|
8912
|
+
|
8913
|
+
|
8914
|
+
setScrollTop: function(top) {
|
8915
|
+
this.scrollEl.scrollTop(top);
|
8916
|
+
},
|
8917
|
+
|
8918
|
+
|
8919
|
+
getClientWidth: function() {
|
8920
|
+
return this.scrollEl[0].clientWidth;
|
8921
|
+
},
|
8922
|
+
|
8923
|
+
|
8924
|
+
getClientHeight: function() {
|
8925
|
+
return this.scrollEl[0].clientHeight;
|
8926
|
+
},
|
8927
|
+
|
8928
|
+
|
8929
|
+
getScrollbarWidths: function() {
|
8930
|
+
return getScrollbarWidths(this.scrollEl);
|
8931
|
+
}
|
8932
|
+
|
8933
|
+
});
|
8934
|
+
|
8935
|
+
;;
|
8936
|
+
|
8357
8937
|
var Calendar = FC.Calendar = Class.extend({
|
8358
8938
|
|
8359
8939
|
dirDefaults: null, // option defaults related to LTR or RTL
|
@@ -8364,6 +8944,7 @@ var Calendar = FC.Calendar = Class.extend({
|
|
8364
8944
|
view: null, // current View object
|
8365
8945
|
header: null,
|
8366
8946
|
loadingLevel: 0, // number of simultaneous loading tasks
|
8947
|
+
isTouch: false,
|
8367
8948
|
|
8368
8949
|
|
8369
8950
|
// a lot of this class' OOP logic is scoped within this constructor function,
|
@@ -8410,6 +8991,10 @@ var Calendar = FC.Calendar = Class.extend({
|
|
8410
8991
|
]);
|
8411
8992
|
populateInstanceComputableOptions(this.options);
|
8412
8993
|
|
8994
|
+
this.isTouch = this.options.isTouch != null ?
|
8995
|
+
this.options.isTouch :
|
8996
|
+
FC.isTouch;
|
8997
|
+
|
8413
8998
|
this.viewSpecCache = {}; // somewhat unrelated
|
8414
8999
|
},
|
8415
9000
|
|
@@ -8608,7 +9193,7 @@ var Calendar = FC.Calendar = Class.extend({
|
|
8608
9193
|
});
|
8609
9194
|
|
8610
9195
|
|
8611
|
-
Calendar.mixin(
|
9196
|
+
Calendar.mixin(EmitterMixin);
|
8612
9197
|
|
8613
9198
|
|
8614
9199
|
function Calendar_constructor(element, overrides) {
|
@@ -8866,6 +9451,10 @@ function Calendar_constructor(element, overrides) {
|
|
8866
9451
|
tm = options.theme ? 'ui' : 'fc';
|
8867
9452
|
element.addClass('fc');
|
8868
9453
|
|
9454
|
+
element.addClass(
|
9455
|
+
t.isTouch ? 'fc-touch' : 'fc-cursor'
|
9456
|
+
);
|
9457
|
+
|
8869
9458
|
if (options.isRTL) {
|
8870
9459
|
element.addClass('fc-rtl');
|
8871
9460
|
}
|
@@ -8908,7 +9497,7 @@ function Calendar_constructor(element, overrides) {
|
|
8908
9497
|
|
8909
9498
|
header.removeElement();
|
8910
9499
|
content.remove();
|
8911
|
-
element.removeClass('fc fc-ltr fc-rtl fc-unthemed ui-widget');
|
9500
|
+
element.removeClass('fc fc-touch fc-cursor fc-ltr fc-rtl fc-unthemed ui-widget');
|
8912
9501
|
|
8913
9502
|
if (windowResizeProxy) {
|
8914
9503
|
$(window).unbind('resize', windowResizeProxy);
|
@@ -9369,7 +9958,9 @@ Calendar.defaults = {
|
|
9369
9958
|
dayPopoverFormat: 'LL',
|
9370
9959
|
|
9371
9960
|
handleWindowResize: true,
|
9372
|
-
windowResizeDelay: 200 // milliseconds before an updateSize happens
|
9961
|
+
windowResizeDelay: 200, // milliseconds before an updateSize happens
|
9962
|
+
|
9963
|
+
longPressDelay: 1000
|
9373
9964
|
|
9374
9965
|
};
|
9375
9966
|
|
@@ -10937,6 +11528,8 @@ function backupEventDates(event) {
|
|
10937
11528
|
|
10938
11529
|
var BasicView = FC.BasicView = View.extend({
|
10939
11530
|
|
11531
|
+
scroller: null,
|
11532
|
+
|
10940
11533
|
dayGridClass: DayGrid, // class the dayGrid will be instantiated from (overridable by subclasses)
|
10941
11534
|
dayGrid: null, // the main subcomponent that does most of the heavy lifting
|
10942
11535
|
|
@@ -10951,6 +11544,11 @@ var BasicView = FC.BasicView = View.extend({
|
|
10951
11544
|
|
10952
11545
|
initialize: function() {
|
10953
11546
|
this.dayGrid = this.instantiateDayGrid();
|
11547
|
+
|
11548
|
+
this.scroller = new Scroller({
|
11549
|
+
overflowX: 'hidden',
|
11550
|
+
overflowY: 'auto'
|
11551
|
+
});
|
10954
11552
|
},
|
10955
11553
|
|
10956
11554
|
|
@@ -11003,9 +11601,12 @@ var BasicView = FC.BasicView = View.extend({
|
|
11003
11601
|
this.el.addClass('fc-basic-view').html(this.renderSkeletonHtml());
|
11004
11602
|
this.renderHead();
|
11005
11603
|
|
11006
|
-
this.
|
11604
|
+
this.scroller.render();
|
11605
|
+
var dayGridContainerEl = this.scroller.el.addClass('fc-day-grid-container');
|
11606
|
+
var dayGridEl = $('<div class="fc-day-grid" />').appendTo(dayGridContainerEl);
|
11607
|
+
this.el.find('.fc-body > tr > td').append(dayGridContainerEl);
|
11007
11608
|
|
11008
|
-
this.dayGrid.setElement(
|
11609
|
+
this.dayGrid.setElement(dayGridEl);
|
11009
11610
|
this.dayGrid.renderDates(this.hasRigidRows());
|
11010
11611
|
},
|
11011
11612
|
|
@@ -11024,6 +11625,7 @@ var BasicView = FC.BasicView = View.extend({
|
|
11024
11625
|
unrenderDates: function() {
|
11025
11626
|
this.dayGrid.unrenderDates();
|
11026
11627
|
this.dayGrid.removeElement();
|
11628
|
+
this.scroller.destroy();
|
11027
11629
|
},
|
11028
11630
|
|
11029
11631
|
|
@@ -11044,11 +11646,7 @@ var BasicView = FC.BasicView = View.extend({
|
|
11044
11646
|
'</thead>' +
|
11045
11647
|
'<tbody class="fc-body">' +
|
11046
11648
|
'<tr>' +
|
11047
|
-
'<td class="' + this.widgetContentClass + '">' +
|
11048
|
-
'<div class="fc-day-grid-container">' +
|
11049
|
-
'<div class="fc-day-grid"/>' +
|
11050
|
-
'</div>' +
|
11051
|
-
'</td>' +
|
11649
|
+
'<td class="' + this.widgetContentClass + '"></td>' +
|
11052
11650
|
'</tr>' +
|
11053
11651
|
'</tbody>' +
|
11054
11652
|
'</table>';
|
@@ -11091,9 +11689,10 @@ var BasicView = FC.BasicView = View.extend({
|
|
11091
11689
|
setHeight: function(totalHeight, isAuto) {
|
11092
11690
|
var eventLimit = this.opt('eventLimit');
|
11093
11691
|
var scrollerHeight;
|
11692
|
+
var scrollbarWidths;
|
11094
11693
|
|
11095
11694
|
// reset all heights to be natural
|
11096
|
-
|
11695
|
+
this.scroller.clear();
|
11097
11696
|
uncompensateScroll(this.headRowEl);
|
11098
11697
|
|
11099
11698
|
this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
|
@@ -11103,6 +11702,8 @@ var BasicView = FC.BasicView = View.extend({
|
|
11103
11702
|
this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
|
11104
11703
|
}
|
11105
11704
|
|
11705
|
+
// distribute the height to the rows
|
11706
|
+
// (totalHeight is a "recommended" value if isAuto)
|
11106
11707
|
scrollerHeight = this.computeScrollerHeight(totalHeight);
|
11107
11708
|
this.setGridHeight(scrollerHeight, isAuto);
|
11108
11709
|
|
@@ -11111,17 +11712,33 @@ var BasicView = FC.BasicView = View.extend({
|
|
11111
11712
|
this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
|
11112
11713
|
}
|
11113
11714
|
|
11114
|
-
if (!isAuto
|
11715
|
+
if (!isAuto) { // should we force dimensions of the scroll container?
|
11115
11716
|
|
11116
|
-
|
11717
|
+
this.scroller.setHeight(scrollerHeight);
|
11718
|
+
scrollbarWidths = this.scroller.getScrollbarWidths();
|
11117
11719
|
|
11118
|
-
|
11119
|
-
|
11120
|
-
|
11720
|
+
if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
|
11721
|
+
|
11722
|
+
compensateScroll(this.headRowEl, scrollbarWidths);
|
11723
|
+
|
11724
|
+
// doing the scrollbar compensation might have created text overflow which created more height. redo
|
11725
|
+
scrollerHeight = this.computeScrollerHeight(totalHeight);
|
11726
|
+
this.scroller.setHeight(scrollerHeight);
|
11727
|
+
}
|
11728
|
+
|
11729
|
+
// guarantees the same scrollbar widths
|
11730
|
+
this.scroller.lockOverflow(scrollbarWidths);
|
11121
11731
|
}
|
11122
11732
|
},
|
11123
11733
|
|
11124
11734
|
|
11735
|
+
// given a desired total height of the view, returns what the height of the scroller should be
|
11736
|
+
computeScrollerHeight: function(totalHeight) {
|
11737
|
+
return totalHeight -
|
11738
|
+
subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
|
11739
|
+
},
|
11740
|
+
|
11741
|
+
|
11125
11742
|
// Sets the height of just the DayGrid component in this view
|
11126
11743
|
setGridHeight: function(height, isAuto) {
|
11127
11744
|
if (isAuto) {
|
@@ -11133,6 +11750,20 @@ var BasicView = FC.BasicView = View.extend({
|
|
11133
11750
|
},
|
11134
11751
|
|
11135
11752
|
|
11753
|
+
/* Scroll
|
11754
|
+
------------------------------------------------------------------------------------------------------------------*/
|
11755
|
+
|
11756
|
+
|
11757
|
+
queryScroll: function() {
|
11758
|
+
return this.scroller.getScrollTop();
|
11759
|
+
},
|
11760
|
+
|
11761
|
+
|
11762
|
+
setScroll: function(top) {
|
11763
|
+
this.scroller.setScrollTop(top);
|
11764
|
+
},
|
11765
|
+
|
11766
|
+
|
11136
11767
|
/* Hit Areas
|
11137
11768
|
------------------------------------------------------------------------------------------------------------------*/
|
11138
11769
|
// forward all hit-related method calls to dayGrid
|
@@ -11368,6 +11999,8 @@ fcViews.month = {
|
|
11368
11999
|
|
11369
12000
|
var AgendaView = FC.AgendaView = View.extend({
|
11370
12001
|
|
12002
|
+
scroller: null,
|
12003
|
+
|
11371
12004
|
timeGridClass: TimeGrid, // class used to instantiate the timeGrid. subclasses can override
|
11372
12005
|
timeGrid: null, // the main time-grid subcomponent of this view
|
11373
12006
|
|
@@ -11377,11 +12010,10 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
11377
12010
|
axisWidth: null, // the width of the time axis running down the side
|
11378
12011
|
|
11379
12012
|
headContainerEl: null, // div that hold's the timeGrid's rendered date header
|
11380
|
-
noScrollRowEls: null, // set of fake row elements that must compensate when
|
12013
|
+
noScrollRowEls: null, // set of fake row elements that must compensate when scroller has scrollbars
|
11381
12014
|
|
11382
12015
|
// when the time-grid isn't tall enough to occupy the given height, we render an <hr> underneath
|
11383
12016
|
bottomRuleEl: null,
|
11384
|
-
bottomRuleHeight: null,
|
11385
12017
|
|
11386
12018
|
|
11387
12019
|
initialize: function() {
|
@@ -11390,6 +12022,11 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
11390
12022
|
if (this.opt('allDaySlot')) { // should we display the "all-day" area?
|
11391
12023
|
this.dayGrid = this.instantiateDayGrid(); // the all-day subcomponent of this view
|
11392
12024
|
}
|
12025
|
+
|
12026
|
+
this.scroller = new Scroller({
|
12027
|
+
overflowX: 'hidden',
|
12028
|
+
overflowY: 'auto'
|
12029
|
+
});
|
11393
12030
|
},
|
11394
12031
|
|
11395
12032
|
|
@@ -11430,10 +12067,12 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
11430
12067
|
this.el.addClass('fc-agenda-view').html(this.renderSkeletonHtml());
|
11431
12068
|
this.renderHead();
|
11432
12069
|
|
11433
|
-
|
11434
|
-
|
12070
|
+
this.scroller.render();
|
12071
|
+
var timeGridWrapEl = this.scroller.el.addClass('fc-time-grid-container');
|
12072
|
+
var timeGridEl = $('<div class="fc-time-grid" />').appendTo(timeGridWrapEl);
|
12073
|
+
this.el.find('.fc-body > tr > td').append(timeGridWrapEl);
|
11435
12074
|
|
11436
|
-
this.timeGrid.setElement(
|
12075
|
+
this.timeGrid.setElement(timeGridEl);
|
11437
12076
|
this.timeGrid.renderDates();
|
11438
12077
|
|
11439
12078
|
// the <hr> that sometimes displays under the time-grid
|
@@ -11470,6 +12109,8 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
11470
12109
|
this.dayGrid.unrenderDates();
|
11471
12110
|
this.dayGrid.removeElement();
|
11472
12111
|
}
|
12112
|
+
|
12113
|
+
this.scroller.destroy();
|
11473
12114
|
},
|
11474
12115
|
|
11475
12116
|
|
@@ -11491,9 +12132,6 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
11491
12132
|
'<hr class="fc-divider ' + this.widgetHeaderClass + '"/>' :
|
11492
12133
|
''
|
11493
12134
|
) +
|
11494
|
-
'<div class="fc-time-grid-container">' +
|
11495
|
-
'<div class="fc-time-grid"/>' +
|
11496
|
-
'</div>' +
|
11497
12135
|
'</td>' +
|
11498
12136
|
'</tr>' +
|
11499
12137
|
'</tbody>' +
|
@@ -11573,16 +12211,11 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
11573
12211
|
setHeight: function(totalHeight, isAuto) {
|
11574
12212
|
var eventLimit;
|
11575
12213
|
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
|
12214
|
+
var scrollbarWidths;
|
11582
12215
|
|
11583
12216
|
// reset all dimensions back to the original state
|
11584
|
-
this.
|
11585
|
-
|
12217
|
+
this.bottomRuleEl.hide(); // .show() will be called later if this <hr> is necessary
|
12218
|
+
this.scroller.clear(); // sets height to 'auto' and clears overflow
|
11586
12219
|
uncompensateScroll(this.noScrollRowEls);
|
11587
12220
|
|
11588
12221
|
// limit number of events in the all-day area
|
@@ -11598,28 +12231,46 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
11598
12231
|
}
|
11599
12232
|
}
|
11600
12233
|
|
11601
|
-
if (!isAuto) { // should we force dimensions of the scroll container
|
12234
|
+
if (!isAuto) { // should we force dimensions of the scroll container?
|
11602
12235
|
|
11603
12236
|
scrollerHeight = this.computeScrollerHeight(totalHeight);
|
11604
|
-
|
12237
|
+
this.scroller.setHeight(scrollerHeight);
|
12238
|
+
scrollbarWidths = this.scroller.getScrollbarWidths();
|
12239
|
+
|
12240
|
+
if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
|
11605
12241
|
|
11606
12242
|
// make the all-day and header rows lines up
|
11607
|
-
compensateScroll(this.noScrollRowEls,
|
12243
|
+
compensateScroll(this.noScrollRowEls, scrollbarWidths);
|
11608
12244
|
|
11609
12245
|
// the scrollbar compensation might have changed text flow, which might affect height, so recalculate
|
11610
12246
|
// and reapply the desired height to the scroller.
|
11611
12247
|
scrollerHeight = this.computeScrollerHeight(totalHeight);
|
11612
|
-
this.
|
12248
|
+
this.scroller.setHeight(scrollerHeight);
|
11613
12249
|
}
|
11614
|
-
|
11615
|
-
|
11616
|
-
|
12250
|
+
|
12251
|
+
// guarantees the same scrollbar widths
|
12252
|
+
this.scroller.lockOverflow(scrollbarWidths);
|
12253
|
+
|
12254
|
+
// if there's any space below the slats, show the horizontal rule.
|
12255
|
+
// this won't cause any new overflow, because lockOverflow already called.
|
12256
|
+
if (this.timeGrid.getTotalSlatHeight() < scrollerHeight) {
|
11617
12257
|
this.bottomRuleEl.show();
|
11618
12258
|
}
|
11619
12259
|
}
|
11620
12260
|
},
|
11621
12261
|
|
11622
12262
|
|
12263
|
+
// given a desired total height of the view, returns what the height of the scroller should be
|
12264
|
+
computeScrollerHeight: function(totalHeight) {
|
12265
|
+
return totalHeight -
|
12266
|
+
subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
|
12267
|
+
},
|
12268
|
+
|
12269
|
+
|
12270
|
+
/* Scroll
|
12271
|
+
------------------------------------------------------------------------------------------------------------------*/
|
12272
|
+
|
12273
|
+
|
11623
12274
|
// Computes the initial pre-configured scroll state prior to allowing the user to change it
|
11624
12275
|
computeInitialScroll: function() {
|
11625
12276
|
var scrollTime = moment.duration(this.opt('scrollTime'));
|
@@ -11636,6 +12287,16 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
11636
12287
|
},
|
11637
12288
|
|
11638
12289
|
|
12290
|
+
queryScroll: function() {
|
12291
|
+
return this.scroller.getScrollTop();
|
12292
|
+
},
|
12293
|
+
|
12294
|
+
|
12295
|
+
setScroll: function(top) {
|
12296
|
+
this.scroller.setScrollTop(top);
|
12297
|
+
},
|
12298
|
+
|
12299
|
+
|
11639
12300
|
/* Hit Areas
|
11640
12301
|
------------------------------------------------------------------------------------------------------------------*/
|
11641
12302
|
// forward all hit-related method calls to the grids (dayGrid might not be defined)
|