@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,1699 @@
1
+ /* eslint-disable max-len */
2
+ import { isNullOrUndefined, L10n, getDefaultDateObject, getValue, cldrData } from '@syncfusion/ej2-base';
3
+ import { MS_PER_DAY, addDays, resetTime, capitalizeFirstWord } from '../schedule/base/util';
4
+ import { CalendarUtil, Islamic, Gregorian, CalendarType } from '../common/calendar-util';
5
+ import { Timezone } from '../schedule/timezone/timezone';
6
+
7
+ /**
8
+ * Date Generator from Recurrence Rule
9
+ */
10
+
11
+ /**
12
+ * Generate Summary from Recurrence Rule
13
+ *
14
+ * @param {string} rule Accepts the Recurrence rule
15
+ * @param {L10n} localeObject Accepts the locale object
16
+ * @param {string} locale Accepts the locale name
17
+ * @param {CalendarType} calendarType Accepts the calendar type
18
+ * @returns {string} Returns the summary string from given recurrence rule
19
+ */
20
+ export function generateSummary(rule: string, localeObject: L10n, locale: string, calendarType: CalendarType = 'Gregorian'): string {
21
+ const ruleObject: RecRule = extractObjectFromRule(rule);
22
+ let summary: string = localeObject.getConstant(EVERY) + ' ';
23
+ let cldrObj: string[];
24
+ let cldrObj1: string[];
25
+ const calendarMode: string = calendarType.toLowerCase();
26
+ if (locale === 'en' || locale === 'en-US') {
27
+ const nameSpace1: string = 'months.stand-alone.abbreviated';
28
+ const nameSpace: string = 'days.stand-alone.abbreviated';
29
+ cldrObj1 = <string[]>(getValue(nameSpace1, getDefaultDateObject(calendarMode)));
30
+ cldrObj = <string[]>(getValue(nameSpace, getDefaultDateObject(calendarMode)));
31
+ } else {
32
+ const nameSpace1: string =
33
+ 'main.' + locale + '.dates.calendars.' + calendarMode + '.months.stand-alone.abbreviated';
34
+ const nameSpace: string =
35
+ 'main.' + locale + '.dates.calendars.' + calendarMode + '.days.stand-alone.abbreviated';
36
+ cldrObj1 =
37
+ <string[]>(getValue(nameSpace1, cldrData));
38
+ cldrObj =
39
+ <string[]>(getValue(nameSpace, cldrData));
40
+ }
41
+ if (ruleObject.interval > 1) {
42
+ summary += ruleObject.interval + ' ';
43
+ }
44
+ switch (ruleObject.freq) {
45
+ case 'DAILY':
46
+ summary += localeObject.getConstant(DAYS);
47
+ break;
48
+ case 'WEEKLY':
49
+ summary += localeObject.getConstant(WEEKS) + ' ' + localeObject.getConstant(ON) + ' ';
50
+ ruleObject.day.forEach((day: string, index: number) => {
51
+ summary += capitalizeFirstWord(<string>getValue(DAYINDEXOBJECT[`${day}`], cldrObj), 'single');
52
+ summary += (((ruleObject.day.length - 1) === index) ? '' : ', ');
53
+ });
54
+ break;
55
+ case 'MONTHLY':
56
+ summary += localeObject.getConstant(MONTHS) + ' ' + localeObject.getConstant(ON) + ' ';
57
+ summary += getMonthSummary(ruleObject, cldrObj, localeObject);
58
+ break;
59
+ case 'YEARLY':
60
+ summary += localeObject.getConstant(YEARS) + ' ' + localeObject.getConstant(ON) + ' ';
61
+ summary += capitalizeFirstWord(<string>getValue((ruleObject.month[0]).toString(), cldrObj1), 'single') + ' ';
62
+ summary += getMonthSummary(ruleObject, cldrObj, localeObject);
63
+ break;
64
+ }
65
+ if (ruleObject.count) {
66
+ summary += ', ' + (ruleObject.count) + ' ' + localeObject.getConstant(TIMES);
67
+ } else if (ruleObject.until) {
68
+ const tempDate: Date = ruleObject.until;
69
+ summary += ', ' + localeObject.getConstant(UNTIL)
70
+ + ' ' + tempDate.getDate()
71
+ + ' ' + capitalizeFirstWord(<string>getValue((tempDate.getMonth() + 1).toString(), cldrObj1), 'single')
72
+ + ' ' + tempDate.getFullYear();
73
+ }
74
+ return summary;
75
+ }
76
+
77
+ /**
78
+ * Generates Month summary
79
+ *
80
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
81
+ * @param {string[]} cldrObj Accepts the collections of month name from calendar
82
+ * @param {L10n} localeObj Accepts the locale object
83
+ * @returns {string} Returns the month summary string from given recurrence rule object
84
+ * @private
85
+ */
86
+ function getMonthSummary(ruleObject: RecRule, cldrObj: string[], localeObj: L10n): string {
87
+ let summary: string = '';
88
+ if (ruleObject.monthDay.length) {
89
+ summary += ruleObject.monthDay[0];
90
+ } else if (ruleObject.day) {
91
+ const pos: number = ruleObject.setPosition - 1;
92
+ summary += localeObj.getConstant(WEEKPOS[pos > -1 ? pos : (WEEKPOS.length - 1)])
93
+ + ' ' + capitalizeFirstWord(<string>getValue(DAYINDEXOBJECT[ruleObject.day[0]], cldrObj), 'single');
94
+ }
95
+ return summary;
96
+ }
97
+
98
+ /**
99
+ * Generates the date collections from the given recurrence rule
100
+ *
101
+ * @param {Date} startDate Accepts the rule start date
102
+ * @param {string} rule Accepts the recurrence rule
103
+ * @param {string} excludeDate Accepts the exception dates in string format
104
+ * @param {number} startDayOfWeek Accepts the start day index of week
105
+ * @param {number} maximumCount Accepts the maximum number count to generate date collections
106
+ * @param {Date} viewDate Accepts the current date instead of start date
107
+ * @param {CalendarType} calendarMode Accepts the calendar type
108
+ * @param {string} newTimezone Accepts the timezone name
109
+ * @returns {number[]} Returns the collection of dates
110
+ */
111
+ export function generate(startDate: Date, rule: string, excludeDate: string, startDayOfWeek: number, maximumCount: number = MAXOCCURRENCE, viewDate: Date = null, calendarMode: CalendarType = 'Gregorian', newTimezone: string = null): number[] {
112
+ const ruleObject: RecRule = extractObjectFromRule(rule);
113
+ let cacheDate: Date; calendarUtil = getCalendarUtil(calendarMode);
114
+ const data: number[] = [];
115
+ const modifiedDate: Date = new Date(startDate.getTime());
116
+ tempExcludeDate = [];
117
+ const tempDate: string[] = isNullOrUndefined(excludeDate) ? [] : excludeDate.split(',');
118
+ const tz: Timezone = new Timezone();
119
+ tempDate.forEach((content: string) => {
120
+ let parsedDate: Date = getDateFromRecurrenceDateString(content);
121
+ if (newTimezone) {
122
+ parsedDate = tz.add(new Date(parsedDate.getTime()), newTimezone);
123
+ }
124
+ tempExcludeDate.push(new Date(parsedDate.getTime()).setHours(0, 0, 0, 0));
125
+ });
126
+ ruleObject.recExceptionCount = !isNullOrUndefined(ruleObject.count) ? tempExcludeDate.length : 0;
127
+
128
+ if (viewDate && viewDate > startDate && !ruleObject.count) {
129
+ tempViewDate = new Date(new Date(viewDate.getTime()).setHours(0, 0, 0));
130
+ } else {
131
+ tempViewDate = null;
132
+ }
133
+ if (!ruleObject.until && tempViewDate) {
134
+ cacheDate = new Date(tempViewDate.getTime());
135
+ cacheDate.setDate(tempViewDate.getDate() + maximumCount * (ruleObject.interval));
136
+ ruleObject.until = cacheDate;
137
+ }
138
+ if (ruleObject.until && startDate > ruleObject.until) {
139
+ return data;
140
+ }
141
+ maxOccurrence = maximumCount;
142
+ startDayOfWeek = startDayOfWeek || 0;
143
+ setFirstDayOfWeek(DAYINDEX[parseInt(startDayOfWeek.toString(), 10)]);
144
+ if (ruleObject.until) {
145
+ const end: Date = resetTime(ruleObject.until);
146
+ ruleObject.until = new Date(end.getFullYear(), end.getMonth(), end.getDate(), 23, 59, 59);
147
+ }
148
+ switch (ruleObject.freq) {
149
+ case 'DAILY':
150
+ dailyType(modifiedDate, ruleObject.until, data, ruleObject);
151
+ break;
152
+ case 'WEEKLY':
153
+ weeklyType(modifiedDate, ruleObject.until, data, ruleObject, startDayOfWeek);
154
+ break;
155
+ case 'MONTHLY':
156
+ monthlyType(modifiedDate, ruleObject.until, data, ruleObject);
157
+ break;
158
+ case 'YEARLY':
159
+ yearlyType(modifiedDate, ruleObject.until, data, ruleObject);
160
+ }
161
+ return data;
162
+ }
163
+
164
+ /**
165
+ * Generate date object from given date string
166
+ *
167
+ * @param {string} recDateString Accepts the exception date as string
168
+ * @returns {Date} Returns the date from exception date string
169
+ */
170
+ export function getDateFromRecurrenceDateString(recDateString: string): Date {
171
+ return new Date(recDateString.substr(0, 4) +
172
+ '-' + recDateString.substr(4, 2) +
173
+ '-' + recDateString.substr(6, 5) +
174
+ ':' + recDateString.substr(11, 2) +
175
+ ':' + recDateString.substr(13));
176
+ }
177
+
178
+ /**
179
+ * Internal method to handle exclude date
180
+ *
181
+ * @param {number[]} data Accepts the exception date collections
182
+ * @param {number} date Accepts the new exclude date
183
+ * @returns {void}
184
+ * @private
185
+ */
186
+ function excludeDateHandler(data: number[], date: number): void {
187
+ const zeroIndex: number = new Date(date).setHours(0, 0, 0, 0);
188
+ if (tempExcludeDate.indexOf(zeroIndex) === -1 && (!tempViewDate || zeroIndex >= tempViewDate.getTime())) {
189
+ data.push(date);
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Internal method for get date count
195
+ *
196
+ * @param {Date} startDate Accepts the date
197
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
198
+ * @returns {number} Returns the number of date count
199
+ * @private
200
+ */
201
+ function getDateCount(startDate: Date, ruleObject: RecRule): number {
202
+ let count: number = maxOccurrence;
203
+ if (ruleObject.count) {
204
+ count = ruleObject.count;
205
+ } else if (ruleObject.until) {
206
+ if (ruleObject.freq === 'DAILY' || ruleObject.freq === 'WEEKLY') {
207
+ count = Math.floor((ruleObject.until.getTime() - startDate.getTime()) / MS_PER_DAY) + 1;
208
+ } else if (ruleObject.freq === 'MONTHLY' || ruleObject.freq === 'YEARLY') {
209
+ count = Math.floor(((ruleObject.until.getMonth() + 12 * ruleObject.until.getFullYear()) -
210
+ (startDate.getMonth() + 12 * startDate.getFullYear())) / ruleObject.interval) +
211
+ (ruleObject.day.length > 1 ? (Math.floor((ruleObject.until.getTime() - startDate.getTime()) / MS_PER_DAY) + 1) : 1);
212
+ if (ruleObject.freq === 'YEARLY') {
213
+ count = ruleObject.month.length > 1 ? ((count as number) * ruleObject.month.length) : count;
214
+ }
215
+ }
216
+ }
217
+ return count;
218
+ }
219
+
220
+ /**
221
+ * Internal method for daily type recurrence rule
222
+ *
223
+ * @param {Date} startDate Accepts the strat date
224
+ * @param {Date} endDate Accepts the end date
225
+ * @param {number[]} data Accepts the collection of dates
226
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
227
+ * @returns {void}
228
+ * @private
229
+ */
230
+ function dailyType(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
231
+ const tempDate: Date = new Date(startDate.getTime());
232
+ const interval: number = ruleObject.interval;
233
+ const expectedCount: number = getDateCount(startDate, ruleObject);
234
+ let state: boolean;
235
+ const expectedDays: string[] = ruleObject.day;
236
+ while (compareDates(tempDate, endDate)) {
237
+ state = true;
238
+ state = validateRules(tempDate, ruleObject);
239
+ if (state && (expectedDays.indexOf(DAYINDEX[tempDate.getDay()]) > -1 || expectedDays.length === 0)) {
240
+ excludeDateHandler(data, tempDate.getTime());
241
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
242
+ break;
243
+ }
244
+ }
245
+ tempDate.setDate(tempDate.getDate() + interval);
246
+ if (tempDate.getHours() !== startDate.getHours()) {
247
+ tempDate.setHours(startDate.getHours());
248
+ }
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Internal method for weekly type recurrence rule
254
+ *
255
+ * @param {Date} startDate Accepts the strat date
256
+ * @param {Date} endDate Accepts the end date
257
+ * @param {number[]} data Accepts the collection of dates
258
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
259
+ * @param {number} startDayOfWeek Accepts the start day index of week
260
+ * @returns {void}
261
+ * @private
262
+ */
263
+ function weeklyType(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule, startDayOfWeek: number): void {
264
+ let tempDate: Date = new Date(startDate.getTime());
265
+ if (!ruleObject.day.length) {
266
+ ruleObject.day.push(DAYINDEX[startDate.getDay()]);
267
+ }
268
+ const interval: number = ruleObject.interval;
269
+ const expectedDays: string[] = ruleObject.day;
270
+ const expectedCount: number = getDateCount(startDate, ruleObject);
271
+ let weekState: boolean = true;
272
+ let wkstIndex: number;
273
+ let weekCollection: number[][] = [];
274
+ if (expectedDays.length > 1) {
275
+ if (isNullOrUndefined(ruleObject.wkst) || ruleObject.wkst === '') {
276
+ ruleObject.wkst = dayIndex[0];
277
+ }
278
+ wkstIndex = DAYINDEX.indexOf(ruleObject.wkst);
279
+ while (compareDates(tempDate, endDate)) {
280
+ let startDateDiff: number = DAYINDEX.indexOf(DAYINDEX[tempDate.getDay()]) - wkstIndex;
281
+ startDateDiff = startDateDiff === -1 ? 6 : startDateDiff;
282
+ const weekstartDate: Date = addDays(tempDate, -startDateDiff);
283
+ let weekendDate: Date = addDays(weekstartDate, 6);
284
+ let compareTempDate: Date = new Date(tempDate.getTime());
285
+ weekendDate = resetTime(weekendDate);
286
+ compareTempDate = resetTime(compareTempDate);
287
+ while (weekendDate >= compareTempDate) {
288
+ if (expectedDays.indexOf(DAYINDEX[tempDate.getDay()]) > -1) {
289
+ weekCollection.push([tempDate.getTime()]);
290
+ }
291
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
292
+ break;
293
+ }
294
+ tempDate.setDate(tempDate.getDate() + 1);
295
+ if (tempDate.getHours() !== startDate.getHours()) {
296
+ tempDate.setHours(startDate.getHours());
297
+ }
298
+ compareTempDate = new Date(tempDate.getTime());
299
+ compareTempDate = resetTime(compareTempDate);
300
+ }
301
+ tempDate.setDate(tempDate.getDate() - 1);
302
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
303
+ break;
304
+ }
305
+ tempDate.setDate((tempDate.getDate()) + 1 + ((interval - 1) * 7));
306
+ insertDataCollection(weekCollection, weekState, startDate, endDate, data, ruleObject);
307
+ weekCollection = [];
308
+ }
309
+ } else {
310
+ tempDate = getStartDateForWeek(startDate, ruleObject.day);
311
+ if (interval > 1 && dayIndex.indexOf(ruleObject.day[0]) < (startDate.getDay() - startDayOfWeek)) {
312
+ tempDate.setDate(tempDate.getDate() + ((interval - 1) * 7));
313
+ }
314
+ while (compareDates(tempDate, endDate)) {
315
+ weekState = validateRules(tempDate, ruleObject);
316
+ if (weekState && (expectedDays.indexOf(DAYINDEX[tempDate.getDay()]) > -1)) {
317
+ excludeDateHandler(data, tempDate.getTime());
318
+ }
319
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
320
+ break;
321
+ }
322
+ tempDate.setDate(tempDate.getDate() + (interval * 7));
323
+ }
324
+ insertDataCollection(weekCollection, weekState, startDate, endDate, data, ruleObject);
325
+ weekCollection = [];
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Internal method for monthly type recurrence rule
331
+ *
332
+ * @param {Date} startDate Accepts the strat date
333
+ * @param {Date} endDate Accepts the end date
334
+ * @param {number[]} data Accepts the collection of dates
335
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
336
+ * @returns {void}
337
+ * @private
338
+ */
339
+ function monthlyType(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
340
+ // Set monthday value if BYDAY, BYMONTH and Month day property is not set based on start date
341
+ if (!ruleObject.month.length && !ruleObject.day.length && !ruleObject.monthDay.length) {
342
+ ruleObject.monthDay.push(startDate.getDate());
343
+ if (ruleObject.freq === 'YEARLY') {
344
+ ruleObject.month.push(startDate.getMonth() + 1);
345
+ }
346
+ } else if (ruleObject.month.length > 0 && !ruleObject.day.length && !ruleObject.monthDay.length) {
347
+ ruleObject.monthDay.push(startDate.getDate());
348
+ }
349
+ const ruleType: MonthlyType = validateMonthlyRuleType(ruleObject);
350
+ switch (ruleType) {
351
+ case 'day':
352
+ switch (ruleObject.freq) {
353
+ case 'MONTHLY':
354
+ monthlyDayTypeProcessforMonthFreq(startDate, endDate, data, ruleObject);
355
+ break;
356
+ case 'YEARLY':
357
+ monthlyDayTypeProcess(startDate, endDate, data, ruleObject);
358
+ break;
359
+ }
360
+ break;
361
+ case 'both':
362
+ case 'date':
363
+ switch (ruleObject.freq) {
364
+ case 'MONTHLY':
365
+ monthlyDateTypeProcessforMonthFreq(startDate, endDate, data, ruleObject);
366
+ break;
367
+ case 'YEARLY':
368
+ monthlyDateTypeProcess(startDate, endDate, data, ruleObject);
369
+ break;
370
+ }
371
+ break;
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Internal method for yearly type recurrence rule
377
+ *
378
+ * @param {Date} startDate Accepts the strat date
379
+ * @param {Date} endDate Accepts the end date
380
+ * @param {number[]} data Accepts the collection of dates
381
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
382
+ * @returns {void}
383
+ * @private
384
+ */
385
+ function yearlyType(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
386
+ const typeValue: YearRuleType = checkYearlyType(ruleObject);
387
+ switch (typeValue) {
388
+ case 'MONTH':
389
+ monthlyType(startDate, endDate, data, ruleObject);
390
+ break;
391
+ case 'WEEKNO':
392
+ processWeekNo(startDate, endDate, data, ruleObject);
393
+ break;
394
+ case 'YEARDAY':
395
+ processYearDay(startDate, endDate, data, ruleObject);
396
+ break;
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Internal method for process week no
402
+ *
403
+ * @param {Date} startDate Accepts the strat date
404
+ * @param {Date} endDate Accepts the end date
405
+ * @param {number[]} data Accepts the collection of dates
406
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
407
+ * @returns {void}
408
+ * @private
409
+ */
410
+ function processWeekNo(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
411
+ let stDate: Date = calendarUtil.getYearLastDate(startDate, 0);
412
+ let tempDate: Date;
413
+ const expectedCount: number = getDateCount(startDate, ruleObject);
414
+ let state: boolean;
415
+ let startDay: number;
416
+ let firstWeekSpan: number;
417
+ const weekNos: number[] = ruleObject.weekNo;
418
+ let weekNo: number;
419
+ let maxDate: number;
420
+ let minDate: number;
421
+ let weekCollection: number[][] = [];
422
+ const expectedDays: string[] = ruleObject.day;
423
+ while (compareDates(stDate, endDate)) {
424
+ startDay = dayIndex.indexOf(DAYINDEX[stDate.getDay()]);
425
+ firstWeekSpan = (6 - startDay) + 1;
426
+ for (let index: number = 0; index < weekNos.length; index++) {
427
+ weekNo = weekNos[parseInt(index.toString(), 10)];
428
+ weekNo = (weekNo > 0) ? weekNo : 53 + weekNo + 1;
429
+ maxDate = (weekNo === 1) ? firstWeekSpan : firstWeekSpan + ((weekNo - 1) * 7);
430
+ minDate = (weekNo === 1) ? firstWeekSpan - 7 : firstWeekSpan + ((weekNo - 2) * 7);
431
+ while (minDate < maxDate) {
432
+ tempDate = new Date(stDate.getTime() + (MS_PER_DAY * minDate));
433
+ if (expectedDays.length === 0 || expectedDays.indexOf(DAYINDEX[tempDate.getDay()]) > -1) {
434
+ if (isNullOrUndefined(ruleObject.setPosition)) {
435
+ insertDateCollection(state, startDate, endDate, data, ruleObject, tempDate.getTime());
436
+ } else {
437
+ weekCollection.push([tempDate.getTime()]);
438
+ }
439
+ }
440
+ minDate++;
441
+ }
442
+ }
443
+ if (!isNullOrUndefined(ruleObject.setPosition)) {
444
+ insertDatasIntoExistingCollection(weekCollection, state, startDate, endDate, data, ruleObject);
445
+ }
446
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
447
+ return;
448
+ }
449
+ stDate = calendarUtil.getYearLastDate(tempDate, ruleObject.interval);
450
+ weekCollection = [];
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Internal method for process year day
456
+ *
457
+ * @param {Date} startDate Accepts the strat date
458
+ * @param {Date} endDate Accepts the end date
459
+ * @param {number[]} data Accepts the collection of dates
460
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
461
+ * @returns {void}
462
+ * @private
463
+ */
464
+ function processYearDay(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
465
+ let stDate: Date = calendarUtil.getYearLastDate(startDate, 0);
466
+ let tempDate: Date;
467
+ const expectedCount: number = getDateCount(startDate, ruleObject);
468
+ let state: boolean;
469
+ let dateCollection: number[][] = [];
470
+ let date: number;
471
+ const expectedDays: string[] = ruleObject.day;
472
+ while (compareDates(stDate, endDate)) {
473
+ for (let index: number = 0; index < ruleObject.yearDay.length; index++) {
474
+ date = ruleObject.yearDay[parseInt(index.toString(), 10)];
475
+ tempDate = new Date(stDate.getTime());
476
+ if ((date === calendarUtil.getLeapYearDaysCount() || date === -calendarUtil.getLeapYearDaysCount()) &&
477
+ (!calendarUtil.isLeapYear(calendarUtil.getFullYear(tempDate), 1))) {
478
+ tempDate.setDate(tempDate.getDate() + 1);
479
+ continue;
480
+ }
481
+ tempDate.setDate(tempDate.getDate() + ((date < 0) ?
482
+ calendarUtil.getYearDaysCount(tempDate, 1) + 1 + date : date));
483
+ if (expectedDays.length === 0 || expectedDays.indexOf(DAYINDEX[tempDate.getDay()]) > -1) {
484
+ if (ruleObject.setPosition == null) {
485
+ insertDateCollection(state, startDate, endDate, data, ruleObject, tempDate.getTime());
486
+ } else {
487
+ dateCollection.push([tempDate.getTime()]);
488
+ }
489
+ }
490
+ }
491
+ if (!isNullOrUndefined(ruleObject.setPosition)) {
492
+ insertDatasIntoExistingCollection(dateCollection, state, startDate, endDate, data, ruleObject);
493
+ }
494
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
495
+ return;
496
+ }
497
+ stDate = calendarUtil.getYearLastDate(tempDate, ruleObject.interval);
498
+ dateCollection = [];
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Internal method to check yearly type
504
+ *
505
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
506
+ * @returns {YearRuleType} Returns the Yearly rule type object
507
+ * @private
508
+ */
509
+ function checkYearlyType(ruleObject: RecRule): YearRuleType {
510
+ if (ruleObject.yearDay.length) {
511
+ return 'YEARDAY';
512
+ } else if (ruleObject.weekNo.length) {
513
+ return 'WEEKNO';
514
+ }
515
+ return 'MONTH';
516
+ }
517
+
518
+ /**
519
+ * Internal method to initialize recurrence rule variables
520
+ *
521
+ * @param {Date} startDate Accepts the start date
522
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
523
+ * @returns {RuleData} Return the rule data object
524
+ * @private
525
+ */
526
+ function initializeRecRuleVariables(startDate: Date, ruleObject: RecRule): RuleData {
527
+ const ruleData: RuleData = {
528
+ monthCollection: [],
529
+ index: 0,
530
+ tempDate: new Date(startDate.getTime()),
531
+ mainDate: new Date(startDate.getTime()),
532
+ expectedCount: getDateCount(startDate, ruleObject),
533
+ monthInit: 0,
534
+ dateCollection: []
535
+ };
536
+ if (ruleObject.month.length) {
537
+ calendarUtil.setMonth(ruleData.tempDate, ruleObject.month[0], ruleData.tempDate.getDate());
538
+ }
539
+ return ruleData;
540
+ }
541
+
542
+ /**
543
+ * Internal method for process monthly date type recurrence rule
544
+ *
545
+ * @param {Date} startDate Accepts the strat date
546
+ * @param {Date} endDate Accepts the end date
547
+ * @param {number[]} data Accepts the collection of dates
548
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
549
+ * @returns {void}
550
+ * @private
551
+ */
552
+ function monthlyDateTypeProcess(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
553
+ if (ruleObject.month.length) {
554
+ monthlyDateTypeProcessforMonthFreq(startDate, endDate, data, ruleObject);
555
+ return;
556
+ }
557
+ const ruleData: RuleData = initializeRecRuleVariables(startDate, ruleObject);
558
+ let currentMonthDate: Date;
559
+ ruleData.tempDate = ruleData.mainDate = calendarUtil.getMonthStartDate(ruleData.tempDate);
560
+ while (compareDates(ruleData.tempDate, endDate)) {
561
+ currentMonthDate = new Date(ruleData.tempDate.getTime());
562
+ while (calendarUtil.isSameYear(currentMonthDate, ruleData.tempDate) &&
563
+ (ruleData.expectedCount && (data.length + ruleObject.recExceptionCount) <= ruleData.expectedCount)) {
564
+ if (ruleObject.month.length === 0 || (ruleObject.month.length > 0
565
+ && !calendarUtil.checkMonth(ruleData.tempDate, ruleObject.month))) {
566
+ processDateCollectionForByMonthDay(ruleObject, ruleData, endDate, false);
567
+ ruleData.beginDate = new Date(ruleData.tempDate.getTime());
568
+ ruleData.monthInit = setNextValidDate(
569
+ ruleData.tempDate, ruleObject, ruleData.monthInit, ruleData.beginDate);
570
+ } else {
571
+ calendarUtil.setValidDate(ruleData.tempDate, 1, 1);
572
+ ruleData.tempDate = getStartDateForWeek(ruleData.tempDate, ruleObject.day);
573
+ break;
574
+ }
575
+ }
576
+ ruleData.tempDate.setFullYear(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), currentMonthDate.getDate());
577
+ insertDataCollection(ruleData.dateCollection, ruleData.state, startDate, endDate, data, ruleObject);
578
+ if (calendarUtil.isLastMonth(ruleData.tempDate)) {
579
+ calendarUtil.setValidDate(ruleData.tempDate, 1, 1);
580
+ ruleData.tempDate = getStartDateForWeek(ruleData.tempDate, ruleObject.day);
581
+ }
582
+ if (ruleData.expectedCount && (data.length + ruleObject.recExceptionCount) >= ruleData.expectedCount) {
583
+ return;
584
+ }
585
+ ruleData.tempDate.setFullYear(ruleData.tempDate.getFullYear() + ruleObject.interval - 1);
586
+ ruleData.tempDate = getStartDateForWeek(ruleData.tempDate, ruleObject.day);
587
+ ruleData.monthInit = setNextValidDate(
588
+ ruleData.tempDate, ruleObject, ruleData.monthInit, ruleData.beginDate);
589
+ ruleData.dateCollection = [];
590
+ }
591
+ }
592
+
593
+ /**
594
+ * Internal method for process monthly date type with month frequency from recurrence rule
595
+ *
596
+ * @param {Date} startDate Accepts the strat date
597
+ * @param {Date} endDate Accepts the end date
598
+ * @param {number[]} data Accepts the collection of dates
599
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
600
+ * @returns {void}
601
+ * @private
602
+ */
603
+ function monthlyDateTypeProcessforMonthFreq(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
604
+ const ruleData: RuleData = initializeRecRuleVariables(startDate, ruleObject);
605
+ ruleData.tempDate = ruleData.mainDate = calendarUtil.getMonthStartDate(ruleData.tempDate);
606
+ if (((ruleObject.freq === 'MONTHLY' && ruleObject.interval === 12) || (ruleObject.freq === 'YEARLY')) &&
607
+ calendarUtil.getMonthDaysCount(startDate) < ruleObject.monthDay[0]) {
608
+ return;
609
+ }
610
+ while (compareDates(ruleData.tempDate, endDate)) {
611
+ ruleData.beginDate = new Date(ruleData.tempDate.getTime());
612
+ processDateCollectionForByMonthDay(ruleObject, ruleData, endDate, true, startDate, data);
613
+ if (!isNullOrUndefined(ruleObject.setPosition)) {
614
+ insertDatasIntoExistingCollection(ruleData.dateCollection, ruleData.state, startDate, endDate, data, ruleObject);
615
+ }
616
+ if (ruleData.expectedCount && (data.length + ruleObject.recExceptionCount) >= ruleData.expectedCount) {
617
+ return;
618
+ }
619
+ ruleData.monthInit = setNextValidDate(ruleData.tempDate, ruleObject, ruleData.monthInit, ruleData.beginDate);
620
+ ruleData.dateCollection = [];
621
+ }
622
+ }
623
+
624
+ /**
625
+ * To process date collection for Monthly & Yearly based on BYMONTH Day property
626
+ *
627
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
628
+ * @param {RuleData} recRuleVariables Accepts the rule data
629
+ * @param {Date} endDate Accepts the end date
630
+ * @param {boolean} isByMonth Accepts the boolean to validate either month or not
631
+ * @param {Date} startDate Accepts the start date
632
+ * @param {number[]} data Accepts the collection of dates
633
+ * @returns {void}
634
+ * @private
635
+ */
636
+ function processDateCollectionForByMonthDay(ruleObject: RecRule, recRuleVariables: RuleData, endDate: Date, isByMonth?: boolean, startDate?: Date, data?: number[]): void {
637
+ for (let index: number = 0; index < ruleObject.monthDay.length; index++) {
638
+ recRuleVariables.date = ruleObject.monthDay[parseInt(index.toString(), 10)];
639
+ recRuleVariables.tempDate = calendarUtil.getMonthStartDate(recRuleVariables.tempDate);
640
+ const maxDate: number = calendarUtil.getMonthDaysCount(recRuleVariables.tempDate);
641
+ recRuleVariables.date = recRuleVariables.date > 0 ? recRuleVariables.date : (maxDate + recRuleVariables.date + 1);
642
+ if (validateProperDate(recRuleVariables.tempDate, recRuleVariables.date, recRuleVariables.mainDate)
643
+ && (recRuleVariables.date > 0)) {
644
+ calendarUtil.setDate(recRuleVariables.tempDate, recRuleVariables.date);
645
+ if (endDate && recRuleVariables.tempDate > endDate) {
646
+ return;
647
+ }
648
+ if (ruleObject.day.length === 0 || ruleObject.day.indexOf(DAYINDEX[recRuleVariables.tempDate.getDay()]) > -1) {
649
+ if (isByMonth && isNullOrUndefined(ruleObject.setPosition) && (recRuleVariables.expectedCount
650
+ && (data.length + ruleObject.recExceptionCount) < recRuleVariables.expectedCount)) {
651
+ insertDateCollection(recRuleVariables.state, startDate, endDate, data, ruleObject, recRuleVariables.tempDate.getTime());
652
+ } else {
653
+ recRuleVariables.dateCollection.push([recRuleVariables.tempDate.getTime()]);
654
+ }
655
+ }
656
+ }
657
+ }
658
+ }
659
+
660
+ /**
661
+ * Internal method to set next valid date
662
+ *
663
+ * @param {Date} tempDate Accepts the date
664
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
665
+ * @param {number} monthInit Accepts the initial month
666
+ * @param {Date} beginDate Accepts the initial date
667
+ * @param {number} interval Accepts the interval duration
668
+ * @returns {number} Returnx the next valid date
669
+ * @private
670
+ */
671
+ function setNextValidDate(tempDate: Date, ruleObject: RecRule, monthInit: number, beginDate: Date = null, interval?: number): number {
672
+ let monthData: number = beginDate ? beginDate.getMonth() : 0;
673
+ const startDate: Date = calendarUtil.getMonthStartDate(tempDate);
674
+ interval = isNullOrUndefined(interval) ? ruleObject.interval : interval;
675
+ tempDate.setFullYear(startDate.getFullYear());
676
+ tempDate.setMonth(startDate.getMonth());
677
+ tempDate.setDate(startDate.getDate());
678
+ if (ruleObject.month.length) {
679
+ monthInit++;
680
+ monthInit = monthInit % ruleObject.month.length;
681
+ calendarUtil.setMonth(tempDate, ruleObject.month[parseInt(monthInit.toString(), 10)], 1);
682
+ if (monthInit === 0) {
683
+ calendarUtil.addYears(tempDate, interval, ruleObject.month[0]);
684
+ }
685
+ } else {
686
+ if (beginDate && (beginDate.getFullYear() < tempDate.getFullYear())) {
687
+ monthData = tempDate.getMonth() - 1;
688
+ }
689
+ calendarUtil.setValidDate(tempDate, interval, 1, monthData, beginDate);
690
+ }
691
+ return monthInit;
692
+ }
693
+
694
+ /**
695
+ * To get month collection when BYDAY property having more than one value in list.
696
+ *
697
+ * @param {Date} startDate Accepts the strat date
698
+ * @param {Date} endDate Accepts the end date
699
+ * @param {number[]} data Accepts the collection of dates
700
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
701
+ * @returns {void}
702
+ * @private
703
+ */
704
+ function getMonthCollection(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
705
+ const expectedDays: string[] = ruleObject.day;
706
+ let tempDate: Date = new Date(startDate.getTime());
707
+ tempDate = calendarUtil.getMonthStartDate(tempDate);
708
+ let monthCollection: number[][] = [];
709
+ let dateCollection: number[][] = [];
710
+ let dates: number[] = [];
711
+ let index: number;
712
+ let state: boolean;
713
+ const expectedCount: number = getDateCount(startDate, ruleObject);
714
+ let monthInit: number = 0;
715
+ let beginDate: Date;
716
+ if (ruleObject.month.length) {
717
+ calendarUtil.setMonth(tempDate, ruleObject.month[0], 1);
718
+ }
719
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
720
+ while (compareDates(tempDate, endDate)
721
+ && (expectedCount && (data.length + ruleObject.recExceptionCount) < expectedCount)) {
722
+ const currentMonthDate: Date = new Date(tempDate.getTime());
723
+ const isHavingNumber: boolean[] = expectedDays.map((item: string) => HASNUMBER.test(item));
724
+ if (isHavingNumber.indexOf(true) > -1) {
725
+ for (let j: number = 0; j <= expectedDays.length - 1; j++) {
726
+ const expectedDaysArray: string[] = expectedDays[parseInt(j.toString(), 10)].match(SPLITNUMBERANDSTRING);
727
+ const position: number = parseInt(expectedDaysArray[0], 10);
728
+ tempDate = new Date(tempDate.getTime());
729
+ tempDate = calendarUtil.getMonthStartDate(tempDate);
730
+ tempDate = getStartDateForWeek(tempDate, expectedDays);
731
+ currentMonthDate.setFullYear(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate());
732
+ while (calendarUtil.isSameYear(currentMonthDate, tempDate) && calendarUtil.isSameMonth(currentMonthDate, tempDate)) {
733
+ if (expectedDaysArray[expectedDaysArray.length - 1] === DAYINDEX[currentMonthDate.getDay()]) {
734
+ monthCollection.push([currentMonthDate.getTime()]);
735
+ }
736
+ currentMonthDate.setDate(currentMonthDate.getDate() + (1));
737
+ }
738
+ currentMonthDate.setDate(currentMonthDate.getDate() - (1));
739
+ if (expectedDaysArray[0].indexOf('-') > -1) {
740
+ index = monthCollection.length - (-1 * position);
741
+ } else {
742
+ index = position - 1;
743
+ }
744
+ index = isNaN(index) ? 0 : index;
745
+ if (monthCollection.length > 0) {
746
+ if (isNullOrUndefined(ruleObject.setPosition)) {
747
+ insertDatasIntoExistingCollection(monthCollection, state, startDate, endDate, data, ruleObject, index);
748
+ } else {
749
+ dateCollection = [(filterDateCollectionByIndex(monthCollection, index, dates))];
750
+ }
751
+ }
752
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
753
+ return;
754
+ }
755
+ monthCollection = [];
756
+ }
757
+ if (!isNullOrUndefined(ruleObject.setPosition)) {
758
+ insertDateCollectionBasedonBySetPos(dateCollection, state, startDate, endDate, data, ruleObject);
759
+ dates = [];
760
+ }
761
+ monthInit = setNextValidDate(tempDate, ruleObject, monthInit, beginDate);
762
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
763
+ monthCollection = [];
764
+ } else {
765
+ let weekCollection: number[] = [];
766
+ const dayCycleData: { [key: string]: number } = processWeekDays(expectedDays);
767
+ currentMonthDate.setFullYear(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate());
768
+ const initialDate: Date = new Date(tempDate.getTime());
769
+ beginDate = new Date(tempDate.getTime());
770
+ while (calendarUtil.isSameMonth(initialDate, tempDate)) {
771
+ weekCollection.push(tempDate.getTime());
772
+ if (expectedDays.indexOf(DAYINDEX[tempDate.getDay()]) > -1) {
773
+ monthCollection.push(weekCollection);
774
+ weekCollection = [];
775
+ }
776
+ tempDate.setDate(tempDate.getDate()
777
+ + dayCycleData[DAYINDEX[tempDate.getDay()]]);
778
+ }
779
+ index = ((ruleObject.setPosition < 1) ? (monthCollection.length + ruleObject.setPosition) : ruleObject.setPosition - 1);
780
+ if (isNullOrUndefined(ruleObject.setPosition)) {
781
+ index = 0;
782
+ const datas: number[] = [];
783
+ for (let week: number = 0; week < monthCollection.length; week++) {
784
+ for (let row: number = 0; row < monthCollection[parseInt(week.toString(), 10)].length; row++) {
785
+ datas.push(monthCollection[parseInt(week.toString(), 10)][parseInt(row.toString(), 10)]);
786
+ }
787
+ }
788
+ monthCollection = [datas];
789
+ }
790
+ if (monthCollection.length > 0) {
791
+ insertDatasIntoExistingCollection(monthCollection, state, startDate, endDate, data, ruleObject, index);
792
+ }
793
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
794
+ return;
795
+ }
796
+ monthInit = setNextValidDate(tempDate, ruleObject, monthInit, beginDate);
797
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
798
+ monthCollection = [];
799
+ }
800
+ }
801
+ }
802
+
803
+ /**
804
+ * To process monday day type for FREQ=MONTHLY
805
+ *
806
+ * @param {Date} startDate Accepts the strat date
807
+ * @param {Date} endDate Accepts the end date
808
+ * @param {number[]} data Accepts the collection of dates
809
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
810
+ * @returns {void}
811
+ * @private
812
+ */
813
+ function monthlyDayTypeProcessforMonthFreq(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
814
+ const expectedDays: string[] = ruleObject.day;
815
+ // When BYDAY property having more than 1 value.
816
+ if (expectedDays.length > 1) {
817
+ getMonthCollection(startDate, endDate, data, ruleObject);
818
+ return;
819
+ }
820
+ let tempDate: Date = new Date(startDate.getTime());
821
+ const expectedCount: number = getDateCount(startDate, ruleObject);
822
+ let monthCollection: number[][] = [];
823
+ let beginDate: Date;
824
+ let monthInit: number = 0;
825
+ tempDate = calendarUtil.getMonthStartDate(tempDate);
826
+ if (ruleObject.month.length) {
827
+ calendarUtil.setMonth(tempDate, ruleObject.month[0], 1);
828
+ }
829
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
830
+ while (compareDates(tempDate, endDate) && (expectedCount && (data.length + ruleObject.recExceptionCount) < expectedCount)) {
831
+ beginDate = new Date(tempDate.getTime());
832
+ const currentMonthDate: Date = new Date(tempDate.getTime());
833
+ while (calendarUtil.isSameMonth(tempDate, currentMonthDate)) {
834
+ monthCollection.push([currentMonthDate.getTime()]);
835
+ currentMonthDate.setDate(currentMonthDate.getDate() + (7));
836
+ }
837
+ // To filter date collection based on BYDAY Index, then BYSETPOS and to insert datas into existing collection
838
+ insertDateCollectionBasedonIndex(monthCollection, startDate, endDate, data, ruleObject);
839
+ monthInit = setNextValidDate(tempDate, ruleObject, monthInit, beginDate);
840
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
841
+ monthCollection = [];
842
+ }
843
+ }
844
+
845
+ /**
846
+ * To process monday day type for FREQ=YEARLY
847
+ *
848
+ * @param {Date} startDate Accepts the strat date
849
+ * @param {Date} endDate Accepts the end date
850
+ * @param {number[]} data Accepts the collection of dates
851
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
852
+ * @returns {void}
853
+ * @private
854
+ */
855
+ function monthlyDayTypeProcess(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
856
+ const expectedDays: string[] = ruleObject.day;
857
+ const isHavingNumber: boolean[] = expectedDays.map((item: string) => HASNUMBER.test(item));
858
+ // If BYDAY property having more than 1 value in list
859
+ if (expectedDays.length > 1 && isHavingNumber.indexOf(true) > -1) {
860
+ processDateCollectionforByDayWithInteger(startDate, endDate, data, ruleObject);
861
+ return;
862
+ } else if (ruleObject.month.length && expectedDays.length === 1 && isHavingNumber.indexOf(true) > -1) {
863
+ monthlyDayTypeProcessforMonthFreq(startDate, endDate, data, ruleObject);
864
+ return;
865
+ }
866
+ let tempDate: Date = new Date(startDate.getTime());
867
+ let currentMonthDate: Date;
868
+ const expectedCount: number = getDateCount(startDate, ruleObject);
869
+ const interval: number = ruleObject.interval;
870
+ let monthCollection: number[][] = [];
871
+ if (ruleObject.month.length) {
872
+ calendarUtil.setMonth(tempDate, ruleObject.month[0], tempDate.getDate());
873
+ }
874
+ // Set the date as start date of the yeear if yearly freq having ByDay property alone
875
+ if (isNullOrUndefined(ruleObject.setPosition) && ruleObject.month.length === 0 && ruleObject.weekNo.length === 0) {
876
+ tempDate.setFullYear(startDate.getFullYear(), 0, 1);
877
+ }
878
+ tempDate = calendarUtil.getMonthStartDate(tempDate);
879
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
880
+ while (compareDates(tempDate, endDate)) {
881
+ currentMonthDate = new Date(tempDate.getTime());
882
+ while (calendarUtil.isSameYear(currentMonthDate, tempDate) &&
883
+ (expectedCount && (data.length + ruleObject.recExceptionCount) <= expectedCount)) {
884
+ currentMonthDate = new Date(tempDate.getTime());
885
+ while (calendarUtil.isSameYear(currentMonthDate, tempDate)) {
886
+ if (ruleObject.month.length === 0 || (ruleObject.month.length > 0
887
+ && !calendarUtil.checkMonth(tempDate, ruleObject.month))) {
888
+ if (expectedDays.length > 1) {
889
+ if (calendarUtil.compareMonth(currentMonthDate, tempDate)) {
890
+ calendarUtil.setValidDate(tempDate, 1, 1);
891
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
892
+ break;
893
+ }
894
+ if (expectedDays.indexOf(DAYINDEX[currentMonthDate.getDay()]) > -1) {
895
+ monthCollection.push([currentMonthDate.getTime()]);
896
+ }
897
+ currentMonthDate.setDate(currentMonthDate.getDate() + (1));
898
+ } else {
899
+ // If BYDAY property having 1 value in list
900
+ if (currentMonthDate.getFullYear() > tempDate.getFullYear()) {
901
+ calendarUtil.setValidDate(tempDate, 1, 1);
902
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
903
+ break;
904
+ }
905
+ const newstr: string = getDayString(expectedDays[0]);
906
+ if (DAYINDEX[currentMonthDate.getDay()] === newstr
907
+ && new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 0)
908
+ > new Date(startDate.getFullYear())) {
909
+ monthCollection.push([currentMonthDate.getTime()]);
910
+ }
911
+ currentMonthDate.setDate(currentMonthDate.getDate() + (7));
912
+ }
913
+ } else {
914
+ calendarUtil.setValidDate(tempDate, 1, 1);
915
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
916
+ break;
917
+ }
918
+ }
919
+ }
920
+ tempDate.setFullYear(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), currentMonthDate.getDate());
921
+ // To filter date collection based on BYDAY Index, then BYSETPOS and to insert datas into existing collection
922
+ insertDateCollectionBasedonIndex(monthCollection, startDate, endDate, data, ruleObject);
923
+ if (calendarUtil.isLastMonth(tempDate)) {
924
+ calendarUtil.setValidDate(tempDate, 1, 1);
925
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
926
+ }
927
+ tempDate.setFullYear(tempDate.getFullYear() + interval - 1);
928
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
929
+ return;
930
+ }
931
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
932
+ monthCollection = [];
933
+ }
934
+ }
935
+
936
+ /**
937
+ * To process the recurrence rule when BYDAY property having values with integer
938
+ *
939
+ * @param {Date} startDate Accepts the strat date
940
+ * @param {Date} endDate Accepts the end date
941
+ * @param {number[]} data Accepts the collection of dates
942
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
943
+ * @returns {void}
944
+ * @private
945
+ */
946
+ function processDateCollectionforByDayWithInteger(startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
947
+ const expectedDays: string[] = ruleObject.day;
948
+ const expectedCount: number = getDateCount(startDate, ruleObject);
949
+ let tempDate: Date = new Date(startDate.getTime());
950
+ const interval: number = ruleObject.interval;
951
+ let monthCollection: number[][] = [];
952
+ let dateCollection: number[][] = [];
953
+ let index: number;
954
+ let state: boolean;
955
+ let monthInit: number = 0;
956
+ let currentMonthDate: Date;
957
+ let currentDate: Date;
958
+ let beginDate: Date;
959
+ tempDate = calendarUtil.getMonthStartDate(tempDate);
960
+ let datas: number[] = [];
961
+ if (ruleObject.month.length) {
962
+ calendarUtil.setMonth(tempDate, ruleObject.month[0], 1);
963
+ }
964
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
965
+ while (compareDates(tempDate, endDate)) {
966
+ currentMonthDate = new Date(tempDate.getTime());
967
+ for (let i: number = 0; i <= ruleObject.month.length; i++) {
968
+ for (let j: number = 0; j <= expectedDays.length - 1; j++) {
969
+ tempDate = calendarUtil.getMonthStartDate(tempDate);
970
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
971
+ monthCollection = [];
972
+ while (calendarUtil.isSameYear(currentMonthDate, tempDate) &&
973
+ (expectedCount && (data.length + ruleObject.recExceptionCount) <= expectedCount)) {
974
+ while (calendarUtil.isSameYear(currentMonthDate, tempDate)) {
975
+ currentMonthDate = new Date(tempDate.getTime());
976
+ if (ruleObject.month.length === 0 ||
977
+ (ruleObject.month.length > 0 && ruleObject.month[parseInt(i.toString(), 10)] === calendarUtil.getMonth(currentMonthDate))) {
978
+ const expectedDaysArray: string[] = expectedDays[parseInt(j.toString(), 10)].match(SPLITNUMBERANDSTRING);
979
+ const position: number = parseInt(expectedDaysArray[0], 10);
980
+ currentDate = new Date(tempDate.getTime());
981
+ while (calendarUtil.isSameYear(currentDate, tempDate)
982
+ && calendarUtil.isSameMonth(currentDate, tempDate)) {
983
+ if (expectedDaysArray[expectedDaysArray.length - 1] === DAYINDEX[currentDate.getDay()]) {
984
+ monthCollection.push([currentDate.getTime()]);
985
+ }
986
+ currentDate.setDate(currentDate.getDate() + (1));
987
+ }
988
+ currentDate.setDate(currentDate.getDate() - (1));
989
+ if (expectedDaysArray[0].indexOf('-') > -1) {
990
+ index = monthCollection.length - (-1 * position);
991
+ } else {
992
+ index = position - 1;
993
+ }
994
+ index = isNaN(index) ? 0 : index;
995
+ }
996
+ monthInit = setNextValidDate(tempDate, ruleObject, monthInit, beginDate, 1);
997
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
998
+ }
999
+ }
1000
+ tempDate = j === 0 && currentDate ? new Date(currentDate.getTime()) : new Date(currentMonthDate.getTime());
1001
+ if (monthCollection.length > 0) {
1002
+ if (isNullOrUndefined(ruleObject.setPosition)) {
1003
+ insertDatasIntoExistingCollection(monthCollection, state, startDate, endDate, data, ruleObject, index);
1004
+ } else {
1005
+ dateCollection = [(filterDateCollectionByIndex(monthCollection, index, datas))];
1006
+ }
1007
+ }
1008
+ if (expectedCount && (data.length + ruleObject.recExceptionCount) >= expectedCount) {
1009
+ return;
1010
+ }
1011
+ }
1012
+ }
1013
+ if (!isNullOrUndefined(ruleObject.setPosition)) {
1014
+ insertDateCollectionBasedonBySetPos(dateCollection, state, startDate, endDate, data, ruleObject);
1015
+ datas = [];
1016
+ }
1017
+ if (calendarUtil.isLastMonth(tempDate)) {
1018
+ calendarUtil.setValidDate(tempDate, 1, 1);
1019
+ tempDate.setFullYear(tempDate.getFullYear() + interval - 1);
1020
+ } else {
1021
+ tempDate.setFullYear(tempDate.getFullYear() + interval);
1022
+ }
1023
+ tempDate = getStartDateForWeek(tempDate, ruleObject.day);
1024
+ if (ruleObject.month.length) {
1025
+ calendarUtil.setMonth(tempDate, ruleObject.month[0], tempDate.getDate());
1026
+ }
1027
+ }
1028
+ }
1029
+
1030
+ /**
1031
+ * To get recurrence collection if BYSETPOS is null
1032
+ *
1033
+ * @param {number[]} monthCollection Accepts the month collection dates
1034
+ * @param {string[]} expectedDays Accepts the exception dates
1035
+ * @returns {RuleData} Returns the rule data object
1036
+ * @private
1037
+ */
1038
+ function getRecurrenceCollection(monthCollection: number[][], expectedDays: string[]): RuleData {
1039
+ let index: number;
1040
+ const recurrenceCollectionObject: RuleData = { monthCollection: [], index: 0 };
1041
+ if (expectedDays.length === 1) {
1042
+ // To split numeric value from BYDAY property value
1043
+ const expectedDaysArrays: string[] = expectedDays[0].match(SPLITNUMBERANDSTRING);
1044
+ let arrPosition: number;
1045
+ if (expectedDaysArrays.length > 1) {
1046
+ arrPosition = parseInt(expectedDaysArrays[0], 10);
1047
+ index = ((arrPosition < 1) ? (monthCollection.length + arrPosition) : arrPosition - 1);
1048
+ } else {
1049
+ index = 0;
1050
+ monthCollection = getDateCollectionforBySetPosNull(monthCollection);
1051
+ }
1052
+ } else {
1053
+ index = 0;
1054
+ monthCollection = getDateCollectionforBySetPosNull(monthCollection);
1055
+ }
1056
+ recurrenceCollectionObject.monthCollection = monthCollection;
1057
+ recurrenceCollectionObject.index = index;
1058
+ return recurrenceCollectionObject;
1059
+ }
1060
+
1061
+ /**
1062
+ * Internal method to process the data collections
1063
+ *
1064
+ * @param {number[]} dateCollection Accepts the date collections
1065
+ * @param {boolean} state Accepts the state
1066
+ * @param {Date} startDate Accepts the start date
1067
+ * @param {Date} endDate Accepts the end date
1068
+ * @param {number[]} data Accepts the collection of numbers
1069
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
1070
+ * @returns {void}
1071
+ * @private
1072
+ */
1073
+ function insertDataCollection(dateCollection: number[][], state: boolean, startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
1074
+ let index: number = ((ruleObject.setPosition < 1) ?
1075
+ (dateCollection.length + ruleObject.setPosition) : ruleObject.setPosition - 1);
1076
+ if (isNullOrUndefined(ruleObject.setPosition)) {
1077
+ index = 0;
1078
+ dateCollection = getDateCollectionforBySetPosNull(dateCollection);
1079
+ }
1080
+ if (dateCollection.length > 0) {
1081
+ insertDatasIntoExistingCollection(dateCollection, state, startDate, endDate, data, ruleObject, index);
1082
+ }
1083
+ }
1084
+
1085
+ /**
1086
+ * To process month collection if BYSETPOS is null
1087
+ *
1088
+ * @param {number[]} monthCollection Accepts the month date collections
1089
+ * @returns {number[]} Returns the month date collections
1090
+ * @private
1091
+ */
1092
+ function getDateCollectionforBySetPosNull(monthCollection: number[][]): number[][] {
1093
+ const datas: number[] = [];
1094
+ for (let week: number = 0; week < monthCollection.length; week++) {
1095
+ for (let row: number = 0; row < monthCollection[parseInt(week.toString(), 10)].length; row++) {
1096
+ datas.push(new Date(monthCollection[parseInt(week.toString(), 10)][parseInt(row.toString(), 10)]).getTime());
1097
+ }
1098
+ }
1099
+ monthCollection = datas.length > 0 ? [datas] : [];
1100
+ return monthCollection;
1101
+ }
1102
+
1103
+ /**
1104
+ * To filter date collection based on BYDAY Index, then BYSETPOS and to insert datas into existing collection
1105
+ *
1106
+ * @param {number[]} monthCollection Accepts the month date collections
1107
+ * @param {Date} startDate Accepts the start date
1108
+ * @param {Date} endDate Accepts the end date
1109
+ * @param {number[]} data Accepts the date collections
1110
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
1111
+ * @returns {void}
1112
+ * @private
1113
+ */
1114
+ function insertDateCollectionBasedonIndex(monthCollection: number[][], startDate: Date, endDate: Date, data: number[], ruleObject: RecRule): void {
1115
+ const expectedDays: string[] = ruleObject.day;
1116
+ let state: boolean;
1117
+ let datas: number[] = [];
1118
+ let dateCollection: number[][] = [];
1119
+ const recurrenceCollections: RuleData = getRecurrenceCollection(monthCollection, expectedDays);
1120
+ monthCollection = recurrenceCollections.monthCollection;
1121
+ const index: number = recurrenceCollections.index;
1122
+ if (ruleObject.setPosition != null) {
1123
+ dateCollection = [(filterDateCollectionByIndex(monthCollection, index, datas))];
1124
+ insertDateCollectionBasedonBySetPos(dateCollection, state, startDate, endDate, data, ruleObject);
1125
+
1126
+ } else {
1127
+ if (monthCollection.length > 0) {
1128
+ insertDatasIntoExistingCollection(monthCollection, state, startDate, endDate, data, ruleObject, index);
1129
+ }
1130
+ }
1131
+ datas = [];
1132
+ }
1133
+
1134
+ /**
1135
+ * To filter date collection when BYDAY property having values with number
1136
+ *
1137
+ * @param {number[]} monthCollection Accepts the date collections
1138
+ * @param {number} index Accepts the index of date collections
1139
+ * @param {number[]} datas Accepts the collection of dates
1140
+ * @returns {number[]} Returns the collection of dates
1141
+ * @private
1142
+ */
1143
+ function filterDateCollectionByIndex(monthCollection: number[][], index: number, datas: number[]): number[] {
1144
+ for (let week: number = 0; week < monthCollection[parseInt(index.toString(), 10)].length; week++) {
1145
+ datas.push(monthCollection[parseInt(index.toString(), 10)][parseInt(week.toString(), 10)]);
1146
+ }
1147
+ return datas;
1148
+ }
1149
+
1150
+ /**
1151
+ * To insert processed date collection in final array element
1152
+ *
1153
+ * @param {boolean} state Accepts the state of the recurrence rule
1154
+ * @param {Date} startDate Accepts the start date
1155
+ * @param {Date} endDate Accepts the end date
1156
+ * @param {number[]} data Accepts the collection of date
1157
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
1158
+ * @param {number} dayData Accepts the date index
1159
+ * @returns {void}
1160
+ * @private
1161
+ */
1162
+ function insertDateCollection(state: boolean, startDate: Date, endDate: Date, data: number[], ruleObject: RecRule, dayData: number): void {
1163
+ const expectedCount: number = getDateCount(startDate, ruleObject);
1164
+ const chDate: Date = new Date(dayData);
1165
+ state = validateRules(chDate, ruleObject);
1166
+ if ((chDate >= startDate) && compareDates(chDate, endDate) && state
1167
+ && expectedCount && (data.length + ruleObject.recExceptionCount) < expectedCount) {
1168
+ excludeDateHandler(data, dayData);
1169
+ }
1170
+ }
1171
+
1172
+ /**
1173
+ * Return the last week number of given month and year.
1174
+ *
1175
+ * @param {number} year Accepts the Year in number format
1176
+ * @param {number} startDayOfWeek Accepts the start date
1177
+ * @param {number[]} monthCollection Accepts the collection of dates
1178
+ * @param {number} week Accepts the week in number format
1179
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
1180
+ * @returns {number} returns week number
1181
+ * @private
1182
+ */
1183
+ function weekCount(year: number, startDayOfWeek: number, monthCollection: number[][], week: number, ruleObject: RecRule): number {
1184
+ const firstDayOfWeek: number = startDayOfWeek || 0;
1185
+ const firstOfMonth: Date = new Date(year, ruleObject.month[0] - 1, 1);
1186
+ const lastOfMonth: Date = new Date(year, ruleObject.month[0], 0);
1187
+ const numberOfDaysInMonth: number = lastOfMonth.getDate();
1188
+ const firstWeekDay: number = (firstOfMonth.getDay() - firstDayOfWeek + 7) % 7;
1189
+ const used: number = firstWeekDay + numberOfDaysInMonth;
1190
+ const count: number = Math.ceil(used / 7) - 1;
1191
+ const dayData: number = monthCollection[parseInt(week.toString(), 10)][parseInt(count.toString(), 10)];
1192
+ const chDate: Date = new Date(dayData);
1193
+ const state: boolean = validateRules(chDate, ruleObject);
1194
+ return (state) ? count : count - 1;
1195
+ }
1196
+
1197
+ /**
1198
+ * To process date collection based on Byset position after process the collection based on BYDAY property value index: EX:BYDAY=1SUm-1SU
1199
+ *
1200
+ * @param {number[]} monthCollection Accepts the collection of dates
1201
+ * @param {boolean} state Accepts the state of the recurrence rule
1202
+ * @param {Date} startDate Accepts the start date
1203
+ * @param {Date} endDate Accepts the end date
1204
+ * @param {number[]} data Accepts the collection of date
1205
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
1206
+ * @returns {void}
1207
+ * @private
1208
+ */
1209
+ function insertDateCollectionBasedonBySetPos
1210
+ (monthCollection: number[][], state: boolean, startDate: Date, endDate: Date, data: number[], ruleObject: RecRule)
1211
+ : void {
1212
+ if (monthCollection.length > 0) {
1213
+ for (let week: number = 0; week < monthCollection.length; week++) {
1214
+ monthCollection[parseInt(week.toString(), 10)].sort();
1215
+ const expectedDays: string[] = ruleObject.day;
1216
+ const isHavingNumber: boolean[] = expectedDays.map((item: string) => HASNUMBER.test(item));
1217
+ const weekIndex: number = (ruleObject.freq === 'YEARLY' && (ruleObject.validRules.indexOf('BYMONTH') > -1) &&
1218
+ !(isHavingNumber.indexOf(true) > -1)) ?
1219
+ weekCount(new Date(monthCollection[0][0]).getFullYear(), 0, monthCollection, week, ruleObject)
1220
+ : (monthCollection[parseInt(week.toString(), 10)].length + ruleObject.setPosition);
1221
+ const index: number = ((ruleObject.setPosition < 1) ? weekIndex : ruleObject.setPosition - 1);
1222
+ const dayData: number = monthCollection[parseInt(week.toString(), 10)][parseInt(index.toString(), 10)];
1223
+ insertDateCollection(state, startDate, endDate, data, ruleObject, dayData);
1224
+ }
1225
+ }
1226
+ }
1227
+
1228
+ /**
1229
+ * To insert datas into existing collection which is processed from previous loop.
1230
+ *
1231
+ * @param {number[]} monthCollection Accepts the collection of dates
1232
+ * @param {boolean} state Accepts the state of the recurrence rule
1233
+ * @param {Date} startDate Accepts the start date
1234
+ * @param {Date} endDate Accepts the end date
1235
+ * @param {number[]} data Accepts the collection of date
1236
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
1237
+ * @param {number} index Accepts the index value
1238
+ * @returns {void}
1239
+ * @private
1240
+ */
1241
+ function insertDatasIntoExistingCollection(monthCollection: number[][], state: boolean, startDate: Date, endDate: Date, data: number[], ruleObject: RecRule, index?: number): void {
1242
+ if (monthCollection.length > 0) {
1243
+ index = !isNullOrUndefined(index) ? index :
1244
+ ((ruleObject.setPosition < 1)
1245
+ ? (monthCollection.length + ruleObject.setPosition) : ruleObject.setPosition - 1);
1246
+ monthCollection[parseInt(index.toString(), 10)].sort();
1247
+ for (let week: number = 0; week < monthCollection[parseInt(index.toString(), 10)].length; week++) {
1248
+ const dayData: number = monthCollection[parseInt(index.toString(), 10)][parseInt(week.toString(), 10)];
1249
+ insertDateCollection(state, startDate, endDate, data, ruleObject, dayData);
1250
+ }
1251
+ }
1252
+ }
1253
+
1254
+ /**
1255
+ * Internal method to compare dates
1256
+ *
1257
+ * @param {Date} startDate Accepts the start date
1258
+ * @param {Date} endDate Accepts the end date
1259
+ * @returns {boolean} Returns the result of checking start and end dates
1260
+ * @private
1261
+ */
1262
+ function compareDates(startDate: Date, endDate: Date): boolean {
1263
+ return endDate ? (startDate <= endDate) : true;
1264
+ }
1265
+
1266
+ /**
1267
+ * Internal method to get day string
1268
+ *
1269
+ * @param {string} expectedDays Accepts the exception date string
1270
+ * @returns {string} Returns the valid string
1271
+ * @private
1272
+ */
1273
+ function getDayString(expectedDays: string): string {
1274
+ // To get BYDAY value without numeric value
1275
+ const newstr: string = expectedDays.replace(REMOVENUMBERINSTRING, '');
1276
+ return newstr;
1277
+ }
1278
+
1279
+ /**
1280
+ * Internal method to check day index
1281
+ *
1282
+ * @param {number} day Accepts the day index
1283
+ * @param {string[]} expectedDays Accepts the exception dates
1284
+ * @returns {boolean} Returns the index date
1285
+ * @private
1286
+ */
1287
+ function checkDayIndex(day: number, expectedDays: string[]): boolean {
1288
+ const sortedExpectedDays: string[] = [];
1289
+ expectedDays.forEach((element: string) => {
1290
+ const expectedDaysNumberSplit: string[] = element.match(SPLITNUMBERANDSTRING);
1291
+ if (expectedDaysNumberSplit.length === 2) {
1292
+ sortedExpectedDays.push(expectedDaysNumberSplit[1]);
1293
+ } else {
1294
+ sortedExpectedDays.push(expectedDaysNumberSplit[0]);
1295
+ }
1296
+ });
1297
+ return (sortedExpectedDays.indexOf(DAYINDEX[parseInt(day.toString(), 10)]) === -1);
1298
+ }
1299
+
1300
+ /**
1301
+ * Internal method to get start date of week
1302
+ *
1303
+ * @param {Date} startDate Accepts the start date
1304
+ * @param {string[]} expectedDays Accepts the exception dates
1305
+ * @returns {Date} Return the week start date
1306
+ * @private
1307
+ */
1308
+ function getStartDateForWeek(startDate: Date, expectedDays: string[]): Date {
1309
+ const tempDate: Date = new Date(startDate.getTime());
1310
+ let newstr: string;
1311
+ if (expectedDays.length > 0) {
1312
+ const expectedDaysArr: string[] = [];
1313
+ for (let i: number = 0; i <= expectedDays.length - 1; i++) {
1314
+ newstr = getDayString(expectedDays[parseInt(i.toString(), 10)]);
1315
+ expectedDaysArr.push(newstr);
1316
+ }
1317
+ if (expectedDaysArr.indexOf(DAYINDEX[tempDate.getDay()]) === -1) {
1318
+ do {
1319
+ tempDate.setDate(tempDate.getDate() + 1);
1320
+ } while (expectedDaysArr.indexOf(DAYINDEX[tempDate.getDay()]) === -1);
1321
+ }
1322
+ }
1323
+ return tempDate;
1324
+ }
1325
+
1326
+ /**
1327
+ * Method to generate recurrence rule object from given rule
1328
+ *
1329
+ * @param {string} rules Accepts the recurrence rule
1330
+ * @returns {RecRule} Returns the recurrence rule object
1331
+ */
1332
+ export function extractObjectFromRule(rules: string): RecRule {
1333
+ const ruleObject: RecRule = {
1334
+ freq: null,
1335
+ interval: 1,
1336
+ count: null,
1337
+ until: null,
1338
+ day: [],
1339
+ wkst: null,
1340
+ month: [],
1341
+ weekNo: [],
1342
+ monthDay: [],
1343
+ yearDay: [],
1344
+ setPosition: null,
1345
+ validRules: []
1346
+ };
1347
+ const rulesList: string[] = rules.split(';');
1348
+ let splitData: string[] = [];
1349
+ let temp: string;
1350
+ rulesList.forEach((data: string) => {
1351
+ splitData = data.split('=');
1352
+ switch (splitData[0]) {
1353
+ case 'UNTIL':
1354
+ temp = splitData[1];
1355
+ ruleObject.until = getDateFromRecurrenceDateString(temp);
1356
+ break;
1357
+ case 'BYDAY':
1358
+ ruleObject.day = splitData[1].split(',');
1359
+ ruleObject.validRules.push(splitData[0]);
1360
+ break;
1361
+ case 'BYMONTHDAY':
1362
+ ruleObject.monthDay = splitData[1].split(',').map(Number);
1363
+ ruleObject.validRules.push(splitData[0]);
1364
+ break;
1365
+ case 'BYMONTH':
1366
+ ruleObject.month = splitData[1].split(',').map(Number);
1367
+ ruleObject.validRules.push(splitData[0]);
1368
+ break;
1369
+ case 'BYYEARDAY':
1370
+ ruleObject.yearDay = splitData[1].split(',').map(Number);
1371
+ ruleObject.validRules.push(splitData[0]);
1372
+ break;
1373
+ case 'BYWEEKNO':
1374
+ ruleObject.weekNo = splitData[1].split(',').map(Number);
1375
+ ruleObject.validRules.push(splitData[0]);
1376
+ break;
1377
+ case 'INTERVAL':
1378
+ ruleObject.interval = parseInt(splitData[1], 10);
1379
+ break;
1380
+ case 'COUNT':
1381
+ ruleObject.count = parseInt(splitData[1], 10);
1382
+ break;
1383
+ case 'BYSETPOS':
1384
+ ruleObject.setPosition = parseInt(splitData[1], 10) > 4 ? -1 : parseInt(splitData[1], 10);
1385
+ break;
1386
+ case 'FREQ':
1387
+ ruleObject.freq = <FreqType>splitData[1];
1388
+ break;
1389
+ case 'WKST':
1390
+ ruleObject.wkst = splitData[1];
1391
+ break;
1392
+ }
1393
+ });
1394
+ if ((ruleObject.freq === 'MONTHLY') && (ruleObject.monthDay.length === 0)) {
1395
+ const index: number = ruleObject.validRules.indexOf('BYDAY');
1396
+ ruleObject.validRules.splice(index, 1);
1397
+ }
1398
+ return ruleObject;
1399
+ }
1400
+
1401
+ /**
1402
+ * Internal method to validate proper date
1403
+ *
1404
+ * @param {Date} tempDate Accepts the date value
1405
+ * @param {number} data Accepts the data value
1406
+ * @param {Date} startDate Accepts the start date
1407
+ * @returns {boolean} Returns the result of date validate
1408
+ * @private
1409
+ */
1410
+ function validateProperDate(tempDate: Date, data: number, startDate: Date): boolean {
1411
+ const maxDate: number = calendarUtil.getMonthDaysCount(tempDate);
1412
+ return (data <= maxDate) && (tempDate >= startDate);
1413
+ }
1414
+
1415
+ /**
1416
+ * Internal method to process week days
1417
+ *
1418
+ * @param {string[]} expectedDays Accepts the expection dates
1419
+ * @returns {Object} Returns the weekdays object
1420
+ * @private
1421
+ */
1422
+ function processWeekDays(expectedDays: string[]): { [key: string]: number } {
1423
+ const dayCycle: { [key: string]: number } = {};
1424
+ expectedDays.forEach((element: string, index: number) => {
1425
+ if (index === expectedDays.length - 1) {
1426
+ const startIndex: number = dayIndex.indexOf(element);
1427
+ let temp: number = startIndex;
1428
+ while (temp % 7 !== dayIndex.indexOf(expectedDays[0])) {
1429
+ temp++;
1430
+ }
1431
+ dayCycle[`${element}`] = temp - startIndex;
1432
+ } else {
1433
+ dayCycle[`${element}`] = dayIndex.indexOf(expectedDays[(<number>index + 1)]) - dayIndex.indexOf(element);
1434
+ }
1435
+ });
1436
+ return dayCycle;
1437
+ }
1438
+
1439
+ /**
1440
+ * Internal method to check date
1441
+ *
1442
+ * @param {Date} tempDate Accepts the date value
1443
+ * @param {number[]} expectedDate Accepts the exception dates
1444
+ * @returns {boolean} Returns the boolean value
1445
+ * @private
1446
+ */
1447
+ function checkDate(tempDate: Date, expectedDate: number[]): boolean {
1448
+ const temp: number[] = expectedDate.slice(0);
1449
+ let data: number;
1450
+ const maxDate: number = calendarUtil.getMonthDaysCount(tempDate);
1451
+ data = temp.shift();
1452
+ while (data) {
1453
+ if (data < 0) {
1454
+ data = <number>data + maxDate + 1;
1455
+ }
1456
+ if (data === tempDate.getDate()) {
1457
+ return false;
1458
+ }
1459
+ data = temp.shift();
1460
+ }
1461
+ return true;
1462
+ }
1463
+
1464
+ /**
1465
+ * Internal method to check the year value
1466
+ *
1467
+ * @param {Date} tempDate Accepts the date value
1468
+ * @param {number[]} expectedyearDay Accepts the exception dates in year
1469
+ * @returns {boolean} Returns the boolean value
1470
+ * @private
1471
+ */
1472
+ function checkYear(tempDate: Date, expectedyearDay: number[]): boolean {
1473
+ const temp: number[] = expectedyearDay.slice(0);
1474
+ let data: number;
1475
+ const yearDay: number = getYearDay(tempDate);
1476
+ data = temp.shift();
1477
+ while (data) {
1478
+ if (data < 0) {
1479
+ data = <number>data + calendarUtil.getYearDaysCount(tempDate, 0) + 1;
1480
+ }
1481
+ if (data === yearDay) {
1482
+ return false;
1483
+ }
1484
+ data = temp.shift();
1485
+ }
1486
+ return true;
1487
+ }
1488
+
1489
+ /**
1490
+ * Internal method to get the year day
1491
+ *
1492
+ * @param {Date} currentDate Accepts the date value
1493
+ * @returns {number} Returns the boolean value
1494
+ * @private
1495
+ */
1496
+ function getYearDay(currentDate: Date): number {
1497
+ if (!startDateCollection[calendarUtil.getFullYear(currentDate)]) {
1498
+ startDateCollection[calendarUtil.getFullYear(currentDate)] = calendarUtil.getYearLastDate(currentDate, 0);
1499
+ }
1500
+ const tempDate: Date = startDateCollection[calendarUtil.getFullYear(currentDate)];
1501
+ const diff: number = currentDate.getTime() - tempDate.getTime();
1502
+ return Math.ceil(diff / MS_PER_DAY);
1503
+ }
1504
+
1505
+ /**
1506
+ * Internal method to validate monthly rule type
1507
+ *
1508
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
1509
+ * @returns {MonthlyType} Returns the monthly type object
1510
+ * @private
1511
+ */
1512
+ function validateMonthlyRuleType(ruleObject: RecRule): MonthlyType {
1513
+ if (ruleObject.monthDay.length && !ruleObject.day.length) {
1514
+ return 'date';
1515
+ } else if (!ruleObject.monthDay.length && ruleObject.day.length) {
1516
+ return 'day';
1517
+ }
1518
+ return 'both';
1519
+ }
1520
+
1521
+ /**
1522
+ * Internal method to re-order the week days based on first day of week
1523
+ *
1524
+ * @param {string[]} days Accepts the week days value
1525
+ * @returns {void}
1526
+ * @private
1527
+ */
1528
+ function rotate(days: string[]): void {
1529
+ const data: string = days.shift();
1530
+ days.push(data);
1531
+ }
1532
+
1533
+ /**
1534
+ * Internal method to set first day of week
1535
+ *
1536
+ * @param {string} day Accepts the first day string
1537
+ * @returns {void}
1538
+ * @private
1539
+ */
1540
+ function setFirstDayOfWeek(day: string): void {
1541
+ while (dayIndex[0] !== day) {
1542
+ rotate(dayIndex);
1543
+ }
1544
+ }
1545
+
1546
+ /**
1547
+ * Internal method to validate recurrence rule
1548
+ *
1549
+ * @param {Date} tempDate Accepts the date value
1550
+ * @param {RecRule} ruleObject Accepts the recurrence rule object
1551
+ * @returns {boolean} Returns the boolean value
1552
+ * @private
1553
+ */
1554
+ function validateRules(tempDate: Date, ruleObject: RecRule): boolean {
1555
+ let state: boolean = true;
1556
+ const expectedDays: string[] = ruleObject.day;
1557
+ const expectedMonth: number[] = ruleObject.month;
1558
+ const expectedDate: number[] = calendarUtil.getExpectedDays(tempDate, ruleObject.monthDay);
1559
+ const expectedyearDay: number[] = ruleObject.yearDay;
1560
+ ruleObject.validRules.forEach((rule: string) => {
1561
+ switch (rule) {
1562
+ case 'BYDAY':
1563
+ if (checkDayIndex(tempDate.getDay(), expectedDays)) {
1564
+ state = false;
1565
+ }
1566
+ break;
1567
+ case 'BYMONTH':
1568
+ if (calendarUtil.checkMonth(tempDate, expectedMonth)) {
1569
+ state = false;
1570
+ }
1571
+ break;
1572
+ case 'BYMONTHDAY':
1573
+ if (checkDate(tempDate, expectedDate)) {
1574
+ state = false;
1575
+ }
1576
+ break;
1577
+ case 'BYYEARDAY':
1578
+ if (checkYear(tempDate, expectedyearDay)) {
1579
+ state = false;
1580
+ }
1581
+ break;
1582
+ }
1583
+ });
1584
+ return state;
1585
+ }
1586
+
1587
+ /**
1588
+ * Internal method to get calendar util
1589
+ *
1590
+ * @param {CalendarType} calendarMode Accepts the calendar type object
1591
+ * @returns {CalendarUtil} Returns the calendar util object
1592
+ * @private
1593
+ */
1594
+ export function getCalendarUtil(calendarMode: CalendarType): CalendarUtil {
1595
+ if (calendarMode === 'Islamic') {
1596
+ return new Islamic();
1597
+ }
1598
+ return new Gregorian();
1599
+ }
1600
+
1601
+ const startDateCollection: { [key: string]: Date } = {};
1602
+
1603
+ /** @private */
1604
+ export interface RecRule {
1605
+ freq: FreqType;
1606
+ interval: number;
1607
+ count: number;
1608
+ until: Date;
1609
+ day: string[];
1610
+ wkst: string;
1611
+ month: number[];
1612
+ weekNo: number[];
1613
+ monthDay: number[];
1614
+ yearDay: number[];
1615
+ setPosition: number;
1616
+ validRules: string[];
1617
+ recExceptionCount?: number;
1618
+ }
1619
+
1620
+ // Variables which are used for recurrence date generation
1621
+ interface RuleData {
1622
+ monthCollection?: number[][];
1623
+ index?: number;
1624
+ tempDate?: Date;
1625
+ mainDate?: Date;
1626
+ expectedCount?: number;
1627
+ monthInit?: number;
1628
+ date?: number;
1629
+ dateCollection?: number[][];
1630
+ beginDate?: Date;
1631
+ state?: boolean;
1632
+ }
1633
+
1634
+ /** @private */
1635
+ export type FreqType = 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY';
1636
+ type MonthlyType = 'date' | 'day' | 'both';
1637
+ type YearRuleType = 'MONTH' | 'WEEKNO' | 'YEARDAY';
1638
+ let tempExcludeDate: number[];
1639
+ const dayIndex: string[] = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
1640
+ let maxOccurrence: number;
1641
+ let tempViewDate: Date;
1642
+ let calendarUtil: CalendarUtil;
1643
+ const DAYINDEX: string[] = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
1644
+ const MAXOCCURRENCE: number = 43;
1645
+ const WEEKPOS: string[] = ['first', 'second', 'third', 'fourth', 'last'];
1646
+ const TIMES: string = 'summaryTimes';
1647
+ const ON: string = 'summaryOn';
1648
+ const EVERY: string = 'every';
1649
+ const UNTIL: string = 'summaryUntil';
1650
+ const DAYS: string = 'summaryDay';
1651
+ const WEEKS: string = 'summaryWeek';
1652
+ const MONTHS: string = 'summaryMonth';
1653
+ const YEARS: string = 'summaryYear';
1654
+ const DAYINDEXOBJECT: { [key: string]: string } = {
1655
+ SU: 'sun',
1656
+ MO: 'mon',
1657
+ TU: 'tue',
1658
+ WE: 'wed',
1659
+ TH: 'thu',
1660
+ FR: 'fri',
1661
+ SA: 'sat'
1662
+ };
1663
+
1664
+ // To check string has number
1665
+ const HASNUMBER: RegExp = /\d/;
1666
+
1667
+ // To find the numbers in string
1668
+ const REMOVENUMBERINSTRING: RegExp = /[^A-Z]+/;
1669
+
1670
+ // To split number and string
1671
+ const SPLITNUMBERANDSTRING: RegExp = /[a-z]+|[^a-z]+/gi;
1672
+
1673
+ /**
1674
+ * Method to generate string from date
1675
+ *
1676
+ * @param {Date} date Accepts the date value
1677
+ * @returns {string} Returns the string value
1678
+ */
1679
+ export function getRecurrenceStringFromDate(date: Date): string {
1680
+ return [date.getUTCFullYear(),
1681
+ roundDateValues(date.getUTCMonth() + 1),
1682
+ roundDateValues(date.getUTCDate()),
1683
+ 'T',
1684
+ roundDateValues(date.getUTCHours()),
1685
+ roundDateValues(date.getUTCMinutes()),
1686
+ roundDateValues(date.getUTCSeconds()),
1687
+ 'Z'].join('');
1688
+ }
1689
+
1690
+ /**
1691
+ * Internal method to round the date values
1692
+ *
1693
+ * @param {string | number} date Accepts the date value in either string or number format
1694
+ * @returns {string} Returns the date value in string format
1695
+ * @private
1696
+ */
1697
+ function roundDateValues(date: string | number): string {
1698
+ return ('0' + date).slice(-2);
1699
+ }