|
|
@@ -1,1275 +0,0 @@
|
|
|
-
|
|
|
-/* Event-rendering and event-interaction methods for the abstract Grid class
|
|
|
-----------------------------------------------------------------------------------------------------------------------
|
|
|
-*/
|
|
|
-
|
|
|
-Grid.mixin({
|
|
|
-
|
|
|
- // self-config, overridable by subclasses
|
|
|
- segSelector: '.fc-event-container > *', // what constitutes an event element?
|
|
|
-
|
|
|
- mousedOverSeg: null, // the segment object the user's mouse is over. null if over nothing
|
|
|
- isDraggingSeg: false, // is a segment being dragged? boolean
|
|
|
- isResizingSeg: false, // is a segment being resized? boolean
|
|
|
- isDraggingExternal: false, // jqui-dragging an external element? boolean
|
|
|
- segs: null, // the *event* segments currently rendered in the grid. TODO: rename to `eventSegs`
|
|
|
-
|
|
|
-
|
|
|
- renderEventsPayload: function(eventsPayload) {
|
|
|
- var id, eventInstanceGroup;
|
|
|
- var eventRenderRanges;
|
|
|
- var eventFootprints;
|
|
|
- var eventSegs;
|
|
|
- var bgSegs = [];
|
|
|
- var fgSegs = [];
|
|
|
-
|
|
|
- for (id in eventsPayload) {
|
|
|
- eventInstanceGroup = eventsPayload[id];
|
|
|
-
|
|
|
- eventRenderRanges = eventInstanceGroup.sliceRenderRanges(this.view.activeUnzonedRange);
|
|
|
- eventFootprints = this.eventRangesToEventFootprints(eventRenderRanges);
|
|
|
- eventSegs = this.eventFootprintsToSegs(eventFootprints);
|
|
|
-
|
|
|
- if (eventInstanceGroup.getEventDef().hasBgRendering()) {
|
|
|
- bgSegs.push.apply(bgSegs, // append
|
|
|
- eventSegs
|
|
|
- );
|
|
|
- }
|
|
|
- else {
|
|
|
- fgSegs.push.apply(fgSegs, // append
|
|
|
- eventSegs
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- this.segs = [].concat( // record all segs
|
|
|
- this.renderBgSegs(bgSegs) || bgSegs,
|
|
|
- this.renderFgSegs(fgSegs) || fgSegs
|
|
|
- );
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Unrenders all events currently rendered on the grid
|
|
|
- unrenderEvents: function() {
|
|
|
- this.handleSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
|
|
|
- this.clearDragListeners();
|
|
|
-
|
|
|
- this.unrenderFgSegs();
|
|
|
- this.unrenderBgSegs();
|
|
|
-
|
|
|
- this.segs = null;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Retrieves all rendered segment objects currently rendered on the grid
|
|
|
- getEventSegs: function() {
|
|
|
- return this.segs || [];
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Background Segment Rendering
|
|
|
- // ---------------------------------------------------------------------------------------------------------------
|
|
|
- // TODO: move this to ChronoComponent, but without fill
|
|
|
-
|
|
|
-
|
|
|
- // Renders the given background event segments onto the grid.
|
|
|
- // Returns a subset of the segs that were actually rendered.
|
|
|
- renderBgSegs: function(segs) {
|
|
|
- return this.renderFill('bgEvent', segs);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Unrenders all the currently rendered background event segments
|
|
|
- unrenderBgSegs: function() {
|
|
|
- this.unrenderFill('bgEvent');
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Renders a background event element, given the default rendering. Called by the fill system.
|
|
|
- bgEventSegEl: function(seg, el) {
|
|
|
- return this.filterEventRenderEl(seg.footprint, el);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Generates an array of classNames to be used for the default rendering of a background event.
|
|
|
- // Called by fillSegHtml.
|
|
|
- bgEventSegClasses: function(seg) {
|
|
|
- var eventDef = seg.footprint.eventDef;
|
|
|
-
|
|
|
- return [ 'fc-bgevent' ].concat(
|
|
|
- eventDef.className,
|
|
|
- eventDef.source.className
|
|
|
- );
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Generates a semicolon-separated CSS string to be used for the default rendering of a background event.
|
|
|
- // Called by fillSegHtml.
|
|
|
- bgEventSegCss: function(seg) {
|
|
|
- return {
|
|
|
- 'background-color': this.getSegSkinCss(seg)['background-color']
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Generates an array of classNames to be used for the rendering business hours overlay. Called by the fill system.
|
|
|
- // Called by fillSegHtml.
|
|
|
- businessHoursSegClasses: function(seg) {
|
|
|
- return [ 'fc-nonbusiness', 'fc-bgevent' ];
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Business Hours
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- // Compute business hour segs for the grid's current date range.
|
|
|
- // Caller must ask if whole-day business hours are needed.
|
|
|
- buildBusinessHourSegs: function(wholeDay) {
|
|
|
- return this.eventFootprintsToSegs(
|
|
|
- this.buildBusinessHourEventFootprints(wholeDay)
|
|
|
- );
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Compute business hour *events* for the grid's current date range.
|
|
|
- // Caller must ask if whole-day business hours are needed.
|
|
|
- // FOR RENDERING
|
|
|
- buildBusinessHourEventFootprints: function(wholeDay) {
|
|
|
- var calendar = this.view.calendar;
|
|
|
-
|
|
|
- return this._buildBusinessHourEventFootprints(wholeDay, calendar.opt('businessHours'));
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- _buildBusinessHourEventFootprints: function(wholeDay, businessHourDef) {
|
|
|
- var calendar = this.view.calendar;
|
|
|
- var eventInstanceGroup;
|
|
|
- var eventRanges;
|
|
|
-
|
|
|
- eventInstanceGroup = calendar.buildBusinessInstanceGroup(
|
|
|
- wholeDay,
|
|
|
- businessHourDef,
|
|
|
- this.unzonedRange
|
|
|
- );
|
|
|
-
|
|
|
- if (eventInstanceGroup) {
|
|
|
- eventRanges = eventInstanceGroup.sliceRenderRanges(
|
|
|
- this.unzonedRange,
|
|
|
- calendar
|
|
|
- );
|
|
|
- }
|
|
|
- else {
|
|
|
- eventRanges = [];
|
|
|
- }
|
|
|
-
|
|
|
- return this.eventRangesToEventFootprints(eventRanges);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Handlers
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- // Attaches event-element-related handlers for *all* rendered event segments of the view.
|
|
|
- bindSegHandlers: function() {
|
|
|
- this.bindSegHandlersToEl(this.el);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Attaches event-element-related handlers to an arbitrary container element. leverages bubbling.
|
|
|
- bindSegHandlersToEl: function(el) {
|
|
|
- this.bindSegHandlerToEl(el, 'touchstart', this.handleSegTouchStart);
|
|
|
- this.bindSegHandlerToEl(el, 'mouseenter', this.handleSegMouseover);
|
|
|
- this.bindSegHandlerToEl(el, 'mouseleave', this.handleSegMouseout);
|
|
|
- this.bindSegHandlerToEl(el, 'mousedown', this.handleSegMousedown);
|
|
|
- this.bindSegHandlerToEl(el, 'click', this.handleSegClick);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Executes a handler for any a user-interaction on a segment.
|
|
|
- // Handler gets called with (seg, ev), and with the `this` context of the Grid
|
|
|
- bindSegHandlerToEl: function(el, name, handler) {
|
|
|
- var _this = this;
|
|
|
-
|
|
|
- el.on(name, this.segSelector, function(ev) {
|
|
|
- var seg = $(this).data('fc-seg'); // grab segment data. put there by View::renderEventsPayload
|
|
|
-
|
|
|
- // only call the handlers if there is not a drag/resize in progress
|
|
|
- if (seg && !_this.isDraggingSeg && !_this.isResizingSeg) {
|
|
|
- return handler.call(_this, seg, ev); // context will be the Grid
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- handleSegClick: function(seg, ev) {
|
|
|
- var res = this.publiclyTrigger('eventClick', { // can return `false` to cancel
|
|
|
- context: seg.el[0],
|
|
|
- args: [ seg.footprint.getEventLegacy(), ev, this.view ]
|
|
|
- });
|
|
|
-
|
|
|
- if (res === false) {
|
|
|
- ev.preventDefault();
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Updates internal state and triggers handlers for when an event element is moused over
|
|
|
- handleSegMouseover: function(seg, ev) {
|
|
|
- if (
|
|
|
- !GlobalEmitter.get().shouldIgnoreMouse() &&
|
|
|
- !this.mousedOverSeg
|
|
|
- ) {
|
|
|
- this.mousedOverSeg = seg;
|
|
|
-
|
|
|
- if (this.view.isEventDefResizable(seg.footprint.eventDef)) {
|
|
|
- seg.el.addClass('fc-allow-mouse-resize');
|
|
|
- }
|
|
|
-
|
|
|
- this.publiclyTrigger('eventMouseover', {
|
|
|
- context: seg.el[0],
|
|
|
- args: [ seg.footprint.getEventLegacy(), ev, this.view ]
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Updates internal state and triggers handlers for when an event element is moused out.
|
|
|
- // Can be given no arguments, in which case it will mouseout the segment that was previously moused over.
|
|
|
- handleSegMouseout: function(seg, ev) {
|
|
|
- ev = ev || {}; // if given no args, make a mock mouse event
|
|
|
-
|
|
|
- if (this.mousedOverSeg) {
|
|
|
- seg = seg || this.mousedOverSeg; // if given no args, use the currently moused-over segment
|
|
|
- this.mousedOverSeg = null;
|
|
|
-
|
|
|
- if (this.view.isEventDefResizable(seg.footprint.eventDef)) {
|
|
|
- seg.el.removeClass('fc-allow-mouse-resize');
|
|
|
- }
|
|
|
-
|
|
|
- this.publiclyTrigger('eventMouseout', {
|
|
|
- context: seg.el[0],
|
|
|
- args: [ seg.footprint.getEventLegacy(), ev, this.view ]
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- handleSegMousedown: function(seg, ev) {
|
|
|
- var isResizing = this.startSegResize(seg, ev, { distance: 5 });
|
|
|
-
|
|
|
- if (!isResizing && this.view.isEventDefDraggable(seg.footprint.eventDef)) {
|
|
|
- this.buildSegDragListener(seg)
|
|
|
- .startInteraction(ev, {
|
|
|
- distance: 5
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- handleSegTouchStart: function(seg, ev) {
|
|
|
- var view = this.view;
|
|
|
- var eventDef = seg.footprint.eventDef;
|
|
|
- var isSelected = view.isEventDefSelected(eventDef);
|
|
|
- var isDraggable = view.isEventDefDraggable(eventDef);
|
|
|
- var isResizable = view.isEventDefResizable(eventDef);
|
|
|
- var isResizing = false;
|
|
|
- var dragListener;
|
|
|
- var eventLongPressDelay;
|
|
|
-
|
|
|
- if (isSelected && isResizable) {
|
|
|
- // only allow resizing of the event is selected
|
|
|
- isResizing = this.startSegResize(seg, ev);
|
|
|
- }
|
|
|
-
|
|
|
- if (!isResizing && (isDraggable || isResizable)) { // allowed to be selected?
|
|
|
-
|
|
|
- eventLongPressDelay = this.opt('eventLongPressDelay');
|
|
|
- if (eventLongPressDelay == null) {
|
|
|
- eventLongPressDelay = this.opt('longPressDelay'); // fallback
|
|
|
- }
|
|
|
-
|
|
|
- dragListener = isDraggable ?
|
|
|
- this.buildSegDragListener(seg) :
|
|
|
- this.buildSegSelectListener(seg); // seg isn't draggable, but still needs to be selected
|
|
|
-
|
|
|
- dragListener.startInteraction(ev, { // won't start if already started
|
|
|
- delay: isSelected ? 0 : eventLongPressDelay // do delay if not already selected
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // returns boolean whether resizing actually started or not.
|
|
|
- // assumes the seg allows resizing.
|
|
|
- // `dragOptions` are optional.
|
|
|
- startSegResize: function(seg, ev, dragOptions) {
|
|
|
- if ($(ev.target).is('.fc-resizer')) {
|
|
|
- this.buildSegResizeListener(seg, $(ev.target).is('.fc-start-resizer'))
|
|
|
- .startInteraction(ev, dragOptions);
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- /* Event Dragging
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- // 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 `segDragListener`
|
|
|
- buildSegDragListener: function(seg) {
|
|
|
- var _this = this;
|
|
|
- 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.segDragListener) {
|
|
|
- return this.segDragListener;
|
|
|
- }
|
|
|
-
|
|
|
- // Tracks mouse movement over the *view's* coordinate map. Allows dragging and dropping between subcomponents
|
|
|
- // of the view.
|
|
|
- var dragListener = this.segDragListener = new HitDragListener(view, {
|
|
|
- scroll: this.opt('dragScroll'),
|
|
|
- subjectEl: el,
|
|
|
- subjectCenter: true,
|
|
|
- interactionStart: function(ev) {
|
|
|
- seg.component = _this; // 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;
|
|
|
- _this.handleSegMouseout(seg, ev); // ensure a mouseout on the manipulated event has been reported
|
|
|
- _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;
|
|
|
- var dragHelperEls;
|
|
|
-
|
|
|
- // 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 = _this.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 &&
|
|
|
- (dragHelperEls = view.renderDrag(
|
|
|
- _this.eventRangesToEventFootprints(
|
|
|
- mutatedEventInstanceGroup.sliceRenderRanges(_this.unzonedRange, calendar)
|
|
|
- ),
|
|
|
- seg
|
|
|
- ))
|
|
|
- ) {
|
|
|
- dragHelperEls.addClass('fc-dragging');
|
|
|
- if (!dragListener.isTouch) {
|
|
|
- _this.applyDragOpacity(dragHelperEls);
|
|
|
- }
|
|
|
-
|
|
|
- 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.segDragListener = null;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return dragListener;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // 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 `segDragListener`
|
|
|
- buildSegSelectListener: 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.segDragListener) {
|
|
|
- return this.segDragListener;
|
|
|
- }
|
|
|
-
|
|
|
- var dragListener = this.segDragListener = 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.segDragListener = null;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return dragListener;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Called before event segment dragging starts
|
|
|
- segDragStart: function(seg, ev) {
|
|
|
- this.isDraggingSeg = true;
|
|
|
- this.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.isDraggingSeg = false;
|
|
|
- this.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 date0 = startFootprint.unzonedRange.getStart();
|
|
|
- var date1 = endFootprint.unzonedRange.getStart();
|
|
|
- var clearEnd = false;
|
|
|
- var forceTimed = false;
|
|
|
- var forceAllDay = false;
|
|
|
- var dateDelta;
|
|
|
- var dateMutation;
|
|
|
- var eventDefMutation;
|
|
|
-
|
|
|
- if (startFootprint.isAllDay !== endFootprint.isAllDay) {
|
|
|
- clearEnd = true;
|
|
|
-
|
|
|
- if (endFootprint.isAllDay) {
|
|
|
- forceAllDay = true;
|
|
|
- date0.stripTime();
|
|
|
- }
|
|
|
- else {
|
|
|
- forceTimed = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- dateDelta = this.diffDates(date1, date0);
|
|
|
-
|
|
|
- dateMutation = new EventDefDateMutation();
|
|
|
- dateMutation.clearEnd = clearEnd;
|
|
|
- dateMutation.forceTimed = forceTimed;
|
|
|
- dateMutation.forceAllDay = forceAllDay;
|
|
|
- dateMutation.setDateDelta(dateDelta);
|
|
|
-
|
|
|
- eventDefMutation = new EventDefMutation();
|
|
|
- eventDefMutation.setDateMutation(dateMutation);
|
|
|
-
|
|
|
- return eventDefMutation;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Utility for apply dragOpacity to a jQuery set
|
|
|
- applyDragOpacity: function(els) {
|
|
|
- var opacity = this.opt('dragOpacity');
|
|
|
-
|
|
|
- if (opacity != null) {
|
|
|
- els.css('opacity', opacity);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* External Element Dragging
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- // Called when a jQuery UI drag is initiated anywhere in the DOM
|
|
|
- externalDragStart: function(ev, ui) {
|
|
|
- var el;
|
|
|
- var accept;
|
|
|
-
|
|
|
- if (this.opt('droppable')) { // only listen if this setting is on
|
|
|
- el = $((ui ? ui.item : null) || ev.target);
|
|
|
-
|
|
|
- // Test that the dragged element passes the dropAccept selector or filter function.
|
|
|
- // FYI, the default is "*" (matches all)
|
|
|
- accept = this.opt('dropAccept');
|
|
|
- if ($.isFunction(accept) ? accept.call(el[0], el) : el.is(accept)) {
|
|
|
- if (!this.isDraggingExternal) { // prevent double-listening if fired twice
|
|
|
- this.listenToExternalDrag(el, ev, ui);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Called when a jQuery UI drag starts and it needs to be monitored for dropping
|
|
|
- listenToExternalDrag: function(el, ev, ui) {
|
|
|
- var _this = this;
|
|
|
- var view = this.view;
|
|
|
- var meta = getDraggedElMeta(el); // extra data about event drop, including possible event to create
|
|
|
- var singleEventDef; // a null value signals an unsuccessful drag
|
|
|
-
|
|
|
- // listener that tracks mouse movement over date-associated pixel regions
|
|
|
- var dragListener = _this.externalDragListener = new HitDragListener(this, {
|
|
|
- interactionStart: function() {
|
|
|
- _this.isDraggingExternal = true;
|
|
|
- },
|
|
|
- hitOver: function(hit) {
|
|
|
- var isAllowed = true;
|
|
|
- var hitFootprint = hit.component.getSafeHitFootprint(hit); // hit might not belong to this grid
|
|
|
- var mutatedEventInstanceGroup;
|
|
|
-
|
|
|
- if (hitFootprint) {
|
|
|
- singleEventDef = _this.computeExternalDrop(hitFootprint, meta);
|
|
|
-
|
|
|
- if (singleEventDef) {
|
|
|
- mutatedEventInstanceGroup = new EventInstanceGroup(
|
|
|
- singleEventDef.buildInstances()
|
|
|
- );
|
|
|
- isAllowed = meta.eventProps ? // isEvent?
|
|
|
- _this.isEventInstanceGroupAllowed(mutatedEventInstanceGroup) :
|
|
|
- _this.isExternalInstanceGroupAllowed(mutatedEventInstanceGroup);
|
|
|
- }
|
|
|
- else {
|
|
|
- isAllowed = false;
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- isAllowed = false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!isAllowed) {
|
|
|
- singleEventDef = null;
|
|
|
- disableCursor();
|
|
|
- }
|
|
|
-
|
|
|
- if (singleEventDef) {
|
|
|
- _this.renderDrag( // called without a seg parameter
|
|
|
- _this.eventRangesToEventFootprints(
|
|
|
- mutatedEventInstanceGroup.sliceRenderRanges(_this.unzonedRange, view.calendar)
|
|
|
- )
|
|
|
- );
|
|
|
- }
|
|
|
- },
|
|
|
- hitOut: function() {
|
|
|
- singleEventDef = null; // signal unsuccessful
|
|
|
- },
|
|
|
- hitDone: function() { // Called after a hitOut OR before a dragEnd
|
|
|
- enableCursor();
|
|
|
- _this.unrenderDrag();
|
|
|
- },
|
|
|
- interactionEnd: function(ev) {
|
|
|
-
|
|
|
- if (singleEventDef) { // element was dropped on a valid hit
|
|
|
- view.reportExternalDrop(
|
|
|
- singleEventDef,
|
|
|
- Boolean(meta.eventProps), // isEvent
|
|
|
- Boolean(meta.stick), // isSticky
|
|
|
- el, ev, ui
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- _this.isDraggingExternal = false;
|
|
|
- _this.externalDragListener = null;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- dragListener.startDrag(ev); // start listening immediately
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Given a hit to be dropped upon, and misc data associated with the jqui drag (guaranteed to be a plain object),
|
|
|
- // returns the zoned start/end dates for the event that would result from the hypothetical drop. end might be null.
|
|
|
- // Returning a null value signals an invalid drop hit.
|
|
|
- // DOES NOT consider overlap/constraint.
|
|
|
- // Assumes both footprints are non-open-ended.
|
|
|
- computeExternalDrop: function(componentFootprint, meta) {
|
|
|
- var calendar = this.view.calendar;
|
|
|
- var start = FC.moment.utc(componentFootprint.unzonedRange.startMs).stripZone();
|
|
|
- var end;
|
|
|
- var eventDef;
|
|
|
-
|
|
|
- if (componentFootprint.isAllDay) {
|
|
|
- // if dropped on an all-day span, and element's metadata specified a time, set it
|
|
|
- if (meta.startTime) {
|
|
|
- start.time(meta.startTime);
|
|
|
- }
|
|
|
- else {
|
|
|
- start.stripTime();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (meta.duration) {
|
|
|
- end = start.clone().add(meta.duration);
|
|
|
- }
|
|
|
-
|
|
|
- start = calendar.applyTimezone(start);
|
|
|
-
|
|
|
- if (end) {
|
|
|
- end = calendar.applyTimezone(end);
|
|
|
- }
|
|
|
-
|
|
|
- eventDef = SingleEventDef.parse(
|
|
|
- $.extend({}, meta.eventProps, {
|
|
|
- start: start,
|
|
|
- end: end
|
|
|
- }),
|
|
|
- new EventSource(calendar)
|
|
|
- );
|
|
|
-
|
|
|
- return eventDef;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Resizing
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- // Creates a listener that tracks the user as they resize an event segment.
|
|
|
- // Generic enough to work with any type of Grid.
|
|
|
- buildSegResizeListener: function(seg, isStart) {
|
|
|
- var _this = this;
|
|
|
- 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;
|
|
|
- var isDragging;
|
|
|
- var resizeMutation; // zoned event date properties. falsy if invalid resize
|
|
|
-
|
|
|
- // Tracks mouse movement over the *grid's* coordinate map
|
|
|
- var dragListener = this.segResizeListener = new HitDragListener(this, {
|
|
|
- scroll: this.opt('dragScroll'),
|
|
|
- subjectEl: el,
|
|
|
- interactionStart: function() {
|
|
|
- isDragging = false;
|
|
|
- },
|
|
|
- dragStart: function(ev) {
|
|
|
- isDragging = true;
|
|
|
- _this.handleSegMouseout(seg, ev); // ensure a mouseout on the manipulated event has been reported
|
|
|
- _this.segResizeStart(seg, ev);
|
|
|
- },
|
|
|
- hitOver: function(hit, isOrig, origHit) {
|
|
|
- var isAllowed = true;
|
|
|
- var origHitFootprint = _this.getSafeHitFootprint(origHit);
|
|
|
- var hitFootprint = _this.getSafeHitFootprint(hit);
|
|
|
- var mutatedEventInstanceGroup;
|
|
|
-
|
|
|
- if (origHitFootprint && hitFootprint) {
|
|
|
- resizeMutation = isStart ?
|
|
|
- _this.computeEventStartResizeMutation(origHitFootprint, hitFootprint, seg.footprint) :
|
|
|
- _this.computeEventEndResizeMutation(origHitFootprint, hitFootprint, seg.footprint);
|
|
|
-
|
|
|
- if (resizeMutation) {
|
|
|
- mutatedEventInstanceGroup = eventManager.buildMutatedEventInstanceGroup(
|
|
|
- eventDef.id,
|
|
|
- resizeMutation
|
|
|
- );
|
|
|
- isAllowed = _this.isEventInstanceGroupAllowed(mutatedEventInstanceGroup);
|
|
|
- }
|
|
|
- else {
|
|
|
- isAllowed = false;
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- isAllowed = false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!isAllowed) {
|
|
|
- resizeMutation = null;
|
|
|
- disableCursor();
|
|
|
- }
|
|
|
- else if (resizeMutation.isEmpty()) {
|
|
|
- // no change. (FYI, event dates might have zones)
|
|
|
- resizeMutation = null;
|
|
|
- }
|
|
|
-
|
|
|
- if (resizeMutation) {
|
|
|
- view.hideEventsWithId(eventDef.id);
|
|
|
-
|
|
|
- _this.renderEventResize(
|
|
|
- _this.eventRangesToEventFootprints(
|
|
|
- mutatedEventInstanceGroup.sliceRenderRanges(_this.unzonedRange, calendar)
|
|
|
- ),
|
|
|
- seg
|
|
|
- );
|
|
|
- }
|
|
|
- },
|
|
|
- hitOut: function() { // called before mouse moves to a different hit OR moved out of all hits
|
|
|
- resizeMutation = null;
|
|
|
- view.showEventsWithId(eventDef.id); // for when out-of-bounds. show original
|
|
|
- },
|
|
|
- hitDone: function() { // resets the rendering to show the original event
|
|
|
- _this.unrenderEventResize();
|
|
|
- enableCursor();
|
|
|
- },
|
|
|
- interactionEnd: function(ev) {
|
|
|
- if (isDragging) {
|
|
|
- _this.segResizeStop(seg, ev);
|
|
|
- }
|
|
|
-
|
|
|
- if (resizeMutation) { // valid date to resize to?
|
|
|
- // no need to re-show original, will rerender all anyways. esp important if eventRenderWait
|
|
|
- view.reportEventResize(eventInstance, resizeMutation, el, ev);
|
|
|
- }
|
|
|
- else {
|
|
|
- view.showEventsWithId(eventDef.id);
|
|
|
- }
|
|
|
- _this.segResizeListener = null;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return dragListener;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Called before event segment resizing starts
|
|
|
- segResizeStart: function(seg, ev) {
|
|
|
- this.isResizingSeg = true;
|
|
|
- this.publiclyTrigger('eventResizeStart', {
|
|
|
- context: seg.el[0],
|
|
|
- args: [
|
|
|
- seg.footprint.getEventLegacy(),
|
|
|
- ev,
|
|
|
- {}, // jqui dummy
|
|
|
- this.view
|
|
|
- ]
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Called after event segment resizing stops
|
|
|
- segResizeStop: function(seg, ev) {
|
|
|
- this.isResizingSeg = false;
|
|
|
- this.publiclyTrigger('eventResizeStop', {
|
|
|
- context: seg.el[0],
|
|
|
- args: [
|
|
|
- seg.footprint.getEventLegacy(),
|
|
|
- ev,
|
|
|
- {}, // jqui dummy
|
|
|
- this.view
|
|
|
- ]
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Returns new date-information for an event segment being resized from its start
|
|
|
- computeEventStartResizeMutation: function(startFootprint, endFootprint, origEventFootprint) {
|
|
|
- var origRange = origEventFootprint.componentFootprint.unzonedRange;
|
|
|
- var startDelta = this.diffDates(
|
|
|
- endFootprint.unzonedRange.getStart(),
|
|
|
- startFootprint.unzonedRange.getStart()
|
|
|
- );
|
|
|
- var dateMutation;
|
|
|
- var eventDefMutation;
|
|
|
-
|
|
|
- if (origRange.getStart().add(startDelta) < origRange.getEnd()) {
|
|
|
-
|
|
|
- dateMutation = new EventDefDateMutation();
|
|
|
- dateMutation.setStartDelta(startDelta);
|
|
|
-
|
|
|
- eventDefMutation = new EventDefMutation();
|
|
|
- eventDefMutation.setDateMutation(dateMutation);
|
|
|
-
|
|
|
- return eventDefMutation;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Returns new date-information for an event segment being resized from its end
|
|
|
- computeEventEndResizeMutation: function(startFootprint, endFootprint, origEventFootprint) {
|
|
|
- var origRange = origEventFootprint.componentFootprint.unzonedRange;
|
|
|
- var endDelta = this.diffDates(
|
|
|
- endFootprint.unzonedRange.getEnd(),
|
|
|
- startFootprint.unzonedRange.getEnd()
|
|
|
- );
|
|
|
- var dateMutation;
|
|
|
- var eventDefMutation;
|
|
|
-
|
|
|
- if (origRange.getEnd().add(endDelta) > origRange.getStart()) {
|
|
|
-
|
|
|
- dateMutation = new EventDefDateMutation();
|
|
|
- dateMutation.setEndDelta(endDelta);
|
|
|
-
|
|
|
- eventDefMutation = new EventDefMutation();
|
|
|
- eventDefMutation.setDateMutation(dateMutation);
|
|
|
-
|
|
|
- return eventDefMutation;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Renders a visual indication of an event being resized.
|
|
|
- // Must return elements used for any mock events.
|
|
|
- renderEventResize: function(eventFootprints, seg) {
|
|
|
- // subclasses must implement
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Unrenders a visual indication of an event being resized.
|
|
|
- unrenderEventResize: function() {
|
|
|
- // subclasses must implement
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Rendering Utils
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- // Compute the text that should be displayed on an event's element.
|
|
|
- // `range` can be the Event object itself, or something range-like, with at least a `start`.
|
|
|
- // If event times are disabled, or the event has no time, will return a blank string.
|
|
|
- // If not specified, formatStr will default to the eventTimeFormat setting,
|
|
|
- // and displayEnd will default to the displayEventEnd setting.
|
|
|
- getEventTimeText: function(eventFootprint, formatStr, displayEnd) {
|
|
|
- return this._getEventTimeText(
|
|
|
- eventFootprint.eventInstance.dateProfile.start,
|
|
|
- eventFootprint.eventInstance.dateProfile.end,
|
|
|
- eventFootprint.componentFootprint.isAllDay,
|
|
|
- formatStr,
|
|
|
- displayEnd
|
|
|
- );
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- _getEventTimeText: function(start, end, isAllDay, formatStr, displayEnd) {
|
|
|
-
|
|
|
- if (formatStr == null) {
|
|
|
- formatStr = this.eventTimeFormat;
|
|
|
- }
|
|
|
-
|
|
|
- if (displayEnd == null) {
|
|
|
- displayEnd = this.displayEventEnd;
|
|
|
- }
|
|
|
-
|
|
|
- if (this.displayEventTime && !isAllDay) {
|
|
|
- if (displayEnd && end) {
|
|
|
- return this.view.formatRange(
|
|
|
- { start: start, end: end },
|
|
|
- false, // allDay
|
|
|
- formatStr
|
|
|
- );
|
|
|
- }
|
|
|
- else {
|
|
|
- return start.format(formatStr);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return '';
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Generic utility for generating the HTML classNames for an event segment's element
|
|
|
- getSegClasses: function(seg, isDraggable, isResizable) {
|
|
|
- var view = this.view;
|
|
|
- var classes = [
|
|
|
- 'fc-event',
|
|
|
- seg.isStart ? 'fc-start' : 'fc-not-start',
|
|
|
- seg.isEnd ? 'fc-end' : 'fc-not-end'
|
|
|
- ].concat(this.getSegCustomClasses(seg));
|
|
|
-
|
|
|
- if (isDraggable) {
|
|
|
- classes.push('fc-draggable');
|
|
|
- }
|
|
|
- if (isResizable) {
|
|
|
- classes.push('fc-resizable');
|
|
|
- }
|
|
|
-
|
|
|
- // event is currently selected? attach a className.
|
|
|
- if (view.isEventDefSelected(seg.footprint.eventDef)) {
|
|
|
- classes.push('fc-selected');
|
|
|
- }
|
|
|
-
|
|
|
- return classes;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // List of classes that were defined by the caller of the API in some way
|
|
|
- getSegCustomClasses: function(seg) {
|
|
|
- var eventDef = seg.footprint.eventDef;
|
|
|
-
|
|
|
- return [].concat(
|
|
|
- eventDef.className, // guaranteed to be an array
|
|
|
- eventDef.source.className
|
|
|
- );
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Utility for generating event skin-related CSS properties
|
|
|
- getSegSkinCss: function(seg) {
|
|
|
- return {
|
|
|
- 'background-color': this.getSegBackgroundColor(seg),
|
|
|
- 'border-color': this.getSegBorderColor(seg),
|
|
|
- color: this.getSegTextColor(seg)
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Queries for caller-specified color, then falls back to default
|
|
|
- getSegBackgroundColor: function(seg) {
|
|
|
- var eventDef = seg.footprint.eventDef;
|
|
|
-
|
|
|
- return eventDef.backgroundColor ||
|
|
|
- eventDef.color ||
|
|
|
- this.getSegDefaultBackgroundColor(seg);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- getSegDefaultBackgroundColor: function(seg) {
|
|
|
- var source = seg.footprint.eventDef.source;
|
|
|
-
|
|
|
- return source.backgroundColor ||
|
|
|
- source.color ||
|
|
|
- this.opt('eventBackgroundColor') ||
|
|
|
- this.opt('eventColor');
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Queries for caller-specified color, then falls back to default
|
|
|
- getSegBorderColor: function(seg) {
|
|
|
- var eventDef = seg.footprint.eventDef;
|
|
|
-
|
|
|
- return eventDef.borderColor ||
|
|
|
- eventDef.color ||
|
|
|
- this.getSegDefaultBorderColor(seg);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- getSegDefaultBorderColor: function(seg) {
|
|
|
- var source = seg.footprint.eventDef.source;
|
|
|
-
|
|
|
- return source.borderColor ||
|
|
|
- source.color ||
|
|
|
- this.opt('eventBorderColor') ||
|
|
|
- this.opt('eventColor');
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Queries for caller-specified color, then falls back to default
|
|
|
- getSegTextColor: function(seg) {
|
|
|
- var eventDef = seg.footprint.eventDef;
|
|
|
-
|
|
|
- return eventDef.textColor ||
|
|
|
- this.getSegDefaultTextColor(seg);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- getSegDefaultTextColor: function(seg) {
|
|
|
- var source = seg.footprint.eventDef.source;
|
|
|
-
|
|
|
- return source.textColor ||
|
|
|
- this.opt('eventTextColor');
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Event Location Validation
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- isEventInstanceGroupAllowed: function(eventInstanceGroup) {
|
|
|
- var eventFootprints = this.eventRangesToEventFootprints(eventInstanceGroup.getAllEventRanges());
|
|
|
- var i;
|
|
|
-
|
|
|
- for (i = 0; i < eventFootprints.length; i++) {
|
|
|
- if (!this.view.validUnzonedRange.containsRange(eventFootprints[i].componentFootprint.unzonedRange)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return this.view.calendar.isEventInstanceGroupAllowed(eventInstanceGroup);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // when it's a completely anonymous external drag, no event.
|
|
|
- isExternalInstanceGroupAllowed: function(eventInstanceGroup) {
|
|
|
- var calendar = this.view.calendar;
|
|
|
- var eventFootprints = this.eventRangesToEventFootprints(eventInstanceGroup.getAllEventRanges());
|
|
|
- var i;
|
|
|
-
|
|
|
- for (i = 0; i < eventFootprints.length; i++) {
|
|
|
- if (!this.view.validUnzonedRange.containsRange(eventFootprints[i].componentFootprint.unzonedRange)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < eventFootprints.length; i++) {
|
|
|
- // treat it as a selection
|
|
|
- if (!calendar.isSelectionFootprintAllowed(eventFootprints[i].componentFootprint)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Converting eventRange -> eventFootprint -> eventSegs
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- eventRangesToEventFootprints: function(eventRanges) {
|
|
|
- var eventFootprints = [];
|
|
|
- var i;
|
|
|
-
|
|
|
- for (i = 0; i < eventRanges.length; i++) {
|
|
|
- eventFootprints.push.apply(eventFootprints,
|
|
|
- this.eventRangeToEventFootprints(eventRanges[i])
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- return eventFootprints;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Given an event's unzoned date range, return an array of eventSpan objects.
|
|
|
- // eventSpan - { start, end, isStart, isEnd, otherthings... }
|
|
|
- // Subclasses can override.
|
|
|
- // Subclasses are obligated to forward eventRange.isStart/isEnd to the resulting spans.
|
|
|
- // TODO: somehow more DRY with Calendar::eventRangeToEventFootprints
|
|
|
- eventRangeToEventFootprints: function(eventRange) {
|
|
|
- return [
|
|
|
- new EventFootprint(
|
|
|
- new ComponentFootprint(
|
|
|
- eventRange.unzonedRange,
|
|
|
- eventRange.eventDef.isAllDay()
|
|
|
- ),
|
|
|
- eventRange.eventDef,
|
|
|
- eventRange.eventInstance // might not exist
|
|
|
- )
|
|
|
- ];
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- eventFootprintsToSegs: function(eventFootprints) {
|
|
|
- var segs = [];
|
|
|
- var i;
|
|
|
-
|
|
|
- for (i = 0; i < eventFootprints.length; i++) {
|
|
|
- segs.push.apply(segs,
|
|
|
- this.eventFootprintToSegs(eventFootprints[i])
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- return segs;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Given an event's span (unzoned start/end and other misc data), and the event itself,
|
|
|
- // slices into segments and attaches event-derived properties to them.
|
|
|
- // eventSpan - { start, end, isStart, isEnd, otherthings... }
|
|
|
- // constraintRange allow additional clipping. optional. eventually remove this.
|
|
|
- eventFootprintToSegs: function(eventFootprint, constraintRange) {
|
|
|
- var unzonedRange = eventFootprint.componentFootprint.unzonedRange;
|
|
|
- var segs;
|
|
|
- var i, seg;
|
|
|
-
|
|
|
- if (constraintRange) {
|
|
|
- unzonedRange = unzonedRange.intersect(constraintRange);
|
|
|
- }
|
|
|
-
|
|
|
- segs = this.componentFootprintToSegs(eventFootprint.componentFootprint);
|
|
|
-
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- seg = segs[i];
|
|
|
-
|
|
|
- if (!unzonedRange.isStart) {
|
|
|
- seg.isStart = false;
|
|
|
- }
|
|
|
- if (!unzonedRange.isEnd) {
|
|
|
- seg.isEnd = false;
|
|
|
- }
|
|
|
-
|
|
|
- seg.footprint = eventFootprint;
|
|
|
- // TODO: rename to seg.eventFootprint
|
|
|
- }
|
|
|
-
|
|
|
- return segs;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- sortEventSegs: function(segs) {
|
|
|
- segs.sort(proxy(this, 'compareEventSegs'));
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // A cmp function for determining which segments should take visual priority
|
|
|
- compareEventSegs: function(seg1, seg2) {
|
|
|
- var f1 = seg1.footprint.componentFootprint;
|
|
|
- var r1 = f1.unzonedRange;
|
|
|
- var f2 = seg2.footprint.componentFootprint;
|
|
|
- var r2 = f2.unzonedRange;
|
|
|
-
|
|
|
- return r1.startMs - r2.startMs || // earlier events go first
|
|
|
- (r2.endMs - r2.startMs) - (r1.endMs - r1.startMs) || // tie? longer events go first
|
|
|
- f2.isAllDay - f1.isAllDay || // tie? put all-day events first (booleans cast to 0/1)
|
|
|
- compareByFieldSpecs(
|
|
|
- seg1.footprint.eventDef,
|
|
|
- seg2.footprint.eventDef,
|
|
|
- this.view.eventOrderSpecs
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
-});
|
|
|
-
|
|
|
-
|
|
|
-/* External-Dragging-Element Data
|
|
|
-----------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-// Require all HTML5 data-* attributes used by FullCalendar to have this prefix.
|
|
|
-// A value of '' will query attributes like data-event. A value of 'fc' will query attributes like data-fc-event.
|
|
|
-FC.dataAttrPrefix = '';
|
|
|
-
|
|
|
-// Given a jQuery element that might represent a dragged FullCalendar event, returns an intermediate data structure
|
|
|
-// to be used for Event Object creation.
|
|
|
-// A defined `.eventProps`, even when empty, indicates that an event should be created.
|
|
|
-function getDraggedElMeta(el) {
|
|
|
- var prefix = FC.dataAttrPrefix;
|
|
|
- var eventProps; // properties for creating the event, not related to date/time
|
|
|
- var startTime; // a Duration
|
|
|
- var duration;
|
|
|
- var stick;
|
|
|
-
|
|
|
- if (prefix) { prefix += '-'; }
|
|
|
- eventProps = el.data(prefix + 'event') || null;
|
|
|
-
|
|
|
- if (eventProps) {
|
|
|
- if (typeof eventProps === 'object') {
|
|
|
- eventProps = $.extend({}, eventProps); // make a copy
|
|
|
- }
|
|
|
- else { // something like 1 or true. still signal event creation
|
|
|
- eventProps = {};
|
|
|
- }
|
|
|
-
|
|
|
- // pluck special-cased date/time properties
|
|
|
- startTime = eventProps.start;
|
|
|
- if (startTime == null) { startTime = eventProps.time; } // accept 'time' as well
|
|
|
- duration = eventProps.duration;
|
|
|
- stick = eventProps.stick;
|
|
|
- delete eventProps.start;
|
|
|
- delete eventProps.time;
|
|
|
- delete eventProps.duration;
|
|
|
- delete eventProps.stick;
|
|
|
- }
|
|
|
-
|
|
|
- // fallback to standalone attribute values for each of the date/time properties
|
|
|
- if (startTime == null) { startTime = el.data(prefix + 'start'); }
|
|
|
- if (startTime == null) { startTime = el.data(prefix + 'time'); } // accept 'time' as well
|
|
|
- if (duration == null) { duration = el.data(prefix + 'duration'); }
|
|
|
- if (stick == null) { stick = el.data(prefix + 'stick'); }
|
|
|
-
|
|
|
- // massage into correct data types
|
|
|
- startTime = startTime != null ? moment.duration(startTime) : null;
|
|
|
- duration = duration != null ? moment.duration(duration) : null;
|
|
|
- stick = Boolean(stick);
|
|
|
-
|
|
|
- return { eventProps: eventProps, startTime: startTime, duration: duration, stick: stick };
|
|
|
-}
|
|
|
-
|