fullcalendar.io-rails 2.6.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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)
|