%PDF-1.5 %���� ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµùÕ5sLOšuY
Server IP : 49.231.201.246 / Your IP : 216.73.216.149 Web Server : Apache/2.4.18 (Ubuntu) System : Linux 246 4.4.0-210-generic #242-Ubuntu SMP Fri Apr 16 09:57:56 UTC 2021 x86_64 User : root ( 0) PHP Version : 7.0.33-0ubuntu0.16.04.16 Disable Function : exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /proc/thread-self/root/var/www/html/ppaobm/vendor/bower-asset/fullcalendar/src/timegrid/ |
Upload File : |
import { htmlEscape, htmlToElement, findElements, removeElement, applyStyle, createElement, PositionCache, Duration, createDuration, addDurations, multiplyDuration, wholeDivideDurations, asRoughMs, startOfDay, DateMarker, DateFormatter, createFormatter, formatIsoTimeString, ComponentContext, DateComponent, Seg, EventSegUiInteractionState, DateProfile, memoizeRendering, MemoizedRendering } from '@fullcalendar/core' import { DayBgRow } from '@fullcalendar/daygrid' import TimeGridEventRenderer from './TimeGridEventRenderer' import TimeGridMirrorRenderer from './TimeGridMirrorRenderer' import TimeGridFillRenderer from './TimeGridFillRenderer' /* A component that renders one or more columns of vertical time slots ----------------------------------------------------------------------------------------------------------------------*/ // potential nice values for the slot-duration and interval-duration // from largest to smallest const AGENDA_STOCK_SUB_DURATIONS = [ { hours: 1 }, { minutes: 30 }, { minutes: 15 }, { seconds: 30 }, { seconds: 15 } ] export interface RenderProps { renderBgIntroHtml: () => string renderIntroHtml: () => string } export interface TimeGridSeg extends Seg { col: number start: DateMarker end: DateMarker } export interface TimeGridCell { date: DateMarker htmlAttrs?: string } export interface TimeGridProps { dateProfile: DateProfile cells: TimeGridCell[] businessHourSegs: TimeGridSeg[] bgEventSegs: TimeGridSeg[] fgEventSegs: TimeGridSeg[] dateSelectionSegs: TimeGridSeg[] eventSelection: string eventDrag: EventSegUiInteractionState | null eventResize: EventSegUiInteractionState | null } export default class TimeGrid extends DateComponent<TimeGridProps> { renderProps: RenderProps slotDuration: Duration // duration of a "slot", a distinct time segment on given day, visualized by lines snapDuration: Duration // granularity of time for dragging and selecting snapsPerSlot: any labelFormat: DateFormatter // formatting string for times running along vertical axis labelInterval: Duration // duration of how often a label should be displayed for a slot colCnt: number colEls: HTMLElement[] // cells elements in the day-row background slatContainerEl: HTMLElement // div that wraps all the slat rows slatEls: HTMLElement[] // elements running horizontally across all columns nowIndicatorEls: HTMLElement[] colPositions: PositionCache slatPositions: PositionCache isSlatSizesDirty: boolean = false isColSizesDirty: boolean = false rootBgContainerEl: HTMLElement bottomRuleEl: HTMLElement // hidden by default contentSkeletonEl: HTMLElement colContainerEls: HTMLElement[] // containers for each column // inner-containers for each column where different types of segs live fgContainerEls: HTMLElement[] bgContainerEls: HTMLElement[] mirrorContainerEls: HTMLElement[] highlightContainerEls: HTMLElement[] businessContainerEls: HTMLElement[] private renderSlats = memoizeRendering(this._renderSlats) private renderColumns: MemoizedRendering<[TimeGridCell[], DateProfile]> private renderBusinessHours: MemoizedRendering<[TimeGridSeg[]]> private renderDateSelection: MemoizedRendering<[TimeGridSeg[]]> private renderBgEvents: MemoizedRendering<[TimeGridSeg[]]> private renderFgEvents: MemoizedRendering<[TimeGridSeg[]]> private renderEventSelection: MemoizedRendering<[string]> private renderEventDrag: MemoizedRendering<[EventSegUiInteractionState]> private renderEventResize: MemoizedRendering<[EventSegUiInteractionState]> constructor(context: ComponentContext, el: HTMLElement, renderProps: RenderProps) { super(context, el) let eventRenderer = this.eventRenderer = new TimeGridEventRenderer(this) let fillRenderer = this.fillRenderer = new TimeGridFillRenderer(this) this.mirrorRenderer = new TimeGridMirrorRenderer(this) let renderColumns = this.renderColumns = memoizeRendering( this._renderColumns, this._unrenderColumns ) this.renderBusinessHours = memoizeRendering( fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [ renderColumns ] ) this.renderDateSelection = memoizeRendering( this._renderDateSelection, this._unrenderDateSelection, [ renderColumns ] ) this.renderFgEvents = memoizeRendering( eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [ renderColumns ] ) this.renderBgEvents = memoizeRendering( fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [ renderColumns ] ) this.renderEventSelection = memoizeRendering( eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [ this.renderFgEvents ] ) this.renderEventDrag = memoizeRendering( this._renderEventDrag, this._unrenderEventDrag, [ renderColumns ] ) this.renderEventResize = memoizeRendering( this._renderEventResize, this._unrenderEventResize, [ renderColumns ] ) this.processOptions() el.innerHTML = '<div class="fc-bg"></div>' + '<div class="fc-slats"></div>' + '<hr class="fc-divider ' + this.theme.getClass('widgetHeader') + '" style="display:none" />' this.rootBgContainerEl = el.querySelector('.fc-bg') this.slatContainerEl = el.querySelector('.fc-slats') this.bottomRuleEl = el.querySelector('.fc-divider') this.renderProps = renderProps } /* Options ------------------------------------------------------------------------------------------------------------------*/ // Parses various options into properties of this object processOptions() { let slotDuration = this.opt('slotDuration') let snapDuration = this.opt('snapDuration') let snapsPerSlot let input slotDuration = createDuration(slotDuration) snapDuration = snapDuration ? createDuration(snapDuration) : slotDuration snapsPerSlot = wholeDivideDurations(slotDuration, snapDuration) if (snapsPerSlot === null) { snapDuration = slotDuration snapsPerSlot = 1 // TODO: say warning? } this.slotDuration = slotDuration this.snapDuration = snapDuration this.snapsPerSlot = snapsPerSlot // might be an array value (for TimelineView). // if so, getting the most granular entry (the last one probably). input = this.opt('slotLabelFormat') if (Array.isArray(input)) { input = input[input.length - 1] } this.labelFormat = createFormatter(input || { hour: 'numeric', minute: '2-digit', omitZeroMinute: true, meridiem: 'short' }) input = this.opt('slotLabelInterval') this.labelInterval = input ? createDuration(input) : this.computeLabelInterval(slotDuration) } // Computes an automatic value for slotLabelInterval computeLabelInterval(slotDuration) { let i let labelInterval let slotsPerLabel // find the smallest stock label interval that results in more than one slots-per-label for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) { labelInterval = createDuration(AGENDA_STOCK_SUB_DURATIONS[i]) slotsPerLabel = wholeDivideDurations(labelInterval, slotDuration) if (slotsPerLabel !== null && slotsPerLabel > 1) { return labelInterval } } return slotDuration // fall back } /* Rendering ------------------------------------------------------------------------------------------------------------------*/ render(props: TimeGridProps) { let cells = props.cells this.colCnt = cells.length this.renderSlats(props.dateProfile) this.renderColumns(props.cells, props.dateProfile) this.renderBusinessHours(props.businessHourSegs) this.renderDateSelection(props.dateSelectionSegs) this.renderFgEvents(props.fgEventSegs) this.renderBgEvents(props.bgEventSegs) this.renderEventSelection(props.eventSelection) this.renderEventDrag(props.eventDrag) this.renderEventResize(props.eventResize) } destroy() { super.destroy() // should unrender everything else too this.renderSlats.unrender() this.renderColumns.unrender() } updateSize(isResize: boolean) { let { fillRenderer, eventRenderer, mirrorRenderer } = this if (isResize || this.isSlatSizesDirty) { this.buildSlatPositions() this.isSlatSizesDirty = false } if (isResize || this.isColSizesDirty) { this.buildColPositions() this.isColSizesDirty = false } fillRenderer.computeSizes(isResize) eventRenderer.computeSizes(isResize) mirrorRenderer.computeSizes(isResize) fillRenderer.assignSizes(isResize) eventRenderer.assignSizes(isResize) mirrorRenderer.assignSizes(isResize) } _renderSlats(dateProfile: DateProfile) { let { theme } = this this.slatContainerEl.innerHTML = '<table class="' + theme.getClass('tableGrid') + '">' + this.renderSlatRowHtml(dateProfile) + '</table>' this.slatEls = findElements(this.slatContainerEl, 'tr') this.slatPositions = new PositionCache( this.el, this.slatEls, false, true // vertical ) this.isSlatSizesDirty = true } // Generates the HTML for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL. renderSlatRowHtml(dateProfile: DateProfile) { let { dateEnv, theme, isRtl } = this let html = '' let dayStart = startOfDay(dateProfile.renderRange.start) let slotTime = dateProfile.minTime let slotIterator = createDuration(0) let slotDate // will be on the view's first day, but we only care about its time let isLabeled let axisHtml // Calculate the time for each slot while (asRoughMs(slotTime) < asRoughMs(dateProfile.maxTime)) { slotDate = dateEnv.add(dayStart, slotTime) isLabeled = wholeDivideDurations(slotIterator, this.labelInterval) !== null axisHtml = '<td class="fc-axis fc-time ' + theme.getClass('widgetContent') + '">' + (isLabeled ? '<span>' + // for matchCellWidths htmlEscape(dateEnv.format(slotDate, this.labelFormat)) + '</span>' : '' ) + '</td>' html += '<tr data-time="' + formatIsoTimeString(slotDate) + '"' + (isLabeled ? '' : ' class="fc-minor"') + '>' + (!isRtl ? axisHtml : '') + '<td class="' + theme.getClass('widgetContent') + '"></td>' + (isRtl ? axisHtml : '') + '</tr>' slotTime = addDurations(slotTime, this.slotDuration) slotIterator = addDurations(slotIterator, this.slotDuration) } return html } _renderColumns(cells: TimeGridCell[], dateProfile: DateProfile) { let { theme, dateEnv, view } = this let bgRow = new DayBgRow(this.context) this.rootBgContainerEl.innerHTML = '<table class="' + theme.getClass('tableGrid') + '">' + bgRow.renderHtml({ cells, dateProfile, renderIntroHtml: this.renderProps.renderBgIntroHtml }) + '</table>' this.colEls = findElements(this.el, '.fc-day, .fc-disabled-day') for (let col = 0; col < this.colCnt; col++) { this.publiclyTrigger('dayRender', [ { date: dateEnv.toDate(cells[col].date), el: this.colEls[col], view } ]) } if (this.isRtl) { this.colEls.reverse() } this.colPositions = new PositionCache( this.el, this.colEls, true, // horizontal false ) this.renderContentSkeleton() this.isColSizesDirty = true } _unrenderColumns() { this.unrenderContentSkeleton() } /* Content Skeleton ------------------------------------------------------------------------------------------------------------------*/ // Renders the DOM that the view's content will live in renderContentSkeleton() { let parts = [] let skeletonEl: HTMLElement parts.push( this.renderProps.renderIntroHtml() ) for (let i = 0; i < this.colCnt; i++) { parts.push( '<td>' + '<div class="fc-content-col">' + '<div class="fc-event-container fc-mirror-container"></div>' + '<div class="fc-event-container"></div>' + '<div class="fc-highlight-container"></div>' + '<div class="fc-bgevent-container"></div>' + '<div class="fc-business-container"></div>' + '</div>' + '</td>' ) } if (this.isRtl) { parts.reverse() } skeletonEl = this.contentSkeletonEl = htmlToElement( '<div class="fc-content-skeleton">' + '<table>' + '<tr>' + parts.join('') + '</tr>' + '</table>' + '</div>' ) this.colContainerEls = findElements(skeletonEl, '.fc-content-col') this.mirrorContainerEls = findElements(skeletonEl, '.fc-mirror-container') this.fgContainerEls = findElements(skeletonEl, '.fc-event-container:not(.fc-mirror-container)') this.bgContainerEls = findElements(skeletonEl, '.fc-bgevent-container') this.highlightContainerEls = findElements(skeletonEl, '.fc-highlight-container') this.businessContainerEls = findElements(skeletonEl, '.fc-business-container') if (this.isRtl) { this.colContainerEls.reverse() this.mirrorContainerEls.reverse() this.fgContainerEls.reverse() this.bgContainerEls.reverse() this.highlightContainerEls.reverse() this.businessContainerEls.reverse() } this.el.appendChild(skeletonEl) } unrenderContentSkeleton() { removeElement(this.contentSkeletonEl) } // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col groupSegsByCol(segs) { let segsByCol = [] let i for (i = 0; i < this.colCnt; i++) { segsByCol.push([]) } for (i = 0; i < segs.length; i++) { segsByCol[segs[i].col].push(segs[i]) } return segsByCol } // Given segments grouped by column, insert the segments' elements into a parallel array of container // elements, each living within a column. attachSegsByCol(segsByCol, containerEls: HTMLElement[]) { let col let segs let i for (col = 0; col < this.colCnt; col++) { // iterate each column grouping segs = segsByCol[col] for (i = 0; i < segs.length; i++) { containerEls[col].appendChild(segs[i].el) } } } /* Now Indicator ------------------------------------------------------------------------------------------------------------------*/ getNowIndicatorUnit() { return 'minute' // will refresh on the minute } renderNowIndicator(segs: TimeGridSeg[], date) { // HACK: if date columns not ready for some reason (scheduler) if (!this.colContainerEls) { return } let top = this.computeDateTop(date) let nodes = [] let i // render lines within the columns for (i = 0; i < segs.length; i++) { let lineEl = createElement('div', { className: 'fc-now-indicator fc-now-indicator-line' }) lineEl.style.top = top + 'px' this.colContainerEls[segs[i].col].appendChild(lineEl) nodes.push(lineEl) } // render an arrow over the axis if (segs.length > 0) { // is the current time in view? let arrowEl = createElement('div', { className: 'fc-now-indicator fc-now-indicator-arrow' }) arrowEl.style.top = top + 'px' this.contentSkeletonEl.appendChild(arrowEl) nodes.push(arrowEl) } this.nowIndicatorEls = nodes } unrenderNowIndicator() { if (this.nowIndicatorEls) { this.nowIndicatorEls.forEach(removeElement) this.nowIndicatorEls = null } } /* Coordinates ------------------------------------------------------------------------------------------------------------------*/ getTotalSlatHeight() { return this.slatContainerEl.offsetHeight } // Computes the top coordinate, relative to the bounds of the grid, of the given date. // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight. computeDateTop(when: DateMarker, startOfDayDate?: DateMarker) { if (!startOfDayDate) { startOfDayDate = startOfDay(when) } return this.computeTimeTop(when.valueOf() - startOfDayDate.valueOf()) } // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration). computeTimeTop(timeMs: number) { let len = this.slatEls.length let dateProfile = this.props.dateProfile let slatCoverage = (timeMs - asRoughMs(dateProfile.minTime)) / asRoughMs(this.slotDuration) // floating-point value of # of slots covered let slatIndex let slatRemainder // compute a floating-point number for how many slats should be progressed through. // from 0 to number of slats (inclusive) // constrained because minTime/maxTime might be customized. slatCoverage = Math.max(0, slatCoverage) slatCoverage = Math.min(len, slatCoverage) // an integer index of the furthest whole slat // from 0 to number slats (*exclusive*, so len-1) slatIndex = Math.floor(slatCoverage) slatIndex = Math.min(slatIndex, len - 1) // how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition. // could be 1.0 if slatCoverage is covering *all* the slots slatRemainder = slatCoverage - slatIndex return this.slatPositions.tops[slatIndex] + this.slatPositions.getHeight(slatIndex) * slatRemainder } // For each segment in an array, computes and assigns its top and bottom properties computeSegVerticals(segs) { let eventMinHeight = this.opt('timeGridEventMinHeight') let i let seg let dayDate for (i = 0; i < segs.length; i++) { seg = segs[i] dayDate = this.props.cells[seg.col].date seg.top = this.computeDateTop(seg.start, dayDate) seg.bottom = Math.max( seg.top + eventMinHeight, this.computeDateTop(seg.end, dayDate) ) } } // Given segments that already have their top/bottom properties computed, applies those values to // the segments' elements. assignSegVerticals(segs) { let i let seg for (i = 0; i < segs.length; i++) { seg = segs[i] applyStyle(seg.el, this.generateSegVerticalCss(seg)) } } // Generates an object with CSS properties for the top/bottom coordinates of a segment element generateSegVerticalCss(seg) { return { top: seg.top, bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container } } /* Sizing ------------------------------------------------------------------------------------------------------------------*/ buildPositionCaches() { this.buildColPositions() this.buildSlatPositions() } buildColPositions() { this.colPositions.build() } buildSlatPositions() { this.slatPositions.build() } /* Hit System ------------------------------------------------------------------------------------------------------------------*/ positionToHit(positionLeft, positionTop) { let { dateEnv, snapsPerSlot, slatPositions, colPositions } = this let colIndex = colPositions.leftToIndex(positionLeft) let slatIndex = slatPositions.topToIndex(positionTop) if (colIndex != null && slatIndex != null) { let slatTop = slatPositions.tops[slatIndex] let slatHeight = slatPositions.getHeight(slatIndex) let partial = (positionTop - slatTop) / slatHeight // floating point number between 0 and 1 let localSnapIndex = Math.floor(partial * snapsPerSlot) // the snap # relative to start of slat let snapIndex = slatIndex * snapsPerSlot + localSnapIndex let dayDate = this.props.cells[colIndex].date let time = addDurations( this.props.dateProfile.minTime, multiplyDuration(this.snapDuration, snapIndex) ) let start = dateEnv.add(dayDate, time) let end = dateEnv.add(start, this.snapDuration) return { col: colIndex, dateSpan: { range: { start, end }, allDay: false }, dayEl: this.colEls[colIndex], relativeRect: { left: colPositions.lefts[colIndex], right: colPositions.rights[colIndex], top: slatTop, bottom: slatTop + slatHeight } } } } /* Event Drag Visualization ------------------------------------------------------------------------------------------------------------------*/ _renderEventDrag(state: EventSegUiInteractionState) { if (state) { this.eventRenderer.hideByHash(state.affectedInstances) if (state.isEvent) { this.mirrorRenderer.renderSegs(state.segs, { isDragging: true, sourceSeg: state.sourceSeg }) } else { this.fillRenderer.renderSegs('highlight', state.segs) } } } _unrenderEventDrag(state: EventSegUiInteractionState) { if (state) { this.eventRenderer.showByHash(state.affectedInstances) this.mirrorRenderer.unrender(state.segs, { isDragging: true, sourceSeg: state.sourceSeg }) this.fillRenderer.unrender('highlight') } } /* Event Resize Visualization ------------------------------------------------------------------------------------------------------------------*/ _renderEventResize(state: EventSegUiInteractionState) { if (state) { this.eventRenderer.hideByHash(state.affectedInstances) this.mirrorRenderer.renderSegs(state.segs, { isResizing: true, sourceSeg: state.sourceSeg }) } } _unrenderEventResize(state: EventSegUiInteractionState) { if (state) { this.eventRenderer.showByHash(state.affectedInstances) this.mirrorRenderer.unrender(state.segs, { isResizing: true, sourceSeg: state.sourceSeg }) } } /* Selection ------------------------------------------------------------------------------------------------------------------*/ // Renders a visual indication of a selection. Overrides the default, which was to simply render a highlight. _renderDateSelection(segs: Seg[]) { if (segs) { if (this.opt('selectMirror')) { this.mirrorRenderer.renderSegs(segs, { isSelecting: true }) } else { this.fillRenderer.renderSegs('highlight', segs) } } } _unrenderDateSelection(segs: Seg[]) { this.mirrorRenderer.unrender(segs, { isSelecting: true }) this.fillRenderer.unrender('highlight') } }