| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054 |
- /* An abstract class from which other views inherit from
- ----------------------------------------------------------------------------------------------------------------------*/
- var View = FC.View = InteractiveDateComponent.extend({
- type: null, // subclass' view name (string)
- name: null, // deprecated. use `type` instead
- title: null, // the text that will be displayed in the header's title
- calendar: null, // owner Calendar object
- viewSpec: null,
- options: null, // hash containing all options. already merged with view-specific-options
- renderQueue: null,
- batchRenderDepth: 0,
- isDatesRendered: false,
- isEventsRendered: false,
- isBaseRendered: false, // related to viewRender/viewDestroy triggers
- queuedScroll: null,
- isSelected: false, // boolean whether a range of time is user-selected or not
- selectedEventInstance: null,
- eventOrderSpecs: null, // criteria for ordering events when they have same date/time
- // for date utils, computed from options
- isHiddenDayHash: null,
- // now indicator
- isNowIndicatorRendered: null,
- initialNowDate: null, // result first getNow call
- initialNowQueriedMs: null, // ms time the getNow was called
- nowIndicatorTimeoutID: null, // for refresh timing of now indicator
- nowIndicatorIntervalID: null, // "
- constructor: function(calendar, viewSpec) {
- this.calendar = calendar;
- this.viewSpec = viewSpec;
- // shortcuts
- this.type = viewSpec.type;
- this.options = viewSpec.options;
- // .name is deprecated
- this.name = this.type;
- InteractiveDateComponent.call(this);
- this.initHiddenDays();
- this.eventOrderSpecs = parseFieldSpecs(this.opt('eventOrder'));
- this.renderQueue = this.buildRenderQueue();
- this.initAutoBatchRender();
- // legacy
- if (this.initialize) {
- this.initialize();
- }
- },
- _getView: function() {
- return this;
- },
- buildRenderQueue: function() {
- var _this = this;
- var renderQueue = new RenderQueue({
- event: this.opt('eventRenderWait')
- });
- renderQueue.on('start', function() {
- _this.freezeHeight();
- _this.addScroll(_this.queryScroll());
- });
- renderQueue.on('stop', function() {
- _this.thawHeight();
- _this.popScroll();
- });
- return renderQueue;
- },
- initAutoBatchRender: function() {
- var _this = this;
- this.on('before:change', function() {
- _this.startBatchRender();
- });
- this.on('change', function() {
- _this.stopBatchRender();
- });
- },
- startBatchRender: function() {
- if (!(this.batchRenderDepth++)) {
- this.renderQueue.pause();
- }
- },
- stopBatchRender: function() {
- if (!(--this.batchRenderDepth)) {
- this.renderQueue.resume();
- }
- },
- // Retrieves an option with the given name
- opt: function(name) {
- return this.options[name];
- },
- /* Title and Date Formatting
- ------------------------------------------------------------------------------------------------------------------*/
- // Computes what the title at the top of the calendar should be for this view
- computeTitle: function(dateProfile) {
- var unzonedRange;
- // for views that span a large unit of time, show the proper interval, ignoring stray days before and after
- if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) {
- unzonedRange = dateProfile.currentUnzonedRange;
- }
- else { // for day units or smaller, use the actual day range
- unzonedRange = dateProfile.activeUnzonedRange;
- }
- return this.formatRange(
- {
- start: this.calendar.msToMoment(unzonedRange.startMs, dateProfile.isRangeAllDay),
- end: this.calendar.msToMoment(unzonedRange.endMs, dateProfile.isRangeAllDay)
- },
- dateProfile.isRangeAllDay,
- this.opt('titleFormat') || this.computeTitleFormat(),
- this.opt('titleRangeSeparator')
- );
- },
- // Generates the format string that should be used to generate the title for the current date range.
- // Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`.
- computeTitleFormat: function() {
- var currentRangeUnit = this.get('dateProfile').currentRangeUnit;
- if (currentRangeUnit == 'year') {
- return 'YYYY';
- }
- else if (currentRangeUnit == 'month') {
- return this.opt('monthYearFormat'); // like "September 2014"
- }
- else if (this.currentRangeAs('days') > 1) {
- return 'll'; // multi-day range. shorter, like "Sep 9 - 10 2014"
- }
- else {
- return 'LL'; // one day. longer, like "September 9 2014"
- }
- },
- // Element
- // -----------------------------------------------------------------------------------------------------------------
- setElement: function(el) {
- var _this = this;
- InteractiveDateComponent.prototype.setElement.apply(this, arguments);
- this.bindBaseRenderHandlers();
- // TODO: not best place for this
- // TODO: better way of forwarding options from calendar -> view
- this.calendar.optionsModel.watch('viewRawBusinessHours', [ 'businessHours' ], function(deps) {
- _this.set('rawBusinessHours', deps.businessHours);
- }, function() {
- _this.unset('rawBusinessHours');
- });
- },
- removeElement: function() {
- this.unsetDate();
- this.unbindBaseRenderHandlers();
- this.calendar.optionsModel.unwatch('viewRawBusinessHours');
- InteractiveDateComponent.prototype.removeElement.apply(this, arguments);
- },
- // Date Setting/Unsetting
- // -----------------------------------------------------------------------------------------------------------------
- setDate: function(date) {
- var currentDateProfile = this.get('dateProfile');
- var newDateProfile = this.buildDateProfile(date, null, true); // forceToValid=true
- if (
- !currentDateProfile ||
- !currentDateProfile.activeUnzonedRange.equals(newDateProfile.activeUnzonedRange)
- ) {
- this.set('dateProfile', newDateProfile);
- }
- },
- unsetDate: function() {
- this.unset('dateProfile');
- },
- handleDateProfileSet: function(dateProfile) {
- InteractiveDateComponent.prototype.handleDateProfileSet.apply(this, arguments);
- var calendar = this.calendar;
- // DEPRECATED, but we need to keep it updated...
- this.start = calendar.msToMoment(dateProfile.activeUnzonedRange.startMs, dateProfile.isRangeAllDay);
- this.end = calendar.msToMoment(dateProfile.activeUnzonedRange.endMs, dateProfile.isRangeAllDay);
- this.intervalStart = calendar.msToMoment(dateProfile.currentUnzonedRange.startMs, dateProfile.isRangeAllDay);
- this.intervalEnd = calendar.msToMoment(dateProfile.currentUnzonedRange.endMs, dateProfile.isRangeAllDay);
- this.title = this.computeTitle(dateProfile);
- calendar.reportViewDatesChanged(this, dateProfile); // TODO: reverse the pubsub
- },
- // Event Data
- // -----------------------------------------------------------------------------------------------------------------
- fetchInitialEvents: function(dateProfile) {
- var calendar = this.calendar;
- var forceAllDay = dateProfile.isRangeAllDay && !this.usesMinMaxTime;
- return calendar.requestEvents(
- calendar.msToMoment(dateProfile.activeUnzonedRange.startMs, forceAllDay),
- calendar.msToMoment(dateProfile.activeUnzonedRange.endMs, forceAllDay)
- );
- },
- bindEventChanges: function() {
- // can write this shorter?
- this.listenTo(this.calendar, 'eventsReset', this.handleEventsReset);
- //this.listenTo(this.calendar, 'eventAdd', this.handleEventAddOrUpdate);
- //this.listenTo(this.calendar, 'eventUpdate', this.handleEventAddOrUpdate);
- //this.listenTo(this.calendar, 'eventRemove', this.handleEventRemove);
- },
- unbindEventChanges: function() {
- // can write this shorter?
- this.stopListeningTo(this.calendar, 'eventsReset');
- //this.stopListeningTo(this.calendar, 'eventAdd');
- //this.stopListeningTo(this.calendar, 'eventUpdate');
- //this.stopListeningTo(this.calendar, 'eventRemove');
- },
- // Date High-level Rendering
- // -----------------------------------------------------------------------------------------------------------------
- // if dateProfile not specified, uses current
- executeDateRender: function(dateProfile, skipScroll) {
- if (this.render) {
- this.render(); // TODO: deprecate
- }
- this.renderDates();
- this.updateSize();
- this.startNowIndicator();
- if (!skipScroll) {
- this.addScroll(this.computeInitialDateScroll());
- }
- this.isDatesRendered = true;
- this.trigger('datesRendered');
- },
- executeDateUnrender: function() {
- this.unselect();
- this.stopNowIndicator();
- this.trigger('before:datesUnrendered');
- this.unrenderDates();
- if (this.destroy) {
- this.destroy(); // TODO: deprecate
- }
- this.isDatesRendered = false;
- },
- // Determing when the "meat" of the view is rendered (aka the base)
- // -----------------------------------------------------------------------------------------------------------------
- bindBaseRenderHandlers: function() {
- var _this = this;
- this.on('datesRendered.baseHandler', function() {
- _this.onBaseRender();
- });
- this.on('before:datesUnrendered.baseHandler', function() {
- _this.onBeforeBaseUnrender();
- });
- },
- unbindBaseRenderHandlers: function() {
- this.off('.baseHandler');
- },
- onBaseRender: function() {
- this.applyScreenState();
- this.publiclyTrigger('viewRender', {
- context: this,
- args: [ this, this.el ]
- });
- },
- onBeforeBaseUnrender: function() {
- this.applyScreenState();
- this.publiclyTrigger('viewDestroy', {
- context: this,
- args: [ this, this.el ]
- });
- },
- // Misc view rendering utils
- // -----------------------------------------------------------------------------------------------------------------
- // Binds DOM handlers to elements that reside outside the view container, such as the document
- bindGlobalHandlers: function() {
- InteractiveDateComponent.prototype.bindGlobalHandlers.apply(this, arguments);
- this.listenTo(GlobalEmitter.get(), {
- touchstart: this.processUnselect,
- mousedown: this.handleDocumentMousedown
- });
- },
- // Unbinds DOM handlers from elements that reside outside the view container
- unbindGlobalHandlers: function() {
- InteractiveDateComponent.prototype.unbindGlobalHandlers.apply(this, arguments);
- this.stopListeningTo(GlobalEmitter.get());
- },
- /* Now Indicator
- ------------------------------------------------------------------------------------------------------------------*/
- // Immediately render the current time indicator and begins re-rendering it at an interval,
- // which is defined by this.getNowIndicatorUnit().
- // TODO: somehow do this for the current whole day's background too
- startNowIndicator: function() {
- var _this = this;
- var unit;
- var update;
- var delay; // ms wait value
- if (this.opt('nowIndicator')) {
- unit = this.getNowIndicatorUnit();
- if (unit) {
- update = proxy(this, 'updateNowIndicator'); // bind to `this`
- this.initialNowDate = this.calendar.getNow();
- this.initialNowQueriedMs = +new Date();
- this.renderNowIndicator(this.initialNowDate);
- this.isNowIndicatorRendered = true;
- // wait until the beginning of the next interval
- delay = this.initialNowDate.clone().startOf(unit).add(1, unit) - this.initialNowDate;
- this.nowIndicatorTimeoutID = setTimeout(function() {
- _this.nowIndicatorTimeoutID = null;
- update();
- delay = +moment.duration(1, unit);
- delay = Math.max(100, delay); // prevent too frequent
- _this.nowIndicatorIntervalID = setInterval(update, delay); // update every interval
- }, delay);
- }
- }
- },
- // rerenders the now indicator, computing the new current time from the amount of time that has passed
- // since the initial getNow call.
- updateNowIndicator: function() {
- if (this.isNowIndicatorRendered) {
- this.unrenderNowIndicator();
- this.renderNowIndicator(
- this.initialNowDate.clone().add(new Date() - this.initialNowQueriedMs) // add ms
- );
- }
- },
- // Immediately unrenders the view's current time indicator and stops any re-rendering timers.
- // Won't cause side effects if indicator isn't rendered.
- stopNowIndicator: function() {
- if (this.isNowIndicatorRendered) {
- if (this.nowIndicatorTimeoutID) {
- clearTimeout(this.nowIndicatorTimeoutID);
- this.nowIndicatorTimeoutID = null;
- }
- if (this.nowIndicatorIntervalID) {
- clearTimeout(this.nowIndicatorIntervalID);
- this.nowIndicatorIntervalID = null;
- }
- this.unrenderNowIndicator();
- this.isNowIndicatorRendered = false;
- }
- },
- /* Dimensions
- ------------------------------------------------------------------------------------------------------------------*/
- // TODO: move some of these to DateComponent
- // Refreshes anything dependant upon sizing of the container element of the grid
- updateSize: function(isResize) {
- var scroll;
- if (isResize) {
- scroll = this.queryScroll();
- }
- this.updateHeight(isResize);
- this.updateWidth(isResize);
- this.updateNowIndicator();
- if (isResize) {
- this.applyScroll(scroll);
- }
- },
- // Refreshes the horizontal dimensions of the calendar
- updateWidth: function(isResize) {
- // subclasses should implement
- },
- // Refreshes the vertical dimensions of the calendar
- updateHeight: function(isResize) {
- var calendar = this.calendar; // we poll the calendar for height information
- this.setHeight(
- calendar.getSuggestedViewHeight(),
- calendar.isHeightAuto()
- );
- },
- // Updates the vertical dimensions of the calendar to the specified height.
- // if `isAuto` is set to true, height becomes merely a suggestion and the view should use its "natural" height.
- setHeight: function(height, isAuto) {
- // subclasses should implement
- },
- /* Scroller
- ------------------------------------------------------------------------------------------------------------------*/
- addForcedScroll: function(scroll) {
- this.addScroll(
- $.extend(scroll, { isForced: true })
- );
- },
- addScroll: function(scroll) {
- var queuedScroll = this.queuedScroll || (this.queuedScroll = {});
- if (!queuedScroll.isForced) {
- $.extend(queuedScroll, scroll);
- }
- },
- popScroll: function() {
- this.applyQueuedScroll();
- this.queuedScroll = null;
- },
- applyQueuedScroll: function() {
- if (this.queuedScroll) {
- this.applyScroll(this.queuedScroll);
- }
- },
- queryScroll: function() {
- var scroll = {};
- if (this.isDatesRendered) {
- $.extend(scroll, this.queryDateScroll());
- }
- return scroll;
- },
- applyScroll: function(scroll) {
- if (this.isDatesRendered) {
- this.applyDateScroll(scroll);
- }
- },
- computeInitialDateScroll: function() {
- return {}; // subclasses must implement
- },
- queryDateScroll: function() {
- return {}; // subclasses must implement
- },
- applyDateScroll: function(scroll) {
- ; // subclasses must implement
- },
- /* Height Freezing
- ------------------------------------------------------------------------------------------------------------------*/
- freezeHeight: function() {
- this.calendar.freezeContentHeight();
- },
- thawHeight: function() {
- this.calendar.thawContentHeight();
- },
- // Event High-level Rendering
- // -----------------------------------------------------------------------------------------------------------------
- executeEventsRender: function(eventsPayload) {
- if (this.renderEvents) { // for legacy custom views
- this.renderEvents(convertEventsPayloadToLegacyArray(eventsPayload));
- }
- else {
- this.renderEventsPayload(eventsPayload);
- }
- this.isEventsRendered = true;
- this.onEventsRender();
- },
- executeEventsUnrender: function() {
- this.onBeforeEventsUnrender();
- if (this.destroyEvents) {
- this.destroyEvents(); // TODO: deprecate
- }
- this.unrenderEvents();
- this.isEventsRendered = false;
- },
- // Event Rendering Triggers
- // -----------------------------------------------------------------------------------------------------------------
- // Signals that all events have been rendered
- onEventsRender: function() {
- var _this = this;
- var hasSingleHandlers = this.hasPublicHandlers('eventAfterRender');
- if (hasSingleHandlers || this.hasPublicHandlers('eventAfterAllRender')) {
- this.applyScreenState();
- }
- if (hasSingleHandlers) {
- this.getEventSegs().forEach(function(seg) {
- var legacy;
- if (seg.el) { // necessary?
- legacy = seg.footprint.getEventLegacy();
- _this.publiclyTrigger('eventAfterRender', {
- context: legacy,
- args: [ legacy, seg.el, _this ]
- });
- }
- });
- }
- this.publiclyTrigger('eventAfterAllRender', {
- context: this,
- args: [ this ]
- });
- },
- // Signals that all event elements are about to be removed
- onBeforeEventsUnrender: function() {
- var _this = this;
- if (this.hasPublicHandlers('eventDestroy')) {
- this.applyScreenState();
- this.getEventSegs().forEach(function(seg) {
- var legacy;
- if (seg.el) { // necessary?
- legacy = seg.footprint.getEventLegacy();
- _this.publiclyTrigger('eventDestroy', {
- context: legacy,
- args: [ legacy, seg.el, _this ]
- });
- }
- });
- }
- },
- applyScreenState: function() {
- this.thawHeight();
- this.freezeHeight();
- this.applyQueuedScroll();
- },
- // Event Rendering Utils
- // -----------------------------------------------------------------------------------------------------------------
- // TODO: move this to DateComponent
- // Hides all rendered event segments linked to the given event
- showEventsWithId: function(eventDefId) {
- this.getEventSegs().forEach(function(seg) {
- if (
- seg.footprint.eventDef.id === eventDefId &&
- seg.el // necessary?
- ) {
- seg.el.css('visibility', '');
- }
- });
- },
- // Shows all rendered event segments linked to the given event
- hideEventsWithId: function(eventDefId) {
- this.getEventSegs().forEach(function(seg) {
- if (
- seg.footprint.eventDef.id === eventDefId &&
- seg.el // necessary?
- ) {
- seg.el.css('visibility', 'hidden');
- }
- });
- },
- /* Event Drag-n-Drop
- ------------------------------------------------------------------------------------------------------------------*/
- reportEventDrop: function(eventInstance, eventMutation, el, ev) {
- var eventManager = this.calendar.eventManager;
- var undoFunc = eventManager.mutateEventsWithId(
- eventInstance.def.id,
- eventMutation,
- this.calendar
- );
- var dateMutation = eventMutation.dateMutation;
- // update the EventInstance, for handlers
- if (dateMutation) {
- eventInstance.dateProfile = dateMutation.buildNewDateProfile(
- eventInstance.dateProfile,
- this.calendar
- );
- }
- this.triggerEventDrop(
- eventInstance,
- // a drop doesn't necessarily mean a date mutation (ex: resource change)
- (dateMutation && dateMutation.dateDelta) || moment.duration(),
- undoFunc,
- el, ev
- );
- },
- // Triggers event-drop handlers that have subscribed via the API
- triggerEventDrop: function(eventInstance, dateDelta, undoFunc, el, ev) {
- this.publiclyTrigger('eventDrop', {
- context: el[0],
- args: [
- eventInstance.toLegacy(),
- dateDelta,
- undoFunc,
- ev,
- {}, // {} = jqui dummy
- this
- ]
- });
- },
- /* External Element Drag-n-Drop
- ------------------------------------------------------------------------------------------------------------------*/
- // Must be called when an external element, via jQuery UI, has been dropped onto the calendar.
- // `meta` is the parsed data that has been embedded into the dragging event.
- // `dropLocation` is an object that contains the new zoned start/end/allDay values for the event.
- reportExternalDrop: function(singleEventDef, isEvent, isSticky, el, ev, ui) {
- if (isEvent) {
- this.calendar.eventManager.addEventDef(singleEventDef, isSticky);
- }
- this.triggerExternalDrop(singleEventDef, isEvent, el, ev, ui);
- },
- // Triggers external-drop handlers that have subscribed via the API
- triggerExternalDrop: function(singleEventDef, isEvent, el, ev, ui) {
- // trigger 'drop' regardless of whether element represents an event
- this.publiclyTrigger('drop', {
- context: el[0],
- args: [
- singleEventDef.dateProfile.start.clone(),
- ev,
- ui,
- this
- ]
- });
- if (isEvent) {
- // signal an external event landed
- this.publiclyTrigger('eventReceive', {
- context: this,
- args: [
- singleEventDef.buildInstance().toLegacy(),
- this
- ]
- });
- }
- },
- /* Event Resizing
- ------------------------------------------------------------------------------------------------------------------*/
- // Must be called when an event in the view has been resized to a new length
- reportEventResize: function(eventInstance, eventMutation, el, ev) {
- var eventManager = this.calendar.eventManager;
- var undoFunc = eventManager.mutateEventsWithId(
- eventInstance.def.id,
- eventMutation,
- this.calendar
- );
- // update the EventInstance, for handlers
- eventInstance.dateProfile = eventMutation.dateMutation.buildNewDateProfile(
- eventInstance.dateProfile,
- this.calendar
- );
- this.triggerEventResize(
- eventInstance,
- eventMutation.dateMutation.endDelta,
- undoFunc,
- el, ev
- );
- },
- // Triggers event-resize handlers that have subscribed via the API
- triggerEventResize: function(eventInstance, durationDelta, undoFunc, el, ev) {
- this.publiclyTrigger('eventResize', {
- context: el[0],
- args: [
- eventInstance.toLegacy(),
- durationDelta,
- undoFunc,
- ev,
- {}, // {} = jqui dummy
- this
- ]
- });
- },
- /* Selection (time range)
- ------------------------------------------------------------------------------------------------------------------*/
- // Selects a date span on the view. `start` and `end` are both Moments.
- // `ev` is the native mouse event that begin the interaction.
- select: function(footprint, ev) {
- this.unselect(ev);
- this.renderSelectionFootprint(footprint);
- this.reportSelection(footprint, ev);
- },
- renderSelectionFootprint: function(footprint, ev) {
- if (this.renderSelection) { // legacy method in custom view classes
- this.renderSelection(
- footprint.toLegacy(this.calendar)
- );
- }
- else {
- InteractiveDateComponent.prototype.renderSelectionFootprint.apply(this, arguments);
- }
- },
- // Called when a new selection is made. Updates internal state and triggers handlers.
- reportSelection: function(footprint, ev) {
- this.isSelected = true;
- this.triggerSelect(footprint, ev);
- },
- // Triggers handlers to 'select'
- triggerSelect: function(footprint, ev) {
- var dateProfile = this.calendar.footprintToDateProfile(footprint); // abuse of "Event"DateProfile?
- this.publiclyTrigger('select', {
- context: this,
- args: [
- dateProfile.start,
- dateProfile.end,
- ev,
- this
- ]
- });
- },
- // Undoes a selection. updates in the internal state and triggers handlers.
- // `ev` is the native mouse event that began the interaction.
- unselect: function(ev) {
- if (this.isSelected) {
- this.isSelected = false;
- if (this.destroySelection) {
- this.destroySelection(); // TODO: deprecate
- }
- this.unrenderSelection();
- this.publiclyTrigger('unselect', {
- context: this,
- args: [ ev, this ]
- });
- }
- },
- /* Event Selection
- ------------------------------------------------------------------------------------------------------------------*/
- selectEventInstance: function(eventInstance) {
- if (
- !this.selectedEventInstance ||
- this.selectedEventInstance !== eventInstance
- ) {
- this.unselectEventInstance();
- this.getEventSegs().forEach(function(seg) {
- if (
- seg.footprint.eventInstance === eventInstance &&
- seg.el // necessary?
- ) {
- seg.el.addClass('fc-selected');
- }
- });
- this.selectedEventInstance = eventInstance;
- }
- },
- unselectEventInstance: function() {
- if (this.selectedEventInstance) {
- this.getEventSegs().forEach(function(seg) {
- if (seg.el) { // necessary?
- seg.el.removeClass('fc-selected');
- }
- });
- this.selectedEventInstance = null;
- }
- },
- isEventDefSelected: function(eventDef) {
- // event references might change on refetchEvents(), while selectedEventInstance doesn't,
- // so compare IDs
- return this.selectedEventInstance && this.selectedEventInstance.def.id === eventDef.id;
- },
- /* Mouse / Touch Unselecting (time range & event unselection)
- ------------------------------------------------------------------------------------------------------------------*/
- // TODO: move consistently to down/start or up/end?
- // TODO: don't kill previous selection if touch scrolling
- handleDocumentMousedown: function(ev) {
- if (isPrimaryMouseButton(ev)) {
- this.processUnselect(ev);
- }
- },
- processUnselect: function(ev) {
- this.processRangeUnselect(ev);
- this.processEventUnselect(ev);
- },
- processRangeUnselect: function(ev) {
- var ignore;
- // is there a time-range selection?
- if (this.isSelected && this.opt('unselectAuto')) {
- // only unselect if the clicked element is not identical to or inside of an 'unselectCancel' element
- ignore = this.opt('unselectCancel');
- if (!ignore || !$(ev.target).closest(ignore).length) {
- this.unselect(ev);
- }
- }
- },
- processEventUnselect: function(ev) {
- if (this.selectedEventInstance) {
- if (!$(ev.target).closest('.fc-selected').length) {
- this.unselectEventInstance();
- }
- }
- },
- /* Day Click
- ------------------------------------------------------------------------------------------------------------------*/
- // Triggers handlers to 'dayClick'
- // Span has start/end of the clicked area. Only the start is useful.
- triggerDayClick: function(footprint, dayEl, ev) {
- var dateProfile = this.calendar.footprintToDateProfile(footprint); // abuse of "Event"DateProfile?
- this.publiclyTrigger('dayClick', {
- context: dayEl,
- args: [ dateProfile.start, ev, this ]
- });
- }
- });
- // responsible for populating data that DateComponent relies on
- View.watch('businessHours', [ 'rawBusinessHours', 'dateProfile' ], function(deps) {
- return new BusinessHours(
- deps.rawBusinessHours,
- deps.dateProfile.activeUnzonedRange,
- this.calendar
- );
- });
- View.watch('initialEvents', [ 'dateProfile' ], function(deps) {
- return this.fetchInitialEvents(deps.dateProfile);
- });
- View.watch('bindingEvents', [ 'initialEvents' ], function(deps) {
- this.handleEventsSet(deps.initialEvents);
- this.bindEventChanges();
- }, function() {
- this.unbindEventChanges();
- this.handleEventsUnset();
- });
- // legacy
- function convertEventsPayloadToLegacyArray(eventsPayload) {
- var legacyEvents = [];
- var id;
- var eventInstances;
- var i;
- for (id in eventsPayload) {
- eventInstances = eventsPayload[id].eventInstances;
- for (i = 0; i < eventInstances.length; i++) {
- legacyEvents.push(
- eventInstances[i].toLegacy()
- );
- }
- }
- return legacyEvents;
- }
|