@syncfusion/ej2-schedule 30.2.7 → 31.1.17

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 (304) 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 +110 -36
  5. package/dist/es6/ej2-schedule.es2015.js.map +1 -1
  6. package/dist/es6/ej2-schedule.es5.js +110 -36
  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/dist/ts/common/calendar-util.d.ts +92 -0
  12. package/dist/ts/common/calendar-util.ts +261 -0
  13. package/dist/ts/common/index.d.ts +4 -0
  14. package/dist/ts/common/index.ts +4 -0
  15. package/dist/ts/components.d.ts +5 -0
  16. package/dist/ts/components.ts +5 -0
  17. package/dist/ts/index.d.ts +6 -0
  18. package/dist/ts/index.ts +7 -0
  19. package/dist/ts/recurrence-editor/date-generator.d.ts +76 -0
  20. package/dist/ts/recurrence-editor/date-generator.ts +1699 -0
  21. package/dist/ts/recurrence-editor/index.d.ts +6 -0
  22. package/dist/ts/recurrence-editor/index.ts +6 -0
  23. package/dist/ts/recurrence-editor/recurrence-editor-model.d.ts +112 -0
  24. package/dist/ts/recurrence-editor/recurrence-editor.d.ts +245 -0
  25. package/dist/ts/recurrence-editor/recurrence-editor.ts +1257 -0
  26. package/dist/ts/schedule/actions/action-base.d.ts +44 -0
  27. package/dist/ts/schedule/actions/action-base.ts +493 -0
  28. package/dist/ts/schedule/actions/crud.d.ts +41 -0
  29. package/dist/ts/schedule/actions/crud.ts +784 -0
  30. package/dist/ts/schedule/actions/data.d.ts +63 -0
  31. package/dist/ts/schedule/actions/data.ts +128 -0
  32. package/dist/ts/schedule/actions/drag.d.ts +75 -0
  33. package/dist/ts/schedule/actions/drag.ts +1401 -0
  34. package/dist/ts/schedule/actions/keyboard.d.ts +100 -0
  35. package/dist/ts/schedule/actions/keyboard.ts +1435 -0
  36. package/dist/ts/schedule/actions/resize.d.ts +27 -0
  37. package/dist/ts/schedule/actions/resize.ts +602 -0
  38. package/dist/ts/schedule/actions/scroll.d.ts +69 -0
  39. package/dist/ts/schedule/actions/scroll.ts +105 -0
  40. package/dist/ts/schedule/actions/touch.d.ts +32 -0
  41. package/dist/ts/schedule/actions/touch.ts +314 -0
  42. package/dist/ts/schedule/actions/virtual-scroll.d.ts +55 -0
  43. package/dist/ts/schedule/actions/virtual-scroll.ts +596 -0
  44. package/dist/ts/schedule/actions/work-cells.d.ts +14 -0
  45. package/dist/ts/schedule/actions/work-cells.ts +151 -0
  46. package/dist/ts/schedule/base/constant.d.ts +102 -0
  47. package/dist/ts/schedule/base/constant.ts +103 -0
  48. package/dist/ts/schedule/base/css-constant.d.ts +475 -0
  49. package/dist/ts/schedule/base/css-constant.ts +475 -0
  50. package/dist/ts/schedule/base/interface.d.ts +673 -0
  51. package/dist/ts/schedule/base/interface.ts +738 -0
  52. package/dist/ts/schedule/base/resource.d.ts +59 -0
  53. package/dist/ts/schedule/base/resource.ts +1091 -0
  54. package/dist/ts/schedule/base/schedule-model.d.ts +930 -0
  55. package/dist/ts/schedule/base/schedule.d.ts +1967 -0
  56. package/dist/ts/schedule/base/schedule.ts +4221 -0
  57. package/dist/ts/schedule/base/type.d.ts +134 -0
  58. package/dist/ts/schedule/base/type.ts +142 -0
  59. package/dist/ts/schedule/base/util.d.ts +266 -0
  60. package/dist/ts/schedule/base/util.ts +492 -0
  61. package/dist/ts/schedule/event-renderer/agenda-base.d.ts +15 -0
  62. package/dist/ts/schedule/event-renderer/agenda-base.ts +423 -0
  63. package/dist/ts/schedule/event-renderer/event-base.d.ts +101 -0
  64. package/dist/ts/schedule/event-renderer/event-base.ts +1501 -0
  65. package/dist/ts/schedule/event-renderer/inline-edit.d.ts +23 -0
  66. package/dist/ts/schedule/event-renderer/inline-edit.ts +287 -0
  67. package/dist/ts/schedule/event-renderer/month.d.ts +60 -0
  68. package/dist/ts/schedule/event-renderer/month.ts +760 -0
  69. package/dist/ts/schedule/event-renderer/timeline-view.d.ts +51 -0
  70. package/dist/ts/schedule/event-renderer/timeline-view.ts +606 -0
  71. package/dist/ts/schedule/event-renderer/vertical-view.d.ts +57 -0
  72. package/dist/ts/schedule/event-renderer/vertical-view.ts +898 -0
  73. package/dist/ts/schedule/event-renderer/year.d.ts +27 -0
  74. package/dist/ts/schedule/event-renderer/year.ts +623 -0
  75. package/dist/ts/schedule/exports/calendar-export.d.ts +16 -0
  76. package/dist/ts/schedule/exports/calendar-export.ts +160 -0
  77. package/dist/ts/schedule/exports/calendar-import.d.ts +18 -0
  78. package/dist/ts/schedule/exports/calendar-import.ts +277 -0
  79. package/dist/ts/schedule/exports/excel-export.d.ts +14 -0
  80. package/dist/ts/schedule/exports/excel-export.ts +89 -0
  81. package/dist/ts/schedule/exports/index.d.ts +7 -0
  82. package/dist/ts/schedule/exports/index.ts +7 -0
  83. package/dist/ts/schedule/exports/print.d.ts +20 -0
  84. package/dist/ts/schedule/exports/print.ts +233 -0
  85. package/dist/ts/schedule/index.d.ts +26 -0
  86. package/dist/ts/schedule/index.ts +26 -0
  87. package/dist/ts/schedule/models/event-settings-model.d.ts +165 -0
  88. package/dist/ts/schedule/models/event-settings.d.ts +149 -0
  89. package/dist/ts/schedule/models/event-settings.ts +187 -0
  90. package/dist/ts/schedule/models/field-options-model.d.ts +37 -0
  91. package/dist/ts/schedule/models/field-options.d.ts +31 -0
  92. package/dist/ts/schedule/models/field-options.ts +41 -0
  93. package/dist/ts/schedule/models/fields-model.d.ts +129 -0
  94. package/dist/ts/schedule/models/fields.d.ts +117 -0
  95. package/dist/ts/schedule/models/fields.ts +149 -0
  96. package/dist/ts/schedule/models/group-model.d.ts +69 -0
  97. package/dist/ts/schedule/models/group.d.ts +60 -0
  98. package/dist/ts/schedule/models/group.ts +75 -0
  99. package/dist/ts/schedule/models/header-rows-model.d.ts +33 -0
  100. package/dist/ts/schedule/models/header-rows.d.ts +30 -0
  101. package/dist/ts/schedule/models/header-rows.ts +35 -0
  102. package/dist/ts/schedule/models/models.d.ts +14 -0
  103. package/dist/ts/schedule/models/models.ts +15 -0
  104. package/dist/ts/schedule/models/quick-info-templates-model.d.ts +52 -0
  105. package/dist/ts/schedule/models/quick-info-templates.d.ts +47 -0
  106. package/dist/ts/schedule/models/quick-info-templates.ts +56 -0
  107. package/dist/ts/schedule/models/resources-model.d.ts +122 -0
  108. package/dist/ts/schedule/models/resources.d.ts +106 -0
  109. package/dist/ts/schedule/models/resources.ts +138 -0
  110. package/dist/ts/schedule/models/time-scale-model.d.ts +57 -0
  111. package/dist/ts/schedule/models/time-scale.d.ts +50 -0
  112. package/dist/ts/schedule/models/time-scale.ts +61 -0
  113. package/dist/ts/schedule/models/toolbar-model.d.ts +196 -0
  114. package/dist/ts/schedule/models/toolbar.d.ts +176 -0
  115. package/dist/ts/schedule/models/toolbar.ts +196 -0
  116. package/dist/ts/schedule/models/views-model.d.ts +370 -0
  117. package/dist/ts/schedule/models/views.d.ts +335 -0
  118. package/dist/ts/schedule/models/views.ts +408 -0
  119. package/dist/ts/schedule/models/work-hours-model.d.ts +29 -0
  120. package/dist/ts/schedule/models/work-hours.d.ts +24 -0
  121. package/dist/ts/schedule/models/work-hours.ts +31 -0
  122. package/dist/ts/schedule/popups/event-tooltip.d.ts +16 -0
  123. package/dist/ts/schedule/popups/event-tooltip.ts +203 -0
  124. package/dist/ts/schedule/popups/event-window.d.ts +118 -0
  125. package/dist/ts/schedule/popups/event-window.ts +2055 -0
  126. package/dist/ts/schedule/popups/form-validator.d.ts +16 -0
  127. package/dist/ts/schedule/popups/form-validator.ts +110 -0
  128. package/dist/ts/schedule/popups/quick-popups.d.ts +78 -0
  129. package/dist/ts/schedule/popups/quick-popups.ts +1470 -0
  130. package/dist/ts/schedule/renderer/agenda.d.ts +45 -0
  131. package/dist/ts/schedule/renderer/agenda.ts +497 -0
  132. package/dist/ts/schedule/renderer/day.d.ts +20 -0
  133. package/dist/ts/schedule/renderer/day.ts +28 -0
  134. package/dist/ts/schedule/renderer/header-renderer.d.ts +48 -0
  135. package/dist/ts/schedule/renderer/header-renderer.ts +736 -0
  136. package/dist/ts/schedule/renderer/month-agenda.d.ts +29 -0
  137. package/dist/ts/schedule/renderer/month-agenda.ts +184 -0
  138. package/dist/ts/schedule/renderer/month.d.ts +61 -0
  139. package/dist/ts/schedule/renderer/month.ts +766 -0
  140. package/dist/ts/schedule/renderer/renderer.d.ts +13 -0
  141. package/dist/ts/schedule/renderer/renderer.ts +165 -0
  142. package/dist/ts/schedule/renderer/timeline-header-row.d.ts +15 -0
  143. package/dist/ts/schedule/renderer/timeline-header-row.ts +132 -0
  144. package/dist/ts/schedule/renderer/timeline-month.d.ts +29 -0
  145. package/dist/ts/schedule/renderer/timeline-month.ts +184 -0
  146. package/dist/ts/schedule/renderer/timeline-view.d.ts +31 -0
  147. package/dist/ts/schedule/renderer/timeline-view.ts +308 -0
  148. package/dist/ts/schedule/renderer/timeline-year.d.ts +22 -0
  149. package/dist/ts/schedule/renderer/timeline-year.ts +450 -0
  150. package/dist/ts/schedule/renderer/vertical-view.d.ts +63 -0
  151. package/dist/ts/schedule/renderer/vertical-view.ts +911 -0
  152. package/dist/ts/schedule/renderer/view-base.d.ts +83 -0
  153. package/dist/ts/schedule/renderer/view-base.ts +709 -0
  154. package/dist/ts/schedule/renderer/week.d.ts +22 -0
  155. package/dist/ts/schedule/renderer/week.ts +35 -0
  156. package/dist/ts/schedule/renderer/work-week.d.ts +22 -0
  157. package/dist/ts/schedule/renderer/work-week.ts +36 -0
  158. package/dist/ts/schedule/renderer/year.d.ts +46 -0
  159. package/dist/ts/schedule/renderer/year.ts +470 -0
  160. package/dist/ts/schedule/timezone/timezone.d.ts +16 -0
  161. package/dist/ts/schedule/timezone/timezone.ts +313 -0
  162. package/package.json +56 -21
  163. package/src/schedule/actions/action-base.js +3 -0
  164. package/src/schedule/actions/drag.js +11 -4
  165. package/src/schedule/actions/keyboard.js +1 -1
  166. package/src/schedule/actions/resize.js +8 -5
  167. package/src/schedule/base/css-constant.d.ts +2 -0
  168. package/src/schedule/base/css-constant.js +2 -0
  169. package/src/schedule/base/schedule.js +15 -1
  170. package/src/schedule/event-renderer/agenda-base.d.ts +1 -1
  171. package/src/schedule/event-renderer/agenda-base.js +5 -4
  172. package/src/schedule/event-renderer/inline-edit.js +11 -6
  173. package/src/schedule/event-renderer/month.js +5 -3
  174. package/src/schedule/event-renderer/vertical-view.js +3 -0
  175. package/src/schedule/event-renderer/year.d.ts +2 -0
  176. package/src/schedule/event-renderer/year.js +28 -4
  177. package/src/schedule/popups/event-tooltip.js +4 -0
  178. package/src/schedule/popups/event-window.js +2 -2
  179. package/src/schedule/popups/quick-popups.js +5 -1
  180. package/src/schedule/renderer/agenda.js +3 -2
  181. package/src/schedule/renderer/vertical-view.js +1 -1
  182. package/src/schedule/renderer/year.js +3 -2
  183. package/styles/bds-lite.css +11 -8
  184. package/styles/bds.css +11 -8
  185. package/styles/bootstrap-dark-lite.css +12 -9
  186. package/styles/bootstrap-dark.css +12 -9
  187. package/styles/bootstrap-lite.css +12 -9
  188. package/styles/bootstrap.css +12 -9
  189. package/styles/bootstrap4-lite.css +11 -8
  190. package/styles/bootstrap4.css +11 -8
  191. package/styles/bootstrap5-dark-lite.css +11 -8
  192. package/styles/bootstrap5-dark.css +11 -8
  193. package/styles/bootstrap5-lite.css +11 -8
  194. package/styles/bootstrap5.3-lite.css +11 -8
  195. package/styles/bootstrap5.3.css +11 -8
  196. package/styles/bootstrap5.css +11 -8
  197. package/styles/fabric-dark-lite.css +12 -9
  198. package/styles/fabric-dark.css +12 -9
  199. package/styles/fabric-lite.css +12 -9
  200. package/styles/fabric.css +12 -9
  201. package/styles/fluent-dark-lite.css +13 -10
  202. package/styles/fluent-dark.css +13 -10
  203. package/styles/fluent-lite.css +13 -10
  204. package/styles/fluent.css +13 -10
  205. package/styles/fluent2-lite.css +11 -8
  206. package/styles/fluent2.css +11 -8
  207. package/styles/highcontrast-light-lite.css +12 -9
  208. package/styles/highcontrast-light.css +12 -9
  209. package/styles/highcontrast-lite.css +12 -9
  210. package/styles/highcontrast.css +12 -9
  211. package/styles/material-dark-lite.css +12 -9
  212. package/styles/material-dark.css +12 -9
  213. package/styles/material-lite.css +12 -9
  214. package/styles/material.css +12 -9
  215. package/styles/material3-dark-lite.css +11 -8
  216. package/styles/material3-dark.css +11 -8
  217. package/styles/material3-lite.css +11 -8
  218. package/styles/material3.css +11 -8
  219. package/styles/recurrence-editor/_bds-definition.scss +1 -0
  220. package/styles/recurrence-editor/_bootstrap-dark-definition.scss +1 -0
  221. package/styles/recurrence-editor/_bootstrap-definition.scss +1 -0
  222. package/styles/recurrence-editor/_bootstrap4-definition.scss +1 -0
  223. package/styles/recurrence-editor/_bootstrap5-definition.scss +1 -0
  224. package/styles/recurrence-editor/_bootstrap5.3-definition.scss +1 -0
  225. package/styles/recurrence-editor/_fabric-dark-definition.scss +1 -0
  226. package/styles/recurrence-editor/_fabric-definition.scss +1 -0
  227. package/styles/recurrence-editor/_fluent-definition.scss +1 -0
  228. package/styles/recurrence-editor/_fluent2-definition.scss +1 -0
  229. package/styles/recurrence-editor/_fusionnew-definition.scss +1 -0
  230. package/styles/recurrence-editor/_highcontrast-definition.scss +1 -0
  231. package/styles/recurrence-editor/_highcontrast-light-definition.scss +1 -0
  232. package/styles/recurrence-editor/_layout.scss +5 -1
  233. package/styles/recurrence-editor/_material-dark-definition.scss +1 -0
  234. package/styles/recurrence-editor/_material-definition.scss +1 -0
  235. package/styles/recurrence-editor/_material3-definition.scss +1 -0
  236. package/styles/recurrence-editor/_tailwind-definition.scss +1 -0
  237. package/styles/recurrence-editor/_tailwind3-definition.scss +1 -0
  238. package/styles/recurrence-editor/bds.css +3 -0
  239. package/styles/recurrence-editor/bootstrap-dark.css +4 -1
  240. package/styles/recurrence-editor/bootstrap.css +4 -1
  241. package/styles/recurrence-editor/bootstrap4.css +3 -0
  242. package/styles/recurrence-editor/bootstrap5-dark.css +3 -0
  243. package/styles/recurrence-editor/bootstrap5.3.css +3 -0
  244. package/styles/recurrence-editor/bootstrap5.css +3 -0
  245. package/styles/recurrence-editor/fabric-dark.css +4 -1
  246. package/styles/recurrence-editor/fabric.css +4 -1
  247. package/styles/recurrence-editor/fluent-dark.css +4 -1
  248. package/styles/recurrence-editor/fluent.css +4 -1
  249. package/styles/recurrence-editor/fluent2.css +3 -0
  250. package/styles/recurrence-editor/highcontrast-light.css +4 -1
  251. package/styles/recurrence-editor/highcontrast.css +4 -1
  252. package/styles/recurrence-editor/material-dark.css +4 -1
  253. package/styles/recurrence-editor/material.css +4 -1
  254. package/styles/recurrence-editor/material3-dark.css +3 -0
  255. package/styles/recurrence-editor/material3.css +3 -0
  256. package/styles/recurrence-editor/tailwind-dark.css +3 -0
  257. package/styles/recurrence-editor/tailwind.css +3 -0
  258. package/styles/recurrence-editor/tailwind3.css +3 -0
  259. package/styles/schedule/_bds-definition.scss +2 -0
  260. package/styles/schedule/_bootstrap-dark-definition.scss +2 -0
  261. package/styles/schedule/_bootstrap-definition.scss +2 -0
  262. package/styles/schedule/_bootstrap4-definition.scss +2 -0
  263. package/styles/schedule/_bootstrap5-definition.scss +2 -0
  264. package/styles/schedule/_bootstrap5.3-definition.scss +2 -0
  265. package/styles/schedule/_fabric-dark-definition.scss +2 -0
  266. package/styles/schedule/_fabric-definition.scss +2 -0
  267. package/styles/schedule/_fluent-definition.scss +3 -1
  268. package/styles/schedule/_fluent2-definition.scss +2 -0
  269. package/styles/schedule/_fusionnew-definition.scss +2 -0
  270. package/styles/schedule/_highcontrast-definition.scss +2 -0
  271. package/styles/schedule/_highcontrast-light-definition.scss +2 -0
  272. package/styles/schedule/_layout.scss +12 -11
  273. package/styles/schedule/_material-dark-definition.scss +2 -0
  274. package/styles/schedule/_material-definition.scss +2 -0
  275. package/styles/schedule/_material3-definition.scss +2 -0
  276. package/styles/schedule/_tailwind-definition.scss +2 -0
  277. package/styles/schedule/_tailwind3-definition.scss +2 -0
  278. package/styles/schedule/bds.css +8 -8
  279. package/styles/schedule/bootstrap-dark.css +8 -8
  280. package/styles/schedule/bootstrap.css +8 -8
  281. package/styles/schedule/bootstrap4.css +8 -8
  282. package/styles/schedule/bootstrap5-dark.css +8 -8
  283. package/styles/schedule/bootstrap5.3.css +8 -8
  284. package/styles/schedule/bootstrap5.css +8 -8
  285. package/styles/schedule/fabric-dark.css +8 -8
  286. package/styles/schedule/fabric.css +8 -8
  287. package/styles/schedule/fluent-dark.css +9 -9
  288. package/styles/schedule/fluent.css +9 -9
  289. package/styles/schedule/fluent2.css +8 -8
  290. package/styles/schedule/highcontrast-light.css +8 -8
  291. package/styles/schedule/highcontrast.css +8 -8
  292. package/styles/schedule/material-dark.css +8 -8
  293. package/styles/schedule/material.css +8 -8
  294. package/styles/schedule/material3-dark.css +8 -8
  295. package/styles/schedule/material3.css +8 -8
  296. package/styles/schedule/tailwind-dark.css +8 -8
  297. package/styles/schedule/tailwind.css +8 -8
  298. package/styles/schedule/tailwind3.css +8 -8
  299. package/styles/tailwind-dark-lite.css +11 -8
  300. package/styles/tailwind-dark.css +11 -8
  301. package/styles/tailwind-lite.css +11 -8
  302. package/styles/tailwind.css +11 -8
  303. package/styles/tailwind3-lite.css +11 -8
  304. package/styles/tailwind3.css +11 -8
@@ -0,0 +1,1435 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { KeyboardEvents, KeyboardEventArgs, closest, EventHandler, extend, Browser } from '@syncfusion/ej2-base';
3
+ import { isNullOrUndefined, addClass, removeClass } from '@syncfusion/ej2-base';
4
+ import { View } from '../base/type';
5
+ import { Schedule } from '../base/schedule';
6
+ import { CellClickEventArgs, KeyEventArgs, ResizeEdges, SelectEventArgs, InlineClickArgs, EventClickArgs, ResourceDetails, BeforePasteEventArgs } from '../base/interface';
7
+ import * as event from '../base/constant';
8
+ import * as util from '../base/util';
9
+ import * as cls from '../base/css-constant';
10
+ import { cellSelect } from '../base/constant';
11
+
12
+ /**
13
+ * Keyboard interaction
14
+ */
15
+ export class KeyboardInteraction {
16
+ /**
17
+ * Constructor
18
+ */
19
+ private parent: Schedule;
20
+ private initialTarget: HTMLTableCellElement;
21
+ public selectedCells: HTMLTableCellElement[] = [];
22
+ private clipBoardTextArea: HTMLInputElement;
23
+ private isCutContentPasted: boolean = false;
24
+ private isCutAction: boolean = false;
25
+ private keyConfigs: { [key: string]: string } = {
26
+ downArrow: 'downarrow',
27
+ upArrow: 'uparrow',
28
+ rightArrow: 'rightarrow',
29
+ leftArrow: 'leftarrow',
30
+ shiftDownArrow: 'shift+downarrow',
31
+ shiftUpArrow: 'shift+uparrow',
32
+ shiftRightArrow: 'shift+rightarrow',
33
+ shiftLeftArrow: 'shift+leftarrow',
34
+ ctrlLeftArrow: 'ctrl+leftarrow',
35
+ ctrlRightArrow: 'ctrl+rightarrow',
36
+ altOne: 'alt+1',
37
+ altTwo: 'alt+2',
38
+ altThree: 'alt+3',
39
+ altFour: 'alt+4',
40
+ altFive: 'alt+5',
41
+ altSix: 'alt+6',
42
+ altSeven: 'alt+7',
43
+ altEight: 'alt+8',
44
+ altNine: 'alt+9',
45
+ enter: 'enter',
46
+ escape: 'escape',
47
+ delete: 'delete',
48
+ backspace: 'backspace',
49
+ home: 'home',
50
+ pageUp: 'pageup',
51
+ pageDown: 'pagedown',
52
+ tab: 'tab',
53
+ shiftTab: 'shift+tab',
54
+ ctrlShiftUpArrow: 'ctrl+shift+uparrow',
55
+ ctrlShiftDownArrow: 'ctrl+shift+downarrow',
56
+ ctrlShiftLeftArrow: 'ctrl+shift+leftarrow',
57
+ ctrlShiftRightArrow: 'ctrl+shift+rightarrow',
58
+ shiftAltY: 'shift+alt+y',
59
+ shiftAltN: 'shift+alt+n',
60
+ cut: 'ctrl+x',
61
+ copy: 'ctrl+c',
62
+ cmdCut: 'cmd+x',
63
+ cmdCopy: 'cmd+c'
64
+ };
65
+ private keyboardModule: KeyboardEvents;
66
+ constructor(parent: Schedule) {
67
+ this.parent = parent;
68
+ this.parent.element.tabIndex = this.parent.element.tabIndex === -1 ? 0 : this.parent.element.tabIndex;
69
+ this.keyboardModule = new KeyboardEvents(this.parent.element, {
70
+ keyAction: this.keyActionHandler.bind(this),
71
+ keyConfigs: this.keyConfigs,
72
+ eventName: 'keydown'
73
+ });
74
+ this.addEventListener();
75
+ this.createClipboardElement();
76
+ }
77
+ private keyActionHandler(e: KeyboardEventArgs): void {
78
+ const target: HTMLElement = e.target as HTMLElement;
79
+ if (e.action === 'home' && target && ['INPUT', 'TEXTAREA', 'SELECT'].indexOf(target.tagName) > -1 && target.closest('.e-quick-popup-wrapper')) {
80
+ return;
81
+ }
82
+ switch (e.action) {
83
+ case 'downArrow':
84
+ case 'shiftDownArrow':
85
+ this.processDown(e, e.shiftKey);
86
+ break;
87
+ case 'upArrow':
88
+ case 'shiftUpArrow':
89
+ this.processUp(e, e.shiftKey);
90
+ break;
91
+ case 'leftArrow':
92
+ case 'shiftLeftArrow':
93
+ this.processLeft(e, e.shiftKey);
94
+ break;
95
+ case 'rightArrow':
96
+ case 'shiftRightArrow':
97
+ this.processRight(e, e.shiftKey);
98
+ break;
99
+ case 'ctrlLeftArrow':
100
+ this.parent.changeDate(this.parent.activeView.getNextPreviousDate('Previous'), e);
101
+ if (this.parent.headerModule) {
102
+ (this.parent.headerModule.element.querySelector('.e-prev button') as HTMLElement).focus();
103
+ }
104
+ break;
105
+ case 'ctrlRightArrow':
106
+ this.parent.changeDate(this.parent.activeView.getNextPreviousDate('Next'), e);
107
+ if (this.parent.headerModule) {
108
+ (this.parent.headerModule.element.querySelector('.e-next button') as HTMLElement).focus();
109
+ }
110
+ break;
111
+ case 'altOne':
112
+ case 'altTwo':
113
+ case 'altThree':
114
+ case 'altFour':
115
+ case 'altFive':
116
+ case 'altSix':
117
+ case 'altSeven':
118
+ case 'altEight':
119
+ case 'altNine':
120
+ this.processViewNavigation(e);
121
+ break;
122
+ case 'enter':
123
+ this.processEnter(e);
124
+ break;
125
+ case 'home':
126
+ this.focusFirstCell();
127
+ break;
128
+ case 'tab':
129
+ case 'shiftTab':
130
+ this.processTab(e, e.shiftKey);
131
+ break;
132
+ case 'delete':
133
+ case 'backspace':
134
+ this.processDelete(e);
135
+ break;
136
+ case 'ctrlShiftUpArrow':
137
+ case 'ctrlShiftDownArrow':
138
+ case 'ctrlShiftLeftArrow':
139
+ case 'ctrlShiftRightArrow':
140
+ this.processCtrlShiftNavigationArrows(e);
141
+ break;
142
+ case 'escape':
143
+ this.processEscape(e);
144
+ break;
145
+ case 'fTwelve':
146
+ if (this.parent.allowInline && this.parent.inlineModule) {
147
+ e.preventDefault();
148
+ this.processFTwelve(e);
149
+ }
150
+ break;
151
+ case 'shiftAltY':
152
+ this.parent.changeDate(new Date(), e);
153
+ break;
154
+ case 'shiftAltN':
155
+ if (this.parent.currentView === 'Agenda' || this.parent.currentView === 'MonthAgenda' ||
156
+ this.parent.currentView === 'Year') {
157
+ return;
158
+ }
159
+ this.processShiftAltN(e);
160
+ break;
161
+ case 'cut':
162
+ case 'cmdCut':
163
+ if (e.ctrlKey || e.metaKey) {
164
+ this.processClipboardAction(true, undefined, e);
165
+ }
166
+ break;
167
+ case 'copy':
168
+ case 'cmdCopy':
169
+ if (e.ctrlKey || e.metaKey) {
170
+ this.processClipboardAction(false, undefined, e);
171
+ }
172
+ break;
173
+ }
174
+ }
175
+
176
+ private processShiftAltN(e: Event): void {
177
+ const selectedCells: Element[] = this.parent.getSelectedCells();
178
+ const target: HTMLTableCellElement = e.target as HTMLTableCellElement;
179
+ let cellData: CellClickEventArgs = <CellClickEventArgs>extend({}, null, true);
180
+ if (selectedCells.length > 0 && (closest(target, '.' + cls.WORK_CELLS_CLASS)
181
+ || closest(target, '.' + cls.ALLDAY_CELLS_CLASS) || closest(target, '.' + cls.HEADER_CELLS_CLASS))) {
182
+ cellData = this.getSelectedElements(target);
183
+ }
184
+ else if (closest(target, '.' + cls.APPOINTMENT_CLASS) && !isNullOrUndefined(this.parent.activeEventData.event)) {
185
+ const event: { StartTime: Date, EndTime: Date, IsAllDay: boolean } =
186
+ this.parent.activeEventData.event as { StartTime: Date, EndTime: Date, IsAllDay: boolean };
187
+ cellData.startTime = event.StartTime;
188
+ cellData.endTime = event.EndTime;
189
+ cellData.isAllDay = event.IsAllDay;
190
+ }
191
+ else {
192
+ const workHour: Date = this.parent.getStartEndTime(this.parent.workHours.start);
193
+ const slotInterval: number = this.parent.activeViewOptions.timeScale.interval /
194
+ this.parent.activeViewOptions.timeScale.slotCount;
195
+ cellData.startTime = new Date(this.parent.selectedDate);
196
+ cellData.startTime.setHours(workHour.getHours(), workHour.getMinutes(), 0, 0);
197
+ cellData.endTime = new Date(cellData.startTime.getTime() + slotInterval * 60000);
198
+ }
199
+ const args: CellClickEventArgs = <CellClickEventArgs>extend(cellData, { cancel: false, event: e });
200
+ if (args != null) {
201
+ this.parent.eventWindow.openEditor(args, 'Add');
202
+ }
203
+ }
204
+
205
+ private processFTwelve(e: KeyboardEventArgs): void {
206
+ const target: HTMLTableCellElement = e.target as HTMLTableCellElement;
207
+ if (target.classList.contains(cls.WORK_CELLS_CLASS) || target.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
208
+ this.parent.activeCellsData = this.getSelectedElements(target);
209
+ const args: CellClickEventArgs = <CellClickEventArgs>extend(this.parent.activeCellsData, { cancel: false, event: e });
210
+ const inlineArgs: InlineClickArgs = {
211
+ element: args.element as HTMLElement,
212
+ groupIndex: args.groupIndex, type: 'Cell'
213
+ };
214
+ this.parent.notify(event.inlineClick, inlineArgs);
215
+ }
216
+ if (target.classList.contains(cls.APPOINTMENT_CLASS)) {
217
+ target.click();
218
+ return;
219
+ }
220
+ }
221
+ private addEventListener(): void {
222
+ this.parent.on(event.cellMouseDown, this.onCellMouseDown, this);
223
+ if (this.parent.allowClipboard) {
224
+ this.parent.on(event.documentPaste, this.pasteHandler, this);
225
+ }
226
+ }
227
+ private removeEventListener(): void {
228
+ this.parent.off(event.cellMouseDown, this.onCellMouseDown);
229
+ this.parent.off(event.documentPaste, this.pasteHandler);
230
+ }
231
+
232
+ private onCellMouseDown(e: { event: Event & MouseEvent }): void {
233
+ if (e.event.shiftKey) { return; }
234
+ this.initialTarget = this.getClosestCell(e.event);
235
+ if (this.parent.activeViewOptions.readonly || this.parent.currentView === 'MonthAgenda' || !this.initialTarget) { return; }
236
+ if ((e.event.target as HTMLElement).classList.contains(cls.WORK_CELLS_CLASS) && e.event.which !== 3) {
237
+ this.parent.removeSelectedClass();
238
+ EventHandler.add(this.parent.getContentTable(), 'mousemove', this.onMouseSelection, this);
239
+ EventHandler.add(this.parent.getContentTable(), 'mouseup mouseleave', this.onMoveUp, this);
240
+ }
241
+ if ((e.event.target as HTMLElement).classList.contains(cls.ALLDAY_CELLS_CLASS) && e.event.which !== 3) {
242
+ this.parent.removeSelectedClass();
243
+ const allDayRow: HTMLTableRowElement = <HTMLTableRowElement>this.parent.getAllDayRow();
244
+ EventHandler.add(allDayRow, 'mousemove', this.onMouseSelection, this);
245
+ EventHandler.add(allDayRow, 'mouseup mouseleave', this.onMoveUp, this);
246
+ }
247
+ }
248
+
249
+ public onMouseSelection(e: MouseEvent): void {
250
+ const appointments: Element[] = [].slice.call(this.parent.element.querySelectorAll('.' + cls.APPOINTMENT_CLASS)) as Element[];
251
+ addClass(appointments, 'e-allow-select');
252
+ const selectionEdges: ResizeEdges = this.parent.boundaryValidation(e.pageY, e.pageX);
253
+ if (selectionEdges.bottom || selectionEdges.top || selectionEdges.left || selectionEdges.right) {
254
+ const parent: HTMLElement = this.parent.element.querySelector('.' + cls.CONTENT_WRAP_CLASS) as HTMLElement;
255
+ const yInBounds: boolean = parent.offsetHeight <= parent.scrollHeight && parent.scrollTop >= 0 &&
256
+ parent.scrollTop + parent.offsetHeight <= parent.scrollHeight;
257
+ const xInBounds: boolean = parent.offsetWidth <= parent.scrollWidth && parent.scrollLeft >= 0 &&
258
+ parent.scrollLeft + parent.offsetWidth <= parent.scrollWidth;
259
+ if (yInBounds && (selectionEdges.top || selectionEdges.bottom)) {
260
+ parent.scrollTop += selectionEdges.top ? -(e.target as HTMLElement).offsetHeight : (e.target as HTMLElement).offsetHeight;
261
+ }
262
+ if (xInBounds && (selectionEdges.left || selectionEdges.right)) {
263
+ parent.scrollLeft += selectionEdges.left ? -(e.target as HTMLElement).offsetWidth : (e.target as HTMLElement).offsetWidth;
264
+ }
265
+ }
266
+ const target: HTMLTableCellElement = this.getClosestCell(e);
267
+ if (target) {
268
+ this.selectCells(true, target);
269
+ }
270
+ }
271
+
272
+ private getClosestCell(e: Event): HTMLTableCellElement {
273
+ return closest(<Element>e.target, '.' + cls.WORK_CELLS_CLASS + ',.' + cls.ALLDAY_CELLS_CLASS) as HTMLTableCellElement;
274
+ }
275
+ private onMoveUp(e: Event): void {
276
+ const appointments: Element[] = [].slice.call(this.parent.element.querySelectorAll('.' + cls.APPOINTMENT_CLASS)) as Element[];
277
+ removeClass(appointments, 'e-allow-select');
278
+ EventHandler.remove(this.parent.getContentTable(), 'mousemove', this.onMouseSelection);
279
+ EventHandler.remove(this.parent.getContentTable(), 'mouseup mouseleave', this.onMoveUp);
280
+ if ((e.target as HTMLElement).classList.contains(cls.ALLDAY_CELLS_CLASS)) {
281
+ const allDayRow: HTMLTableRowElement = <HTMLTableRowElement>this.parent.getAllDayRow();
282
+ EventHandler.remove(allDayRow, 'mousemove', this.onMouseSelection);
283
+ EventHandler.remove(allDayRow, 'mouseup mouseleave', this.onMoveUp);
284
+ }
285
+ if (this.isPreventAction(e)) {
286
+ return;
287
+ }
288
+ const queryStr: string = '.' + cls.WORK_CELLS_CLASS + ',.' + cls.ALLDAY_CELLS_CLASS + ',.' + cls.HEADER_CELLS_CLASS;
289
+ let target: HTMLTableCellElement = closest((e.target as Element), queryStr) as HTMLTableCellElement;
290
+ if (!target) { return; }
291
+ const selectedCells: Element[] = this.parent.getSelectedCells();
292
+ if (selectedCells.length > 0 && selectedCells.indexOf(target) === -1) {
293
+ target = selectedCells[selectedCells.length - 1] as HTMLTableCellElement;
294
+ }
295
+ if (this.parent.currentView === 'TimelineYear' && target.classList.contains(cls.OTHERMONTH_CLASS)) {
296
+ return;
297
+ }
298
+ this.parent.activeCellsData = this.getSelectedElements(target);
299
+ const cellData: Record<string, any> = {};
300
+ if (this.parent.eventWindow) {
301
+ this.parent.eventWindow.convertToEventData(this.parent.activeCellsData as unknown as Record<string, any>, cellData);
302
+ }
303
+ const args: SelectEventArgs = {
304
+ data: cellData, element: this.parent.activeCellsData.element, event: e,
305
+ requestType: cellSelect, showQuickPopup: false
306
+ };
307
+ this.parent.trigger(event.select, args, (selectArgs: SelectEventArgs) => {
308
+ const isPopupShow: boolean = selectArgs.showQuickPopup || this.parent.quickInfoOnSelectionEnd;
309
+ if (isPopupShow && selectedCells.length > 1) {
310
+ const cellArgs: CellClickEventArgs =
311
+ <CellClickEventArgs>extend(this.parent.activeCellsData, { cancel: false, event: e, name: 'cellClick' });
312
+ this.parent.notify(event.cellClick, cellArgs);
313
+ }
314
+ });
315
+ }
316
+ private processEnter(e: Event): void {
317
+ if ((this.parent.activeViewOptions.readonly && !(e.target as Element).classList.contains(cls.APPOINTMENT_CLASS))
318
+ || this.isPreventAction(e)) {
319
+ return;
320
+ }
321
+ if (this.parent.currentView === 'TimelineYear' && (e.target as Element).classList.contains(cls.OTHERMONTH_CLASS)) {
322
+ return;
323
+ }
324
+ const target: HTMLTableCellElement = e.target as HTMLTableCellElement;
325
+ if (closest(target, '.' + cls.POPUP_WRAPPER_CLASS)) {
326
+ if (target.classList.contains(cls.QUICK_POPUP_EVENT_DETAILS_CLASS) ||
327
+ target.classList.contains(cls.EVENT_CREATE_CLASS) ||
328
+ target.classList.contains(cls.EDIT_EVENT_CLASS) ||
329
+ target.classList.contains(cls.DELETE_EVENT_CLASS) ||
330
+ target.classList.contains(cls.CLOSE_CLASS)) {
331
+ target.click();
332
+ e.preventDefault();
333
+ } else if (target.classList.contains(cls.SUBJECT_CLASS)) {
334
+ (this.parent.element.querySelector('.' + cls.EVENT_CREATE_CLASS) as HTMLElement).click();
335
+ e.preventDefault();
336
+ }
337
+ return;
338
+ }
339
+ if (target.classList.contains(cls.RESOURCE_CELLS_CLASS) && target.classList.contains(cls.RESOURCE_PARENT_CLASS)) {
340
+ const resourceIcon: HTMLElement = target.querySelector('.' + cls.RESOURCE_TREE_ICON_CLASS) as HTMLElement;
341
+ if (resourceIcon) {
342
+ resourceIcon.click();
343
+ }
344
+ return;
345
+ }
346
+ if (target.classList.contains(cls.WORK_CELLS_CLASS) || target.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
347
+ this.parent.activeCellsData = this.getSelectedElements(target);
348
+ const args: CellClickEventArgs = <CellClickEventArgs>extend(this.parent.activeCellsData, { cancel: false, event: e });
349
+ if (this.parent.allowInline) {
350
+ const inlineArgs: InlineClickArgs = {
351
+ element: args.element as HTMLElement,
352
+ groupIndex: args.groupIndex, type: 'Cell'
353
+ };
354
+ this.parent.notify(event.inlineClick, inlineArgs);
355
+ } else {
356
+ if (this.parent.currentView === 'Year') {
357
+ target.click();
358
+ } else {
359
+ this.parent.notify(event.cellClick, args);
360
+ }
361
+ }
362
+ return;
363
+ }
364
+ if (target.classList.contains(cls.INLINE_SUBJECT_CLASS) && this.parent.inlineModule) {
365
+ this.parent.inlineModule.inlineCrudActions(target);
366
+ return;
367
+ }
368
+ if (target.classList.contains(cls.APPOINTMENT_CLASS) || target.classList.contains(cls.MORE_EVENT_CLOSE_CLASS) ||
369
+ target.classList.contains(cls.ALLDAY_APPOINTMENT_SECTION_CLASS) || target.classList.contains(cls.MORE_INDICATOR_CLASS)) {
370
+ target.click();
371
+ return;
372
+ }
373
+ if (target.classList.contains(cls.MORE_EVENT_HEADER_DATE_CLASS)) {
374
+ this.parent.setProperties({ selectedDate: this.parent.getDateFromElement(target) }, true);
375
+ this.parent.changeView(this.parent.getNavigateView(), e);
376
+ this.processEscape(e);
377
+ return;
378
+ }
379
+ }
380
+ private getSelectedElements(target: HTMLTableCellElement): CellClickEventArgs {
381
+ let cellDetails: CellClickEventArgs;
382
+ if (this.selectedCells.length > 1 && target.classList.contains(cls.SELECTED_CELL_CLASS)) {
383
+ const start: CellClickEventArgs = this.parent.getCellDetails(this.selectedCells[0]);
384
+ const end: CellClickEventArgs = this.parent.getCellDetails(this.selectedCells.slice(-1)[0]);
385
+ start.endTime = end.endTime;
386
+ start.element = target;
387
+ cellDetails = start;
388
+ } else {
389
+ cellDetails = this.parent.getCellDetails(target);
390
+ }
391
+ return cellDetails;
392
+ }
393
+ private getCells(isInverseTable: boolean, start: HTMLTableCellElement, end: HTMLTableCellElement): HTMLTableCellElement[] {
394
+ const tableEle: HTMLTableElement = this.parent.getContentTable() as HTMLTableElement;
395
+ const isTimelineYear: boolean = this.parent.currentView === 'TimelineYear';
396
+ const query: string = isTimelineYear && !isInverseTable ? '.' + cls.WORK_CELLS_CLASS + ':not(.' + cls.OTHERMONTH_CLASS + ')' : 'td';
397
+ let cells: HTMLTableCellElement[] = [].slice.call(tableEle.querySelectorAll(query));
398
+ let maxRow: number = tableEle.rows.length;
399
+ let maxColumn: number = tableEle.rows[0].cells.length;
400
+ if (start && start.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
401
+ const allDayRow: HTMLTableRowElement = this.parent.getAllDayRow() as HTMLTableRowElement;
402
+ cells = [].slice.call(allDayRow.cells);
403
+ maxRow = 1;
404
+ maxColumn = allDayRow.cells.length;
405
+ }
406
+ let startIndex: number = cells.indexOf(start);
407
+ let endIndex: number = cells.indexOf(end);
408
+ const inverseCells: HTMLTableCellElement[] = [];
409
+ if (isInverseTable) {
410
+ for (let i: number = 0; i < maxColumn; i++) {
411
+ for (let j: number = 0; j < maxRow; j++) {
412
+ const cell: HTMLTableCellElement = cells[maxColumn * j + i];
413
+ if (isTimelineYear && cell.classList.contains(cls.OTHERMONTH_CLASS)) {
414
+ continue;
415
+ }
416
+ inverseCells.push(cell);
417
+ }
418
+ }
419
+ startIndex = inverseCells.indexOf(start);
420
+ endIndex = inverseCells.indexOf(end);
421
+ }
422
+ if (startIndex > endIndex) {
423
+ const temp: number = startIndex;
424
+ startIndex = endIndex;
425
+ endIndex = temp;
426
+ }
427
+ const sCells: HTMLTableCellElement[] = isInverseTable ? inverseCells : cells;
428
+ return sCells.slice(startIndex, endIndex + 1);
429
+ }
430
+ private focusFirstCell(): void {
431
+ if (this.parent.currentView === 'Agenda') {
432
+ const focusCell: HTMLElement = this.parent.getContentTable().querySelector('.' + cls.AGENDA_CELLS_CLASS) as HTMLTableCellElement;
433
+ focusCell.setAttribute('tabindex', '0');
434
+ focusCell.focus();
435
+ return;
436
+ }
437
+ this.parent.eventBase.removeSelectedAppointmentClass();
438
+ if (this.parent.activeView.isTimelineView() && this.parent.currentView !== 'TimelineYear') {
439
+ const cell: Element = this.parent.element.querySelector('.' + cls.CONTENT_TABLE_CLASS +
440
+ ' tr:not(.' + cls.HIDDEN_CLASS + ') .' + cls.WORK_CELLS_CLASS + ':not(.' + cls.RESOURCE_GROUP_CELLS_CLASS + ')');
441
+ this.selectCells(false, cell as HTMLTableCellElement);
442
+ } else if (this.parent.currentView.indexOf('Year') > -1) {
443
+ let query: string = '.' + cls.WORK_CELLS_CLASS + ':not(.' + cls.OTHERMONTH_CLASS + ')' + ':not(.' + cls.RESOURCE_GROUP_CELLS_CLASS + ')';
444
+ const isVerticalYear: boolean = this.parent.currentView === 'TimelineYear' && this.parent.activeViewOptions.orientation === 'Vertical';
445
+ query += isVerticalYear ? '[data-date="' + this.parent.activeView.startDate().getTime() + '"]' : '';
446
+ this.selectCells(false, this.parent.element.querySelector(query));
447
+ } else {
448
+ this.selectCells(false, this.parent.getWorkCellElements()[0] as HTMLTableCellElement);
449
+ }
450
+ }
451
+ private isInverseTableSelect(): boolean {
452
+ return this.parent.activeView.isInverseTableSelect;
453
+ }
454
+ /**
455
+ * Internal method to select cells
456
+ *
457
+ * @param {boolean} isMultiple Accepts to select multiple cells or not
458
+ * @param {HTMLTableCellElement} targetCell Accepts the target cells
459
+ * @returns {void}
460
+ * @private
461
+ */
462
+ public selectCells(isMultiple: boolean, targetCell: HTMLTableCellElement): void {
463
+ this.parent.removeSelectedClass();
464
+ let target: HTMLTableCellElement = (targetCell instanceof Array) ? targetCell.slice(-1)[0] : targetCell;
465
+ if (isMultiple) {
466
+ let initialId: string;
467
+ const views: string[] = ['Day', 'Week', 'WorkWeek', 'TimelineDay', 'TimelineWeek', 'TimelineWorkWeek', 'TimelineMonth', 'TimelineYear'];
468
+ const args: SelectEventArgs = { element: targetCell, requestType: 'mousemove', allowMultipleRow: true };
469
+ this.parent.inlineModule.removeInlineAppointmentElement();
470
+ this.parent.trigger(event.select, args, (selectArgs: SelectEventArgs) => {
471
+ const allowMultipleRow: boolean = (!selectArgs.allowMultipleRow) || (!this.parent.allowMultiRowSelection);
472
+ if (allowMultipleRow) {
473
+ const isTimelineYear: boolean = this.parent.currentView === 'TimelineYear';
474
+ if (isTimelineYear && this.parent.activeViewOptions.orientation === 'Horizontal' || this.parent.currentView === 'Month') {
475
+ const isGroupYear: boolean = isTimelineYear && this.parent.activeViewOptions.group.resources.length > 0;
476
+ target = isGroupYear ? this.initialTarget :
477
+ this.initialTarget.parentElement.children[target.cellIndex] as HTMLTableCellElement;
478
+ } else if (views.indexOf(this.parent.currentView) > -1) {
479
+ target = target.parentElement.children[this.initialTarget.cellIndex] as HTMLTableCellElement;
480
+ }
481
+ }
482
+ let selectedCells: HTMLTableCellElement[] = this.getCells(this.isInverseTableSelect(), this.initialTarget, target);
483
+ if (this.parent.activeViewOptions.group.resources.length > 0) {
484
+ initialId = this.initialTarget.getAttribute('data-group-index');
485
+ const resourceSelectedCells: HTMLTableCellElement[] = [];
486
+ for (const cell of selectedCells) {
487
+ if (cell.getAttribute('data-group-index') === initialId) {
488
+ resourceSelectedCells.push(cell);
489
+ }
490
+ }
491
+ selectedCells = resourceSelectedCells;
492
+ }
493
+ if (!this.parent.allowMultiCellSelection) {
494
+ selectedCells = [this.initialTarget];
495
+ }
496
+ this.selectedCells = selectedCells;
497
+ if (selectedCells.length > 2 && !target.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
498
+ let allDayCells: HTMLTableCellElement[] = this.getAllDayCells(selectedCells);
499
+ if (this.parent.activeViewOptions.group.resources.length > 0) {
500
+ const resourceAllDayCells: HTMLTableCellElement[] = [];
501
+ for (const cell of allDayCells) {
502
+ if (cell.getAttribute('data-group-index') === initialId) {
503
+ resourceAllDayCells.push(cell);
504
+ }
505
+ }
506
+ allDayCells = resourceAllDayCells;
507
+ }
508
+ selectedCells = selectedCells.concat(allDayCells);
509
+ }
510
+ if ((target.getAttribute('data-group-index') !== initialId) && this.parent.activeViewOptions.group.resources.length > 0) {
511
+ target = this.selectedCells[this.selectedCells.length - 1];
512
+ }
513
+ this.parent.addSelectedClass(selectedCells, target);
514
+ });
515
+ } else {
516
+ const args: SelectEventArgs = {
517
+ element: target, requestType: cellSelect
518
+ };
519
+ // activeCellsData is not reset on schedule property changed(group properties)
520
+ // const cellData: Record<string, any> = {};
521
+ // const cellDetails: CellClickEventArgs = this.parent.getCellDetails(target);
522
+ // if (this.parent.eventWindow && cellDetails) {
523
+ // if (this.parent.activeCellsData.element !== cellDetails.element) {
524
+ // this.parent.activeCellsData = cellDetails;
525
+ // }
526
+ // this.parent.eventWindow.convertToEventData(this.parent.activeCellsData as unknown as Record<string, any>, cellData);
527
+ // args.data = cellData;
528
+ // }
529
+ this.parent.trigger(event.select, args, () => {
530
+ this.initialTarget = target;
531
+ this.selectedCells = [target];
532
+ this.parent.addSelectedClass([target], target);
533
+ });
534
+ }
535
+ }
536
+ private selectAppointment(isReverse: boolean, target: Element): void {
537
+ const appointments: HTMLElement[] = this.getAppointmentElements();
538
+ if (appointments.length < 0) {
539
+ return;
540
+ }
541
+ this.parent.eventBase.removeSelectedAppointmentClass();
542
+ let nextAppEle: HTMLElement;
543
+ if (target.classList.contains(cls.APPOINTMENT_CLASS)) {
544
+ const targetIndex: number = appointments.indexOf(target as HTMLElement);
545
+ nextAppEle = appointments[(isReverse ? targetIndex - 1 : targetIndex + 1)];
546
+ } else {
547
+ nextAppEle = isReverse ? appointments[appointments.length - 1] : appointments[0];
548
+ }
549
+ if (nextAppEle) {
550
+ this.parent.eventBase.addSelectedAppointments([nextAppEle], true);
551
+ nextAppEle.focus();
552
+ addClass([nextAppEle as Element], cls.AGENDA_SELECTED_CELL);
553
+ }
554
+ }
555
+ private selectAppointmentElementFromWorkCell(isReverse: boolean, target: Element): void {
556
+ this.parent.eventBase.removeSelectedAppointmentClass();
557
+ this.parent.removeSelectedClass();
558
+ if (target.classList.contains(cls.WORK_CELLS_CLASS) || target.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
559
+ const appointmentElements: HTMLElement[] = this.getUniqueAppointmentElements();
560
+ const filteredElements: HTMLElement[] = [];
561
+ const selectedDate: number = this.parent.getDateFromElement(target).getTime();
562
+ const selectedSeriesEvents: Record<string, any>[] = this.parent.eventsProcessed.filter((eventObject: Record<string, any>) => {
563
+ return (!isReverse ? ((<Date>eventObject[this.parent.eventFields.startTime]).getTime() >= selectedDate) :
564
+ ((<Date>eventObject[this.parent.eventFields.startTime]).getTime() <= selectedDate));
565
+ }) as Record<string, any>[];
566
+ selectedSeriesEvents.filter((event: Record<string, any>) => {
567
+ appointmentElements.filter((element: HTMLElement) => {
568
+ if (JSON.stringify(event.Guid) === JSON.stringify(element.getAttribute('data-guid'))) {
569
+ filteredElements.push(element);
570
+ }
571
+ });
572
+ });
573
+ if (filteredElements.length > 0) {
574
+ const selectedElement: Element = isReverse ? filteredElements[filteredElements.length - 1] : filteredElements[0];
575
+ const focusElements: HTMLElement[] = this.getAppointmentElementsByGuid(selectedElement.getAttribute('data-guid'));
576
+ this.parent.eventBase.addSelectedAppointments(focusElements, true);
577
+ (focusElements[focusElements.length - 1]).focus();
578
+ }
579
+ }
580
+ }
581
+ private getAllDayCells(cells: HTMLTableCellElement[]): HTMLTableCellElement[] {
582
+ const allDayRow: HTMLTableRowElement = <HTMLTableRowElement>this.parent.getAllDayRow();
583
+ if (!allDayRow) { return []; }
584
+ const startCell: HTMLTableCellElement = cells[0];
585
+ const endCell: HTMLTableCellElement = cells[cells.length - 1];
586
+ const start: CellClickEventArgs = this.parent.getCellDetails(startCell);
587
+ const end: CellClickEventArgs = this.parent.getCellDetails(endCell);
588
+ if (end.endTime.getTime() - start.startTime.getTime() >= util.MS_PER_DAY) {
589
+ const allDayCells: HTMLTableCellElement[] = [].slice.call(allDayRow.cells);
590
+ return allDayCells.slice(startCell.cellIndex, endCell.cellIndex + 1);
591
+ }
592
+ return [];
593
+ }
594
+ private getAppointmentElements(): HTMLElement[] {
595
+ return [].slice.call(this.parent.element.querySelectorAll('.' + cls.APPOINTMENT_CLASS));
596
+ }
597
+ private getAppointmentElementsByGuid(guid: string): HTMLElement[] {
598
+ return [].slice.call(this.parent.element.querySelectorAll('div[data-guid="' + guid + '"]'));
599
+ }
600
+ private getUniqueAppointmentElements(): HTMLElement[] {
601
+ const appointments: HTMLElement[] = this.getAppointmentElements();
602
+ const appointmentElements: HTMLElement[] = [];
603
+ appointments.map((value: HTMLElement) => value.getAttribute('data-guid')).filter((value: string, index: number, self: string[]) => {
604
+ if (self.indexOf(value) === index) {
605
+ appointmentElements.push(appointments[parseInt(index.toString(), 10)]);
606
+ }
607
+ });
608
+ return appointmentElements;
609
+ }
610
+ private getWorkCellFromAppointmentElement(target: Element): HTMLTableCellElement {
611
+ const selectedObject: Record<string, any> =
612
+ this.parent.eventBase.getEventByGuid(target.getAttribute('data-guid')) as Record<string, any>;
613
+ return this.parent.eventBase.selectWorkCellByTime([selectedObject]) as HTMLTableCellElement;
614
+ }
615
+ private processViewNavigation(e: KeyboardEventArgs): void {
616
+ if (isNullOrUndefined(e.code)) {
617
+ return;
618
+ }
619
+ const index: number = parseInt(e.code.slice(e.code.length - 1), 10) - 1;
620
+ if (!isNaN(index) && !isNullOrUndefined(this.parent.views) && index < this.parent.views.length) {
621
+ const view: View = this.parent.viewCollections[parseInt(index.toString(), 10)].option;
622
+ this.parent.changeView(view, e, undefined, index);
623
+ if (this.parent.headerModule) {
624
+ (this.parent.headerModule.element.querySelector('.e-active-view button') as HTMLElement).focus();
625
+ }
626
+ }
627
+ }
628
+ private isCalendarTarget(e: KeyboardEventArgs): boolean {
629
+ const keyTarget: Element = e.currentTarget as Element || e.target as Element;
630
+ if (keyTarget && !isNullOrUndefined(keyTarget.querySelector('.e-header-popup.e-popup-open'))) {
631
+ return true;
632
+ }
633
+ return false;
634
+ }
635
+ private cancelUpDownAction(isTimelineYear: boolean): boolean {
636
+ const isVerticalYear: boolean = isTimelineYear && this.parent.activeViewOptions.orientation === 'Vertical';
637
+ const isGroup: boolean = this.parent.activeViewOptions.group.resources.length > 0;
638
+ if (isVerticalYear && isGroup || isTimelineYear && this.initialTarget.classList.contains(cls.OTHERMONTH_CLASS)) {
639
+ return true;
640
+ }
641
+ if (this.parent.activeView.isTimelineView() && !isTimelineYear || this.parent.currentView === 'MonthAgenda') {
642
+ return true;
643
+ }
644
+ return false;
645
+ }
646
+ private processUp(e: KeyboardEventArgs, isMultiple: boolean): void {
647
+ const isTimelineYear: boolean = this.parent.currentView === 'TimelineYear';
648
+ if (isMultiple && this.cancelUpDownAction(isTimelineYear) || (this.isCalendarTarget(e))) {
649
+ return;
650
+ }
651
+ let target: HTMLTableCellElement = (e.target) as HTMLTableCellElement;
652
+ const selectedElements: Element[] = this.parent.getSelectedCells();
653
+ const selectedEventElements: Element[] = this.parent.eventBase.getSelectedAppointments();
654
+ const moreEventWrapper: HTMLElement = <HTMLElement>this.parent.element.querySelector('.' + cls.MORE_POPUP_WRAPPER_CLASS);
655
+ const quickPopupWrapper: HTMLElement = this.getQuickPopupElement();
656
+ if (selectedElements.length > 0 && !(e.target as HTMLTableCellElement).classList.contains(cls.WORK_CELLS_CLASS)) {
657
+ target = selectedElements[selectedElements.length - 1] as HTMLTableCellElement;
658
+ }
659
+ if (selectedEventElements.length > 0 && !moreEventWrapper.classList.contains(cls.POPUP_OPEN) &&
660
+ !quickPopupWrapper.classList.contains(cls.POPUP_OPEN) &&
661
+ ['Day', 'Week', 'WorkWeek', 'Month'].indexOf(this.parent.currentView) !== -1) {
662
+ target = this.getWorkCellFromAppointmentElement(selectedEventElements[selectedEventElements.length - 1]);
663
+ this.parent.eventBase.removeSelectedAppointmentClass();
664
+ }
665
+ if (!target) { return; }
666
+ if (target.classList.contains(cls.WORK_CELLS_CLASS) && !this.parent.element.querySelector('.' + cls.POPUP_OPEN)) {
667
+ const tableRows: HTMLTableRowElement[] = this.parent.getTableRows() as HTMLTableRowElement[];
668
+ const curRowIndex: number = tableRows.indexOf(target.parentElement as HTMLTableRowElement);
669
+ let targetCell: HTMLTableCellElement;
670
+ if (isTimelineYear && isMultiple && this.parent.activeViewOptions.group.resources.length === 0) {
671
+ targetCell = this.isInverseTableSelect() ? this.getVerticalUpDownCell(tableRows, target, curRowIndex, true) :
672
+ this.getHorizontalUpDownCell(tableRows, target, curRowIndex, true);
673
+ }
674
+ if ((curRowIndex > 0 || targetCell) && curRowIndex < tableRows.length) {
675
+ targetCell = targetCell ? targetCell : (tableRows[curRowIndex - 1]).cells[target.cellIndex];
676
+ if (this.parent.currentView === 'Year' && targetCell.classList.contains(cls.OTHERMONTH_CLASS)) {
677
+ if (this.parent.activeView.getStartDate().getTime() < +targetCell.getAttribute('data-date')) {
678
+ targetCell = this.getYearUpDownCell(tableRows, curRowIndex - 1, target.cellIndex, true);
679
+ } else {
680
+ return;
681
+ }
682
+ }
683
+ e.preventDefault();
684
+ this.selectCells(isMultiple, targetCell);
685
+ }
686
+ } else if (this.parent.currentView === 'Agenda' || this.parent.currentView === 'MonthAgenda') {
687
+ this.selectAppointment(true, target);
688
+ }
689
+ }
690
+ private processDown(e: KeyboardEventArgs, isMultiple: boolean): void {
691
+ const isTimelineYear: boolean = this.parent.currentView === 'TimelineYear';
692
+ if (isMultiple && this.cancelUpDownAction(isTimelineYear) || (this.isCalendarTarget(e))) {
693
+ return;
694
+ }
695
+ let target: HTMLTableCellElement = (e.target) as HTMLTableCellElement;
696
+ const selectedCells: Element[] = this.parent.getSelectedCells();
697
+ const selectedElements: Element[] = this.parent.eventBase.getSelectedAppointments();
698
+ const moreEventWrapper: HTMLElement = <HTMLElement>this.parent.element.querySelector('.' + cls.MORE_POPUP_WRAPPER_CLASS);
699
+ const quickPopupWrapper: HTMLElement = this.getQuickPopupElement();
700
+ if (selectedCells.length > 0 && !(e.target as HTMLTableCellElement).classList.contains(cls.WORK_CELLS_CLASS)) {
701
+ target = selectedCells[selectedCells.length - 1] as HTMLTableCellElement;
702
+ }
703
+ if (selectedElements.length > 0 && !moreEventWrapper.classList.contains(cls.POPUP_OPEN) &&
704
+ !quickPopupWrapper.classList.contains(cls.POPUP_OPEN) &&
705
+ ['Day', 'Week', 'WorkWeek', 'Month'].indexOf(this.parent.currentView) !== -1) {
706
+ target = this.getWorkCellFromAppointmentElement(selectedElements[selectedElements.length - 1]);
707
+ this.parent.eventBase.removeSelectedAppointmentClass();
708
+ }
709
+ const tableRows: HTMLTableRowElement[] = this.parent.getTableRows() as HTMLTableRowElement[];
710
+ if (!target) { return; }
711
+ if (target.classList.contains(cls.WORK_CELLS_CLASS) && !this.parent.element.querySelector('.' + cls.POPUP_OPEN)) {
712
+ const curRowIndex: number = tableRows.indexOf(target.parentElement as HTMLTableRowElement);
713
+ let targetCell: HTMLTableCellElement;
714
+ if (isTimelineYear && isMultiple && this.parent.activeViewOptions.group.resources.length === 0) {
715
+ targetCell = this.isInverseTableSelect() ? this.getVerticalUpDownCell(tableRows, target, curRowIndex, false)
716
+ : this.getHorizontalUpDownCell(tableRows, target, curRowIndex, false);
717
+ }
718
+ if (curRowIndex >= 0 && ((curRowIndex < tableRows.length - 1) || targetCell)) {
719
+ targetCell = targetCell ? targetCell : (tableRows[curRowIndex + 1]).cells[target.cellIndex];
720
+ if (this.parent.currentView === 'Year' && targetCell.classList.contains(cls.OTHERMONTH_CLASS)) {
721
+ if (this.parent.activeView.getEndDate().getTime() > +targetCell.getAttribute('data-date')) {
722
+ targetCell = this.getYearUpDownCell(tableRows, curRowIndex + 1, target.cellIndex, false);
723
+ } else {
724
+ return;
725
+ }
726
+ }
727
+ e.preventDefault();
728
+ this.selectCells(isMultiple, targetCell);
729
+ }
730
+ } else if (this.parent.currentView === 'Agenda' || this.parent.currentView === 'MonthAgenda') {
731
+ this.selectAppointment(false, target);
732
+ }
733
+ }
734
+ private getYearUpDownCell(tableRows: HTMLTableRowElement[], rowIndex: number, cellIndex: number, isUp: boolean): HTMLTableCellElement {
735
+ while (tableRows[parseInt(rowIndex.toString(), 10)] &&
736
+ tableRows[parseInt(rowIndex.toString(), 10)].cells[parseInt(cellIndex.toString(), 10)].classList.contains(cls.OTHERMONTH_CLASS)) {
737
+ rowIndex = rowIndex + (isUp ? -1 : 1);
738
+ }
739
+ return tableRows[parseInt(rowIndex.toString(), 10)].cells[parseInt(cellIndex.toString(), 10)];
740
+ }
741
+ // eslint-disable-next-line max-len
742
+ private getHorizontalUpDownCell(tableRows: HTMLTableRowElement[], target: HTMLTableCellElement, curRowIndex: number, isUp: boolean): HTMLTableCellElement {
743
+ const row: HTMLTableRowElement = tableRows[curRowIndex + (isUp ? -1 : 1)];
744
+ let cell: HTMLTableCellElement = row ? row.cells[target.cellIndex] : target;
745
+ if (cell.classList.contains(cls.OTHERMONTH_CLASS)) {
746
+ const workCell: HTMLTableCellElement = row.querySelector('.' + cls.WORK_CELLS_CLASS + ':not(.' + cls.OTHERMONTH_CLASS + ')');
747
+ const date: Date = new Date(+workCell.getAttribute('data-date'));
748
+ const query: string = '[data-date="' + new Date(date.getFullYear(), date.getMonth() + 1, 0).getTime() + '"]';
749
+ cell = cell.cellIndex < workCell.cellIndex ? workCell : row.querySelector(query);
750
+ }
751
+ return cell;
752
+ }
753
+ // eslint-disable-next-line max-len
754
+ private getVerticalUpDownCell(tableRows: HTMLTableRowElement[], target: HTMLTableCellElement, curRowIndex: number, isUp: boolean): HTMLTableCellElement {
755
+ const hasRow: boolean = isUp && curRowIndex > 0 || !isUp && curRowIndex < tableRows.length - 1;
756
+ let targetCell: HTMLTableCellElement = hasRow ? tableRows[curRowIndex + (isUp ? -1 : 1)].cells[target.cellIndex] : undefined;
757
+ if (!targetCell || targetCell.classList.contains(cls.OTHERMONTH_CLASS)) {
758
+ const column: HTMLTableCellElement = tableRows[parseInt(curRowIndex.toString(), 10)].cells[target.cellIndex - (isUp ? 1 : -1)];
759
+ if (column) {
760
+ const dateAttr: number = +target.getAttribute('data-date') - (isUp ? util.MS_PER_DAY : -util.MS_PER_DAY);
761
+ return this.parent.getContentTable().querySelector('.' + cls.WORK_CELLS_CLASS + '[data-date="' + dateAttr + '"]');
762
+ }
763
+ targetCell = target;
764
+ }
765
+ return targetCell;
766
+ }
767
+ private processLeftRight(target: HTMLTableCellElement): KeyEventArgs {
768
+ const tableEle: HTMLTableElement = (this.parent.currentView === 'Year' ? target.closest('tbody') : this.parent.getContentTable()) as HTMLTableElement;
769
+ const curRowIndex: number = (target.parentNode as HTMLTableRowElement).sectionRowIndex;
770
+ const key: KeyEventArgs = {
771
+ element: tableEle,
772
+ rowIndex: curRowIndex,
773
+ columnIndex: target.cellIndex,
774
+ maxIndex: tableEle.rows[parseInt(curRowIndex.toString(), 10)].cells.length
775
+ };
776
+ return key;
777
+ }
778
+ private getQuickPopupElement(): HTMLElement {
779
+ return (this.parent.isAdaptive ? document.body : this.parent.element).querySelector('.' + cls.POPUP_WRAPPER_CLASS) as HTMLElement;
780
+ }
781
+ private isCancelLeftRightAction(e: KeyboardEventArgs, isMultiple: boolean, isTimelineYear: boolean): boolean {
782
+ const prevent: boolean = this.parent.currentView === 'MonthAgenda' || isTimelineYear && this.initialTarget.classList.contains(cls.OTHERMONTH_CLASS);
783
+ if (this.parent.currentView === 'Agenda' || (isMultiple && prevent)) {
784
+ return true;
785
+ }
786
+ if (this.isPreventAction(e) && isMultiple) {
787
+ return true;
788
+ }
789
+ const moreEventWrapper: HTMLElement = <HTMLElement>this.parent.element.querySelector('.' + cls.MORE_POPUP_WRAPPER_CLASS);
790
+ const quickPopupWrapper: HTMLElement = this.getQuickPopupElement();
791
+ if (moreEventWrapper.classList.contains(cls.POPUP_OPEN) || quickPopupWrapper.classList.contains(cls.POPUP_OPEN)) {
792
+ return true;
793
+ }
794
+ return false;
795
+ }
796
+ private processRight(e: KeyboardEventArgs, isMultiple: boolean): void {
797
+ const isTimelineYear: boolean = this.parent.currentView === 'TimelineYear';
798
+ if (this.isCancelLeftRightAction(e, isMultiple, isTimelineYear) || (this.isCalendarTarget(e))) {
799
+ return;
800
+ }
801
+ const selectedCells: Element[] = this.parent.getSelectedCells();
802
+ let targetCell: HTMLTableCellElement;
803
+ const selectedAppointments: Element[] = this.parent.eventBase.getSelectedAppointments();
804
+ let target: HTMLTableCellElement = (e.target) as HTMLTableCellElement;
805
+ if (selectedCells.length > 0 && !target.classList.contains(cls.WORK_CELLS_CLASS) &&
806
+ !target.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
807
+ target = selectedCells[selectedCells.length - 1] as HTMLTableCellElement;
808
+ }
809
+ if (selectedAppointments.length > 0) {
810
+ target = this.getWorkCellFromAppointmentElement(selectedAppointments[selectedAppointments.length - 1]);
811
+ this.parent.eventBase.removeSelectedAppointmentClass();
812
+ if (!target) { return; }
813
+ }
814
+ if (target.classList.contains(cls.WORK_CELLS_CLASS) &&
815
+ ((e.target) as HTMLTableCellElement).classList.contains(cls.WORK_CELLS_CLASS)) {
816
+ const key: KeyEventArgs = this.processLeftRight(target);
817
+ const targetDate: Date = new Date(+target.getAttribute('data-date'));
818
+ const isMonthEnd: boolean = this.parent.currentView === 'Year' && targetDate.getTime() === util.lastDateOfMonth(targetDate).getTime();
819
+ if (key.columnIndex >= 0 && key.columnIndex < key.maxIndex - 1 && !isMonthEnd) {
820
+ targetCell = this.calculateNextPrevDate(target, key.element.rows[key.rowIndex].cells[target.cellIndex + 1], 'right');
821
+ if (isTimelineYear && isMultiple && targetCell.classList.contains(cls.OTHERMONTH_CLASS)) {
822
+ targetCell = this.getTimelineYearTargetCell(key, target, true);
823
+ }
824
+ if (!isNullOrUndefined(targetCell)) {
825
+ this.selectCells(isMultiple, targetCell);
826
+ }
827
+ } else if (key.columnIndex === key.maxIndex - 1 || isMonthEnd) {
828
+ if (!this.isInverseTableSelect() && key.rowIndex < key.element.rows.length - 1 && !isMonthEnd) {
829
+ targetCell = this.calculateNextPrevDate(target, key.element.rows[key.rowIndex + 1].cells[0], 'right');
830
+ const changeTargetCell: boolean = isTimelineYear && isMultiple && targetCell.classList.contains(cls.OTHERMONTH_CLASS);
831
+ targetCell = changeTargetCell ? this.getHorizontalLeftRightCell(key, target, true) : targetCell;
832
+ if (!isNullOrUndefined(targetCell)) {
833
+ this.selectCells(isMultiple, targetCell);
834
+ }
835
+ } else if (!isMultiple) {
836
+ if (isMonthEnd && targetDate.getTime() !== this.parent.activeView.getEndDate().getTime()) {
837
+ this.selectCells(isMultiple, this.parent.element.querySelector(':not(.' + cls.OTHERMONTH_CLASS + ')[data-date="' + (targetDate.getTime() + util.MS_PER_DAY) + '"]'));
838
+ return;
839
+ }
840
+
841
+ const rowIndex: number = this.isInverseTableSelect() ? key.rowIndex : 0;
842
+ this.parent.changeDate(this.parent.activeView.getNextPreviousDate('Next'), e);
843
+ const tableEle: HTMLTableElement = this.parent.getContentTable() as HTMLTableElement;
844
+ const cell: HTMLTableCellElement = isMonthEnd ? tableEle.rows[parseInt(rowIndex.toString(), 10)].querySelector('.' + cls.WORK_CELLS_CLASS + ':not(.' + cls.OTHERMONTH_CLASS + ')')
845
+ : tableEle.rows[parseInt(rowIndex.toString(), 10)].cells[0];
846
+ this.selectCells(false, cell);
847
+ }
848
+ }
849
+ } else if (target.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
850
+ const curColIndex: number = target.cellIndex;
851
+ const allDayRow: HTMLTableRowElement = <HTMLTableRowElement>this.parent.getAllDayRow();
852
+ const maxColIndex: number = allDayRow.cells.length;
853
+ if (curColIndex >= 0 && curColIndex < maxColIndex - 1) {
854
+ this.selectCells(isMultiple, allDayRow.cells[curColIndex + 1]);
855
+ } else if (curColIndex === maxColIndex - 1 && !isMultiple) {
856
+ this.parent.changeDate(this.parent.activeView.getNextPreviousDate('Next'), e);
857
+ const allDayRow: HTMLTableRowElement = <HTMLTableRowElement>this.parent.getAllDayRow();
858
+ this.selectCells(false, allDayRow.cells[0]);
859
+ }
860
+ }
861
+ }
862
+ private processLeft(e: KeyboardEventArgs, isMultiple: boolean): void {
863
+ const isTimelineYear: boolean = this.parent.currentView === 'TimelineYear';
864
+ if (this.isCancelLeftRightAction(e, isMultiple, isTimelineYear) || (this.isCalendarTarget(e))) {
865
+ return;
866
+ }
867
+ let target: HTMLTableCellElement = (e.target) as HTMLTableCellElement;
868
+ const selectedCells: Element[] = this.parent.getSelectedCells();
869
+ let targetCell: HTMLTableCellElement;
870
+ if (selectedCells.length > 0 && !target.classList.contains(cls.WORK_CELLS_CLASS) &&
871
+ !target.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
872
+ target = selectedCells[selectedCells.length - 1] as HTMLTableCellElement;
873
+ }
874
+ const selectedElements: Element[] = this.parent.eventBase.getSelectedAppointments();
875
+ if (selectedElements.length > 0) {
876
+ target = this.getWorkCellFromAppointmentElement(selectedElements[selectedElements.length - 1]);
877
+ this.parent.eventBase.removeSelectedAppointmentClass();
878
+ if (!target) { return; }
879
+ }
880
+ if (((e.target) as HTMLTableCellElement).classList.contains(cls.WORK_CELLS_CLASS) &&
881
+ target.classList.contains(cls.WORK_CELLS_CLASS)) {
882
+ const key: KeyEventArgs = this.processLeftRight(target);
883
+ const targetDate: Date = new Date(+target.getAttribute('data-date'));
884
+ const isMonthStart: boolean = this.parent.currentView === 'Year' && targetDate.getTime() === util.firstDateOfMonth(targetDate).getTime();
885
+ if (key.columnIndex > 0 && key.columnIndex < key.maxIndex && !isMonthStart) {
886
+ targetCell = this.calculateNextPrevDate(target, key.element.rows[key.rowIndex].cells[target.cellIndex - 1], 'left');
887
+ if (isTimelineYear && isMultiple && targetCell.classList.contains(cls.OTHERMONTH_CLASS)) {
888
+ targetCell = this.getTimelineYearTargetCell(key, target, false);
889
+ }
890
+ if (!isNullOrUndefined(targetCell)) {
891
+ this.selectCells(isMultiple, targetCell);
892
+ }
893
+ } else if (key.columnIndex === 0 || isMonthStart) {
894
+ if (!this.isInverseTableSelect() && key.rowIndex > 0) {
895
+ targetCell = this.calculateNextPrevDate(target, key.element.rows[key.rowIndex - 1].cells[key.maxIndex - 1], 'left');
896
+ const otherMonthCell: boolean = isTimelineYear && isMultiple && targetCell.classList.contains(cls.OTHERMONTH_CLASS);
897
+ targetCell = otherMonthCell ? this.getHorizontalLeftRightCell(key, target, false) : targetCell;
898
+ if (!isNullOrUndefined(targetCell)) {
899
+ this.selectCells(isMultiple, targetCell);
900
+ }
901
+ } else if (!isMultiple) {
902
+ if (isMonthStart && targetDate.getTime() !== this.parent.activeView.getStartDate().getTime()) {
903
+ this.selectCells(isMultiple, this.parent.element.querySelector('[data-date="' + (targetDate.getTime() - util.MS_PER_DAY) + '"]'));
904
+ return;
905
+ }
906
+
907
+ this.parent.changeDate(this.parent.activeView.getNextPreviousDate('Previous'), e);
908
+ const tableEle: HTMLTableElement = this.parent.getContentTable() as HTMLTableElement;
909
+ const rowIndex: number = this.isInverseTableSelect() ? key.rowIndex : tableEle.rows.length - 1;
910
+ let cell: HTMLTableCellElement = tableEle.rows[parseInt(rowIndex.toString(), 10)].cells[key.maxIndex - 1];
911
+ if (isMonthStart) {
912
+ const tbody: NodeListOf<Element> = this.parent.element.querySelectorAll('.' + cls.CONTENT_TABLE_CLASS + ' tbody');
913
+ cell = tbody.item(tbody.length - 1).querySelector(':not(.' + cls.OTHERMONTH_CLASS + ')[data-date="' + this.parent.activeView.getEndDate().getTime() + '"]');
914
+ }
915
+ this.selectCells(false, cell);
916
+ }
917
+ }
918
+ } else if (target.classList.contains(cls.ALLDAY_CELLS_CLASS)) {
919
+ const curColIndex: number = target.cellIndex;
920
+ const allDayRow: HTMLTableRowElement = <HTMLTableRowElement>this.parent.getAllDayRow();
921
+ const maxColIndex: number = allDayRow.cells.length;
922
+ if (curColIndex > 0 && curColIndex < maxColIndex) {
923
+ this.selectCells(isMultiple, allDayRow.cells[curColIndex - 1]);
924
+ } else if (curColIndex === 0 && !isMultiple) {
925
+ this.parent.changeDate(this.parent.activeView.getNextPreviousDate('Previous'), e);
926
+ const allDayRow: HTMLTableRowElement = <HTMLTableRowElement>this.parent.getAllDayRow();
927
+ this.selectCells(false, allDayRow.cells[maxColIndex - 1]);
928
+ }
929
+ }
930
+ }
931
+ private getTimelineYearTargetCell(key: KeyEventArgs, target: HTMLTableCellElement, isRight: boolean): HTMLTableCellElement {
932
+ return this.isInverseTableSelect() ? this.getVerticalLeftRightCell(target, isRight) :
933
+ this.getHorizontalLeftRightCell(key, target, isRight);
934
+ }
935
+ private getHorizontalLeftRightCell(key: KeyEventArgs, target: HTMLTableCellElement, isRight: boolean): HTMLTableCellElement {
936
+ const row: HTMLTableRowElement = key.element.rows[(<HTMLTableRowElement>target.parentNode).sectionRowIndex + (isRight ? 1 : -1)];
937
+ if (row) {
938
+ const query: string = isRight ? '.' + cls.WORK_CELLS_CLASS + ':not(.' + cls.OTHERMONTH_CLASS + ')'
939
+ : '[data-date="' + ((+target.getAttribute('data-date')) - util.MS_PER_DAY) + '"]';
940
+ return row.querySelector(query);
941
+ }
942
+ return target;
943
+ }
944
+ private getVerticalLeftRightCell(target: HTMLTableCellElement, isRight: boolean): HTMLTableCellElement {
945
+ const date: Date = new Date(+target.getAttribute('data-date'));
946
+ const start: Date = new Date(date.getFullYear(), date.getMonth() + (isRight ? 1 : -1), 1);
947
+ const tableEle: HTMLTableElement = this.parent.getContentTable() as HTMLTableElement;
948
+ const targetCell: HTMLTableCellElement = tableEle.querySelector('[data-date="' + start.getTime() + '"]');
949
+ if ((targetCell.parentNode as HTMLTableRowElement).sectionRowIndex > (target.parentNode as HTMLTableRowElement).sectionRowIndex) {
950
+ return targetCell;
951
+ }
952
+ return tableEle.querySelector('[data-date="' + new Date(start.getFullYear(), start.getMonth() + 1, 0).getTime() + '"]');
953
+ }
954
+ private calculateNextPrevDate(currentCell: HTMLTableCellElement, target: HTMLTableCellElement, type: string): HTMLTableCellElement {
955
+ const initialId: string = this.initialTarget.getAttribute('data-group-index');
956
+ if (this.parent.activeViewOptions.group.resources.length > 0 && this.parent.currentView === 'Month') {
957
+ if (currentCell && target && target.getAttribute('data-group-index') !== initialId) {
958
+ const currentDate: Date = this.parent.getDateFromElement(currentCell);
959
+ const nextPrevDate: Date = (type === 'right') ? new Date(currentDate.setDate(currentDate.getDate() + 1))
960
+ : new Date(currentDate.setDate(currentDate.getDate() - 1));
961
+ target = [].slice.call(this.parent.element.querySelectorAll('td[data-date="'
962
+ + nextPrevDate.getTime().toString() + '"]' + '[data-group-index="' + initialId + '"]'))[0];
963
+ }
964
+ }
965
+ return target;
966
+ }
967
+
968
+ private getFocusableElements(container: Element): Element[] {
969
+ const queryString: string = 'a[href]:not([tabindex="-1"]),input:not([disabled]):not([tabindex="-1"]),' +
970
+ 'textarea:not([disabled]):not([tabindex="-1"]),button:not([disabled]):not([tabindex="-1"]),' +
971
+ 'select:not([disabled]):not([tabindex="-1"]),[tabindex]:not([tabindex="-1"]),[contentEditable=true]:not([tabindex="-1"])';
972
+ return [].slice.call(container.querySelectorAll(queryString));
973
+ }
974
+
975
+ private processTabOnPopup(e: KeyboardEventArgs, popupElement: Element): void {
976
+ let focusableElements: Element[] = this.getFocusableElements(popupElement);
977
+ focusableElements = focusableElements.filter((element: Element) => {
978
+ const footerEle: HTMLElement = this.parent.element.querySelector('.' + cls.POPUP_FOOTER_CLASS) as HTMLElement;
979
+ if (footerEle && footerEle.offsetParent) {
980
+ return !(element.classList.contains(cls.EDIT_CLASS) || element.classList.contains(cls.DELETE_CLASS));
981
+ } else {
982
+ return !(element.classList.contains(cls.EDIT_EVENT_CLASS) || element.classList.contains(cls.DELETE_EVENT_CLASS));
983
+ }
984
+ });
985
+ const firstEle: Element = focusableElements[0];
986
+ const lastEle: Element = focusableElements[focusableElements.length - 1];
987
+ if (!isNullOrUndefined(lastEle) && document.activeElement === lastEle && !e.shiftKey) {
988
+ e.preventDefault();
989
+ (firstEle as HTMLElement).focus();
990
+ }
991
+ if (!isNullOrUndefined(firstEle) && document.activeElement === firstEle && e.shiftKey) {
992
+ e.preventDefault();
993
+ (lastEle as HTMLElement).focus();
994
+ }
995
+ }
996
+
997
+ private processTab(e: KeyboardEventArgs, isReverse: boolean): void {
998
+ let target: Element = e.target as Element;
999
+ if (target.classList.contains(cls.INLINE_SUBJECT_CLASS) && this.parent.inlineModule) {
1000
+ target = target.closest('.e-appointment');
1001
+ this.parent.inlineModule.inlineCrudActions(e.target as HTMLTableCellElement);
1002
+ }
1003
+ if (this.parent.currentView === 'TimelineYear' && target.classList.contains(cls.OTHERMONTH_CLASS)) {
1004
+ if (target.classList.contains(cls.SELECTED_CELL_CLASS)) {
1005
+ this.parent.removeSelectedClass();
1006
+ }
1007
+ return;
1008
+ }
1009
+ const popupWrapper: Element = closest(target, '.' + cls.POPUP_WRAPPER_CLASS + ',.' + cls.MORE_POPUP_WRAPPER_CLASS);
1010
+ if (popupWrapper && popupWrapper.classList.contains(cls.POPUP_OPEN)) {
1011
+ if (popupWrapper.classList.contains(cls.MORE_POPUP_WRAPPER_CLASS)) {
1012
+ this.parent.eventBase.removeSelectedAppointmentClass();
1013
+ }
1014
+ this.processTabOnPopup(e, popupWrapper);
1015
+ return;
1016
+ }
1017
+ if (target.classList.contains(cls.ROOT)) {
1018
+ this.parent.eventBase.removeSelectedAppointmentClass();
1019
+ return;
1020
+ }
1021
+ if (target.classList.contains(cls.RESOURCE_CELLS_CLASS) && this.parent.activeView.isTimelineView()
1022
+ && this.parent.activeViewOptions.group.resources.length > 0) {
1023
+ const index: number = parseInt(target.getAttribute('data-group-index'), 10);
1024
+ const appSelector: string = `.${cls.APPOINTMENT_CLASS}[data-group-index="${isReverse ? index - 1 : index}"]`;
1025
+ const appElements: HTMLElement[] = [].slice.call(this.parent.element.querySelectorAll(appSelector));
1026
+ if (appElements.length > 0) {
1027
+ this.parent.eventBase.removeSelectedAppointmentClass();
1028
+ const focusAppointment: HTMLElement = isReverse ? appElements.slice(-1)[0] : appElements[0];
1029
+ this.parent.eventBase.addSelectedAppointments([focusAppointment], true);
1030
+ focusAppointment.focus();
1031
+ e.preventDefault();
1032
+ } else if (index + 1 === this.parent.resourceBase.lastResourceLevel.length) {
1033
+ this.parent.element.focus();
1034
+ e.preventDefault();
1035
+ } else if (this.parent.virtualScrollModule) {
1036
+ const virtual: HTMLElement = this.parent.element.querySelector('.' + cls.VIRTUAL_TRACK_CLASS) as HTMLElement;
1037
+ const averageRowHeight: number = Math.round(virtual.offsetHeight / this.parent.resourceBase.expandedResources.length);
1038
+ this.parent.element.querySelector('.e-content-wrap').scrollTop = ((isReverse ? index - 1 : index + 1) * averageRowHeight);
1039
+ this.parent.virtualScrollModule.virtualScrolling();
1040
+ } else {
1041
+ this.setScrollPosition(index);
1042
+ }
1043
+ return;
1044
+ }
1045
+ if (target.classList.contains(cls.APPOINTMENT_CLASS)) {
1046
+ let appElements: HTMLElement[] = [].slice.call(this.parent.element.querySelectorAll('.' + cls.APPOINTMENT_CLASS));
1047
+ const isTimelineYear: boolean = this.parent.currentView === 'TimelineYear';
1048
+ const isTimeline: boolean = this.parent.activeView.isTimelineView() && !isTimelineYear;
1049
+ if ((isTimeline || isTimelineYear && this.parent.activeViewOptions.orientation === 'Vertical') && this.parent.activeViewOptions.group.resources.length > 0) {
1050
+ const index: number = parseInt(target.getAttribute('data-group-index'), 10);
1051
+ appElements = [].slice.call(this.parent.element.querySelectorAll(`.${cls.APPOINTMENT_CLASS}[data-group-index="${index}"]`));
1052
+ const resCellSelector: string = `.${cls.RESOURCE_CELLS_CLASS}[data-group-index="${isReverse ? index : index + 1}"]`;
1053
+ const resourceCell: HTMLElement = this.parent.element.querySelector(resCellSelector) as HTMLElement;
1054
+ if (resourceCell && (isReverse && target.getAttribute('data-guid') === appElements[0].getAttribute('data-guid') ||
1055
+ !isReverse && target.getAttribute('data-guid') === appElements.slice(-1)[0].getAttribute('data-guid'))) {
1056
+ this.parent.eventBase.removeSelectedAppointmentClass();
1057
+ if (this.parent.virtualScrollModule) {
1058
+ resourceCell.focus({ preventScroll: true });
1059
+ } else {
1060
+ resourceCell.focus();
1061
+ }
1062
+ if (this.parent.activeView.isTimelineView() && this.parent.activeViewOptions.group.resources.length > 0 &&
1063
+ isNullOrUndefined(this.parent.virtualScrollModule)) {
1064
+ this.setScrollPosition(index);
1065
+ }
1066
+ e.preventDefault();
1067
+ return;
1068
+ }
1069
+ }
1070
+ const selectedAppointments: Element[] = this.parent.eventBase.getSelectedAppointments();
1071
+ if (selectedAppointments.length > 0) {
1072
+ target = selectedAppointments[selectedAppointments.length - 1] as Element;
1073
+ }
1074
+ this.parent.eventBase.removeSelectedAppointmentClass();
1075
+ if (!isReverse && target.getAttribute('data-guid') === appElements[appElements.length - 1].getAttribute('data-guid') ||
1076
+ isReverse && target.getAttribute('data-guid') === appElements[0].getAttribute('data-guid')) {
1077
+ return;
1078
+ }
1079
+ if (this.parent.currentView === 'Agenda' || this.parent.currentView === 'MonthAgenda') {
1080
+ this.selectAppointment(isReverse, target);
1081
+ e.preventDefault();
1082
+ }
1083
+ return;
1084
+ }
1085
+ const selectedCells: Element[] = this.parent.getSelectedCells();
1086
+ if (selectedCells.length > 0 && !target.classList.contains(cls.APPOINTMENT_CLASS)) {
1087
+ target = selectedCells[selectedCells.length - 1] as Element;
1088
+ this.selectAppointmentElementFromWorkCell(isReverse, target);
1089
+ e.preventDefault();
1090
+ return;
1091
+ }
1092
+ if (target && !target.classList.contains(cls.RESOURCE_CELLS_CLASS) && this.parent.activeView.isTimelineView()
1093
+ && this.parent.activeViewOptions.group.resources.length > 0) {
1094
+ this.processTabOnResourceCells(target, isReverse);
1095
+ }
1096
+ }
1097
+ private processDelete(e: KeyboardEventArgs): void {
1098
+ let activeEle: Element = document.activeElement;
1099
+ if (this.parent.currentView === 'MonthAgenda' || (!isNullOrUndefined(activeEle) && closest(activeEle, '.' + cls.POPUP_WRAPPER_CLASS))) {
1100
+ const selectedEle: HTMLElement[] = this.parent.eventBase.getSelectedEvents().element as HTMLElement[];
1101
+ activeEle = <Element>((selectedEle && isNullOrUndefined(selectedEle.length)) ? selectedEle : selectedEle[0]);
1102
+ }
1103
+ if (activeEle && activeEle.classList.contains(cls.APPOINTMENT_CLASS)) {
1104
+ addClass([activeEle], cls.APPOINTMENT_BORDER);
1105
+ this.parent.activeEventData = this.parent.eventBase.getSelectedEvents();
1106
+ if (this.parent.activeViewOptions.readonly || activeEle.classList.contains(cls.READ_ONLY) ||
1107
+ !this.parent.eventSettings.allowDeleting) {
1108
+ return;
1109
+ }
1110
+ this.parent.quickPopup.deleteClick(e);
1111
+ }
1112
+ }
1113
+ private processCtrlShiftNavigationArrows(e: KeyboardEventArgs): void {
1114
+ if (this.parent.activeViewOptions.group.resources.length > 0 && document.activeElement.classList.contains(cls.APPOINTMENT_CLASS)) {
1115
+ const groupIndex: number = parseInt(document.activeElement.getAttribute('data-group-index'), 10);
1116
+ let index: number = (e.action === 'ctrlShiftLeftArrow' || e.action === 'ctrlShiftUpArrow') ? groupIndex - 1 : groupIndex + 1;
1117
+ index = index < 0 ? 0 : index > this.parent.resourceBase.lastResourceLevel.length ?
1118
+ this.parent.resourceBase.lastResourceLevel.length : index;
1119
+ let eventEle: HTMLElement[] = [];
1120
+ while (eventEle.length === 0 && index >= 0 && index <= this.parent.resourceBase.lastResourceLevel.length) {
1121
+ eventEle = [].slice.call(this.parent.element.querySelectorAll(`.${cls.APPOINTMENT_CLASS}[data-group-index="${index}"]`));
1122
+ index = (e.action === 'ctrlShiftLeftArrow' || e.action === 'ctrlShiftUpArrow') ? index - 1 : index + 1;
1123
+ }
1124
+ const nextAppEle: HTMLElement = eventEle[0];
1125
+ if (nextAppEle) {
1126
+ this.parent.eventBase.removeSelectedAppointmentClass();
1127
+ this.parent.eventBase.addSelectedAppointments([nextAppEle], true);
1128
+ nextAppEle.focus();
1129
+ }
1130
+ } else if (this.parent.activeViewOptions.group.resources.length > 0 &&
1131
+ !document.activeElement.classList.contains(cls.APPOINTMENT_CLASS)) {
1132
+ const index: number = this.parent.resourceBase.renderedResources[0].groupIndex;
1133
+ const appSelector: string = `.${cls.APPOINTMENT_CLASS}[data-group-index="${index}"]`;
1134
+ const appElements: HTMLElement[] = [].slice.call(this.parent.element.querySelectorAll(appSelector));
1135
+ if (appElements.length > 0) {
1136
+ this.parent.eventBase.removeSelectedAppointmentClass();
1137
+ const focusAppointment: HTMLElement = appElements[0];
1138
+ this.parent.eventBase.addSelectedAppointments([focusAppointment], true);
1139
+ focusAppointment.focus();
1140
+ e.preventDefault();
1141
+ }
1142
+ }
1143
+ }
1144
+ private processEscape(event: Event): void {
1145
+ this.parent.quickPopup.onClosePopup(event);
1146
+ this.parent.quickPopup.morePopup.hide();
1147
+ if (this.parent.headerModule) {
1148
+ this.parent.headerModule.hideHeaderPopup();
1149
+ }
1150
+ if (this.parent.inlineModule) {
1151
+ this.parent.inlineModule.removeInlineAppointmentElement();
1152
+ }
1153
+ }
1154
+ private isPreventAction(e: Event): boolean {
1155
+ const target: Element = closest((e.target as Element), '.' + cls.RESOURCE_GROUP_CELLS_CLASS);
1156
+ if (this.parent.activeView.isTimelineView() && !isNullOrUndefined(target)) {
1157
+ return true;
1158
+ }
1159
+ return false;
1160
+ }
1161
+ private processTabOnResourceCells(target: Element, isReverse: boolean): void {
1162
+ const tabElements: Element[] = [].slice.call(this.parent.element.querySelectorAll('[tabIndex="0"]'));
1163
+ const currentTabIndex: number = tabElements.indexOf(target);
1164
+ const nextTabElement: Element = !isReverse ? tabElements[currentTabIndex + 1] : tabElements[currentTabIndex - 1];
1165
+ if (nextTabElement && nextTabElement.classList.contains(cls.RESOURCE_CELLS_CLASS)) {
1166
+ const groupIndex: number = parseInt(nextTabElement.getAttribute('data-group-index'), 10);
1167
+ if (this.parent.virtualScrollModule) {
1168
+ const resColWrap: HTMLElement = this.parent.element.querySelector('.' + cls.RESOURCE_COLUMN_WRAP_CLASS);
1169
+ const resCells: HTMLElement[] = [].slice.call(this.parent.element.querySelectorAll('.' + cls.RESOURCE_CELLS_CLASS));
1170
+ resCells.forEach((element: HTMLElement) => {
1171
+ if (element.getBoundingClientRect().top < resColWrap.getBoundingClientRect().top) {
1172
+ element.setAttribute('tabindex', '-1');
1173
+ }
1174
+ });
1175
+ } else {
1176
+ this.setScrollPosition(groupIndex);
1177
+ }
1178
+ }
1179
+ }
1180
+ private setScrollPosition(index: number): void {
1181
+ const workCell: HTMLElement =
1182
+ this.parent.element.querySelector(`.${cls.WORK_CELLS_CLASS}[data-group-index="${index}"]`) as HTMLElement;
1183
+ if (workCell) {
1184
+ this.parent.element.querySelector('.' + cls.CONTENT_WRAP_CLASS).scrollTop = workCell.offsetTop;
1185
+ }
1186
+ }
1187
+
1188
+ private createClipboardElement(): void {
1189
+ if (!this.parent.allowClipboard) {
1190
+ return;
1191
+ }
1192
+ this.clipBoardTextArea = this.parent.createElement('textarea', {
1193
+ className: cls.CLIPBOARD_CLASS,
1194
+ attrs: {
1195
+ tabindex: '-1'
1196
+ }
1197
+ }) as HTMLInputElement;
1198
+ this.parent.element.appendChild(this.clipBoardTextArea);
1199
+ }
1200
+
1201
+ private setCopy(copiedEventData?: Record<string, any>[], e?: KeyboardEventArgs): void {
1202
+ if (window.getSelection().toString() !== '') {
1203
+ return;
1204
+ }
1205
+ this.clipBoardTextArea.value = '';
1206
+ let eventDetails: Record<string, any> = { elements: [], eventDatas: [] };
1207
+ if (copiedEventData) {
1208
+ const filteredContent: Record<string, any>[] = this.filterReadOnlyEvent(copiedEventData);
1209
+ eventDetails.eventDatas = filteredContent;
1210
+ } else {
1211
+ if (!this.parent || !this.parent.eventBase) {
1212
+ return;
1213
+ }
1214
+ const selectedEvents: EventClickArgs = this.parent.eventBase.getSelectedEvents();
1215
+ selectedEvents.event = selectedEvents && selectedEvents.event ? Array.isArray(selectedEvents.event)
1216
+ ? this.filterReadOnlyEvent(selectedEvents.event) :
1217
+ this.filterReadOnlyEvent([selectedEvents.event]) : undefined;
1218
+ if (selectedEvents.element && selectedEvents.event) {
1219
+ eventDetails = {
1220
+ elements: selectedEvents.element,
1221
+ eventDatas: selectedEvents.event
1222
+ };
1223
+ }
1224
+ else {
1225
+ eventDetails.element = [e.target];
1226
+ eventDetails.eventDatas = [this.parent.getEventDetails(e.target as Element)];
1227
+ }
1228
+ }
1229
+ this.clipBoardTextArea.value = JSON.stringify(eventDetails.eventDatas, undefined, 2);
1230
+ if (!/ipad|ipod|iphone/i.test(Browser.userAgent)) {
1231
+ this.clipBoardTextArea.select();
1232
+ } else {
1233
+ this.clipBoardTextArea.setSelectionRange(0, this.clipBoardTextArea.value.length);
1234
+ }
1235
+ if (!isNullOrUndefined((navigator as any).clipboard)) {
1236
+ (navigator as any).clipboard.writeText(this.clipBoardTextArea.value)
1237
+ .then(() => {
1238
+ this.clipBoardTextArea.blur();
1239
+ window.getSelection().removeAllRanges();
1240
+ if (e) {
1241
+ const closestAppointment: Element = (e.target as HTMLElement).closest('.' + cls.APPOINTMENT_CLASS);
1242
+ if (closestAppointment) {
1243
+ (closestAppointment as HTMLElement).focus();
1244
+ }
1245
+ }
1246
+ })
1247
+ .catch((err: Error) => {
1248
+ throw err;
1249
+ });
1250
+ }
1251
+ }
1252
+
1253
+ private filterReadOnlyEvent(args: Record<string, any>[]): Record<string, any>[] {
1254
+ return args.filter((event: Record<string, any>) => !event[this.parent.eventFields.isReadonly]);
1255
+ }
1256
+
1257
+ public processClipboardAction(isCut: boolean, copiedEventData?: Record<string, any>[], e?: KeyboardEventArgs): void {
1258
+ if (!this.parent.allowClipboard) {
1259
+ return;
1260
+ }
1261
+ this.isCutAction = isCut;
1262
+ this.isCutContentPasted = false;
1263
+ this.parent.closeQuickInfoPopup();
1264
+ this.setCopy(copiedEventData ? copiedEventData : undefined, e);
1265
+ }
1266
+
1267
+ public pasteHandler(clipboardEvent: ClipboardEvent, targetElement?: HTMLElement): void {
1268
+ if (!this.parent.allowClipboard || this.isCutContentPasted) {
1269
+ return;
1270
+ }
1271
+ const target: Element[] = this.parent.getSelectedCells().length > 0
1272
+ ? this.parent.getSelectedCells()
1273
+ : (targetElement ? [targetElement] : []);
1274
+ if (target.length === 0 || this.parent.eventWindow.dialogObject.visible ||
1275
+ this.parent.quickPopup.quickPopup.element.classList.contains(cls.POPUP_OPEN)) {
1276
+ return;
1277
+ }
1278
+ const clipboardData: DataTransfer = clipboardEvent.clipboardData || (clipboardEvent as any).event.clipboardData
1279
+ || (window as any).clipboardData;
1280
+ const pastedData: string = clipboardData.getData('text');
1281
+ if (pastedData === '[]' || pastedData === '') {
1282
+ return;
1283
+ }
1284
+ let parsedData: any;
1285
+ try {
1286
+ parsedData = JSON.parse(pastedData);
1287
+ } catch {
1288
+ parsedData = pastedData;
1289
+ }
1290
+ const args: Record<string, any> = { cancel: false, data: parsedData, element: target };
1291
+ this.parent.trigger(event.beforePaste, args, (pasteEventArgs: BeforePasteEventArgs) => {
1292
+ if (!pasteEventArgs.cancel) {
1293
+ if (!Array.isArray(pasteEventArgs.data) || !pasteEventArgs.data.every((item: any) => typeof item === 'object')) {
1294
+ this.clipBoardTextArea.value = '';
1295
+ return;
1296
+ }
1297
+ this.processPaste(pasteEventArgs.data, targetElement);
1298
+ if (this.isCutAction) {
1299
+ this.isCutContentPasted = true;
1300
+ }
1301
+ }
1302
+ else {
1303
+ this.parent.closeQuickInfoPopup();
1304
+ return;
1305
+ }
1306
+ });
1307
+ }
1308
+
1309
+ private processPaste(data?: any, targetElement?: HTMLElement): void {
1310
+ if (!this.parent || !this.parent.eventWindow || !this.parent.eventBase || !data || !data.length) {
1311
+ return;
1312
+ }
1313
+ const target: Element[] = this.getTargetElements(targetElement);
1314
+ const cellDetails: CellClickEventArgs = this.parent.getCellDetails(target);
1315
+ const eventDetails: Record<string, any> = {};
1316
+ this.parent.eventWindow.convertToEventData(cellDetails, eventDetails);
1317
+ const isAllDay: boolean = this.isAllDayEvent(target);
1318
+ const resources: ResourceDetails | null = this.parent.getResourcesByIndex(cellDetails.groupIndex);
1319
+ const processedData: Record<string, any>[] = this.processEventData(data, isAllDay, cellDetails, resources);
1320
+ const selectedEventData: Array<Record<string, any>> = this.parent.eventBase.sortByDateTime(processedData);
1321
+ const saveData: Record<string, any>[] = this.prepareEventData(selectedEventData, eventDetails);
1322
+ removeClass(target, cls.SELECTED_CELL_CLASS);
1323
+ if (saveData.length > 0) {
1324
+ if (this.isCutAction) {
1325
+ this.parent.saveEvent(saveData);
1326
+ } else {
1327
+ this.parent.addEvent(saveData);
1328
+ }
1329
+ }
1330
+ }
1331
+
1332
+ private getTargetElements(targetElement?: HTMLElement): Element[] {
1333
+ return this.parent.getSelectedCells().length > 0
1334
+ ? this.parent.getSelectedCells()
1335
+ : (targetElement ? [targetElement] : []);
1336
+ }
1337
+
1338
+ private isAllDayEvent(target: Element[]): boolean {
1339
+ return !target.some((cell: Element) => cell.classList.contains(cls.WORK_CELLS_CLASS));
1340
+ }
1341
+
1342
+ private processEventData(
1343
+ data: any[], isAllDay: boolean, cellDetails: CellClickEventArgs, resources: ResourceDetails | null): Record<string, any>[] {
1344
+ return data.map((item: Record<string, any>) => {
1345
+ const processedItem: Record<string, any> = {
1346
+ ...item,
1347
+ [this.parent.eventFields.startTime]: new Date(item[this.parent.eventFields.startTime]),
1348
+ [this.parent.eventFields.endTime]: new Date(item[this.parent.eventFields.endTime]),
1349
+ [this.parent.eventFields.recurrenceID]: null as any,
1350
+ [this.parent.eventFields.isAllDay]: isAllDay
1351
+ };
1352
+ this.adjustEventTime(processedItem, isAllDay, item.IsAllDay, cellDetails);
1353
+
1354
+ if (resources && resources.groupData) {
1355
+ Object.assign(processedItem, resources.groupData);
1356
+ }
1357
+
1358
+ return processedItem;
1359
+ });
1360
+ }
1361
+
1362
+ private adjustEventTime(
1363
+ processedItem: Record<string, any>, isAllDay: boolean, itemIsAllDay: boolean, cellDetails: CellClickEventArgs): void {
1364
+ const isTimeScaleEnabled: boolean = this.parent.activeViewOptions.timeScale.enable;
1365
+ if ((['Day', 'Week', 'WorkWeek'].indexOf(this.parent.currentView) !== -1) && isTimeScaleEnabled) {
1366
+ if (isAllDay) {
1367
+ processedItem[this.parent.eventFields.endTime] = new Date(processedItem[this.parent.eventFields.startTime]);
1368
+ processedItem[this.parent.eventFields.endTime].setDate(processedItem[this.parent.eventFields.startTime].getDate() + 1);
1369
+
1370
+ } else if (itemIsAllDay) {
1371
+ processedItem[this.parent.eventFields.startTime] = new Date(processedItem[this.parent.eventFields.startTime]
1372
+ .setHours(0, 0, 0, 0));
1373
+ processedItem[this.parent.eventFields.endTime] = new Date(processedItem[this.parent.eventFields.startTime].getTime() + (
1374
+ cellDetails.endTime.getTime() - cellDetails.startTime.getTime()));
1375
+ }
1376
+ } else if (itemIsAllDay) {
1377
+ processedItem[this.parent.eventFields.isAllDay] = true;
1378
+ processedItem[this.parent.eventFields.startTime] = new Date(processedItem[this.parent.eventFields.startTime]
1379
+ .setHours(0, 0, 0, 0));
1380
+ processedItem[this.parent.eventFields.endTime] = new Date(processedItem[this.parent.eventFields.startTime].getTime() +
1381
+ (processedItem[this.parent.eventFields.endTime].getTime() - processedItem[this.parent.eventFields.startTime].getTime()));
1382
+ }
1383
+ }
1384
+
1385
+ private prepareEventData(selectedEventData: Array<Record<string, any>>, eventDetails: Record<string, any>): Record<string, any>[] {
1386
+ const initialEventTime: Date = new Date(selectedEventData[0][this.parent.eventFields.startTime]);
1387
+ let uniqueId: number = this.parent.getEventMaxID() as number;
1388
+ return selectedEventData.map((events: Record<string, any>) => {
1389
+ const totalDuration: number = this.calculateTotalDuration(events, eventDetails, initialEventTime);
1390
+ const cloneDetails: Record<string, any> = this.isCutAction ? events : { ...events };
1391
+ if (!events[this.parent.eventFields.recurrenceRule]) {
1392
+ cloneDetails[this.parent.eventFields.startTime] =
1393
+ new Date(new Date(cloneDetails[this.parent.eventFields.startTime]).setMilliseconds(totalDuration));
1394
+ cloneDetails[this.parent.eventFields.endTime] =
1395
+ new Date(new Date(cloneDetails[this.parent.eventFields.endTime]).setMilliseconds(totalDuration));
1396
+ }
1397
+ if (!this.isCutAction) {
1398
+ cloneDetails[this.parent.eventFields.id] = uniqueId++;
1399
+ }
1400
+ return cloneDetails;
1401
+ });
1402
+ }
1403
+
1404
+ private calculateTotalDuration(events: Record<string, any>, eventDetails: Record<string, any>, initialEventTime: Date): number {
1405
+ if ((['Month', 'TimelineMonth', 'TimelineYear'].indexOf(this.parent.currentView) !== -1) || !this.parent.activeViewOptions.timeScale.enable) {
1406
+ const eventStartTime: Date = new Date(events[this.parent.eventFields.startTime]);
1407
+ return eventDetails[this.parent.eventFields.startTime].getTime() - initialEventTime.getTime() +
1408
+ (eventStartTime.getHours() * 3600000 + eventStartTime.getMinutes() * 60000 + eventStartTime.getSeconds() * 1000);
1409
+ } else {
1410
+ return eventDetails[this.parent.eventFields.startTime].getTime() - initialEventTime.getTime();
1411
+ }
1412
+ }
1413
+
1414
+ /**
1415
+ * Get module name.
1416
+ *
1417
+ * @returns {string} Returns the module name.
1418
+ */
1419
+ protected getModuleName(): string {
1420
+ return 'keyboard';
1421
+ }
1422
+ /**
1423
+ * To destroy the keyboard module.
1424
+ *
1425
+ * @returns {void}
1426
+ * @private
1427
+ */
1428
+ public destroy(): void {
1429
+ this.removeEventListener();
1430
+ this.selectedCells = [];
1431
+ this.clipBoardTextArea = null;
1432
+ this.keyboardModule.destroy();
1433
+ }
1434
+
1435
+ }