@syncfusion/ej2-schedule 31.1.17 → 31.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/dist/ej2-schedule.min.js +2 -2
  2. package/dist/ej2-schedule.umd.min.js +2 -2
  3. package/dist/ej2-schedule.umd.min.js.map +1 -1
  4. package/dist/es6/ej2-schedule.es2015.js +7 -3
  5. package/dist/es6/ej2-schedule.es2015.js.map +1 -1
  6. package/dist/es6/ej2-schedule.es5.js +6 -2
  7. package/dist/es6/ej2-schedule.es5.js.map +1 -1
  8. package/dist/global/ej2-schedule.min.js +2 -2
  9. package/dist/global/ej2-schedule.min.js.map +1 -1
  10. package/dist/global/index.d.ts +1 -1
  11. package/package.json +18 -53
  12. package/src/schedule/actions/virtual-scroll.js +5 -1
  13. package/src/schedule/event-renderer/event-base.js +1 -1
  14. package/styles/bootstrap4-lite.css +8 -0
  15. package/styles/bootstrap4.css +8 -0
  16. package/styles/recurrence-editor/bootstrap4.css +8 -0
  17. package/styles/schedule/bootstrap4.css +8 -0
  18. package/dist/ts/common/calendar-util.d.ts +0 -92
  19. package/dist/ts/common/calendar-util.ts +0 -261
  20. package/dist/ts/common/index.d.ts +0 -4
  21. package/dist/ts/common/index.ts +0 -4
  22. package/dist/ts/components.d.ts +0 -5
  23. package/dist/ts/components.ts +0 -5
  24. package/dist/ts/index.d.ts +0 -6
  25. package/dist/ts/index.ts +0 -7
  26. package/dist/ts/recurrence-editor/date-generator.d.ts +0 -76
  27. package/dist/ts/recurrence-editor/date-generator.ts +0 -1699
  28. package/dist/ts/recurrence-editor/index.d.ts +0 -6
  29. package/dist/ts/recurrence-editor/index.ts +0 -6
  30. package/dist/ts/recurrence-editor/recurrence-editor-model.d.ts +0 -112
  31. package/dist/ts/recurrence-editor/recurrence-editor.d.ts +0 -245
  32. package/dist/ts/recurrence-editor/recurrence-editor.ts +0 -1257
  33. package/dist/ts/schedule/actions/action-base.d.ts +0 -44
  34. package/dist/ts/schedule/actions/action-base.ts +0 -493
  35. package/dist/ts/schedule/actions/crud.d.ts +0 -41
  36. package/dist/ts/schedule/actions/crud.ts +0 -784
  37. package/dist/ts/schedule/actions/data.d.ts +0 -63
  38. package/dist/ts/schedule/actions/data.ts +0 -128
  39. package/dist/ts/schedule/actions/drag.d.ts +0 -75
  40. package/dist/ts/schedule/actions/drag.ts +0 -1401
  41. package/dist/ts/schedule/actions/keyboard.d.ts +0 -100
  42. package/dist/ts/schedule/actions/keyboard.ts +0 -1435
  43. package/dist/ts/schedule/actions/resize.d.ts +0 -27
  44. package/dist/ts/schedule/actions/resize.ts +0 -602
  45. package/dist/ts/schedule/actions/scroll.d.ts +0 -69
  46. package/dist/ts/schedule/actions/scroll.ts +0 -105
  47. package/dist/ts/schedule/actions/touch.d.ts +0 -32
  48. package/dist/ts/schedule/actions/touch.ts +0 -314
  49. package/dist/ts/schedule/actions/virtual-scroll.d.ts +0 -55
  50. package/dist/ts/schedule/actions/virtual-scroll.ts +0 -596
  51. package/dist/ts/schedule/actions/work-cells.d.ts +0 -14
  52. package/dist/ts/schedule/actions/work-cells.ts +0 -151
  53. package/dist/ts/schedule/base/constant.d.ts +0 -102
  54. package/dist/ts/schedule/base/constant.ts +0 -103
  55. package/dist/ts/schedule/base/css-constant.d.ts +0 -475
  56. package/dist/ts/schedule/base/css-constant.ts +0 -475
  57. package/dist/ts/schedule/base/interface.d.ts +0 -673
  58. package/dist/ts/schedule/base/interface.ts +0 -738
  59. package/dist/ts/schedule/base/resource.d.ts +0 -59
  60. package/dist/ts/schedule/base/resource.ts +0 -1091
  61. package/dist/ts/schedule/base/schedule-model.d.ts +0 -930
  62. package/dist/ts/schedule/base/schedule.d.ts +0 -1967
  63. package/dist/ts/schedule/base/schedule.ts +0 -4221
  64. package/dist/ts/schedule/base/type.d.ts +0 -134
  65. package/dist/ts/schedule/base/type.ts +0 -142
  66. package/dist/ts/schedule/base/util.d.ts +0 -266
  67. package/dist/ts/schedule/base/util.ts +0 -492
  68. package/dist/ts/schedule/event-renderer/agenda-base.d.ts +0 -15
  69. package/dist/ts/schedule/event-renderer/agenda-base.ts +0 -423
  70. package/dist/ts/schedule/event-renderer/event-base.d.ts +0 -101
  71. package/dist/ts/schedule/event-renderer/event-base.ts +0 -1501
  72. package/dist/ts/schedule/event-renderer/inline-edit.d.ts +0 -23
  73. package/dist/ts/schedule/event-renderer/inline-edit.ts +0 -287
  74. package/dist/ts/schedule/event-renderer/month.d.ts +0 -60
  75. package/dist/ts/schedule/event-renderer/month.ts +0 -760
  76. package/dist/ts/schedule/event-renderer/timeline-view.d.ts +0 -51
  77. package/dist/ts/schedule/event-renderer/timeline-view.ts +0 -606
  78. package/dist/ts/schedule/event-renderer/vertical-view.d.ts +0 -57
  79. package/dist/ts/schedule/event-renderer/vertical-view.ts +0 -898
  80. package/dist/ts/schedule/event-renderer/year.d.ts +0 -27
  81. package/dist/ts/schedule/event-renderer/year.ts +0 -623
  82. package/dist/ts/schedule/exports/calendar-export.d.ts +0 -16
  83. package/dist/ts/schedule/exports/calendar-export.ts +0 -160
  84. package/dist/ts/schedule/exports/calendar-import.d.ts +0 -18
  85. package/dist/ts/schedule/exports/calendar-import.ts +0 -277
  86. package/dist/ts/schedule/exports/excel-export.d.ts +0 -14
  87. package/dist/ts/schedule/exports/excel-export.ts +0 -89
  88. package/dist/ts/schedule/exports/index.d.ts +0 -7
  89. package/dist/ts/schedule/exports/index.ts +0 -7
  90. package/dist/ts/schedule/exports/print.d.ts +0 -20
  91. package/dist/ts/schedule/exports/print.ts +0 -233
  92. package/dist/ts/schedule/index.d.ts +0 -26
  93. package/dist/ts/schedule/index.ts +0 -26
  94. package/dist/ts/schedule/models/event-settings-model.d.ts +0 -165
  95. package/dist/ts/schedule/models/event-settings.d.ts +0 -149
  96. package/dist/ts/schedule/models/event-settings.ts +0 -187
  97. package/dist/ts/schedule/models/field-options-model.d.ts +0 -37
  98. package/dist/ts/schedule/models/field-options.d.ts +0 -31
  99. package/dist/ts/schedule/models/field-options.ts +0 -41
  100. package/dist/ts/schedule/models/fields-model.d.ts +0 -129
  101. package/dist/ts/schedule/models/fields.d.ts +0 -117
  102. package/dist/ts/schedule/models/fields.ts +0 -149
  103. package/dist/ts/schedule/models/group-model.d.ts +0 -69
  104. package/dist/ts/schedule/models/group.d.ts +0 -60
  105. package/dist/ts/schedule/models/group.ts +0 -75
  106. package/dist/ts/schedule/models/header-rows-model.d.ts +0 -33
  107. package/dist/ts/schedule/models/header-rows.d.ts +0 -30
  108. package/dist/ts/schedule/models/header-rows.ts +0 -35
  109. package/dist/ts/schedule/models/models.d.ts +0 -14
  110. package/dist/ts/schedule/models/models.ts +0 -15
  111. package/dist/ts/schedule/models/quick-info-templates-model.d.ts +0 -52
  112. package/dist/ts/schedule/models/quick-info-templates.d.ts +0 -47
  113. package/dist/ts/schedule/models/quick-info-templates.ts +0 -56
  114. package/dist/ts/schedule/models/resources-model.d.ts +0 -122
  115. package/dist/ts/schedule/models/resources.d.ts +0 -106
  116. package/dist/ts/schedule/models/resources.ts +0 -138
  117. package/dist/ts/schedule/models/time-scale-model.d.ts +0 -57
  118. package/dist/ts/schedule/models/time-scale.d.ts +0 -50
  119. package/dist/ts/schedule/models/time-scale.ts +0 -61
  120. package/dist/ts/schedule/models/toolbar-model.d.ts +0 -196
  121. package/dist/ts/schedule/models/toolbar.d.ts +0 -176
  122. package/dist/ts/schedule/models/toolbar.ts +0 -196
  123. package/dist/ts/schedule/models/views-model.d.ts +0 -370
  124. package/dist/ts/schedule/models/views.d.ts +0 -335
  125. package/dist/ts/schedule/models/views.ts +0 -408
  126. package/dist/ts/schedule/models/work-hours-model.d.ts +0 -29
  127. package/dist/ts/schedule/models/work-hours.d.ts +0 -24
  128. package/dist/ts/schedule/models/work-hours.ts +0 -31
  129. package/dist/ts/schedule/popups/event-tooltip.d.ts +0 -16
  130. package/dist/ts/schedule/popups/event-tooltip.ts +0 -203
  131. package/dist/ts/schedule/popups/event-window.d.ts +0 -118
  132. package/dist/ts/schedule/popups/event-window.ts +0 -2055
  133. package/dist/ts/schedule/popups/form-validator.d.ts +0 -16
  134. package/dist/ts/schedule/popups/form-validator.ts +0 -110
  135. package/dist/ts/schedule/popups/quick-popups.d.ts +0 -78
  136. package/dist/ts/schedule/popups/quick-popups.ts +0 -1470
  137. package/dist/ts/schedule/renderer/agenda.d.ts +0 -45
  138. package/dist/ts/schedule/renderer/agenda.ts +0 -497
  139. package/dist/ts/schedule/renderer/day.d.ts +0 -20
  140. package/dist/ts/schedule/renderer/day.ts +0 -28
  141. package/dist/ts/schedule/renderer/header-renderer.d.ts +0 -48
  142. package/dist/ts/schedule/renderer/header-renderer.ts +0 -736
  143. package/dist/ts/schedule/renderer/month-agenda.d.ts +0 -29
  144. package/dist/ts/schedule/renderer/month-agenda.ts +0 -184
  145. package/dist/ts/schedule/renderer/month.d.ts +0 -61
  146. package/dist/ts/schedule/renderer/month.ts +0 -766
  147. package/dist/ts/schedule/renderer/renderer.d.ts +0 -13
  148. package/dist/ts/schedule/renderer/renderer.ts +0 -165
  149. package/dist/ts/schedule/renderer/timeline-header-row.d.ts +0 -15
  150. package/dist/ts/schedule/renderer/timeline-header-row.ts +0 -132
  151. package/dist/ts/schedule/renderer/timeline-month.d.ts +0 -29
  152. package/dist/ts/schedule/renderer/timeline-month.ts +0 -184
  153. package/dist/ts/schedule/renderer/timeline-view.d.ts +0 -31
  154. package/dist/ts/schedule/renderer/timeline-view.ts +0 -308
  155. package/dist/ts/schedule/renderer/timeline-year.d.ts +0 -22
  156. package/dist/ts/schedule/renderer/timeline-year.ts +0 -450
  157. package/dist/ts/schedule/renderer/vertical-view.d.ts +0 -63
  158. package/dist/ts/schedule/renderer/vertical-view.ts +0 -911
  159. package/dist/ts/schedule/renderer/view-base.d.ts +0 -83
  160. package/dist/ts/schedule/renderer/view-base.ts +0 -709
  161. package/dist/ts/schedule/renderer/week.d.ts +0 -22
  162. package/dist/ts/schedule/renderer/week.ts +0 -35
  163. package/dist/ts/schedule/renderer/work-week.d.ts +0 -22
  164. package/dist/ts/schedule/renderer/work-week.ts +0 -36
  165. package/dist/ts/schedule/renderer/year.d.ts +0 -46
  166. package/dist/ts/schedule/renderer/year.ts +0 -470
  167. package/dist/ts/schedule/timezone/timezone.d.ts +0 -16
  168. package/dist/ts/schedule/timezone/timezone.ts +0 -313
@@ -1,1501 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /* eslint-disable max-len */
3
- import { isNullOrUndefined, closest, extend, EventHandler, setStyleAttribute } from '@syncfusion/ej2-base';
4
- import { createElement, prepend, append, addClass, removeClass } from '@syncfusion/ej2-base';
5
- import { DataManager, Query, Predicate } from '@syncfusion/ej2-data';
6
- import { EventFieldsMapping, EventClickArgs, CellClickEventArgs, TdData, SelectEventArgs, InlineClickArgs, CallbackFunction } from '../base/interface';
7
- import { Schedule } from '../base/schedule';
8
- import { ResourcesModel } from '../models/resources-model';
9
- import { generate, getDateFromRecurrenceDateString } from '../../recurrence-editor/date-generator';
10
- import { CalendarType } from '../../common/calendar-util';
11
- import * as util from '../base/util';
12
- import * as cls from '../base/css-constant';
13
- import * as event from '../base/constant';
14
- import { CurrentAction } from '../base/type';
15
- /**
16
- * EventBase for appointment rendering
17
- */
18
- export class EventBase {
19
- public parent: Schedule;
20
- public slots: number[] = [];
21
- public cssClass: string;
22
- public groupOrder: string[];
23
- public processedData: Record<string, any>[] = [];
24
- private isDoubleTapped: boolean = false;
25
-
26
- /**
27
- * Constructor for EventBase
28
- *
29
- * @param {Schedule} parent Accepts the schedule instance
30
- */
31
- constructor(parent: Schedule) {
32
- this.parent = parent;
33
- }
34
-
35
- public processData(events: Record<string, any>[], timeZonePropChanged?: boolean, oldTimezone?: string): Record<string, any>[] {
36
- const start: Date = this.parent.activeView.startDate();
37
- const end: Date = this.parent.activeView.endDate();
38
- const fields: EventFieldsMapping = this.parent.eventFields;
39
- let processed: Record<string, any>[] = [];
40
- let temp: number = 1;
41
- let generateID: boolean = false;
42
- const resourceCollection: ResourcesModel[] = this.parent.resourceBase ? this.parent.resourceBase.resourceCollection : [];
43
- if (events.length > 0 && isNullOrUndefined(events[0][fields.id])) {
44
- generateID = true;
45
- }
46
- for (let event of events) {
47
- if (generateID) {
48
- event[fields.id] = temp++;
49
- }
50
- event = this.updateEventDateTime(event);
51
- if (timeZonePropChanged) {
52
- this.processTimezoneChange(event, oldTimezone);
53
- } else if (!this.parent.isPrinting && !this.parent.uiStateValues.isPreventTimezone) {
54
- event = this.processTimezone(event);
55
- }
56
- for (let level: number = 0; level < resourceCollection.length; level++) {
57
- if (event[resourceCollection[parseInt(level.toString(), 10)].field] === null || event[resourceCollection[parseInt(level.toString(), 10)].field] === 0) {
58
- event[resourceCollection[parseInt(level.toString(), 10)].field] = undefined;
59
- }
60
- }
61
- if (!isNullOrUndefined(event[fields.recurrenceRule]) && event[fields.recurrenceRule] === '') {
62
- event[fields.recurrenceRule] = null;
63
- }
64
- if (!isNullOrUndefined(event[fields.recurrenceRule]) && isNullOrUndefined(event[fields.recurrenceID]) &&
65
- !(this.parent.crudModule && this.parent.crudModule.crudObj.isCrudAction)) {
66
- processed = processed.concat(this.generateOccurrence(event, null, true));
67
- } else {
68
- if (this.parent.crudModule && this.parent.crudModule.crudObj.isCrudAction) {
69
- if (!isNullOrUndefined(event[fields.recurrenceRule]) && isNullOrUndefined(event[fields.recurrenceID])) {
70
- const recurrenceEvent: Record<string, any>[] = this.generateOccurrence(event, null, true);
71
- for (const occurrence of recurrenceEvent) {
72
- const app: Record<string, any>[] = this.parent.eventsProcessed.filter((data: Record<string, Date>) =>
73
- data[fields.startTime].getTime() - (<Date>occurrence[fields.startTime]).getTime() === 0 &&
74
- data[fields.id] === occurrence[fields.id]);
75
- occurrence.Guid = (app.length > 0) ? app[0].Guid : this.generateGuid();
76
- processed.push(occurrence);
77
- }
78
- } else {
79
- const app: Record<string, any>[] = this.parent.eventsProcessed.filter((data: Record<string, any>) =>
80
- data[this.parent.eventFields.id] === event[this.parent.eventFields.id]);
81
- event.Guid = (app.length > 0) ? app[0].Guid : this.generateGuid();
82
- processed.push(event);
83
- }
84
- } else {
85
- event.Guid = this.generateGuid();
86
- processed.push(event);
87
- }
88
- }
89
- }
90
- this.parent.eventsProcessed = [];
91
- const eventData: Record<string, any>[] = processed.filter((data: Record<string, any>) =>
92
- !data[this.parent.eventFields.isBlock]);
93
- this.parent.eventsProcessed = this.filterEvents(start, end, eventData);
94
- if (!this.parent.activeViewOptions.allowOverlap && this.parent.eventsProcessed.length > 0) {
95
- this.processedData = this.parent.eventsProcessed;
96
- const nonOverlapList: Record<string, any>[] = [];
97
- const fields: EventFieldsMapping = this.parent.eventFields;
98
- for (const data of this.parent.eventsProcessed as Record<string, any>[]) {
99
- const overlappingData: Record<string, any> = this.findOverlappingData(data, nonOverlapList);
100
- if (!overlappingData) {
101
- nonOverlapList.push(data);
102
- } else if (!this.parent.eventSettings.sortComparer) {
103
- const dataDuration: number = new Date(data[fields.endTime]).getTime() - new Date(data[fields.startTime]).getTime();
104
- const duplicateDuration: number = new Date(overlappingData[fields.endTime]).getTime() - new Date(overlappingData[fields.startTime]).getTime();
105
- if ((dataDuration > duplicateDuration && data[fields.startTime] === overlappingData[fields.startTime]) || (data[fields.isAllDay] === true)) {
106
- const index: number = nonOverlapList.indexOf(overlappingData);
107
- if (index !== -1) { nonOverlapList.splice(index, 1); }
108
- nonOverlapList.push(data);
109
- }
110
- }
111
- }
112
- this.parent.eventsProcessed = nonOverlapList;
113
- }
114
- const blockData: Record<string, any>[] = processed.filter((data: Record<string, any>) =>
115
- data[this.parent.eventFields.isBlock]);
116
- for (const eventObj of blockData) {
117
- if (eventObj[fields.isAllDay]) {
118
- const isDifferentDate: boolean = util.resetTime(new Date((eventObj[fields.startTime] as Date).getTime())) <
119
- util.resetTime(new Date((eventObj[fields.endTime] as Date).getTime()));
120
- if (!isDifferentDate) {
121
- eventObj[fields.startTime] = util.resetTime(eventObj[fields.startTime] as Date);
122
- eventObj[fields.endTime] = util.addDays(util.resetTime(eventObj[fields.endTime] as Date), 1);
123
- }
124
- }
125
- }
126
- this.parent.blockProcessed = blockData;
127
- return eventData;
128
- }
129
-
130
- private findOverlappingData(eventData: Record<string, any>, eventList: Record<string, any>[]): Record<string, any> | undefined {
131
- const isResource: boolean = this.parent.activeViewOptions.group.resources.length > 0;
132
- const resourceCollection: ResourcesModel[] = isResource ? this.parent.resourceBase.resourceCollection : [];
133
- const lastLevelResource: string = isResource ? resourceCollection[resourceCollection.length - 1].field : null;
134
- const fields: EventFieldsMapping = this.parent.eventFields;
135
- const newStartTime: Date = new Date(eventData[fields.startTime]);
136
- const newEndTime: Date = new Date(eventData[fields.endTime]);
137
- for (const existingEvent of eventList) {
138
- if (
139
- newStartTime < existingEvent[fields.endTime] &&
140
- newEndTime > existingEvent[fields.startTime] &&
141
- existingEvent[fields.id] !== eventData[fields.id] &&
142
- (!isResource || isNullOrUndefined(lastLevelResource) ||
143
- this.compareResourceValues(existingEvent[`${lastLevelResource}`], eventData[`${lastLevelResource}`]))
144
- ) {
145
- return existingEvent;
146
- }
147
- }
148
- return undefined;
149
- }
150
-
151
- private isOverlapRange(eventData: Record<string, any> | Record<string, any>[], currentAction: CurrentAction = null): boolean {
152
- const isResource: boolean = this.parent.activeViewOptions.group.resources.length > 0;
153
- const resourceCollection: ResourcesModel[] = isResource ? this.parent.resourceBase.resourceCollection : [];
154
- const lastLevelResource: string = isResource ? resourceCollection[resourceCollection.length - 1].field : null;
155
- const eventCollection: Record<string, any>[] = Array.isArray(eventData) ? eventData : [eventData];
156
- const fields: EventFieldsMapping = this.parent.eventFields;
157
-
158
- const processOverlappingEvents: (data: Record<string, any>) => Record<string, any>[] = (data: Record<string, any>) => {
159
- return this.processedData.filter((x: Record<string, any>) =>
160
- data[fields.startTime] < x[fields.endTime] &&
161
- data[fields.endTime] > x[fields.startTime] &&
162
- x[fields.id] !== data[fields.id] &&
163
- (!isResource || isNullOrUndefined(lastLevelResource) || this.compareResourceValues(x[`${lastLevelResource}`], data[`${lastLevelResource}`]))
164
- );
165
- };
166
- const overlappedEvents: Record<string, any>[] = [];
167
- let isOverlapAlert: boolean = false;
168
-
169
- for (const event of eventCollection) {
170
- const dataCol: Record<string, any>[] = !isNullOrUndefined(event[fields.recurrenceRule]) &&
171
- (isNullOrUndefined(event[fields.recurrenceID]) || event[fields.recurrenceID] === event[fields.id]) &&
172
- (isNullOrUndefined(event[fields.recurrenceID]) || currentAction === 'EditSeries')
173
- ? this.generateOccurrence(event)
174
- : [event];
175
-
176
- for (const data of dataCol) {
177
- const overlappingEvents: Record<string, any>[] = processOverlappingEvents(data);
178
- if (overlappingEvents.length > 0) {
179
- overlappedEvents.push(...overlappingEvents);
180
- }
181
- if (this.findOverlappingData(data, this.parent.eventsProcessed)) {
182
- isOverlapAlert = true;
183
- }
184
- }
185
- }
186
- this.parent.overlapAppointments = overlappedEvents;
187
- return isOverlapAlert;
188
- }
189
-
190
- private compareResourceValues(a: string | number | (string | number)[], b: string | number | (string | number)[]): boolean {
191
- type GetValueFunction = (value: string | number | (string | number)[]) => string | number;
192
- const getValue: GetValueFunction = (value: string | number | (string | number)[]) => Array.isArray(value) ? value[0] : value;
193
- return getValue(a) === getValue(b);
194
- }
195
-
196
- public checkOverlap(eventData: Record<string, any> | Record<string, any>[]): boolean {
197
- if (!this.parent.activeViewOptions.allowOverlap) {
198
- if (this.isOverlapRange(eventData)) {
199
- this.parent.quickPopup.openValidationError('overlapAlert', eventData);
200
- return true;
201
- }
202
- }
203
- return false;
204
- }
205
-
206
- public updateEventDateTime(eventData: Record<string, any>): Record<string, any> {
207
- if (typeof eventData[this.parent.eventFields.startTime] === 'string') {
208
- eventData[this.parent.eventFields.startTime] = util.getDateFromString(eventData[this.parent.eventFields.startTime]);
209
- }
210
- if (typeof eventData[this.parent.eventFields.endTime] === 'string') {
211
- eventData[this.parent.eventFields.endTime] = util.getDateFromString(eventData[this.parent.eventFields.endTime]);
212
- }
213
- return eventData;
214
- }
215
-
216
- public getProcessedEvents(eventCollection: Record<string, any>[] = this.parent.eventsData): Record<string, any>[] {
217
- let processed: Record<string, any>[] = [];
218
- for (const event of eventCollection) {
219
- if (!isNullOrUndefined(event[this.parent.eventFields.recurrenceRule]) &&
220
- isNullOrUndefined(event[this.parent.eventFields.recurrenceID])) {
221
- processed = processed.concat(this.generateOccurrence(event));
222
- } else {
223
- processed.push(event);
224
- }
225
- }
226
- return processed;
227
- }
228
-
229
- public timezonePropertyChange(oldTimezone: string): void {
230
- const data: Record<string, any>[] = this.parent.eventsData.concat(this.parent.blockData) as Record<string, any>[];
231
- const processed: Record<string, any>[] = this.processData(data, true, oldTimezone);
232
- this.parent.notify(event.dataReady, { processedData: processed });
233
- }
234
-
235
- public timezoneConvert(eventData: Record<string, any>): void {
236
- const fields: EventFieldsMapping = this.parent.eventFields;
237
- eventData[fields.startTimezone] = eventData[fields.startTimezone] || eventData[fields.endTimezone];
238
- eventData[fields.endTimezone] = eventData[fields.endTimezone] || eventData[fields.startTimezone];
239
- if (this.parent.timezone) {
240
- const startTz: string = eventData[fields.startTimezone] as string;
241
- const endTz: string = eventData[fields.endTimezone] as string;
242
- eventData[fields.startTime] = this.parent.tzModule.convert(<Date>eventData[fields.startTime], this.parent.timezone, startTz);
243
- eventData[fields.endTime] = this.parent.tzModule.convert(<Date>eventData[fields.endTime], this.parent.timezone, endTz);
244
- }
245
- }
246
-
247
- private processTimezoneChange(event: Record<string, any>, oldTimezone: string): void {
248
- const fields: EventFieldsMapping = this.parent.eventFields;
249
- if (event[fields.isAllDay]) {
250
- return;
251
- }
252
- if (oldTimezone && this.parent.timezone) {
253
- event[fields.startTime] = this.parent.tzModule.convert(<Date>event[fields.startTime], oldTimezone, this.parent.timezone);
254
- event[fields.endTime] = this.parent.tzModule.convert(<Date>event[fields.endTime], oldTimezone, this.parent.timezone);
255
- } else if (!oldTimezone && this.parent.timezone) {
256
- event[fields.startTime] = this.parent.tzModule.add(<Date>event[fields.startTime], this.parent.timezone);
257
- event[fields.endTime] = this.parent.tzModule.add(<Date>event[fields.endTime], this.parent.timezone);
258
- } else if (oldTimezone && !this.parent.timezone) {
259
- event[fields.startTime] = this.parent.tzModule.remove(<Date>event[fields.startTime], oldTimezone);
260
- event[fields.endTime] = this.parent.tzModule.remove(<Date>event[fields.endTime], oldTimezone);
261
- }
262
- }
263
-
264
- public processTimezone(event: Record<string, any>, isReverse: boolean = false): Record<string, any> {
265
- const fields: EventFieldsMapping = this.parent.eventFields;
266
- if (event[fields.isAllDay]) {
267
- return event;
268
- }
269
- if (event[fields.startTimezone] || event[fields.endTimezone]) {
270
- const startTimezone: string = <string>event[fields.startTimezone] || <string>event[fields.endTimezone];
271
- const endTimezone: string = <string>event[fields.endTimezone] || <string>event[fields.startTimezone];
272
- if (isReverse) {
273
- if (this.parent.timezone) {
274
- event[fields.startTime] = this.parent.tzModule.convert(<Date>event[fields.startTime], startTimezone, this.parent.timezone);
275
- event[fields.endTime] = this.parent.tzModule.convert(<Date>event[fields.endTime], endTimezone, this.parent.timezone);
276
- event[fields.startTime] = this.parent.tzModule.remove(<Date>event[fields.startTime], this.parent.timezone);
277
- event[fields.endTime] = this.parent.tzModule.remove(<Date>event[fields.endTime], this.parent.timezone);
278
- } else {
279
- event[fields.startTime] = this.parent.tzModule.remove(<Date>event[fields.startTime], startTimezone);
280
- event[fields.endTime] = this.parent.tzModule.remove(<Date>event[fields.endTime], endTimezone);
281
- }
282
- } else {
283
- event[fields.startTime] = this.parent.tzModule.add(<Date>event[fields.startTime], startTimezone);
284
- event[fields.endTime] = this.parent.tzModule.add(<Date>event[fields.endTime], endTimezone);
285
- if (this.parent.timezone) {
286
- event[fields.startTime] = this.parent.tzModule.convert(<Date>event[fields.startTime], startTimezone, this.parent.timezone);
287
- event[fields.endTime] = this.parent.tzModule.convert(<Date>event[fields.endTime], endTimezone, this.parent.timezone);
288
- }
289
- }
290
- } else if (this.parent.timezone) {
291
- if (isReverse) {
292
- event[fields.startTime] = this.parent.tzModule.remove(<Date>event[fields.startTime], this.parent.timezone);
293
- event[fields.endTime] = this.parent.tzModule.remove(<Date>event[fields.endTime], this.parent.timezone);
294
- } else {
295
- event[fields.startTime] = this.parent.tzModule.add(<Date>event[fields.startTime], this.parent.timezone);
296
- event[fields.endTime] = this.parent.tzModule.add(<Date>event[fields.endTime], this.parent.timezone);
297
- }
298
- }
299
- return event;
300
- }
301
-
302
- public filterBlockEvents(eventObj: Record<string, any>): Record<string, any>[] {
303
- const fields: EventFieldsMapping = this.parent.eventFields;
304
- const eStart: Date = eventObj[fields.startTime] as Date;
305
- const eEnd: Date = eventObj[fields.endTime] as Date;
306
- let resourceData: TdData;
307
- if (this.parent.activeViewOptions.group.resources.length > 0) {
308
- const data: number = this.getGroupIndexFromEvent(eventObj);
309
- resourceData = this.parent.resourceBase.lastResourceLevel[parseInt(data.toString(), 10)];
310
- }
311
- const blockEvents: Record<string, any>[] = <Record<string, any>[]>extend([], this.parent.blockProcessed, null, true);
312
- for (const eventObj of blockEvents) {
313
- if (eventObj[fields.isAllDay]) {
314
- const isDifferentTime: boolean = (eventObj[fields.endTime] as Date).getTime() >
315
- util.resetTime(new Date((eventObj[fields.endTime] as Date).getTime())).getTime();
316
- if (isDifferentTime) {
317
- eventObj[fields.startTime] = util.resetTime(eventObj[fields.startTime] as Date);
318
- eventObj[fields.endTime] = util.addDays(util.resetTime(eventObj[fields.endTime] as Date), 1);
319
- }
320
- }
321
- }
322
- return this.filterEvents(eStart, eEnd, blockEvents, resourceData);
323
- }
324
-
325
- public filterEvents(startDate: Date, endDate: Date, appointments: Record<string, any>[] = this.parent.eventsProcessed, resourceTdData?: TdData): Record<string, any>[] {
326
- const predicate: Predicate = this.parent.dataModule.getStartEndQuery(startDate, endDate);
327
- let filter: Record<string, any>[] = new DataManager({ json: appointments }).executeLocal(new Query().where(predicate)) as Record<string, any>[];
328
- if (resourceTdData) {
329
- filter = this.filterEventsByResource(resourceTdData, filter);
330
- }
331
- return this.sortByTime(filter);
332
- }
333
-
334
- public filterEventsByRange(eventCollection: Record<string, any>[], startDate?: Date, endDate?: Date): Record<string, any>[] {
335
- let filteredEvents: Record<string, any>[] = [];
336
- if (startDate && endDate) {
337
- filteredEvents = this.filterEvents(startDate, endDate, eventCollection);
338
- } else if (startDate && !endDate) {
339
- filteredEvents = eventCollection.filter((e: Record<string, any>) => e[this.parent.eventFields.startTime] >= startDate);
340
- } else if (!startDate && endDate) {
341
- filteredEvents = eventCollection.filter((e: Record<string, any>) => e[this.parent.eventFields.endTime] <= endDate);
342
- } else {
343
- filteredEvents = eventCollection;
344
- }
345
- return this.sortByTime(filteredEvents);
346
- }
347
-
348
- public filterEventsByResource(resourceTdData: TdData, appointments: Record<string, any>[] = this.parent.eventsProcessed): Record<string, any>[] {
349
- const predicate: Record<string, number | string> = {};
350
- const resourceCollection: ResourcesModel[] = this.parent.resourceBase.resourceCollection;
351
- for (let level: number = 0; level < resourceCollection.length; level++) {
352
- predicate[resourceCollection[parseInt(level.toString(), 10)].field] = resourceTdData.groupOrder[parseInt(level.toString(), 10)];
353
- }
354
- const keys: string[] = Object.keys(predicate);
355
- const filteredCollection: Record<string, any>[] = appointments.filter((eventObj: Record<string, any>) => keys.every((key: string) => {
356
- if (eventObj[`${key}`] instanceof Array) {
357
- return (<(string | number)[]>eventObj[`${key}`]).indexOf(predicate[`${key}`]) > -1;
358
- } else {
359
- return eventObj[`${key}`] === predicate[`${key}`];
360
- }
361
- }));
362
- return filteredCollection;
363
- }
364
-
365
- public sortByTime(appointmentsCollection: Record<string, any>[]): Record<string, any>[] {
366
- if (this.parent.eventSettings.sortComparer && (typeof(this.parent.eventSettings.sortComparer) === 'function' || typeof(this.parent.eventSettings.sortComparer) === 'string')) {
367
- appointmentsCollection = this.customSorting(appointmentsCollection);
368
- } else {
369
- const fieldMappings: EventFieldsMapping = this.parent.eventFields;
370
- appointmentsCollection.sort((a: Record<string, any>, b: Record<string, any>) => {
371
- const d1: Date = a[fieldMappings.startTime] as Date;
372
- const d2: Date = b[fieldMappings.startTime] as Date;
373
- return d1.getTime() - d2.getTime();
374
- });
375
- }
376
- return appointmentsCollection;
377
- }
378
-
379
- public sortByDateTime(appointments: Record<string, any>[]): Record<string, any>[] {
380
- if (this.parent.eventSettings.sortComparer && (typeof(this.parent.eventSettings.sortComparer) === 'function' || typeof(this.parent.eventSettings.sortComparer) === 'string')) {
381
- appointments = this.customSorting(appointments);
382
- } else {
383
- const fieldMapping: EventFieldsMapping = this.parent.eventFields;
384
- appointments.sort((object1: Record<string, any>, object2: Record<string, any>) => {
385
- const d3: Date = object1[fieldMapping.startTime] as Date;
386
- const d4: Date = object2[fieldMapping.startTime] as Date;
387
- const d5: Date = object1[fieldMapping.endTime] as Date;
388
- const d6: Date = object2[fieldMapping.endTime] as Date;
389
- const d1: number = d5.getTime() - d3.getTime();
390
- const d2: number = d6.getTime() - d4.getTime();
391
- return (d3.getTime() - d4.getTime() || d2 - d1);
392
- });
393
- }
394
- return appointments;
395
- }
396
-
397
- private customSorting(appointments: Record<string, any>[]): Record<string, any>[] {
398
- if (typeof(this.parent.eventSettings.sortComparer) === 'function') {
399
- return this.parent.eventSettings.sortComparer.call(this.parent, appointments);
400
- } else if (typeof(this.parent.eventSettings.sortComparer) === 'string') {
401
- const splits: string[] = (this.parent.eventSettings.sortComparer as string).split('.');
402
- let sortFn: Function;
403
- if (!isNullOrUndefined(window)) {
404
- sortFn = (window as Record<string, any>)[splits[splits.length - 1]];
405
- }
406
- if (sortFn) {
407
- return sortFn(appointments);
408
- }
409
- }
410
- return appointments;
411
- }
412
-
413
- public getSmallestMissingNumber(array: number[]): number {
414
- const large: number = Math.max(...array);
415
- for (let i: number = 0; i < large; i++) {
416
- if (array.indexOf(i) === -1) { return i; }
417
- }
418
- return large + 1;
419
- }
420
-
421
- public splitEventByDay(event: Record<string, any>): Record<string, any>[] {
422
- const eventFields: EventFieldsMapping = this.parent.eventFields;
423
- const data: Record<string, any>[] = [];
424
- const eventStartTime: Date = event[eventFields.startTime] as Date;
425
- const eventEndTime: Date = event[eventFields.endTime] as Date;
426
- const isDifferentDate: boolean = util.resetTime(new Date(eventStartTime.getTime())) <
427
- util.resetTime(new Date(eventEndTime.getTime()));
428
- if (isDifferentDate) {
429
- let start: Date = new Date(eventStartTime.getTime());
430
- let end: Date = util.addDays(util.resetTime(new Date(eventStartTime.getTime())), 1);
431
- const endDate: Date = (eventEndTime.getHours() === 0 && eventEndTime.getMinutes() === 0) ?
432
- eventEndTime : util.addDays(eventEndTime, 1);
433
- let index: number = 1;
434
- const eventLength: number = util.getDaysCount(eventStartTime.getTime(), endDate.getTime());
435
- while (end <= eventEndTime && start.getTime() !== end.getTime()) {
436
- const app: Record<string, any> = <Record<string, any>>extend({}, event);
437
- app[eventFields.startTime] = start;
438
- app[eventFields.endTime] = end;
439
- app.data = { index: index, count: eventLength };
440
- app.Guid = this.generateGuid();
441
- app.isSpanned = true;
442
- data.push(app);
443
- start = end;
444
- if ((util.resetTime(new Date(start.getTime())).getTime() === util.resetTime(new Date(eventEndTime.getTime())).getTime())
445
- && !(end.getTime() === eventEndTime.getTime())) {
446
- end = new Date(start.getTime());
447
- end = new Date(end.setHours(eventEndTime.getHours(), eventEndTime.getMinutes(), eventEndTime.getSeconds()));
448
- } else {
449
- end = util.addDays(util.resetTime(new Date(start.getTime())), 1);
450
- }
451
- index++;
452
- }
453
- } else {
454
- data.push(event);
455
- }
456
- return data;
457
- }
458
-
459
- public splitEvent(event: Record<string, any>, dateRender: Date[]): Record<string, any>[] {
460
- const fields: EventFieldsMapping = this.parent.eventFields;
461
- let start: number = util.resetTime(event[fields.startTime]).getTime();
462
- let end: number = util.resetTime(event[fields.endTime]).getTime();
463
- if (util.getDateInMs(event[fields.endTime] as Date) <= 0) {
464
- const temp: number = util.addDays(util.resetTime(event[fields.endTime]), -1).getTime();
465
- end = start > temp ? start : temp;
466
- }
467
- const orgStart: number = start;
468
- const orgEnd: number = end;
469
- const ranges: Record<string, any>[] = [];
470
- if (start !== end) {
471
- if (start < dateRender[0].getTime()) {
472
- start = dateRender[0].getTime();
473
- }
474
- if (end > dateRender[dateRender.length - 1].getTime()) {
475
- end = dateRender[dateRender.length - 1].getTime();
476
- }
477
- let cStart: number = start;
478
- for (let level: number = 0; level < this.slots.length; level++) {
479
- let slot: number[] = <[number]><unknown>this.slots[parseInt(level.toString(), 10)];
480
- if (this.parent.currentView === 'WorkWeek' || this.parent.currentView === 'TimelineWorkWeek'
481
- || this.parent.activeViewOptions.group.byDate || this.parent.activeViewOptions.showWeekend) {
482
- const slotDates: Date[] = [];
483
- for (const s of slot) {
484
- slotDates.push(new Date(s));
485
- }
486
- const renderedDates: Date[] = this.getRenderedDates(slotDates);
487
- if (!isNullOrUndefined(renderedDates) && renderedDates.length > 0) {
488
- slot = [];
489
- for (const date of renderedDates) {
490
- slot.push(date.getTime());
491
- }
492
- }
493
- }
494
- if (typeof (slot) === 'number') {
495
- const temp: number = slot;
496
- slot = [];
497
- slot.push(temp);
498
- }
499
- const firstSlot: number = <number>slot[0];
500
- cStart = (cStart <= firstSlot && end >= firstSlot) ? firstSlot : cStart;
501
- if (cStart > end || firstSlot > end) {
502
- break;
503
- }
504
- if (!this.parent.activeViewOptions.group.byDate && this.parent.activeViewOptions.showWeekend &&
505
- this.parent.currentView !== 'WorkWeek' && this.parent.currentView !== 'TimelineWorkWeek') {
506
- const startIndex: number = slot.indexOf(cStart);
507
- if (startIndex !== -1) {
508
- let endIndex: number = slot.indexOf(end);
509
- const hasBreak: boolean = endIndex !== -1;
510
- endIndex = hasBreak ? endIndex : slot.length - 1;
511
- const count: number = ((endIndex - startIndex) + 1);
512
- const isLeft: boolean = (slot[parseInt(startIndex.toString(), 10)] !== orgStart);
513
- const isRight: boolean = (slot[parseInt(endIndex.toString(), 10)] !== orgEnd);
514
- ranges.push(this.cloneEventObject(event, slot[parseInt(startIndex.toString(), 10)], slot[parseInt(endIndex.toString(), 10)], count, isLeft, isRight));
515
- if (hasBreak) {
516
- break;
517
- }
518
- }
519
- } else {
520
- if (this.dateInRange(cStart, slot[0], slot[slot.length - 1])) {
521
- const availSlot: number[] = [];
522
- for (let i: number = 0; i < slot.length; i++) {
523
- if (this.dateInRange(<number>slot[parseInt(i.toString(), 10)], orgStart, orgEnd)) {
524
- availSlot.push(slot[parseInt(i.toString(), 10)]);
525
- }
526
- }
527
- if (availSlot.length > 0) {
528
- if (!this.parent.activeViewOptions.group.byDate) {
529
- const isLeft: boolean = (availSlot[0] !== orgStart);
530
- const isRight: boolean = (availSlot[availSlot.length - 1] !== orgEnd);
531
- ranges.push(this.cloneEventObject(
532
- event, availSlot[0], availSlot[availSlot.length - 1], availSlot.length, isLeft, isRight));
533
- } else {
534
- for (const slot of availSlot) {
535
- ranges.push(this.cloneEventObject(event, slot, slot, 1, (slot !== orgStart), (slot !== orgEnd)));
536
- }
537
- }
538
- }
539
- }
540
- }
541
- }
542
- } else {
543
- ranges.push(this.cloneEventObject(event, start, end, 1, false, false));
544
- }
545
- return ranges;
546
- }
547
-
548
- public cloneEventObject(event: Record<string, any>, start: number, end: number, count: number, isLeft: boolean, isRight: boolean): Record<string, any> {
549
- const fields: EventFieldsMapping = this.parent.eventFields;
550
- const e: Record<string, any> = extend({}, event, null, true) as Record<string, any>;
551
- const data: Record<string, any> = { count: count, isLeft: isLeft, isRight: isRight };
552
- data[fields.startTime] = event[fields.startTime];
553
- data[fields.endTime] = event[fields.endTime];
554
- e.data = data;
555
- e[fields.startTime] = new Date(start);
556
- e[fields.endTime] = new Date(end);
557
- return e;
558
- }
559
-
560
- private dateInRange(date: number, start: number, end: number): boolean {
561
- return start <= date && date <= end;
562
- }
563
-
564
- public getSelectedEventElements(target: Element): Element[] {
565
- this.removeSelectedAppointmentClass();
566
- if (this.parent.selectedElements.length <= 0) {
567
- this.parent.selectedElements.push(target);
568
- } else {
569
- const isAlreadySelected: Element[] = this.parent.selectedElements.filter((element: HTMLElement) =>
570
- element.getAttribute('data-guid') === target.getAttribute('data-guid'));
571
- if (isAlreadySelected.length <= 0) {
572
- const elementSelector: string = 'div[data-guid="' + target.getAttribute('data-guid') + '"]';
573
- const focusElements: Element[] = [].slice.call(this.parent.element.querySelectorAll(elementSelector));
574
- for (const element of focusElements) {
575
- this.parent.selectedElements.push(element);
576
- }
577
- } else {
578
- const selectedElements: Element[] = this.parent.selectedElements.filter((element: HTMLElement) =>
579
- element.getAttribute('data-guid') !== target.getAttribute('data-guid'));
580
- this.parent.selectedElements = selectedElements;
581
- }
582
- }
583
- if (target && this.parent.selectedElements.length > 0) {
584
- this.addSelectedAppointments(this.parent.selectedElements, false);
585
- }
586
- return this.parent.selectedElements;
587
- }
588
-
589
- public getSelectedEvents(): EventClickArgs {
590
- const eventSelect: Record<string, any>[] = [];
591
- const elementSelect: HTMLElement[] = [];
592
- const selectAppointments: Element[] = [].slice.call(this.parent.element.querySelectorAll('.' + cls.APPOINTMENT_BORDER)) as Element[];
593
- selectAppointments.filter((element: HTMLElement) => {
594
- const isAlreadyAdded: Record<string, any>[] = eventSelect.filter((event: Record<string, any>) => {
595
- return event.Guid === element.getAttribute('data-guid');
596
- });
597
- if (isAlreadyAdded.length === 0) {
598
- eventSelect.push(this.getEventByGuid(element.getAttribute('data-guid')));
599
- }
600
- elementSelect.push(element);
601
- });
602
- return {
603
- event: eventSelect.length > 1 ? eventSelect : eventSelect[0],
604
- element: elementSelect.length > 1 ? elementSelect : elementSelect[0]
605
- } as EventClickArgs;
606
- }
607
-
608
- public removeSelectedAppointmentClass(): void {
609
- const selectedAppointments: Element[] = this.getSelectedAppointments();
610
- removeClass(selectedAppointments, cls.APPOINTMENT_BORDER);
611
- if (this.parent.currentView === 'Agenda' || this.parent.currentView === 'MonthAgenda') {
612
- removeClass(selectedAppointments, cls.AGENDA_SELECTED_CELL);
613
- }
614
- }
615
-
616
- public addSelectedAppointments(cells: Element[], preventFocus?: boolean): void {
617
- if (this.parent.currentView !== 'MonthAgenda') {
618
- this.parent.removeSelectedClass();
619
- }
620
- addClass(cells, cls.APPOINTMENT_BORDER);
621
- if (cells.length > 0 && !preventFocus) {
622
- (cells[cells.length - 1] as HTMLElement).focus();
623
- }
624
- }
625
-
626
- public getSelectedAppointments(): Element[] {
627
- return [].slice.call(this.parent.element.querySelectorAll('.' + cls.APPOINTMENT_BORDER + ',.' + cls.APPOINTMENT_CLASS + ':focus'));
628
- }
629
-
630
- public focusElement(isFocused?: boolean): void {
631
- if (this.parent.eventWindow.dialogObject && this.parent.eventWindow.dialogObject.visible) {
632
- return;
633
- }
634
- const activeEle: Element = document.activeElement;
635
- const selectedCell: Element[] = this.parent.getSelectedCells();
636
- if (selectedCell.length > 0 && ((activeEle && (this.parent.element.contains(activeEle) ||
637
- selectedCell.indexOf(activeEle) !== -1)) || isFocused)) {
638
- if (this.parent.keyboardInteractionModule) {
639
- const target: HTMLTableCellElement = ((!isNullOrUndefined(this.parent.activeCellsData) &&
640
- this.parent.activeCellsData.element) || selectedCell[selectedCell.length - 1]) as HTMLTableCellElement;
641
- this.parent.keyboardInteractionModule.selectCells(target instanceof Array, target);
642
- }
643
- return;
644
- }
645
- const selectedAppointments: Element[] = this.getSelectedAppointments();
646
- if (selectedAppointments.length > 0) {
647
- if (this.parent.activeEventData && this.parent.activeEventData.element && selectedAppointments.indexOf(this.parent.activeEventData.element as Element) > -1) {
648
- (this.parent.activeEventData.element as HTMLElement).focus();
649
- return;
650
- }
651
- (selectedAppointments[selectedAppointments.length - 1] as HTMLElement).focus();
652
- return;
653
- }
654
- }
655
-
656
- public selectWorkCellByTime(eventsData: Record<string, any>[]): Element {
657
- let target: Element;
658
- if (this.parent.currentView === 'Agenda' || this.parent.currentView === 'MonthAgenda') {
659
- return target;
660
- }
661
- if (eventsData.length > 0) {
662
- const selectedObject: Record<string, any> = eventsData[eventsData.length - 1];
663
- const eventStartTime: Date = <Date>selectedObject[this.parent.eventFields.startTime];
664
- let nearestTime: number;
665
- const isAllDay: boolean = this.isAllDayAppointment(selectedObject);
666
- if (this.parent.currentView === 'Month' || isAllDay || !this.parent.activeViewOptions.timeScale.enable) {
667
- nearestTime = new Date(+eventStartTime).setHours(0, 0, 0, 0);
668
- }
669
- else {
670
- nearestTime = this.findNearestSlot(eventStartTime);
671
- }
672
- let targetArea: Element;
673
- if (isAllDay && ['Day', 'Week', 'WorkWeek'].indexOf(this.parent.currentView) !== -1) {
674
- targetArea = this.parent.getAllDayRow();
675
- } else {
676
- targetArea = this.parent.getContentTable();
677
- }
678
- let queryString: string = '[data-date="' + new Date(nearestTime).getTime() + '"]';
679
- if (!isNullOrUndefined(this.parent.activeViewOptions.group.resources) &&
680
- this.parent.activeViewOptions.group.resources.length > 0) {
681
- queryString += '[data-group-index="' + this.getGroupIndexFromEvent(selectedObject) + '"]';
682
- }
683
- target = targetArea.querySelector(queryString) as Element;
684
- if (target) {
685
- this.parent.activeCellsData = this.parent.getCellDetails(target);
686
- if (this.parent.keyboardInteractionModule) {
687
- this.parent.keyboardInteractionModule.selectCells(false, target as HTMLTableCellElement);
688
- }
689
- return target;
690
- }
691
- }
692
- return target;
693
- }
694
-
695
- private findNearestSlot(appointmentTime: Date): number {
696
- const msMajorInterval: number = this.parent.activeViewOptions.timeScale.interval * util.MS_PER_MINUTE;
697
- const msInterval: number = msMajorInterval / this.parent.activeViewOptions.timeScale.slotCount;
698
- const numberOfSlots: number = Math.round(util.MS_PER_DAY / msInterval);
699
- const startTime: Date = new Date(appointmentTime);
700
- startTime.setHours(0, 0, 0, 0);
701
- const slots: Date[] = Array.from({ length: numberOfSlots }, (_: any, i: number) => {
702
- const slotTime: Date = new Date(startTime.getTime() + i * msInterval);
703
- return slotTime;
704
- });
705
- const nearestSlot: Date = slots.reduce((nearest: Date, slot: Date) => {
706
- const difference: number = Math.abs(appointmentTime.getTime() - slot.getTime());
707
- if (!nearest || difference < Math.abs(appointmentTime.getTime() - nearest.getTime())) {
708
- return slot;
709
- }
710
- return nearest;
711
- }, null);
712
- return Math.trunc(nearestSlot.getTime() / 1000) * 1000;
713
- }
714
-
715
-
716
- public getGroupIndexFromEvent(eventData: Record<string, any>): number {
717
- let levelIndex: number;
718
- let resource: ResourcesModel;
719
- let levelName: string;
720
- let idField: string;
721
- for (let i: number = this.parent.resourceBase.resourceCollection.length - 1; i >= 0; i--) {
722
- const resourceData: Record<string, any> | string | number = eventData[this.parent.resourceBase.resourceCollection[parseInt(i.toString(), 10)].field] as Record<string, any>;
723
- if (!isNullOrUndefined(resourceData)) {
724
- resource = this.parent.resourceBase.resourceCollection[parseInt(i.toString(), 10)];
725
- levelIndex = i;
726
- levelName = resource.name;
727
- idField = resource.field;
728
- break;
729
- }
730
- }
731
- if (isNullOrUndefined(levelName) && isNullOrUndefined(levelIndex)) {
732
- levelName = this.parent.resourceCollection.slice(-1)[0].name;
733
- levelIndex = this.parent.resourceCollection.length - 1;
734
- idField = this.parent.resourceCollection.slice(-1)[0].field;
735
- resource = this.parent.resourceCollection.filter((e: ResourcesModel, index: number) => {
736
- if (e.name === levelName) {
737
- levelIndex = index;
738
- return e;
739
- }
740
- return null;
741
- })[0];
742
- }
743
- const id: number = ((eventData[`${idField}`] instanceof Array) ?
744
- (eventData[`${idField}`] as Record<string, any>)[0] : eventData[`${idField}`]) as number;
745
- if (levelIndex > 0) {
746
- const parentField: string = this.parent.resourceCollection[levelIndex - 1].field;
747
- return this.parent.resourceBase.getIndexFromResourceId(id, levelName, resource, eventData, parentField);
748
- } else {
749
- return this.parent.resourceBase.getIndexFromResourceId(id, levelName, resource);
750
- }
751
- }
752
-
753
- public isAllDayAppointment(event: Record<string, any>): boolean {
754
- const fieldMapping: EventFieldsMapping = this.parent.eventFields;
755
- const isAllDay: boolean = event[fieldMapping.isAllDay] as boolean;
756
- const isFullDay: boolean = ((util.getUniversalTime(<Date>event[fieldMapping.endTime]) - util.getUniversalTime(<Date>event[fieldMapping.startTime]))
757
- / util.MS_PER_DAY) >= 1;
758
- return (isAllDay || (this.parent.eventSettings.spannedEventPlacement !== 'TimeSlot' && isFullDay)) ? true : false;
759
- }
760
-
761
- public addEventListener(): void {
762
- this.parent.on(event.documentClick, this.appointmentBorderRemove, this);
763
- }
764
-
765
- public removeEventListener(): void {
766
- this.parent.off(event.documentClick, this.appointmentBorderRemove);
767
- }
768
-
769
- private appointmentBorderRemove(event: Event & CellClickEventArgs): void {
770
- const element: HTMLElement = event.event.target as HTMLElement;
771
- if (closest(element as Element, '.' + cls.APPOINTMENT_CLASS)) {
772
- if (this.parent.currentView !== 'MonthAgenda') {
773
- this.parent.removeSelectedClass();
774
- }
775
- } else if (!closest(element as Element, '.' + cls.POPUP_OPEN)) {
776
- if (this.parent.uiStateValues.isTapHold && closest(element, '.' + cls.WORK_CELLS_CLASS + ',.' + cls.ALLDAY_CELLS_CLASS)) {
777
- return;
778
- }
779
- this.parent.uiStateValues.isTapHold = false;
780
- this.removeSelectedAppointmentClass();
781
- this.parent.selectedElements = [];
782
- }
783
- }
784
-
785
- public wireAppointmentEvents(element: HTMLElement, event?: Record<string, any>, isPreventCrud: boolean = false): void {
786
- const isReadOnly: boolean = (!isNullOrUndefined(event)) ? event[this.parent.eventFields.isReadonly] as boolean : false;
787
- EventHandler.add(element, 'click', this.eventClick, this);
788
- if (!this.parent.isAdaptive && !this.parent.activeViewOptions.readonly && !isReadOnly) {
789
- EventHandler.add(element, 'touchend', this.eventTouchClick, this);
790
- EventHandler.add(element, 'dblclick', this.eventDoubleClick, this);
791
- }
792
- if (!this.parent.activeViewOptions.readonly && !isReadOnly && !isPreventCrud) {
793
- if (this.parent.resizeModule) {
794
- this.parent.resizeModule.wireResizeEvent(element);
795
- }
796
- if (this.parent.dragAndDropModule) {
797
- this.parent.dragAndDropModule.wireDragEvent(element);
798
- }
799
- }
800
- }
801
-
802
- private eventTouchClick(e: Event): void {
803
- if (this.parent.uiStateValues.isTouchScroll || this.parent.uiStateValues.isTapHold || this.parent.uiStateValues.action) {
804
- this.parent.uiStateValues.isTouchScroll = this.parent.uiStateValues.isTapHold = false;
805
- return;
806
- }
807
- setTimeout(() => this.isDoubleTapped = false, 250);
808
- e.preventDefault();
809
- if (this.isDoubleTapped) {
810
- this.eventDoubleClick(e);
811
- } else if (!this.isDoubleTapped) {
812
- this.isDoubleTapped = true;
813
- this.eventClick(e as Event & MouseEvent);
814
- }
815
- }
816
-
817
- public renderResizeHandler(element: HTMLElement, spanEvent: Record<string, any>, isReadOnly: boolean): void {
818
- if (!this.parent.resizeModule || !this.parent.allowResizing || this.parent.activeViewOptions.readonly || isReadOnly) {
819
- return;
820
- }
821
- for (const resizeEdge of Object.keys(spanEvent)) {
822
- const resizeHandler: HTMLElement = createElement('div', { className: cls.EVENT_RESIZE_CLASS });
823
- switch (resizeEdge) {
824
- case 'isLeft':
825
- if (!spanEvent.isLeft) {
826
- resizeHandler.appendChild(createElement('div', { className: 'e-left-right-resize' }));
827
- addClass([resizeHandler], this.parent.enableRtl ? cls.RIGHT_RESIZE_HANDLER : cls.LEFT_RESIZE_HANDLER);
828
- prepend([resizeHandler], element);
829
- }
830
- break;
831
- case 'isRight':
832
- if (!spanEvent.isRight) {
833
- resizeHandler.appendChild(createElement('div', { className: 'e-left-right-resize' }));
834
- addClass([resizeHandler], this.parent.enableRtl ? cls.LEFT_RESIZE_HANDLER : cls.RIGHT_RESIZE_HANDLER);
835
- append([resizeHandler], element);
836
- }
837
- break;
838
- case 'isTop':
839
- if (!spanEvent.isTop) {
840
- resizeHandler.appendChild(createElement('div', { className: 'e-top-bottom-resize' }));
841
- addClass([resizeHandler], cls.TOP_RESIZE_HANDLER);
842
- prepend([resizeHandler], element);
843
- }
844
- break;
845
- case 'isBottom':
846
- if (!spanEvent.isBottom) {
847
- resizeHandler.appendChild(createElement('div', { className: 'e-top-bottom-resize' }));
848
- addClass([resizeHandler], cls.BOTTOM_RESIZE_HANDLER);
849
- append([resizeHandler], element);
850
- }
851
- break;
852
- }
853
- }
854
- }
855
-
856
- private eventClick(eventData: Event & MouseEvent): void {
857
- const target: HTMLElement = eventData.target as HTMLElement;
858
- if (target.classList.contains(cls.DRAG_CLONE_CLASS) || target.classList.contains(cls.RESIZE_CLONE_CLASS) ||
859
- target.classList.contains(cls.INLINE_SUBJECT_CLASS)) {
860
- return;
861
- }
862
- if ((eventData.ctrlKey || eventData.metaKey) && eventData.which === 1 && this.parent.keyboardInteractionModule) {
863
- this.parent.quickPopup.quickPopup.hide();
864
- this.parent.selectedElements = [].slice.call(this.parent.element.querySelectorAll('.' + cls.APPOINTMENT_BORDER)) as Element[];
865
- const target: Element = closest(<Element>eventData.target, '.' + cls.APPOINTMENT_CLASS) as Element;
866
- this.getSelectedEventElements(target);
867
- this.activeEventData(eventData, false);
868
- const selectArgs: SelectEventArgs = {
869
- data: this.parent.activeEventData.event,
870
- element: this.parent.activeEventData.element,
871
- event: eventData, requestType: 'eventSelect'
872
- };
873
- this.parent.trigger(event.select, selectArgs);
874
- const args: EventClickArgs = <EventClickArgs>extend(this.parent.activeEventData, { cancel: false, originalEvent: eventData });
875
- this.parent.trigger(event.eventClick, args);
876
- } else {
877
- this.removeSelectedAppointmentClass();
878
- this.activeEventData(eventData, true);
879
- const selectEventArgs: SelectEventArgs = {
880
- data: this.parent.activeEventData.event,
881
- element: this.parent.activeEventData.element,
882
- event: eventData, requestType: 'eventSelect'
883
- };
884
- this.parent.trigger(event.select, selectEventArgs);
885
- const args: EventClickArgs = <EventClickArgs>extend(this.parent.activeEventData, { cancel: false, originalEvent: eventData });
886
- this.parent.trigger(event.eventClick, args, (eventClickArgs: EventClickArgs) => {
887
- if (eventClickArgs.cancel) {
888
- this.removeSelectedAppointmentClass();
889
- this.parent.selectedElements = [];
890
- if (this.parent.quickPopup) {
891
- this.parent.quickPopup.quickPopupHide();
892
- }
893
- } else {
894
- if (this.parent.currentView === 'Agenda' || this.parent.currentView === 'MonthAgenda') {
895
- addClass([this.parent.activeEventData.element as Element], cls.AGENDA_SELECTED_CELL);
896
- }
897
- if (this.parent.allowInline) {
898
- const inlineArgs: InlineClickArgs = {
899
- data: eventClickArgs.event as Record<string, any>,
900
- element: eventClickArgs.element as HTMLElement,
901
- type: 'Event'
902
- };
903
- this.parent.notify(event.inlineClick, inlineArgs);
904
- } else {
905
- this.parent.notify(event.eventClick, eventClickArgs);
906
- }
907
- }
908
- });
909
- }
910
- }
911
-
912
- private eventDoubleClick(eventData: Event): void {
913
- if (this.parent.quickPopup) {
914
- this.parent.quickPopup.quickPopupHide(true);
915
- }
916
- if (eventData.type === 'touchend') {
917
- this.activeEventData(eventData, true);
918
- }
919
- this.removeSelectedAppointmentClass();
920
- this.parent.selectedElements = [];
921
- if ((this.parent.activeEventData.element as HTMLElement).classList.contains(cls.INLINE_APPOINTMENT_CLASS) ||
922
- (this.parent.activeEventData.element as HTMLElement).querySelector('.' + cls.INLINE_SUBJECT_CLASS)) {
923
- return;
924
- }
925
- const args: EventClickArgs = <EventClickArgs>extend(this.parent.activeEventData, { cancel: false, originalEvent: eventData });
926
- this.parent.trigger(event.eventDoubleClick, args, (eventDoubleClickArgs: EventClickArgs) => {
927
- if (!eventDoubleClickArgs.cancel) {
928
- if (!isNullOrUndefined(this.parent.activeEventData.event) &&
929
- isNullOrUndefined((<Record<string, any>>this.parent.activeEventData.event)[this.parent.eventFields.recurrenceID])) {
930
- this.parent.eventWindow.openEditor(this.parent.activeEventData.event as Record<string, any>, 'Save');
931
- } else {
932
- this.parent.currentAction = 'EditOccurrence';
933
- this.parent.quickPopup.openRecurrenceAlert();
934
- }
935
- }
936
- });
937
- }
938
-
939
- public getEventByGuid(guid: string): Record<string, any> {
940
- return this.parent.eventsProcessed.filter((data: Record<string, any>) => data.Guid === guid)[0];
941
- }
942
-
943
- public getEventById(id: number | string): Record<string, any> {
944
- return this.parent.eventsData.filter((data: Record<string, any>) =>
945
- data[this.parent.eventFields.id] === id)[0] as Record<string, any>;
946
- }
947
-
948
- public generateGuid(): string {
949
- return 'xyxxxxyx-xxxy-yxxx-xyxx-xxyxxxxyyxxx'.replace(/[xy]/g, (c: string) => {
950
- const r: number = Math.random() * 16 | 0;
951
- const v: number = (c === 'x') ? r : (r & 0x3 | 0x8);
952
- return v.toString(16);
953
- });
954
- }
955
-
956
- public getEventIDType(): string {
957
- if (this.parent.eventsData.length !== 0) {
958
- return typeof ((<Record<string, any>>this.parent.eventsData[0])[this.parent.eventFields.id]);
959
- }
960
- if (this.parent.blockData.length !== 0) {
961
- return typeof ((<Record<string, any>>this.parent.blockData[0])[this.parent.eventFields.id]);
962
- }
963
- return 'string';
964
- }
965
-
966
- public getEventMaxID(resourceId?: number): number | string {
967
- if (this.parent.eventsData.length < 1 && this.parent.blockData.length < 1) {
968
- return 1;
969
- }
970
- let eventId: string | number;
971
- const idType: string = this.getEventIDType();
972
- if (idType === 'string') {
973
- eventId = this.generateGuid();
974
- }
975
- if (idType === 'number') {
976
- const datas: Record<string, number>[] = this.parent.eventsData.concat(this.parent.blockData) as Record<string, number>[];
977
- const appIds: number[] = datas.map((event: Record<string, number>) => event[this.parent.eventFields.id]);
978
- let maxId: number = appIds.reduce((a: number, b: number) => Math.max(a, b));
979
- maxId = isNullOrUndefined(resourceId) ? maxId : maxId + resourceId;
980
- eventId = maxId + 1;
981
- }
982
- return eventId;
983
- }
984
-
985
- private activeEventData(eventData: Event, isMultiple: boolean): void {
986
- const target: Element = closest(<Element>eventData.target, '.' + cls.APPOINTMENT_CLASS);
987
- const guid: string = target.getAttribute('data-guid');
988
- if (isMultiple) {
989
- this.addSelectedAppointments([].slice.call(this.parent.element.querySelectorAll('div[data-guid="' + guid + '"]')), true);
990
- (target as HTMLElement).focus();
991
- }
992
- let eventObject: Record<string, any> = this.getEventByGuid(guid);
993
- if (eventObject && eventObject.isSpanned) {
994
- eventObject = this.parent.eventsData.filter((obj: Record<string, any>) =>
995
- obj[this.parent.eventFields.id] === eventObject[this.parent.eventFields.id])[0];
996
- }
997
- this.parent.activeEventData = { event: eventObject, element: target } as EventClickArgs;
998
- }
999
-
1000
- public generateOccurrence(event: Record<string, any>, viewDate?: Date, isMaxCount?: boolean): Record<string, any>[] {
1001
- let startDate: Date = event[this.parent.eventFields.startTime] as Date;
1002
- const endDate: Date = event[this.parent.eventFields.endTime] as Date;
1003
- const eventRule: string = event[this.parent.eventFields.recurrenceRule] as string;
1004
- const timeZoneDiff: number = endDate.getTimezoneOffset() - startDate.getTimezoneOffset();
1005
- const duration: number = (endDate.getTime() - startDate.getTime()) - (timeZoneDiff * 60000);
1006
- viewDate = new Date((viewDate || this.parent.activeView.startDate()).getTime() - duration);
1007
- const exception: string = event[this.parent.eventFields.recurrenceException] as string;
1008
- let maxCount: number;
1009
- if (this.parent.currentView !== 'Agenda' && isMaxCount) {
1010
- maxCount = util.getDateCount(viewDate, this.parent.activeView.endDate()) + 1;
1011
- }
1012
- const newTimezone: string = this.parent.timezone || this.parent.tzModule.getLocalTimezoneName();
1013
- const firstDay: number = this.parent.activeViewOptions.firstDayOfWeek;
1014
- const calendarMode: CalendarType = this.parent.calendarMode;
1015
- if (event[this.parent.eventFields.recurrenceRule] && this.isDayBasedRecurrence(event) &&
1016
- this.parent.timezone && event[this.parent.eventFields.startTimezone] && event[this.parent.eventFields.endTimezone]) {
1017
- startDate = this.parent.tzModule.convert(event[this.parent.eventFields.startTime],
1018
- this.parent.timezone, event[this.parent.eventFields.startTimezone]);
1019
- }
1020
- const dates: number[] =
1021
- generate(startDate, eventRule, exception, firstDay, maxCount, viewDate, calendarMode, newTimezone);
1022
- if (this.parent.currentView === 'Agenda' && eventRule.indexOf('COUNT') === -1 && eventRule.indexOf('UNTIL') === -1) {
1023
- if (isNullOrUndefined(event.generatedDates)) {
1024
- event.generatedDates = { start: new Date(dates[0]), end: new Date(dates[dates.length - 1]) };
1025
- } else {
1026
- if (dates[0] < (<Date>(<Record<string, any>>event.generatedDates).start).getTime()) {
1027
- (<Record<string, any>>event.generatedDates).start = new Date(dates[0]);
1028
- }
1029
- if (dates[dates.length - 1] > (<Date>(<Record<string, any>>event.generatedDates).end).getTime()) {
1030
- (<Record<string, any>>event.generatedDates).end = new Date(dates[dates.length - 1]);
1031
- }
1032
- }
1033
- }
1034
- let isDSTAdjusted: boolean = false;
1035
- let convertedDates: number[] = [];
1036
- if (event[this.parent.eventFields.recurrenceRule] && this.isDayBasedRecurrence(event) &&
1037
- this.parent.timezone && event[this.parent.eventFields.startTimezone] && event[this.parent.eventFields.endTimezone]) {
1038
- isDSTAdjusted = true;
1039
- convertedDates.push(...dates.map((date: number) =>
1040
- this.parent.tzModule.convert(new Date(date), event[this.parent.eventFields.startTimezone], this.parent.timezone).getTime()
1041
- ));
1042
- }
1043
- convertedDates = convertedDates.length > 0 ? convertedDates : dates;
1044
- const occurrenceCollection: Record<string, any>[] = [];
1045
- for (let date of convertedDates) {
1046
- const clonedObject: Record<string, any> = extend({}, event, null, true) as Record<string, any>;
1047
- date = !isDSTAdjusted ? this.getDSTAdjustedTime(date, clonedObject) : date;
1048
- clonedObject[this.parent.eventFields.startTime] = new Date(date);
1049
- clonedObject[this.parent.eventFields.endTime] = new Date(new Date(date).setMilliseconds(duration));
1050
- clonedObject[this.parent.eventFields.recurrenceID] = clonedObject[this.parent.eventFields.id];
1051
- delete clonedObject[this.parent.eventFields.recurrenceException];
1052
- delete clonedObject[this.parent.eventFields.followingID];
1053
- clonedObject.Guid = this.generateGuid();
1054
- occurrenceCollection.push(clonedObject);
1055
- }
1056
- return occurrenceCollection;
1057
- }
1058
-
1059
- private isDayBasedRecurrence(event: Record<string, any>): boolean {
1060
- return (event[this.parent.eventFields.recurrenceRule].includes('BYMONTHDAY')
1061
- || event[this.parent.eventFields.recurrenceRule].includes('BYDAY'));
1062
- }
1063
-
1064
- private getDSTAdjustedTime(date: number, event: Record<string, any>): number {
1065
- let occurDate: number = date;
1066
- if (this.parent.timezone &&
1067
- (event[this.parent.eventFields.startTimezone] || event[this.parent.eventFields.endTimezone])) {
1068
- const eventOffset: number = this.getDSTDiff(event[this.parent.eventFields.startTime], new Date(date), event[this.parent.eventFields.startTimezone]);
1069
- const schOffset: number = this.getDSTDiff(event[this.parent.eventFields.startTime], new Date(date), this.parent.timezone);
1070
- occurDate = (new Date(date).getTime() - (eventOffset - schOffset) * 60000);
1071
- }
1072
- return occurDate;
1073
- }
1074
-
1075
- private getDSTDiff(startDate: Date, occurDate: Date, timezone: string): number {
1076
- const startOffset: number = this.parent.tzModule.offset(new Date(startDate), timezone);
1077
- const occurOffset: number = this.parent.tzModule.offset(new Date(occurDate), timezone);
1078
- return startOffset - occurOffset;
1079
- }
1080
-
1081
- public getParentEvent(eventObj: Record<string, any>, isParent: boolean = false): Record<string, any> {
1082
- let parentEvent: Record<string, any>;
1083
- do {
1084
- eventObj = this.getFollowingEvent(eventObj);
1085
- if (eventObj) {
1086
- parentEvent = extend({}, eventObj, null, true) as Record<string, any>;
1087
- }
1088
- } while (eventObj && isParent);
1089
- if (isParent && parentEvent) {
1090
- const collection: Record<string, unknown[]> = this.getEventCollections(parentEvent);
1091
- let followObj: Record<string, any> = collection.follow.slice(-1)[0] as Record<string, any>;
1092
- if (collection.occurrence.length > 0 && !parentEvent[this.parent.eventFields.recurrenceException]) {
1093
- followObj = collection.occurrence.slice(-1)[0] as Record<string, any>;
1094
- }
1095
- if (followObj) {
1096
- parentEvent[this.parent.eventFields.recurrenceRule] = followObj[this.parent.eventFields.recurrenceRule];
1097
- }
1098
- }
1099
- return parentEvent;
1100
- }
1101
-
1102
- public getEventCollections(parentObj: Record<string, any>, childObj?: Record<string, any>): { [key: string]: Record<string, any>[] } {
1103
- const followingCollection: Record<string, any>[] = [];
1104
- let occurrenceCollection: Record<string, any>[] = [];
1105
- let followingEvent: Record<string, any> = parentObj;
1106
- do {
1107
- followingEvent = this.getFollowingEvent(followingEvent, true);
1108
- if (followingEvent) {
1109
- followingCollection.push(followingEvent);
1110
- }
1111
- occurrenceCollection = occurrenceCollection.concat(this.getOccurrenceEvent(followingEvent || parentObj));
1112
- } while (followingEvent);
1113
- let collections: { [key: string]: Record<string, any>[] } = {};
1114
- if (childObj) {
1115
- const fields: EventFieldsMapping = this.parent.eventFields;
1116
- collections = {
1117
- follow: followingCollection.filter((eventData: Record<string, any>) =>
1118
- eventData[fields.startTime] >= childObj[fields.startTime]),
1119
- occurrence: occurrenceCollection.filter((eventData: Record<string, any>) =>
1120
- eventData[fields.startTime] >= childObj[fields.startTime])
1121
- };
1122
- } else {
1123
- collections = { follow: followingCollection, occurrence: occurrenceCollection };
1124
- }
1125
- return collections;
1126
- }
1127
-
1128
- public getFollowingEvent(parentObj: Record<string, any>, isReverse?: boolean): Record<string, any> {
1129
- const fields: EventFieldsMapping = this.parent.eventFields;
1130
- let fieldValue: string | number;
1131
- if (isReverse) {
1132
- fieldValue = parentObj[fields.id] as string | number;
1133
- } else {
1134
- fieldValue = (parentObj[fields.recurrenceID] || parentObj[fields.followingID]) as string | number;
1135
- }
1136
- const parentApp: Record<string, any>[] = this.parent.eventsData.filter((data: Record<string, any>) =>
1137
- data[isReverse ? fields.followingID : fields.id] === fieldValue);
1138
- return parentApp.shift() as Record<string, any>;
1139
- }
1140
-
1141
- public isFollowingEvent(parentObj: Record<string, any>, childObj: Record<string, any>): boolean {
1142
- const parentStart: Date = parentObj[this.parent.eventFields.startTime] as Date;
1143
- const childStart: Date = childObj[this.parent.eventFields.startTime] as Date;
1144
- return parentStart.getHours() === childStart.getHours() && parentStart.getMinutes() === childStart.getMinutes() &&
1145
- parentStart.getSeconds() === childStart.getSeconds();
1146
- }
1147
-
1148
- public getOccurrenceEvent(eventObj: Record<string, any>, isGuid: boolean = false, isFollowing: boolean = false): Record<string, any>[] {
1149
- const idField: string = isGuid ? 'Guid' : (isFollowing) ? this.parent.eventFields.followingID : this.parent.eventFields.recurrenceID;
1150
- const fieldKey: string = isGuid ? 'Guid' : this.parent.eventFields.id;
1151
- const dataSource: Record<string, any>[] = isGuid ? this.parent.eventsProcessed : this.parent.eventsData;
1152
- return dataSource.filter((data: Record<string, any>) => data[`${idField}`] === eventObj[`${fieldKey}`]);
1153
- }
1154
-
1155
- public getOccurrencesByID(id: number | string): Record<string, any>[] {
1156
- const fields: EventFieldsMapping = this.parent.eventFields;
1157
- let occurrenceCollection: Record<string, any>[] = [];
1158
- const parentObject: Record<string, any>[] = this.parent.eventsData.filter((obj: Record<string, any>) => obj[fields.id] === id);
1159
- for (const event of parentObject) {
1160
- if (!isNullOrUndefined(event[fields.recurrenceRule])) {
1161
- occurrenceCollection = occurrenceCollection.concat(this.generateOccurrence(event));
1162
- }
1163
- }
1164
- return occurrenceCollection;
1165
- }
1166
-
1167
- public getOccurrencesByRange(startTime: Date, endTime: Date): Record<string, any>[] {
1168
- const fields: EventFieldsMapping = this.parent.eventFields;
1169
- let occurrenceCollection: Record<string, any>[] = [];
1170
- for (const event of this.parent.eventsData) {
1171
- if (!isNullOrUndefined(event[fields.recurrenceRule])) {
1172
- occurrenceCollection = occurrenceCollection.concat(this.generateOccurrence(event));
1173
- }
1174
- }
1175
- const filter: Record<string, any>[] = occurrenceCollection.filter((obj: Record<string, any>) =>
1176
- obj[fields.startTime] >= startTime && obj[fields.endTime] <= endTime && !isNullOrUndefined(obj[fields.recurrenceID]));
1177
- return filter;
1178
- }
1179
-
1180
- public getDeletedOccurrences(recurrenceData: string | number | Record<string, any>): Record<string, any>[] {
1181
- const fields: EventFieldsMapping = this.parent.eventFields;
1182
- let parentObject: Record<string, any>;
1183
- const deletedOccurrences: Record<string, any>[] = [];
1184
- if (typeof recurrenceData === 'string' || typeof recurrenceData === 'number') {
1185
- parentObject = this.parent.eventsData.filter((obj: Record<string, any>) =>
1186
- obj[fields.id] === recurrenceData)[0] as Record<string, any>;
1187
- } else {
1188
- parentObject = extend({}, recurrenceData, null, true) as Record<string, any>;
1189
- }
1190
- if (parentObject[fields.recurrenceException]) {
1191
- const exDateString: string[] = (<string>parentObject[fields.recurrenceException]).split(',');
1192
- for (let i: number = 0, len: number = exDateString.length; i < len; i++) {
1193
- const edited: Record<string, any>[] = this.parent.eventsData.filter((eventObj: Record<string, any>) =>
1194
- eventObj[fields.recurrenceID] === parentObject[fields.id] && eventObj[fields.recurrenceException] === exDateString[parseInt(i.toString(), 10)]);
1195
- if (edited.length === 0) {
1196
- const exDate: Date = getDateFromRecurrenceDateString(exDateString[parseInt(i.toString(), 10)]);
1197
- const childObject: Record<string, any> = extend({}, recurrenceData, null, true) as Record<string, any>;
1198
- childObject[fields.recurrenceID] = parentObject[fields.id];
1199
- delete childObject[fields.followingID];
1200
- childObject[fields.recurrenceException] = exDateString[parseInt(i.toString(), 10)];
1201
- const startDate: Date = new Date(exDate.getTime());
1202
- const time: number = (<Date>parentObject[fields.endTime]).getTime() - (<Date>parentObject[fields.startTime]).getTime();
1203
- const endDate: Date = new Date(startDate.getTime());
1204
- endDate.setMilliseconds(time);
1205
- childObject[fields.startTime] = new Date(startDate.getTime());
1206
- childObject[fields.endTime] = new Date(endDate.getTime());
1207
- deletedOccurrences.push(childObject);
1208
- }
1209
- }
1210
- }
1211
- return deletedOccurrences;
1212
- }
1213
-
1214
- public applyResourceColor(element: HTMLElement, data: Record<string, any>, type: string, index?: string[], alpha?: string): void {
1215
- if (!this.parent.resourceBase) {
1216
- return;
1217
- }
1218
- const alphaColor: CallbackFunction = (color: string, alpha: string): string => {
1219
- color = color.replace('#', '');
1220
- const r: number = parseInt(color.substring(0, color.length / 3), 16);
1221
- const g: number = parseInt(color.substring(color.length / 3, 2 * color.length / 3), 16);
1222
- const b: number = parseInt(color.substring(2 * color.length / 3, 3 * color.length / 3), 16);
1223
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
1224
- };
1225
- // index refers groupOrder
1226
- const color: string = this.parent.resourceBase.getResourceColor(data, index);
1227
- if (color) {
1228
- element.style[<any>type] = !isNullOrUndefined(alpha) ? alphaColor(color, alpha) : color;
1229
- }
1230
- }
1231
-
1232
- public createBlockAppointmentElement(record: Record<string, any>, resIndex: number, isResourceEventTemplate: boolean): HTMLElement {
1233
- const eventSubject: string = (record[this.parent.eventFields.subject] || this.parent.eventSettings.fields.subject.default) as string;
1234
- const appointmentWrapper: HTMLElement = createElement('div', {
1235
- className: cls.BLOCK_APPOINTMENT_CLASS,
1236
- attrs: {
1237
- 'data-id': 'Appointment_' + record[this.parent.eventFields.id],
1238
- 'aria-disabled': 'true'
1239
- }
1240
- });
1241
- let templateElement: HTMLElement[];
1242
- if (!isNullOrUndefined(this.parent.activeViewOptions.eventTemplate)) {
1243
- const scheduleId: string = this.parent.element.id + '_';
1244
- const viewName: string = this.parent.activeViewOptions.eventTemplateName;
1245
- const templateId: string = scheduleId + viewName + 'eventTemplate';
1246
- const templateName: string = isResourceEventTemplate && this.parent.currentView.indexOf('Year') === -1 ?
1247
- this.parent.getEventTemplateName(resIndex) : 'eventTemplate';
1248
- templateElement = this.parent.getAppointmentTemplate()(record, this.parent, templateName, templateId, false,
1249
- undefined, undefined, this.parent.root);
1250
- } else {
1251
- const appointmentSubject: HTMLElement = createElement('div', { className: cls.SUBJECT_CLASS });
1252
- this.parent.sanitize(eventSubject, appointmentSubject);
1253
- templateElement = [appointmentSubject];
1254
- }
1255
- append(templateElement, appointmentWrapper);
1256
- this.setWrapperAttributes(appointmentWrapper, resIndex);
1257
- return appointmentWrapper;
1258
- }
1259
-
1260
- public setWrapperAttributes(appointmentWrapper: HTMLElement, resIndex: number): void {
1261
- if (!isNullOrUndefined(this.cssClass)) {
1262
- addClass([appointmentWrapper], this.cssClass);
1263
- }
1264
- if (this.parent.activeViewOptions.group.resources.length > 0) {
1265
- appointmentWrapper.setAttribute('data-group-index', resIndex.toString());
1266
- }
1267
- }
1268
-
1269
- public getReadonlyAttribute(event: Record<string, any>): string {
1270
- return (event[this.parent.eventFields.isReadonly] || this.parent.readonly).toString();
1271
- }
1272
-
1273
- public isBlockRange(eventData: Record<string, any> | Record<string, any>[]): boolean {
1274
- const eventCollection: Record<string, any>[] = (eventData instanceof Array) ? eventData : [eventData];
1275
- let isBlockAlert: boolean = false;
1276
- const fields: EventFieldsMapping = this.parent.eventFields;
1277
- for (const event of eventCollection as Record<string, any>[]) {
1278
- let dataCol: Record<string, any>[] = [];
1279
- if (!isNullOrUndefined(event[fields.recurrenceRule]) &&
1280
- (isNullOrUndefined(event[fields.recurrenceID]) || event[fields.id] === event[fields.recurrenceID])) {
1281
- dataCol = this.generateOccurrence(event);
1282
- } else {
1283
- dataCol.push(event);
1284
- }
1285
- for (const data of dataCol) {
1286
- const filterBlockEvents: Record<string, any>[] = this.filterBlockEvents(data);
1287
- if (filterBlockEvents.length > 0) {
1288
- isBlockAlert = true;
1289
- break;
1290
- }
1291
- }
1292
- }
1293
- this.parent.uiStateValues.isBlock = isBlockAlert;
1294
- return isBlockAlert;
1295
- }
1296
-
1297
- public getFilterEventsList(dataSource: Record<string, any>[], query: Predicate): Record<string, any>[] {
1298
- return new DataManager(dataSource).executeLocal(new Query().where(query)) as Record<string, any>[];
1299
- }
1300
-
1301
- public getSeriesEvents(parentEvent: Record<string, any>, startTime?: string): Record<string, any>[] {
1302
- const fields: EventFieldsMapping = this.parent.eventFields;
1303
- startTime = isNullOrUndefined(startTime) ? parentEvent[fields.startTime] as string : startTime;
1304
- let deleteFutureEditEvents: Record<string, any>;
1305
- let futureEvents: Record<string, any>[];
1306
- const deleteFutureEditEventList: Record<string, any>[] = [];
1307
- let delId: string = parentEvent[fields.id] as string;
1308
- let followingId: string = parentEvent[fields.followingID] as string;
1309
- let deleteFutureEvent: Predicate;
1310
- const startTimeQuery: string = this.parent.currentAction === 'EditSeries' ? 'greaterthan' : 'greaterthanorequal';
1311
- do {
1312
- deleteFutureEvent = ((new Predicate(fields.followingID, 'equal', delId))).
1313
- and(new Predicate(fields.startTime, startTimeQuery, startTime));
1314
- futureEvents = this.getFilterEventsList(this.parent.eventsData, deleteFutureEvent);
1315
- deleteFutureEditEvents = futureEvents.slice(-1)[0];
1316
- if (!isNullOrUndefined(deleteFutureEditEvents) && deleteFutureEditEvents[fields.id] !== followingId) {
1317
- deleteFutureEditEventList.push(deleteFutureEditEvents);
1318
- delId = deleteFutureEditEvents[fields.id] as string;
1319
- followingId = deleteFutureEditEvents[fields.followingID] as string;
1320
- } else { followingId = null; }
1321
- } while (futureEvents.length === 1 && !isNullOrUndefined(deleteFutureEditEvents[fields.followingID]));
1322
- return deleteFutureEditEventList;
1323
- }
1324
-
1325
- public getEditedOccurrences(deleteFutureEditEventList: Record<string, any>[], startTime?: string): Record<string, any>[] {
1326
- const fields: EventFieldsMapping = this.parent.eventFields;
1327
- let deleteRecurrenceEventList: Record<string, any>[] = [];
1328
- let delEditedEvents: Record<string, any>[];
1329
- for (const event of deleteFutureEditEventList) {
1330
- let delEventQuery: Predicate = new Predicate(fields.recurrenceID, 'equal', event[fields.id] as string).
1331
- or(new Predicate(fields.recurrenceID, 'equal', event[fields.followingID] as string).
1332
- and(new Predicate(fields.recurrenceID, 'notequal', undefined)).
1333
- and(new Predicate(fields.recurrenceID, 'notequal', null)));
1334
- if (this.parent.currentAction === 'EditFollowingEvents' || this.parent.currentAction === 'DeleteFollowingEvents') {
1335
- delEventQuery = delEventQuery.and(new Predicate(fields.startTime, 'greaterthanorequal', startTime));
1336
- }
1337
- delEditedEvents = this.getFilterEventsList(this.parent.eventsData, delEventQuery);
1338
- deleteRecurrenceEventList = deleteRecurrenceEventList.concat(delEditedEvents);
1339
- }
1340
- return deleteRecurrenceEventList;
1341
- }
1342
-
1343
- public getRenderedDates(dateRender: Date[]): Date[] {
1344
- let firstDate: number = 0;
1345
- let lastDate: number = dateRender.length;
1346
- let filteredDates: Date[];
1347
- const maxDate: Date = isNullOrUndefined(this.parent.maxDate) ? new Date(2099, 11, 31) : this.parent.maxDate;
1348
- const minDate: Date = isNullOrUndefined(this.parent.minDate) ? new Date(1900, 0, 1) : this.parent.minDate;
1349
- if (dateRender[0] < minDate && dateRender[dateRender.length - 1] > maxDate) {
1350
- for (let i: number = 0; i < dateRender.length; i++) {
1351
- if (util.resetTime(dateRender[parseInt(i.toString(), 10)]).getTime() === util.resetTime(new Date(minDate)).getTime()) {
1352
- firstDate = i;
1353
- }
1354
- if (util.resetTime(dateRender[parseInt(i.toString(), 10)]).getTime() === util.resetTime(new Date(maxDate)).getTime()) {
1355
- lastDate = i;
1356
- }
1357
- }
1358
- filteredDates = dateRender.filter((date: Date) => date >= dateRender[parseInt(firstDate.toString(), 10)] && date <= dateRender[parseInt(lastDate.toString(), 10)]);
1359
- }
1360
- return filteredDates;
1361
- }
1362
-
1363
- public isValidEvent(eventObj: Record<string, any>, start: Date, end: Date, schedule: { [key: string]: Date }): boolean {
1364
- const isHourRange: boolean = end.getTime() > schedule.startHour.getTime() && start.getTime() < schedule.endHour.getTime();
1365
- const isSameRange: boolean = schedule.startHour.getTime() <= start.getTime() &&
1366
- (<Date>eventObj[this.parent.eventFields.startTime]).getTime() >= schedule.startHour.getTime() &&
1367
- (<Date>eventObj[this.parent.eventFields.endTime]).getTime() < schedule.endHour.getTime() && start.getTime() === end.getTime();
1368
- return isHourRange || isSameRange;
1369
- }
1370
-
1371
- public allDayExpandScroll(dateHeader: HTMLElement): void {
1372
- let indentHeight: number = 0;
1373
- const headerRows: HTMLElement[] = [].slice.call(this.parent.element.querySelectorAll('.' + cls.HEADER_ROW_CLASS));
1374
- headerRows.forEach((element: HTMLElement) => {
1375
- const tdEle: HTMLElement[] = [].slice.call(element.children);
1376
- tdEle.forEach((ele: HTMLElement) => { ele.style.top = indentHeight + 'px'; });
1377
- indentHeight += element.offsetHeight;
1378
- });
1379
- indentHeight = dateHeader.offsetHeight - indentHeight;
1380
- (this.parent.element.querySelector('.' + cls.ALLDAY_CELLS_CLASS) as HTMLElement).style.height = (indentHeight / 12) + 'em';
1381
- if (this.parent.uiStateValues.expand) {
1382
- addClass([dateHeader], cls.ALLDAY_APPOINTMENT_SCROLL);
1383
- } else {
1384
- removeClass([dateHeader], cls.ALLDAY_APPOINTMENT_SCROLL);
1385
- }
1386
- }
1387
-
1388
- public updateEventMinimumDuration(startEndHours: Record<string, Date>, startTime: Date, endTime: Date): Record<string, Date> {
1389
- if (startTime.getTime() < endTime.getTime() || (startTime.getTime() === endTime.getTime() &&
1390
- (startEndHours.startHour.getTime() < endTime.getTime() && startEndHours.endHour.getTime() > startTime.getTime()) &&
1391
- this.parent.currentView.indexOf('Timeline') === -1)) {
1392
- const eventDuration: number = (util.getUniversalTime(endTime) - util.getUniversalTime(startTime)) / util.MS_PER_MINUTE;
1393
- if (eventDuration < this.parent.eventSettings.minimumEventDuration) {
1394
- const tempEnd: Date = new Date(startTime);
1395
- tempEnd.setMinutes(tempEnd.getMinutes() + this.parent.eventSettings.minimumEventDuration);
1396
- endTime = tempEnd;
1397
- if (endTime.getTime() > startEndHours.endHour.getTime()) {
1398
- const tempStart: Date = new Date(startEndHours.endHour.getTime());
1399
- tempStart.setMinutes(tempStart.getMinutes() - this.parent.eventSettings.minimumEventDuration);
1400
- startTime = tempStart;
1401
- endTime = startEndHours.endHour;
1402
- }
1403
- }
1404
- }
1405
- return { startDate: startTime, endDate: endTime };
1406
- }
1407
-
1408
- public createEventWrapper(type: string = '', index: number = 0): HTMLElement {
1409
- const tr: HTMLElement = createElement('tr');
1410
- const levels: TdData[] = this.parent.activeView.colLevels.slice(-1)[0];
1411
- const className: string = (this.parent as any).isReact && this.parent.activeViewOptions.eventTemplate ?
1412
- ' ' + cls.APPOINTMENT_WRAPPER_HIDDEN_CLASS : '';
1413
- for (let i: number = 0, len: number = levels.length; i < len; i++) {
1414
- const col: TdData = levels[parseInt(i.toString(), 10)];
1415
- const appointmentWrap: HTMLElement = createElement('td', {
1416
- className: (type === 'allDay') ? cls.ALLDAY_APPOINTMENT_WRAPPER_CLASS + className : (type === 'timeIndicator') ?
1417
- cls.TIMELINE_WRAPPER_CLASS : cls.DAY_WRAPPER_CLASS + className, attrs: { 'data-date': col.date.getTime().toString() }
1418
- });
1419
- if (!isNullOrUndefined(col.groupIndex)) {
1420
- appointmentWrap.setAttribute('data-group-index', col.groupIndex.toString());
1421
- }
1422
- if (type === '') {
1423
- const innerWrapper: HTMLElement = createElement('div', {
1424
- id: cls.APPOINTMENT_WRAPPER_CLASS + '-' + index.toString(),
1425
- className: cls.APPOINTMENT_WRAPPER_CLASS
1426
- });
1427
- appointmentWrap.appendChild(innerWrapper);
1428
- }
1429
- tr.appendChild(appointmentWrap);
1430
- index = index + 1;
1431
- }
1432
- return tr;
1433
- }
1434
-
1435
- public getPageCoordinates(e: MouseEvent & TouchEvent): (MouseEvent & TouchEvent) | Touch {
1436
- if (isNullOrUndefined(e)) {
1437
- return e;
1438
- }
1439
- const eventArgs: TouchEvent = (e as Record<string, any> & MouseEvent & TouchEvent).event as TouchEvent;
1440
- return eventArgs && eventArgs.changedTouches ? eventArgs.changedTouches[0] : e.changedTouches ? e.changedTouches[0] :
1441
- (<MouseEvent & TouchEvent>eventArgs) || e;
1442
- }
1443
-
1444
- public renderSpannedIcon(element: HTMLElement, spanEvent: Record<string, any>): void {
1445
- const iconElement: HTMLElement = createElement('div', { className: cls.EVENT_INDICATOR_CLASS + ' ' + cls.ICON });
1446
- if (spanEvent.isLeft) {
1447
- const iconLeft: HTMLElement = iconElement.cloneNode() as HTMLElement;
1448
- addClass([iconLeft], cls.EVENT_ICON_LEFT_CLASS);
1449
- prepend([iconLeft], element);
1450
- }
1451
- if (spanEvent.isRight) {
1452
- const iconRight: HTMLElement = iconElement.cloneNode() as HTMLElement;
1453
- addClass([iconRight], cls.EVENT_ICON_RIGHT_CLASS);
1454
- append([iconRight], element);
1455
- }
1456
- if (spanEvent.isTop) {
1457
- const iconTop: HTMLElement = iconElement.cloneNode() as HTMLElement;
1458
- addClass([iconTop], cls.EVENT_ICON_UP_CLASS);
1459
- prepend([iconTop], element);
1460
- }
1461
- if (spanEvent.isBottom) {
1462
- const iconBottom: HTMLElement = iconElement.cloneNode() as HTMLElement;
1463
- addClass([iconBottom], cls.EVENT_ICON_DOWN_CLASS);
1464
- append([iconBottom], element);
1465
- }
1466
- }
1467
-
1468
- public addCellHeight(selector: string, eventHeight: number, eventGap: number, headerHeight: number, indHeight: number, isScrollUpdate: boolean = true): void {
1469
- if (this.parent.activeViewOptions.maxEventsPerRow && !this.parent.rowAutoHeight) {
1470
- const rows: HTMLElement[] = [].slice.call(this.parent.element.querySelectorAll(selector));
1471
- const weekNumberRows: HTMLElement[] = this.parent.showWeekNumber
1472
- ? [].slice.call(this.parent.element.querySelectorAll('.' + cls.WEEK_NUMBER_WRAPPER_CLASS + ' tbody tr'))
1473
- : [];
1474
- for (const row of rows) {
1475
- const height: number = (this.parent.activeViewOptions.maxEventsPerRow *
1476
- ((eventHeight + (this.parent.currentView === 'Month' ? eventGap : 2)))) + headerHeight + indHeight;
1477
- if (weekNumberRows.length > 0) {
1478
- setStyleAttribute(weekNumberRows[rows.indexOf(row)].firstElementChild as HTMLElement, { 'height': height + 'px' });
1479
- }
1480
- setStyleAttribute(row.firstElementChild as HTMLElement, { 'height': height + 'px' });
1481
- }
1482
-
1483
- if (!this.parent.enablePersistence && !this.parent.activeViewOptions.allowVirtualScrolling && isScrollUpdate) {
1484
- this.parent.notify(event.contentReady, {});
1485
- }
1486
- }
1487
- }
1488
-
1489
- private unWireEvents(): void {
1490
- const appElements: Element[] = [].slice.call(this.parent.element.querySelectorAll('.' + cls.APPOINTMENT_CLASS));
1491
- for (const element of appElements) {
1492
- EventHandler.clearEvents(element);
1493
- }
1494
- }
1495
-
1496
- public destroy(): void {
1497
- this.unWireEvents();
1498
- this.parent = null;
1499
- }
1500
-
1501
- }