| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- var EventDragging = FC.EventDragging = Interaction.extend({
- eventPointing: null,
- dragListener: null,
- isDragging: false,
- /*
- component implements:
- - bindSegHandlerToEl
- - publiclyTrigger
- - diffDates
- - eventRangesToEventFootprints
- - isEventInstanceGroupAllowed
- */
- constructor: function(component, eventPointing) {
- Interaction.call(this, component);
- this.eventPointing = eventPointing;
- },
- end: function() {
- if (this.dragListener) {
- this.dragListener.endInteraction();
- }
- },
- getSelectionDelay: function() {
- var delay = this.opt('eventLongPressDelay');
- if (delay == null) {
- delay = this.opt('longPressDelay'); // fallback
- }
- return delay;
- },
- bindToEl: function(el) {
- var component = this.component;
- component.bindSegHandlerToEl(el, 'mousedown', this.handleMousedown.bind(this));
- component.bindSegHandlerToEl(el, 'touchstart', this.handleTouchStart.bind(this));
- },
- handleMousedown: function(seg, ev) {
- if (this.component.canStartDrag(seg, ev)) {
- this.buildDragListener(seg).startInteraction(ev, { distance: 5 });
- }
- },
- handleTouchStart: function(seg, ev) {
- var component = this.component;
- var settings = {
- delay: this.view.isEventDefSelected(seg.footprint.eventDef) ? // already selected?
- 0 : this.getSelectionDelay()
- };
- if (component.canStartDrag(seg, ev)) {
- this.buildDragListener(seg).startInteraction(ev, settings);
- }
- else if (component.canStartSelection(seg, ev)) {
- this.buildSelectListener(seg).startInteraction(ev, settings);
- }
- },
- // seg isn't draggable, but let's use a generic DragListener
- // simply for the delay, so it can be selected.
- // Has side effect of setting/unsetting `dragListener`
- buildSelectListener: function(seg) {
- var _this = this;
- var view = this.view;
- var eventDef = seg.footprint.eventDef;
- var eventInstance = seg.footprint.eventInstance; // null for inverse-background events
- if (this.dragListener) {
- return this.dragListener;
- }
- var dragListener = this.dragListener = new DragListener({
- dragStart: function(ev) {
- if (
- dragListener.isTouch &&
- !view.isEventDefSelected(eventDef) &&
- eventInstance
- ) {
- // if not previously selected, will fire after a delay. then, select the event
- view.selectEventInstance(eventInstance);
- }
- },
- interactionEnd: function(ev) {
- _this.dragListener = null;
- }
- });
- return dragListener;
- },
- // Builds a listener that will track user-dragging on an event segment.
- // Generic enough to work with any type of Grid.
- // Has side effect of setting/unsetting `dragListener`
- buildDragListener: function(seg) {
- var _this = this;
- var component = this.component;
- var view = this.view;
- var calendar = view.calendar;
- var eventManager = calendar.eventManager;
- var el = seg.el;
- var eventDef = seg.footprint.eventDef;
- var eventInstance = seg.footprint.eventInstance; // null for inverse-background events
- var isDragging;
- var mouseFollower; // A clone of the original element that will move with the mouse
- var eventDefMutation;
- if (this.dragListener) {
- return this.dragListener;
- }
- // Tracks mouse movement over the *view's* coordinate map. Allows dragging and dropping between subcomponents
- // of the view.
- var dragListener = this.dragListener = new HitDragListener(view, {
- scroll: this.opt('dragScroll'),
- subjectEl: el,
- subjectCenter: true,
- interactionStart: function(ev) {
- seg.component = component; // for renderDrag
- isDragging = false;
- mouseFollower = new MouseFollower(seg.el, {
- additionalClass: 'fc-dragging',
- parentEl: view.el,
- opacity: dragListener.isTouch ? null : _this.opt('dragOpacity'),
- revertDuration: _this.opt('dragRevertDuration'),
- zIndex: 2 // one above the .fc-view
- });
- mouseFollower.hide(); // don't show until we know this is a real drag
- mouseFollower.start(ev);
- },
- dragStart: function(ev) {
- if (
- dragListener.isTouch &&
- !view.isEventDefSelected(eventDef) &&
- eventInstance
- ) {
- // if not previously selected, will fire after a delay. then, select the event
- view.selectEventInstance(eventInstance);
- }
- isDragging = true;
- // ensure a mouseout on the manipulated event has been reported
- _this.eventPointing.handleMouseout(seg, ev);
- _this.segDragStart(seg, ev);
- view.hideEventsWithId(eventDef.id); // hide all event segments. our mouseFollower will take over
- },
- hitOver: function(hit, isOrig, origHit) {
- var isAllowed = true;
- var origFootprint;
- var footprint;
- var mutatedEventInstanceGroup;
- // starting hit could be forced (DayGrid.limit)
- if (seg.hit) {
- origHit = seg.hit;
- }
- // hit might not belong to this grid, so query origin grid
- origFootprint = origHit.component.getSafeHitFootprint(origHit);
- footprint = hit.component.getSafeHitFootprint(hit);
- if (origFootprint && footprint) {
- eventDefMutation = _this.computeEventDropMutation(origFootprint, footprint, eventDef);
- if (eventDefMutation) {
- mutatedEventInstanceGroup = eventManager.buildMutatedEventInstanceGroup(
- eventDef.id,
- eventDefMutation
- );
- isAllowed = component.isEventInstanceGroupAllowed(mutatedEventInstanceGroup);
- }
- else {
- isAllowed = false;
- }
- }
- else {
- isAllowed = false;
- }
- if (!isAllowed) {
- eventDefMutation = null;
- disableCursor();
- }
- // if a valid drop location, have the subclass render a visual indication
- if (
- eventDefMutation &&
- view.renderDrag( // truthy if rendered something
- component.eventRangesToEventFootprints(
- mutatedEventInstanceGroup.sliceRenderRanges(component.get('dateProfile').renderUnzonedRange, calendar)
- ),
- seg,
- dragListener.isTouch
- )
- ) {
- mouseFollower.hide(); // if the subclass is already using a mock event "helper", hide our own
- }
- else {
- mouseFollower.show(); // otherwise, have the helper follow the mouse (no snapping)
- }
- if (isOrig) {
- // needs to have moved hits to be a valid drop
- eventDefMutation = null;
- }
- },
- hitOut: function() { // called before mouse moves to a different hit OR moved out of all hits
- view.unrenderDrag(); // unrender whatever was done in renderDrag
- mouseFollower.show(); // show in case we are moving out of all hits
- eventDefMutation = null;
- },
- hitDone: function() { // Called after a hitOut OR before a dragEnd
- enableCursor();
- },
- interactionEnd: function(ev) {
- delete seg.component; // prevent side effects
- // do revert animation if hasn't changed. calls a callback when finished (whether animation or not)
- mouseFollower.stop(!eventDefMutation, function() {
- if (isDragging) {
- view.unrenderDrag();
- _this.segDragStop(seg, ev);
- }
- if (eventDefMutation) {
- // no need to re-show original, will rerender all anyways. esp important if eventRenderWait
- view.reportEventDrop(eventInstance, eventDefMutation, el, ev);
- }
- else {
- view.showEventsWithId(eventDef.id);
- }
- });
- _this.dragListener = null;
- }
- });
- return dragListener;
- },
- // Called before event segment dragging starts
- segDragStart: function(seg, ev) {
- this.isDragging = true;
- this.component.publiclyTrigger('eventDragStart', {
- context: seg.el[0],
- args: [
- seg.footprint.getEventLegacy(),
- ev,
- {}, // jqui dummy
- this.view
- ]
- });
- },
- // Called after event segment dragging stops
- segDragStop: function(seg, ev) {
- this.isDragging = false;
- this.component.publiclyTrigger('eventDragStop', {
- context: seg.el[0],
- args: [
- seg.footprint.getEventLegacy(),
- ev,
- {}, // jqui dummy
- this.view
- ]
- });
- },
- // DOES NOT consider overlap/constraint
- computeEventDropMutation: function(startFootprint, endFootprint, eventDef) {
- var eventDefMutation = new EventDefMutation();
- eventDefMutation.setDateMutation(
- this.computeEventDateMutation(startFootprint, endFootprint)
- );
- return eventDefMutation;
- },
- computeEventDateMutation: function(startFootprint, endFootprint) {
- var date0 = startFootprint.unzonedRange.getStart();
- var date1 = endFootprint.unzonedRange.getStart();
- var clearEnd = false;
- var forceTimed = false;
- var forceAllDay = false;
- var dateDelta;
- var dateMutation;
- if (startFootprint.isAllDay !== endFootprint.isAllDay) {
- clearEnd = true;
- if (endFootprint.isAllDay) {
- forceAllDay = true;
- date0.stripTime();
- }
- else {
- forceTimed = true;
- }
- }
- dateDelta = this.component.diffDates(date1, date0);
- dateMutation = new EventDefDateMutation();
- dateMutation.clearEnd = clearEnd;
- dateMutation.forceTimed = forceTimed;
- dateMutation.forceAllDay = forceAllDay;
- dateMutation.setDateDelta(dateDelta);
- return dateMutation;
- }
- });
|