%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/11585/cwd/html/ppaobm/vendor/bower-asset/fullcalendar/src/daygrid/ |
Upload File : |
import { createElement, insertAfterElement, findElements, findChildren, removeElement, computeRect, PositionCache, addDays, DateMarker, createFormatter, DateComponent, EventSegUiInteractionState, Seg, rangeContainsMarker, intersectRanges, EventRenderRange, buildGotoAnchorHtml, getDayClasses, DateProfile, memoizeRendering, MemoizedRendering } from '@fullcalendar/core' import Popover from './Popover' import DayGridEventRenderer from './DayGridEventRenderer' import DayGridMirrorRenderer from './DayGridMirrorRenderer' import DayGridFillRenderer from './DayGridFillRenderer' import DayTile from './DayTile' import DayBgRow from './DayBgRow' const DAY_NUM_FORMAT = createFormatter({ day: 'numeric' }) const WEEK_NUM_FORMAT = createFormatter({ week: 'numeric' }) /* A component that renders a grid of whole-days that runs horizontally. There can be multiple rows, one per week. ----------------------------------------------------------------------------------------------------------------------*/ export interface RenderProps { // TODO: combine with DayGridProps renderNumberIntroHtml: (row: number, dayGrid: DayGrid) => string renderBgIntroHtml: () => string renderIntroHtml: () => string colWeekNumbersVisible: boolean // week numbers render in own column? (caller does HTML via intro) cellWeekNumbersVisible: boolean // display week numbers in day cell? } export interface DayGridSeg extends Seg { row: number firstCol: number lastCol: number } export interface DayGridCell { date: DateMarker htmlAttrs?: string } export interface DayGridProps { dateProfile: DateProfile cells: DayGridCell[][] businessHourSegs: DayGridSeg[] bgEventSegs: DayGridSeg[] fgEventSegs: DayGridSeg[] dateSelectionSegs: DayGridSeg[] eventSelection: string eventDrag: EventSegUiInteractionState | null eventResize: EventSegUiInteractionState | null // isRigid determines whether the individual rows should ignore the contents and be a constant height. // Relies on the view's colCnt and rowCnt. In the future, this component should probably be self-sufficient. isRigid: boolean } export default class DayGrid extends DateComponent<DayGridProps> { eventRenderer: DayGridEventRenderer renderProps: RenderProps rowCnt: number colCnt: number bottomCoordPadding: number = 0 // hack for extending the hit area for the last row of the coordinate grid rowEls: HTMLElement[] // set of fake row elements cellEls: HTMLElement[] // set of whole-day elements comprising the row's background isCellSizesDirty: boolean = false rowPositions: PositionCache colPositions: PositionCache segPopover: Popover // the Popover that holds events that can't fit in a cell. null when not visible segPopoverTile: DayTile private renderCells: MemoizedRendering<[DayGridCell[][], boolean]> private renderBusinessHours: MemoizedRendering<[DayGridSeg[]]> private renderDateSelection: MemoizedRendering<[DayGridSeg[]]> private renderBgEvents: MemoizedRendering<[DayGridSeg[]]> private renderFgEvents: MemoizedRendering<[DayGridSeg[]]> private renderEventSelection: MemoizedRendering<[string]> private renderEventDrag: MemoizedRendering<[EventSegUiInteractionState]> private renderEventResize: MemoizedRendering<[EventSegUiInteractionState]> constructor(context, el, renderProps: RenderProps) { super(context, el) let eventRenderer = this.eventRenderer = new DayGridEventRenderer(this) let fillRenderer = this.fillRenderer = new DayGridFillRenderer(this) this.mirrorRenderer = new DayGridMirrorRenderer(this) let renderCells = this.renderCells = memoizeRendering( this._renderCells, this._unrenderCells ) this.renderBusinessHours = memoizeRendering( fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [ renderCells ] ) this.renderDateSelection = memoizeRendering( fillRenderer.renderSegs.bind(fillRenderer, 'highlight'), fillRenderer.unrender.bind(fillRenderer, 'highlight'), [ renderCells ] ) this.renderBgEvents = memoizeRendering( fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [ renderCells ] ) this.renderFgEvents = memoizeRendering( eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [ renderCells ] ) this.renderEventSelection = memoizeRendering( eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [ this.renderFgEvents ] ) this.renderEventDrag = memoizeRendering( this._renderEventDrag, this._unrenderEventDrag, [ renderCells ] ) this.renderEventResize = memoizeRendering( this._renderEventResize, this._unrenderEventResize, [ renderCells ] ) this.renderProps = renderProps } render(props: DayGridProps) { let cells = props.cells this.rowCnt = cells.length this.colCnt = cells[0].length this.renderCells(cells, props.isRigid) this.renderBusinessHours(props.businessHourSegs) this.renderDateSelection(props.dateSelectionSegs) this.renderBgEvents(props.bgEventSegs) this.renderFgEvents(props.fgEventSegs) this.renderEventSelection(props.eventSelection) this.renderEventDrag(props.eventDrag) this.renderEventResize(props.eventResize) if (this.segPopoverTile) { this.updateSegPopoverTile() } } destroy() { super.destroy() this.renderCells.unrender() // will unrender everything else } getCellRange(row, col) { let start = this.props.cells[row][col].date let end = addDays(start, 1) return { start, end } } updateSegPopoverTile(date?, segs?) { let ownProps = this.props this.segPopoverTile.receiveProps({ date: date || (this.segPopoverTile.props as any).date, fgSegs: segs || (this.segPopoverTile.props as any).fgSegs, eventSelection: ownProps.eventSelection, eventDragInstances: ownProps.eventDrag ? ownProps.eventDrag.affectedInstances : null, eventResizeInstances: ownProps.eventResize ? ownProps.eventResize.affectedInstances : null }) } /* Date Rendering ------------------------------------------------------------------------------------------------------------------*/ _renderCells(cells: DayGridCell[][], isRigid: boolean) { let { view, dateEnv } = this let { rowCnt, colCnt } = this let html = '' let row let col for (row = 0; row < rowCnt; row++) { html += this.renderDayRowHtml(row, isRigid) } this.el.innerHTML = html this.rowEls = findElements(this.el, '.fc-row') this.cellEls = findElements(this.el, '.fc-day, .fc-disabled-day') if (this.isRtl) { this.cellEls.reverse() } this.rowPositions = new PositionCache( this.el, this.rowEls, false, true // vertical ) this.colPositions = new PositionCache( this.el, this.cellEls.slice(0, colCnt), // only the first row true, false // horizontal ) // trigger dayRender with each cell's element for (row = 0; row < rowCnt; row++) { for (col = 0; col < colCnt; col++) { this.publiclyTrigger('dayRender', [ { date: dateEnv.toDate(cells[row][col].date), el: this.getCellEl(row, col), view } ]) } } this.isCellSizesDirty = true } _unrenderCells() { this.removeSegPopover() } // Generates the HTML for a single row, which is a div that wraps a table. // `row` is the row number. renderDayRowHtml(row, isRigid) { let { theme } = this let classes = [ 'fc-row', 'fc-week', theme.getClass('dayRow') ] if (isRigid) { classes.push('fc-rigid') } let bgRow = new DayBgRow(this.context) return '' + '<div class="' + classes.join(' ') + '">' + '<div class="fc-bg">' + '<table class="' + theme.getClass('tableGrid') + '">' + bgRow.renderHtml({ cells: this.props.cells[row], dateProfile: this.props.dateProfile, renderIntroHtml: this.renderProps.renderBgIntroHtml }) + '</table>' + '</div>' + '<div class="fc-content-skeleton">' + '<table>' + (this.getIsNumbersVisible() ? '<thead>' + this.renderNumberTrHtml(row) + '</thead>' : '' ) + '</table>' + '</div>' + '</div>' } getIsNumbersVisible() { return this.getIsDayNumbersVisible() || this.renderProps.cellWeekNumbersVisible || this.renderProps.colWeekNumbersVisible } getIsDayNumbersVisible() { return this.rowCnt > 1 } /* Grid Number Rendering ------------------------------------------------------------------------------------------------------------------*/ renderNumberTrHtml(row: number) { let intro = this.renderProps.renderNumberIntroHtml(row, this) return '' + '<tr>' + (this.isRtl ? '' : intro) + this.renderNumberCellsHtml(row) + (this.isRtl ? intro : '') + '</tr>' } renderNumberCellsHtml(row) { let htmls = [] let col let date for (col = 0; col < this.colCnt; col++) { date = this.props.cells[row][col].date htmls.push(this.renderNumberCellHtml(date)) } if (this.isRtl) { htmls.reverse() } return htmls.join('') } // Generates the HTML for the <td>s of the "number" row in the DayGrid's content skeleton. // The number row will only exist if either day numbers or week numbers are turned on. renderNumberCellHtml(date) { let { view, dateEnv } = this let html = '' let isDateValid = rangeContainsMarker(this.props.dateProfile.activeRange, date) // TODO: called too frequently. cache somehow. let isDayNumberVisible = this.getIsDayNumbersVisible() && isDateValid let classes let weekCalcFirstDow if (!isDayNumberVisible && !this.renderProps.cellWeekNumbersVisible) { // no numbers in day cell (week number must be along the side) return '<td></td>' // will create an empty space above events :( } classes = getDayClasses(date, this.props.dateProfile, this.context) classes.unshift('fc-day-top') if (this.renderProps.cellWeekNumbersVisible) { weekCalcFirstDow = dateEnv.weekDow } html += '<td class="' + classes.join(' ') + '"' + (isDateValid ? ' data-date="' + dateEnv.formatIso(date, { omitTime: true }) + '"' : '' ) + '>' if (this.renderProps.cellWeekNumbersVisible && (date.getUTCDay() === weekCalcFirstDow)) { html += buildGotoAnchorHtml( view, { date, type: 'week' }, { 'class': 'fc-week-number' }, dateEnv.format(date, WEEK_NUM_FORMAT) // inner HTML ) } if (isDayNumberVisible) { html += buildGotoAnchorHtml( view, date, { 'class': 'fc-day-number' }, dateEnv.format(date, DAY_NUM_FORMAT) // inner HTML ) } html += '</td>' return html } /* Sizing ------------------------------------------------------------------------------------------------------------------*/ updateSize(isResize: boolean) { let { fillRenderer, eventRenderer, mirrorRenderer } = this if ( isResize || this.isCellSizesDirty || this.view.calendar.isEventsUpdated // hack ) { this.buildPositionCaches() this.isCellSizesDirty = false } fillRenderer.computeSizes(isResize) eventRenderer.computeSizes(isResize) mirrorRenderer.computeSizes(isResize) fillRenderer.assignSizes(isResize) eventRenderer.assignSizes(isResize) mirrorRenderer.assignSizes(isResize) } buildPositionCaches() { this.buildColPositions() this.buildRowPositions() } buildColPositions() { this.colPositions.build() } buildRowPositions() { this.rowPositions.build() this.rowPositions.bottoms[this.rowCnt - 1] += this.bottomCoordPadding // hack } /* Hit System ------------------------------------------------------------------------------------------------------------------*/ positionToHit(leftPosition, topPosition) { let { colPositions, rowPositions } = this let col = colPositions.leftToIndex(leftPosition) let row = rowPositions.topToIndex(topPosition) if (row != null && col != null) { return { row, col, dateSpan: { range: this.getCellRange(row, col), allDay: true }, dayEl: this.getCellEl(row, col), relativeRect: { left: colPositions.lefts[col], right: colPositions.rights[col], top: rowPositions.tops[row], bottom: rowPositions.bottoms[row] } } } } /* Cell System ------------------------------------------------------------------------------------------------------------------*/ // FYI: the first column is the leftmost column, regardless of date getCellEl(row, col) { return this.cellEls[row * this.colCnt + col] } /* Event Drag Visualization ------------------------------------------------------------------------------------------------------------------*/ _renderEventDrag(state: EventSegUiInteractionState) { if (state) { this.eventRenderer.hideByHash(state.affectedInstances) this.fillRenderer.renderSegs('highlight', state.segs) } } _unrenderEventDrag(state: EventSegUiInteractionState) { if (state) { this.eventRenderer.showByHash(state.affectedInstances) this.fillRenderer.unrender('highlight') } } /* Event Resize Visualization ------------------------------------------------------------------------------------------------------------------*/ _renderEventResize(state: EventSegUiInteractionState) { if (state) { this.eventRenderer.hideByHash(state.affectedInstances) this.fillRenderer.renderSegs('highlight', state.segs) this.mirrorRenderer.renderSegs(state.segs, { isResizing: true, sourceSeg: state.sourceSeg }) } } _unrenderEventResize(state: EventSegUiInteractionState) { if (state) { this.eventRenderer.showByHash(state.affectedInstances) this.fillRenderer.unrender('highlight') this.mirrorRenderer.unrender(state.segs, { isResizing: true, sourceSeg: state.sourceSeg }) } } /* More+ Link Popover ------------------------------------------------------------------------------------------------------------------*/ removeSegPopover() { if (this.segPopover) { this.segPopover.hide() // in handler, will call segPopover's removeElement } } // Limits the number of "levels" (vertically stacking layers of events) for each row of the grid. // `levelLimit` can be false (don't limit), a number, or true (should be computed). limitRows(levelLimit) { let rowStructs = this.eventRenderer.rowStructs || [] let row // row # let rowLevelLimit for (row = 0; row < rowStructs.length; row++) { this.unlimitRow(row) if (!levelLimit) { rowLevelLimit = false } else if (typeof levelLimit === 'number') { rowLevelLimit = levelLimit } else { rowLevelLimit = this.computeRowLevelLimit(row) } if (rowLevelLimit !== false) { this.limitRow(row, rowLevelLimit) } } } // Computes the number of levels a row will accomodate without going outside its bounds. // Assumes the row is "rigid" (maintains a constant height regardless of what is inside). // `row` is the row number. computeRowLevelLimit(row): (number | false) { let rowEl = this.rowEls[row] // the containing "fake" row div let rowBottom = rowEl.getBoundingClientRect().bottom // relative to viewport! let trEls = findChildren(this.eventRenderer.rowStructs[row].tbodyEl) as HTMLTableRowElement[] let i let trEl: HTMLTableRowElement // Reveal one level <tr> at a time and stop when we find one out of bounds for (i = 0; i < trEls.length; i++) { trEl = trEls[i] trEl.classList.remove('fc-limited') // reset to original state (reveal) if (trEl.getBoundingClientRect().bottom > rowBottom) { return i } } return false // should not limit at all } // Limits the given grid row to the maximum number of levels and injects "more" links if necessary. // `row` is the row number. // `levelLimit` is a number for the maximum (inclusive) number of levels allowed. limitRow(row, levelLimit) { let { colCnt, isRtl } = this let rowStruct = this.eventRenderer.rowStructs[row] let moreNodes = [] // array of "more" <a> links and <td> DOM nodes let col = 0 // col #, left-to-right (not chronologically) let levelSegs // array of segment objects in the last allowable level, ordered left-to-right let cellMatrix // a matrix (by level, then column) of all <td> elements in the row let limitedNodes // array of temporarily hidden level <tr> and segment <td> DOM nodes let i let seg let segsBelow // array of segment objects below `seg` in the current `col` let totalSegsBelow // total number of segments below `seg` in any of the columns `seg` occupies let colSegsBelow // array of segment arrays, below seg, one for each column (offset from segs's first column) let td: HTMLTableCellElement let rowSpan let segMoreNodes // array of "more" <td> cells that will stand-in for the current seg's cell let j let moreTd: HTMLTableCellElement let moreWrap let moreLink // Iterates through empty level cells and places "more" links inside if need be let emptyCellsUntil = (endCol) => { // goes from current `col` to `endCol` while (col < endCol) { segsBelow = this.getCellSegs(row, col, levelLimit) if (segsBelow.length) { td = cellMatrix[levelLimit - 1][col] moreLink = this.renderMoreLink(row, col, segsBelow) moreWrap = createElement('div', null, moreLink) td.appendChild(moreWrap) moreNodes.push(moreWrap) } col++ } } if (levelLimit && levelLimit < rowStruct.segLevels.length) { // is it actually over the limit? levelSegs = rowStruct.segLevels[levelLimit - 1] cellMatrix = rowStruct.cellMatrix limitedNodes = findChildren(rowStruct.tbodyEl).slice(levelLimit) // get level <tr> elements past the limit limitedNodes.forEach(function(node) { node.classList.add('fc-limited') // hide elements and get a simple DOM-nodes array }) // iterate though segments in the last allowable level for (i = 0; i < levelSegs.length; i++) { seg = levelSegs[i] let leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol let rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol emptyCellsUntil(leftCol) // process empty cells before the segment // determine *all* segments below `seg` that occupy the same columns colSegsBelow = [] totalSegsBelow = 0 while (col <= rightCol) { segsBelow = this.getCellSegs(row, col, levelLimit) colSegsBelow.push(segsBelow) totalSegsBelow += segsBelow.length col++ } if (totalSegsBelow) { // do we need to replace this segment with one or many "more" links? td = cellMatrix[levelLimit - 1][leftCol] // the segment's parent cell rowSpan = td.rowSpan || 1 segMoreNodes = [] // make a replacement <td> for each column the segment occupies. will be one for each colspan for (j = 0; j < colSegsBelow.length; j++) { moreTd = createElement('td', { className: 'fc-more-cell', rowSpan }) as HTMLTableCellElement segsBelow = colSegsBelow[j] moreLink = this.renderMoreLink( row, leftCol + j, [ seg ].concat(segsBelow) // count seg as hidden too ) moreWrap = createElement('div', null, moreLink) moreTd.appendChild(moreWrap) segMoreNodes.push(moreTd) moreNodes.push(moreTd) } td.classList.add('fc-limited') insertAfterElement(td, segMoreNodes) limitedNodes.push(td) } } emptyCellsUntil(this.colCnt) // finish off the level rowStruct.moreEls = moreNodes // for easy undoing later rowStruct.limitedEls = limitedNodes // for easy undoing later } } // Reveals all levels and removes all "more"-related elements for a grid's row. // `row` is a row number. unlimitRow(row) { let rowStruct = this.eventRenderer.rowStructs[row] if (rowStruct.moreEls) { rowStruct.moreEls.forEach(removeElement) rowStruct.moreEls = null } if (rowStruct.limitedEls) { rowStruct.limitedEls.forEach(function(limitedEl) { limitedEl.classList.remove('fc-limited') }) rowStruct.limitedEls = null } } // Renders an <a> element that represents hidden event element for a cell. // Responsible for attaching click handler as well. renderMoreLink(row, col, hiddenSegs) { let { view, dateEnv } = this let a = createElement('a', { className: 'fc-more' }) a.innerText = this.getMoreLinkText(hiddenSegs.length) a.addEventListener('click', (ev) => { let clickOption = this.opt('eventLimitClick') let _col = this.isRtl ? this.colCnt - col - 1 : col // HACK: props.cells has different dir system? let date = this.props.cells[row][_col].date let moreEl = ev.currentTarget as HTMLElement let dayEl = this.getCellEl(row, col) let allSegs = this.getCellSegs(row, col) // rescope the segments to be within the cell's date let reslicedAllSegs = this.resliceDaySegs(allSegs, date) let reslicedHiddenSegs = this.resliceDaySegs(hiddenSegs, date) if (typeof clickOption === 'function') { // the returned value can be an atomic option clickOption = this.publiclyTrigger('eventLimitClick', [ { date: dateEnv.toDate(date), allDay: true, dayEl: dayEl, moreEl: moreEl, segs: reslicedAllSegs, hiddenSegs: reslicedHiddenSegs, jsEvent: ev, view } ]) } if (clickOption === 'popover') { this.showSegPopover(row, col, moreEl, reslicedAllSegs) } else if (typeof clickOption === 'string') { // a view name view.calendar.zoomTo(date, clickOption) } }) return a } // Reveals the popover that displays all events within a cell showSegPopover(row, col, moreLink: HTMLElement, segs) { let { calendar, view, theme } = this let _col = this.isRtl ? this.colCnt - col - 1 : col // HACK: props.cells has different dir system? let moreWrap = moreLink.parentNode as HTMLElement // the <div> wrapper around the <a> let topEl: HTMLElement // the element we want to match the top coordinate of let options if (this.rowCnt === 1) { topEl = view.el // will cause the popover to cover any sort of header } else { topEl = this.rowEls[row] // will align with top of row } options = { className: 'fc-more-popover ' + theme.getClass('popover'), parentEl: view.el, // will be outside of all scrollers within the view top: computeRect(topEl).top, autoHide: true, // when the user clicks elsewhere, hide the popover content: (el) => { this.segPopoverTile = new DayTile( this.context, el ) this.updateSegPopoverTile( this.props.cells[row][_col].date, segs ) }, hide: () => { this.segPopoverTile.destroy() this.segPopoverTile = null this.segPopover.destroy() this.segPopover = null } } // Determine horizontal coordinate. // We use the moreWrap instead of the <td> to avoid border confusion. if (this.isRtl) { options.right = computeRect(moreWrap).right + 1 // +1 to be over cell border } else { options.left = computeRect(moreWrap).left - 1 // -1 to be over cell border } this.segPopover = new Popover(options) this.segPopover.show() calendar.releaseAfterSizingTriggers() // hack for eventPositioned } // Given the events within an array of segment objects, reslice them to be in a single day resliceDaySegs(segs, dayDate) { let dayStart = dayDate let dayEnd = addDays(dayStart, 1) let dayRange = { start: dayStart, end: dayEnd } let newSegs = [] for (let seg of segs) { let eventRange = seg.eventRange let origRange = eventRange.range let slicedRange = intersectRanges(origRange, dayRange) if (slicedRange) { newSegs.push({ ...seg, eventRange: { def: eventRange.def, ui: { ...eventRange.ui, durationEditable: false }, // hack to disable resizing instance: eventRange.instance, range: slicedRange } as EventRenderRange, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() }) } } return newSegs } // Generates the text that should be inside a "more" link, given the number of events it represents getMoreLinkText(num) { let opt = this.opt('eventLimitText') if (typeof opt === 'function') { return opt(num) } else { return '+' + num + ' ' + opt } } // Returns segments within a given cell. // If `startLevel` is specified, returns only events including and below that level. Otherwise returns all segs. getCellSegs(row, col, startLevel?) { let segMatrix = this.eventRenderer.rowStructs[row].segMatrix let level = startLevel || 0 let segs = [] let seg while (level < segMatrix.length) { seg = segMatrix[level][col] if (seg) { segs.push(seg) } level++ } return segs } }