@syncfusion/ej2-calendars 30.2.4 → 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 (46) hide show
  1. package/dist/ej2-calendars.min.js +1 -1
  2. package/dist/ej2-calendars.umd.min.js +1 -1
  3. package/dist/global/ej2-calendars.min.js +1 -1
  4. package/dist/global/index.d.ts +1 -1
  5. package/dist/ts/calendar/calendar-model.d.ts +340 -0
  6. package/dist/ts/calendar/calendar.d.ts +733 -0
  7. package/dist/ts/calendar/calendar.ts +3009 -0
  8. package/dist/ts/calendar/index.d.ts +6 -0
  9. package/dist/ts/calendar/index.ts +6 -0
  10. package/dist/ts/calendar/islamic.d.ts +28 -0
  11. package/dist/ts/calendar/islamic.ts +597 -0
  12. package/dist/ts/common/index.d.ts +5 -0
  13. package/dist/ts/common/index.ts +5 -0
  14. package/dist/ts/common/maskplaceholder-model.d.ts +57 -0
  15. package/dist/ts/common/maskplaceholder.d.ts +45 -0
  16. package/dist/ts/common/maskplaceholder.ts +54 -0
  17. package/dist/ts/datepicker/datepicker-model.d.ts +364 -0
  18. package/dist/ts/datepicker/datepicker.d.ts +583 -0
  19. package/dist/ts/datepicker/datepicker.ts +2528 -0
  20. package/dist/ts/datepicker/index.d.ts +5 -0
  21. package/dist/ts/datepicker/index.ts +5 -0
  22. package/dist/ts/daterangepicker/daterangepicker-model.d.ts +496 -0
  23. package/dist/ts/daterangepicker/daterangepicker.d.ts +857 -0
  24. package/dist/ts/daterangepicker/daterangepicker.ts +5096 -0
  25. package/dist/ts/daterangepicker/index.d.ts +5 -0
  26. package/dist/ts/daterangepicker/index.ts +5 -0
  27. package/dist/ts/datetimepicker/datetimepicker-model.d.ts +479 -0
  28. package/dist/ts/datetimepicker/datetimepicker.d.ts +626 -0
  29. package/dist/ts/datetimepicker/datetimepicker.ts +2212 -0
  30. package/dist/ts/datetimepicker/index.d.ts +5 -0
  31. package/dist/ts/datetimepicker/index.ts +5 -0
  32. package/dist/ts/index.d.ts +9 -0
  33. package/dist/ts/index.ts +9 -0
  34. package/dist/ts/maskbase/index.d.ts +5 -0
  35. package/dist/ts/maskbase/index.ts +5 -0
  36. package/dist/ts/maskbase/interface.d.ts +28 -0
  37. package/dist/ts/maskbase/interface.ts +29 -0
  38. package/dist/ts/maskbase/masked-date-time.d.ts +77 -0
  39. package/dist/ts/maskbase/masked-date-time.ts +1013 -0
  40. package/dist/ts/timepicker/index.d.ts +5 -0
  41. package/dist/ts/timepicker/index.ts +5 -0
  42. package/dist/ts/timepicker/timepicker-model.d.ts +393 -0
  43. package/dist/ts/timepicker/timepicker.d.ts +681 -0
  44. package/dist/ts/timepicker/timepicker.ts +2944 -0
  45. package/package.json +59 -16
  46. package/aceconfig.js +0 -17
@@ -0,0 +1,2944 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { EventHandler, Property, Internationalization, NotifyPropertyChanges } from '@syncfusion/ej2-base';
3
+ import { KeyboardEvents, KeyboardEventArgs, Animation, AnimationModel, Browser, BaseEventArgs } from '@syncfusion/ej2-base';
4
+ import { EmitType, cldrData, L10n, Component, getDefaultDateObject, rippleEffect, RippleOptions, Event } from '@syncfusion/ej2-base';
5
+ import { remove, addClass, detach, removeClass, closest, append, attributes, setStyleAttribute } from '@syncfusion/ej2-base';
6
+ import { isNullOrUndefined, formatUnit, getValue, extend, getUniqueID, ModuleDeclaration, ChildProperty } from '@syncfusion/ej2-base';
7
+ import { Popup } from '@syncfusion/ej2-popups';
8
+ import { FocusEventArgs, BlurEventArgs, ClearedEventArgs } from '../calendar/calendar';
9
+ import { Input, InputObject, IInput, FloatLabelType } from '@syncfusion/ej2-inputs';
10
+ import { ListBase, ListBaseOptions, createElementParams } from '@syncfusion/ej2-lists';
11
+ import { TimePickerModel, TimeMaskPlaceholderModel } from './timepicker-model';
12
+
13
+ const WRAPPERCLASS: string = 'e-time-wrapper';
14
+ const POPUP: string = 'e-popup';
15
+ const ERROR: string = 'e-error';
16
+ const POPUPDIMENSION: string = '240px';
17
+ const DAY: number = new Date().getDate();
18
+ const MONTH: number = new Date().getMonth();
19
+ const YEAR: number = new Date().getFullYear();
20
+ const ROOT: string = 'e-timepicker';
21
+ const LIBRARY: string = 'e-lib';
22
+ const CONTROL: string = 'e-control';
23
+ const CONTENT: string = 'e-content';
24
+ const SELECTED: string = 'e-active';
25
+ const HOVER: string = 'e-hover';
26
+ const NAVIGATION: string = 'e-navigation';
27
+ const DISABLED: string = 'e-disabled';
28
+ const ICONANIMATION: string = 'e-icon-anim';
29
+ const FOCUS: string = 'e-input-focus';
30
+ const LISTCLASS: string = 'e-list-item';
31
+ const HALFPOSITION: number = 2;
32
+ const ANIMATIONDURATION: number = 50;
33
+ const OVERFLOW: string = 'e-time-overflow';
34
+ const OFFSETVAL: number = 4;
35
+ const EDITABLE: string = 'e-non-edit';
36
+ const wrapperAttributes: string[] = ['title', 'class', 'style'];
37
+
38
+
39
+ export interface ChangeEventArgs {
40
+ /** Defines the boolean that returns true when the value is changed by user interaction, otherwise returns false. */
41
+ isInteracted?: boolean
42
+ /** Defines the selected time value of the TimePicker.
43
+ *
44
+ * @isGenericType true
45
+ */
46
+ value?: Date
47
+ /** Defines the selected time value as string. */
48
+ text?: string
49
+ /** Defines the original event arguments. */
50
+ event?: KeyboardEventArgs | FocusEvent | MouseEvent | Event
51
+ /** Defines the element */
52
+ element: HTMLInputElement | HTMLElement
53
+ }
54
+
55
+ /**
56
+ * Interface for before list item render .
57
+ */
58
+ export interface ItemEventArgs extends BaseEventArgs {
59
+ /** Defines the created LI element. */
60
+ element: HTMLElement
61
+ /** Defines the displayed text value in a popup list. */
62
+ text: string
63
+ /** Defines the Date object of displayed text in a popup list.
64
+ *
65
+ * @isGenericType true
66
+ */
67
+ value: Date
68
+ /** Specifies whether to disable the current time value or not. */
69
+ isDisabled: boolean
70
+ }
71
+
72
+ export interface CursorPositionDetails {
73
+ /** Defines the text selection starting position. */
74
+ start: number
75
+ /** Defines the text selection end position. */
76
+ end: number
77
+ }
78
+
79
+ export interface MeridianText {
80
+ /** Defines the culture specific meridian text for AM. */
81
+ am: string
82
+ /** Defines the culture specific meridian text for PM. */
83
+ pm: string
84
+ }
85
+
86
+ export interface TimeFormatObject {
87
+ /**
88
+ * Specifies the format in which the date format will process
89
+ */
90
+ skeleton?: string
91
+ }
92
+
93
+ export interface PopupEventArgs {
94
+ /** Specifies the name of the event */
95
+ name?: string
96
+ /**
97
+ * Illustrates whether the current action needs to be prevented or not.
98
+ */
99
+ cancel?: boolean
100
+ /**
101
+ * Defines the TimePicker popup object.
102
+ *
103
+ * @deprecated
104
+ */
105
+ popup?: Popup
106
+ /**
107
+ * Specifies the original event arguments.
108
+ */
109
+ event?: MouseEvent | KeyboardEvent | FocusEvent | Event
110
+ /**
111
+ * Specifies the node to which the popup element to be appended.
112
+ */
113
+ appendTo?: HTMLElement
114
+ }
115
+ // eslint-disable-next-line @typescript-eslint/no-namespace
116
+ export namespace TimePickerBase {
117
+ // eslint-disable-next-line max-len, jsdoc/require-jsdoc
118
+ export function createListItems(createdEl: createElementParams, min: Date, max: Date,
119
+ globalize: Internationalization,
120
+ timeFormat: string, step: number): { collection: number[], list: HTMLElement } {
121
+ let formatOptions: object;
122
+ if (this.calendarMode === 'Gregorian') {
123
+ formatOptions = { format: timeFormat, type: 'time' };
124
+ } else {
125
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
126
+ formatOptions = { format: timeFormat, type: 'time', calendar: 'islamic' };
127
+ }
128
+ let start: number;
129
+ const interval: number = step * 60000;
130
+ const listItems: string[] = [];
131
+ const timeCollections: number[] = [];
132
+ start = +(min.setMilliseconds(0));
133
+ const end: number = +(max.setMilliseconds(0));
134
+ while (end >= start) {
135
+ timeCollections.push(start);
136
+ listItems.push(globalize.formatDate(new Date(start), { format: timeFormat, type: 'time' }));
137
+ start += interval;
138
+ }
139
+ const listTag: HTMLElement = ListBase.createList(createdEl, listItems, null, true);
140
+ return { collection: timeCollections, list: listTag };
141
+ }
142
+ }
143
+
144
+ export class TimeMaskPlaceholder extends ChildProperty<TimeMaskPlaceholder> {
145
+ /**
146
+ * Specifies the mask placeholder value for day property.
147
+ *
148
+ * @default 'day'
149
+ */
150
+ @Property('day')
151
+ public day: string;
152
+ /**
153
+ * Specifies the mask placeholder value for month property.
154
+ *
155
+ * @default 'month'
156
+ */
157
+ @Property('month')
158
+ public month: string;
159
+ /**
160
+ * Specifies the mask placeholder value for year property.
161
+ *
162
+ * @default 'year'
163
+ */
164
+ @Property('year')
165
+ public year: string;
166
+ /**
167
+ * Specifies the mask placeholder value for day of the week property.
168
+ *
169
+ * @default 'day of the week'
170
+ */
171
+ @Property('day of the week')
172
+ public dayOfTheWeek: string;
173
+ /**
174
+ * Specifies the mask placeholder value for hour property.
175
+ *
176
+ * @default 'hour'
177
+ */
178
+ @Property('hour')
179
+ public hour: string;
180
+ /**
181
+ * Specifies the mask placeholder value for minute property.
182
+ *
183
+ * @default 'minute'
184
+ */
185
+ @Property('minute')
186
+ public minute: string;
187
+ /**
188
+ * Specifies the mask placeholder value for second property.
189
+ *
190
+ * @default 'second'
191
+ */
192
+ @Property('second')
193
+ public second: string;
194
+
195
+ }
196
+
197
+ /**
198
+ * TimePicker is an intuitive interface component which provides an options to select a time value
199
+ * from popup list or to set a desired time value.
200
+ * ```
201
+ * <input id='timepicker' type='text'/>
202
+ * <script>
203
+ * var timePickerObj = new TimePicker({ value: new Date() });
204
+ * timePickerObj.appendTo('#timepicker');
205
+ * </script>
206
+ * ```
207
+ */
208
+ @NotifyPropertyChanges
209
+ export class TimePicker extends Component<HTMLElement> implements IInput {
210
+ private inputWrapper: InputObject;
211
+ private popupWrapper: HTMLElement;
212
+ private cloneElement: HTMLElement;
213
+ private listWrapper: HTMLElement;
214
+ private listTag: HTMLElement;
215
+ private anchor: HTMLElement;
216
+ private selectedElement: HTMLElement;
217
+ private liCollections: HTMLElement[] = [];
218
+ protected inputElement: HTMLInputElement;
219
+ private popupObj: Popup;
220
+ protected inputEvent: KeyboardEvents;
221
+ protected globalize: Internationalization;
222
+ private defaultCulture: Internationalization;
223
+ private containerStyle: ClientRect;
224
+ private rippleFn: Function;
225
+ private l10n: L10n;
226
+ private cursorDetails: CursorPositionDetails;
227
+ private activeIndex: number;
228
+ private timeCollections: number[] = [];
229
+ private isNavigate: boolean;
230
+ private disableItemCollection: string[] = [];
231
+ protected isPreventBlur: boolean;
232
+ private isTextSelected: boolean;
233
+ private prevValue: string;
234
+ private inputStyle: string;
235
+ private angularTag: string;
236
+ private valueWithMinutes: Date;
237
+ private prevDate: Date;
238
+ private initValue: Date;
239
+ private initMin: Date;
240
+ private initMax: Date;
241
+ private inputEleValue: Date;
242
+ private openPopupEventArgs: PopupEventArgs;
243
+ private formatString: string;
244
+ protected tabIndex: string;
245
+ private formElement: Element;
246
+ private modal: HTMLElement;
247
+ private invalidValueString: string = null;
248
+ protected keyConfigure: { [key: string]: string };
249
+ private timeOptions : TimePickerModel;
250
+ private mobileTimePopupWrap: HTMLElement;
251
+ protected preventChange: boolean = false;
252
+ protected maskedDateValue: string = '';
253
+ protected moduleName: string = this.getModuleName();
254
+ protected clearButton: HTMLElement;
255
+ /**
256
+ * Gets or sets the width of the TimePicker component. The width of the popup is based on the width of the component.
257
+ *
258
+ * @default null
259
+ */
260
+ @Property(null)
261
+ public width: string | number;
262
+ /**
263
+ * Specifies the root CSS class of the TimePicker that allows to
264
+ * customize the appearance by overriding the styles.
265
+ *
266
+ * @default null
267
+ */
268
+ @Property(null)
269
+ public cssClass: string;
270
+ /**
271
+ * Specifies the component to act as strict so that, it allows to enter only a valid time value within a specified range or else
272
+ * resets to previous value. By default, strictMode is in false.
273
+ * > For more details refer to
274
+ * [`Strict Mode`](../../timepicker/strict-mode/) documentation.
275
+ *
276
+ * @default false
277
+ */
278
+ @Property(false)
279
+ public strictMode: boolean;
280
+ /**    
281
+ * Customizes the key actions in TimePicker.
282
+ * For example, when using German keyboard, the key actions can be customized using these shortcuts.
283
+ *
284
+ * <table>
285
+ * <tr>
286
+ * <td colSpan=1 rowSpan=1>
287
+ * Key action<br/></td><td colSpan=1 rowSpan=1>
288
+ * Key<br/></td></tr>
289
+ * <tr>
290
+ * <td colSpan=1 rowSpan=1>
291
+ * enter<br/></td><td colSpan=1 rowSpan=1>
292
+ * enter<br/></td></tr>
293
+ * <tr>
294
+ * <td colSpan=1 rowSpan=1>
295
+ * escape<br/></td><td colSpan=1 rowSpan=1>
296
+ * escape<br/></td></tr>
297
+ * <tr>
298
+ * <td colSpan=1 rowSpan=1>
299
+ * end<br/></td><td colSpan=1 rowSpan=1>
300
+ * end<br/></td></tr>
301
+ * <tr>
302
+ * <td colSpan=1 rowSpan=1>
303
+ * tab<br/></td><td colSpan=1 rowSpan=1>
304
+ * tab<br/></td></tr>
305
+ * <tr>
306
+ * <td colSpan=1 rowSpan=1>
307
+ * home<br/></td><td colSpan=1 rowSpan=1>
308
+ * home<br/></td></tr>
309
+ * <tr>
310
+ * <td colSpan=1 rowSpan=1>
311
+ * down<br/></td><td colSpan=1 rowSpan=1>
312
+ * downarrow<br/></td></tr>
313
+ * <tr>
314
+ * <td colSpan=1 rowSpan=1>
315
+ * up<br/></td><td colSpan=1 rowSpan=1>
316
+ * uparrow<br/></td></tr>
317
+ * <tr>
318
+ * <td colSpan=1 rowSpan=1>
319
+ * left<br/></td><td colSpan=1 rowSpan=1>
320
+ * leftarrow<br/></td></tr>
321
+ * <tr>
322
+ * <td colSpan=1 rowSpan=1>
323
+ * right<br/></td><td colSpan=1 rowSpan=1>
324
+ * rightarrow<br/></td></tr>
325
+ * <tr>
326
+ * <td colSpan=1 rowSpan=1>
327
+ * open<br/></td><td colSpan=1 rowSpan=1>
328
+ * alt+downarrow<br/></td></tr>
329
+ * <tr>
330
+ * <td colSpan=1 rowSpan=1>
331
+ * close<br/></td><td colSpan=1 rowSpan=1>
332
+ * alt+uparrow<br/></td></tr>
333
+ * </table>
334
+ *
335
+ * {% codeBlock src='timepicker/keyConfigs/index.md' %}{% endcodeBlock %}
336
+ *
337
+ * @default null
338
+ */
339
+ @Property(null)
340
+ public keyConfigs: { [key: string]: string };
341
+ /**
342
+ * Specifies the format of value that is to be displayed in component. By default, the format is
343
+ * based on the culture.
344
+ * > For more details refer to
345
+ * [`Format`](../../timepicker/getting-started#setting-the-time-format) documentation.
346
+ * {% codeBlock src='timepicker/format/index.md' %}{% endcodeBlock %}
347
+ *
348
+ * @default null
349
+ * @aspType string
350
+ */
351
+ @Property(null)
352
+ public format: string | TimeFormatObject;
353
+ /**
354
+ * Specifies whether the component to be disabled or not.
355
+ *
356
+ * @default true
357
+ */
358
+ @Property(true)
359
+ public enabled: boolean;
360
+ /**
361
+ * Specifies the component popup display full screen in mobile devices.
362
+ *
363
+ * @default false
364
+ */
365
+ @Property(false)
366
+ public fullScreenMode : boolean;
367
+ /**
368
+ * Specifies the component in readonly state.
369
+ *
370
+ * @default false
371
+ */
372
+ @Property(false)
373
+ public readonly: boolean;
374
+ /**
375
+ * You can add the additional html attributes such as disabled, value etc., to the element.
376
+ * If you configured both property and equivalent html attribute then the component considers the property value.
377
+ * {% codeBlock src='timepicker/htmlAttributes/index.md' %}{% endcodeBlock %}
378
+ *
379
+ * @default {}
380
+ */
381
+ @Property({})
382
+ public htmlAttributes: { [key: string]: string };
383
+ /**
384
+ * Specifies the placeholder text to be floated.
385
+ * Possible values are:
386
+ * Never: The label will never float in the input when the placeholder is available.
387
+ * Always: The floating label will always float above the input.
388
+ * Auto: The floating label will float above the input after focusing or entering a value in the input.
389
+ *
390
+ * @default Syncfusion.EJ2.Inputs.FloatLabelType.Never
391
+ * @aspType Syncfusion.EJ2.Inputs.FloatLabelType
392
+ * @isEnumeration true
393
+ */
394
+ @Property('Never')
395
+ public floatLabelType: FloatLabelType | string;
396
+ /**
397
+ * Specifies the placeholder text that is displayed in textbox.
398
+ *
399
+ * @default null
400
+ */
401
+ @Property(null)
402
+ public placeholder: string;
403
+ /**
404
+ * specifies the z-index value of the timePicker popup element.
405
+ *
406
+ * @default 1000
407
+ * @aspType int
408
+ */
409
+ @Property(1000)
410
+ public zIndex: number;
411
+ /**
412
+ * Enable or disable the persisting component's state between the page reloads. If enabled, following list of states will be persisted.
413
+ * 1. Value
414
+ *
415
+      * @default false
416
+      */
417
+ @Property(false)
418
+ public enablePersistence: boolean;
419
+ /**
420
+ * Specifies whether to show or hide the clear icon.
421
+ *
422
+ * @default true
423
+ */
424
+ @Property(true)
425
+ public showClearButton: boolean;
426
+ /**
427
+ * Specifies the time interval between the two adjacent time values in the popup list.
428
+ * > For more details refer to
429
+ * [`Format`](../../timepicker/getting-started#setting-the-time-format)documentation.
430
+ *
431
+ * @default 30
432
+ *
433
+ */
434
+ @Property(30)
435
+ public step: number;
436
+ /**
437
+ * Specifies the scroll bar position if there is no value is selected in the popup list or
438
+ * the given value is not present in the popup list.
439
+ * > For more details refer to
440
+ * [`Time Duration`](https://ej2.syncfusion.com/demos/#/material/timepicker/list-formatting.html) sample.
441
+ * {% codeBlock src='timepicker/scrollTo/index.md' %}{% endcodeBlock %}
442
+ *
443
+ * @default null
444
+ */
445
+ @Property(null)
446
+ public scrollTo: Date;
447
+ /**
448
+ * Gets or sets the value of the component. The value is parsed based on the culture specific time format.
449
+ *
450
+ * @default null
451
+ * @isGenericType true
452
+ */
453
+ @Property(null)
454
+ public value: Date;
455
+ /**
456
+ * Gets or sets the minimum time value that can be allowed to select in TimePicker.
457
+ * > For more details refer to
458
+ * [`Time Range`](../../timepicker/time-range/) documentation.
459
+ *
460
+ * @default 00:00
461
+ */
462
+ @Property(null)
463
+ public min: Date;
464
+ /**
465
+ * Gets or sets the maximum time value that can be allowed to select in TimePicker.
466
+ * > For more details refer to
467
+ * [`Time Range`](../../timepicker/time-range/) documentation.
468
+ *
469
+ * @default 00:00
470
+ */
471
+ @Property(null)
472
+ public max: Date;
473
+ /**
474
+ * > Support for `allowEdit` has been provided from
475
+ * [`v16.2.46`](https://ej2.syncfusion.com/angular/documentation/release-notes/16.2.46/#timepicker).
476
+ *
477
+ * Specifies whether the input textbox is editable or not. Here the user can select the value from the
478
+ * popup and cannot edit in the input textbox.
479
+ *
480
+ * @default true
481
+ */
482
+ @Property(true)
483
+ public allowEdit: boolean;
484
+ /**
485
+ * By default, the popup opens while clicking on the timepicker icon.
486
+ * If you want to open the popup while focusing the time input then specify its value as true.
487
+ *
488
+ * @default false
489
+ */
490
+ @Property(false)
491
+ public openOnFocus : boolean;
492
+ /**
493
+ * Specifies whether it is a masked timepicker or not.
494
+ * By default the timepicker component render without masked input.
495
+ * If you need masked timepicker input then specify it as true.
496
+ *
497
+ * @default false
498
+ */
499
+ @Property(false)
500
+ public enableMask: boolean;
501
+
502
+ /**
503
+ * Specifies the mask placeholder to be displayed on masked timepicker.
504
+ *
505
+ * @default {day:'day' , month:'month', year: 'year', hour:'hour',minute:'minute',second:'second',dayOfTheWeek: 'day of the week'}
506
+ * @aspType TimePickerMaskPlaceholder
507
+ */
508
+ @Property({day: 'day' , month: 'month', year: 'year', hour: 'hour', minute: 'minute', second: 'second', dayOfTheWeek: 'day of the week'})
509
+ public maskPlaceholder: TimeMaskPlaceholderModel;
510
+
511
+ /**
512
+ * By default, the time value will be processed based on system time zone.
513
+ * If you want to process the initial time value using server time zone
514
+ * then specify the time zone value to `serverTimezoneOffset` property.
515
+ *
516
+ * @default null
517
+ * @deprecated
518
+ */
519
+ @Property(null)
520
+ public serverTimezoneOffset: number;
521
+
522
+ /**
523
+ * Triggers when the value is changed.
524
+ *
525
+ * @event change
526
+ */
527
+ @Event()
528
+ public change: EmitType<ChangeEventArgs>;
529
+
530
+ /**
531
+ * Triggers when the component is created.
532
+ *
533
+ * @event created
534
+ */
535
+ @Event()
536
+ public created: EmitType<Object>;
537
+
538
+ /**
539
+ * Triggers when the component is destroyed.
540
+ *
541
+ * @event destroyed
542
+ */
543
+ @Event()
544
+ public destroyed: EmitType<Object>;
545
+ /**
546
+ * Triggers when the popup is opened.
547
+ *
548
+ * @event open
549
+ */
550
+ @Event()
551
+ public open: EmitType<PopupEventArgs>;
552
+ /**
553
+ * Triggers while rendering the each popup list item.
554
+ *
555
+ * @event itemRender
556
+ */
557
+ @Event()
558
+ public itemRender: EmitType<ItemEventArgs>;
559
+ /**
560
+ * Triggers when the popup is closed.
561
+ *
562
+ * @event close
563
+ */
564
+ @Event()
565
+ public close: EmitType<PopupEventArgs>;
566
+ /**
567
+ * Triggers when timepicker value is cleared using clear button.
568
+ *
569
+ * @event cleared
570
+ */
571
+ @Event()
572
+ public cleared: EmitType<ClearedEventArgs>;
573
+ /**
574
+ * Triggers when the control loses the focus.
575
+ *
576
+ * @event blur
577
+ */
578
+ @Event()
579
+ public blur: EmitType<BlurEventArgs>;
580
+ /**
581
+ * Triggers when the control gets focused.
582
+ *
583
+ * @event focus
584
+ */
585
+ @Event()
586
+ public focus: EmitType<FocusEventArgs>;
587
+
588
+
589
+ /**
590
+ * Constructor for creating the widget
591
+ *
592
+ * @param {TimePickerModel} options - Specifies the TimePicker model.
593
+ * @param {string | HTMLInputElement} element - Specifies the element to render as component.
594
+ * @private
595
+ */
596
+ public constructor(options?: TimePickerModel, element?: string | HTMLInputElement) {
597
+ super(options, element);
598
+ this.timeOptions = options;
599
+ }
600
+ /**
601
+ * Initialize the event handler
602
+ *
603
+ * @returns {void}
604
+ * @private
605
+ */
606
+ protected preRender(): void {
607
+ this.keyConfigure = {
608
+ enter: 'enter',
609
+ escape: 'escape',
610
+ end: 'end',
611
+ tab: 'tab',
612
+ home: 'home',
613
+ down: 'downarrow',
614
+ up: 'uparrow',
615
+ left: 'leftarrow',
616
+ right: 'rightarrow',
617
+ open: 'alt+downarrow',
618
+ close: 'alt+uparrow'
619
+ };
620
+ this.cloneElement = <HTMLElement>this.element.cloneNode(true);
621
+ removeClass([this.cloneElement], [ROOT, CONTROL, LIBRARY]);
622
+ this.inputElement = <HTMLInputElement>this.element;
623
+ this.angularTag = null;
624
+ this.formElement = closest(this.element, 'form');
625
+ if (this.element.tagName === 'EJS-TIMEPICKER') {
626
+ this.angularTag = this.element.tagName;
627
+ this.inputElement = <HTMLInputElement>this.createElement('input');
628
+ this.element.appendChild(this.inputElement);
629
+ }
630
+ this.tabIndex = this.element.hasAttribute('tabindex') ? this.element.getAttribute('tabindex') : '0';
631
+ this.element.removeAttribute('tabindex');
632
+ this.openPopupEventArgs = {
633
+ appendTo: document.body
634
+ };
635
+ }
636
+ // element creation
637
+ protected render(): void {
638
+ this.initialize();
639
+ this.createInputElement();
640
+ this.updateHtmlAttributeToWrapper();
641
+ this.setTimeAllowEdit();
642
+ this.setEnable();
643
+ this.validateInterval();
644
+ this.bindEvents();
645
+ this.validateDisable();
646
+ this.setTimeZone();
647
+ this.setValue(this.getFormattedValue(this.value));
648
+ if (this.enableMask && !this.value && this.maskedDateValue && (this.floatLabelType === 'Always' || !this.floatLabelType || !this.placeholder)){
649
+ this.updateInputValue(this.maskedDateValue);
650
+ this.checkErrorState(this.maskedDateValue);
651
+ }
652
+ this.anchor = this.inputElement;
653
+ this.inputElement.setAttribute('value', this.inputElement.value);
654
+ this.inputEleValue = this.getDateObject(this.inputElement.value);
655
+ if (!isNullOrUndefined(this.inputWrapper.buttons[0]) && !isNullOrUndefined(this.inputWrapper.container.getElementsByClassName('e-float-text-overflow')[0]) && this.floatLabelType !== 'Never') {
656
+ this.inputWrapper.container.getElementsByClassName('e-float-text-overflow')[0].classList.add('e-icon');
657
+ }
658
+ if (!isNullOrUndefined(closest(this.element, 'fieldset') as HTMLFieldSetElement) && (closest(this.element, 'fieldset') as HTMLFieldSetElement).disabled) {
659
+ this.enabled = false;
660
+ }
661
+ this.renderComplete();
662
+ }
663
+ protected setTimeZone(): void {
664
+ if (!isNullOrUndefined(this.serverTimezoneOffset) && this.value) {
665
+ const clientTimeZoneDiff: number = new Date().getTimezoneOffset() / 60;
666
+ const serverTimezoneDiff: number = this.serverTimezoneOffset;
667
+ let timeZoneDiff: number = serverTimezoneDiff + clientTimeZoneDiff;
668
+ timeZoneDiff = this.isDayLightSaving() ? timeZoneDiff-- : timeZoneDiff;
669
+ this.value = new Date((this.value).getTime() + (timeZoneDiff * 60 * 60 * 1000));
670
+ }
671
+ }
672
+ protected isDayLightSaving(): boolean {
673
+ const firstOffset: number = new Date(this.value.getFullYear(), 0 , 1).getTimezoneOffset();
674
+ const secondOffset: number = new Date(this.value.getFullYear(), 6 , 1).getTimezoneOffset();
675
+ return (this.value.getTimezoneOffset() < Math.max(firstOffset, secondOffset));
676
+ }
677
+ private setTimeAllowEdit(): void {
678
+ if (this.allowEdit) {
679
+ if (!this.readonly) {
680
+ this.inputElement.removeAttribute('readonly');
681
+ }
682
+ } else {
683
+ attributes(this.inputElement, { 'readonly': '' });
684
+ }
685
+ this.clearIconState();
686
+ }
687
+ private clearIconState(): void {
688
+ if (!this.allowEdit && this.inputWrapper && !this.readonly) {
689
+ if (this.inputElement.value === '') {
690
+ removeClass([this.inputWrapper.container], [EDITABLE]);
691
+ } else {
692
+ addClass([this.inputWrapper.container], [EDITABLE]);
693
+ }
694
+ } else if (this.inputWrapper) {
695
+ removeClass([this.inputWrapper.container], [EDITABLE]);
696
+ }
697
+ }
698
+ private validateDisable(): void {
699
+ this.setMinMax(this.initMin, this.initMax);
700
+ if (!isNullOrUndefined(this.value)){
701
+ this.popupCreation();
702
+ this.popupObj.destroy();
703
+ this.popupWrapper = this.popupObj = null;
704
+ }
705
+ if ((!isNaN(+this.value) && this.value !== null)) {
706
+ if (!this.valueIsDisable(this.value)) {
707
+ //disable value given in value property so reset the date based on current date
708
+ if (this.strictMode) {
709
+ this.resetState();
710
+ }
711
+ this.initValue = null;
712
+ this.initMax = this.getDateObject(this.initMax);
713
+ this.initMin = this.getDateObject(this.initMin);
714
+ this.timeCollections = this.liCollections = [];
715
+ this.setMinMax(this.initMin, this.initMax);
716
+ }
717
+ }
718
+ }
719
+
720
+ protected validationAttribute(target: HTMLElement, input: Element): void {
721
+ const name: string = target.getAttribute('name') ? target.getAttribute('name') : target.getAttribute('id');
722
+ input.setAttribute('name', name);
723
+ target.removeAttribute('name');
724
+ const attributes: string[] = ['required', 'aria-required', 'form'];
725
+ for (let i: number = 0; i < attributes.length; i++) {
726
+ if (isNullOrUndefined(target.getAttribute(attributes[i as number]))) {
727
+ continue;
728
+ }
729
+ const attr: string = target.getAttribute(attributes[i as number]);
730
+ input.setAttribute(attributes[i as number], attr);
731
+ target.removeAttribute(attributes[i as number]);
732
+ }
733
+ }
734
+
735
+ private initialize(): void {
736
+ this.globalize = new Internationalization(this.locale);
737
+ this.defaultCulture = new Internationalization('en');
738
+ this.checkTimeFormat();
739
+ this.checkInvalidValue(this.value);
740
+ // persist the value property.
741
+ this.setProperties({ value: this.checkDateValue(new Date(this.checkInValue(this.value))) }, true);
742
+ this.setProperties({ min: this.checkDateValue(new Date(this.checkInValue(this.min))) }, true);
743
+ this.setProperties({ max: this.checkDateValue(new Date(this.checkInValue(this.max))) }, true);
744
+ this.setProperties({ scrollTo: this.checkDateValue(new Date(this.checkInValue(this.scrollTo))) }, true);
745
+ if (this.angularTag !== null) {
746
+ this.validationAttribute(this.element, this.inputElement);
747
+ }
748
+ this.updateHtmlAttributeToElement();
749
+ this.checkAttributes(false); //check the input element attributes
750
+ const localeText: { placeholder: string } = { placeholder: this.placeholder };
751
+ this.l10n = new L10n('timepicker', localeText, this.locale);
752
+ this.setProperties({ placeholder: this.placeholder || this.l10n.getConstant('placeholder') }, true);
753
+ this.initValue = this.checkDateValue(this.value);
754
+ this.initMin = this.checkDateValue(this.min);
755
+ this.initMax = this.checkDateValue(this.max);
756
+ this.isNavigate = this.isPreventBlur = this.isTextSelected = false;
757
+ this.activeIndex = this.valueWithMinutes = this.prevDate = null;
758
+ if (!isNullOrUndefined(this.element.getAttribute('id'))) {
759
+ if (this.angularTag !== null) {
760
+ this.inputElement.id = this.element.getAttribute('id') + '_input';
761
+ }
762
+ } else {
763
+ //for angular case
764
+ this.element.id = getUniqueID('ej2_timepicker');
765
+ if (this.angularTag !== null) {
766
+ attributes(this.inputElement, { 'id': this.element.id + '_input' });
767
+ }
768
+ }
769
+ if (isNullOrUndefined(this.inputElement.getAttribute('name'))) {
770
+ attributes(this.inputElement, { 'name': this.element.id });
771
+ }
772
+ if (this.enableMask) {
773
+ this.notify('createMask', {
774
+ module: 'MaskedDateTime'
775
+ });
776
+ }
777
+ }
778
+ protected checkTimeFormat(): void {
779
+ if (this.format) {
780
+ if (typeof this.format === 'string') {
781
+ this.formatString = this.format;
782
+ } else if (!isNullOrUndefined(this.format.skeleton) && this.format.skeleton !== '') {
783
+ const skeletonString: string = this.format.skeleton;
784
+ this.formatString = this.globalize.getDatePattern({ type: 'time', skeleton: skeletonString });
785
+ } else {
786
+ this.formatString = this.globalize.getDatePattern({ type: 'time', skeleton: 'short' });
787
+ }
788
+ } else {
789
+ this.formatString = null;
790
+ }
791
+ }
792
+ private checkDateValue(value: Date): Date {
793
+ return (!isNullOrUndefined(value) && value instanceof Date && !isNaN(+value)) ? value : null;
794
+ }
795
+ private createInputElement(): void {
796
+ if (this.fullScreenMode && Browser.isDevice){
797
+ this.cssClass += ' ' + 'e-popup-expand';
798
+ }
799
+ let updatedCssClassesValue: string = this.cssClass;
800
+ const isBindClearAction: boolean = this.enableMask ? false : true;
801
+ if (!isNullOrUndefined(this.cssClass) && this.cssClass !== '') {
802
+ updatedCssClassesValue = (this.cssClass.replace(/\s+/g, ' ')).trim();
803
+ }
804
+ this.inputWrapper = Input.createInput(
805
+ {
806
+ element: this.inputElement,
807
+ bindClearAction: isBindClearAction,
808
+ floatLabelType: this.floatLabelType,
809
+ properties: {
810
+ readonly: this.readonly,
811
+ placeholder: this.placeholder,
812
+ cssClass: updatedCssClassesValue,
813
+ enabled: this.enabled,
814
+ enableRtl: this.enableRtl,
815
+ showClearButton: this.showClearButton
816
+ },
817
+ buttons: [' e-input-group-icon e-time-icon e-icons']
818
+ },
819
+ this.createElement
820
+ );
821
+ this.inputWrapper.container.style.width = this.setWidth(this.width);
822
+ attributes(this.inputElement, {
823
+ 'aria-autocomplete': 'list', 'tabindex': '0',
824
+ 'aria-expanded': 'false', 'role': 'combobox', 'autocomplete': 'off',
825
+ 'autocorrect': 'off', 'autocapitalize': 'off', 'spellcheck': 'false',
826
+ 'aria-disabled': 'false', 'aria-invalid': 'false'
827
+ });
828
+ if (!this.isNullOrEmpty(this.inputStyle)) {
829
+ Input.addAttributes({ 'style': this.inputStyle }, this.inputElement);
830
+ }
831
+ addClass([this.inputWrapper.container], WRAPPERCLASS);
832
+ }
833
+ private getCldrDateTimeFormat(): string {
834
+ const culture: Internationalization = new Internationalization(this.locale);
835
+ let cldrTime: string;
836
+ const dateFormat: string = culture.getDatePattern({ skeleton: 'yMd' });
837
+ if (this.isNullOrEmpty(this.formatString)) {
838
+ cldrTime = dateFormat + ' ' + this.cldrFormat('time');
839
+ } else {
840
+ cldrTime = this.formatString;
841
+ }
842
+ return cldrTime;
843
+ }
844
+ private checkInvalidValue(value: Date): void {
845
+ let isInvalid: boolean = false;
846
+ if (typeof value !== 'object' && !isNullOrUndefined(value)) {
847
+ let valueString: string = <string>value;
848
+ if (typeof valueString === 'string') {
849
+ valueString = valueString.trim();
850
+ }
851
+ let valueExpression: object = null;
852
+ let valueExp: Date = null;
853
+ if (typeof value === 'number') {
854
+ valueString = (value as string).toString();
855
+ } else if (typeof value === 'string') {
856
+ if (!(/^[a-zA-Z0-9- ]*$/).test(value)) {
857
+ valueExpression = this.setCurrentDate(this.getDateObject(value));
858
+ if (isNullOrUndefined(valueExpression)) {
859
+ valueExpression = this.checkDateValue(this.globalize.parseDate(this.getAmPmValue(valueString), {
860
+ format: this.getCldrDateTimeFormat(), type: 'datetime'
861
+ }));
862
+ if (isNullOrUndefined(valueExpression)) {
863
+ valueExpression = this.checkDateValue(this.globalize.parseDate(this.getAmPmValue(valueString), {
864
+ format: this.formatString, type: 'dateTime', skeleton: 'yMd'
865
+ }));
866
+ }
867
+ }
868
+ }
869
+ }
870
+ valueExp = this.globalize.parseDate(this.getAmPmValue(valueString), {
871
+ format: this.getCldrDateTimeFormat(), type: 'datetime'
872
+ });
873
+ valueExpression = (!isNullOrUndefined(valueExp) && valueExp instanceof Date && !isNaN(+valueExp)) ? valueExp : null;
874
+ if (isNullOrUndefined(valueExpression) && valueString.replace(/\s/g, '').length) {
875
+ let extISOString: RegExp = null;
876
+ let basicISOString: RegExp = null;
877
+ // eslint-disable-next-line
878
+ extISOString = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/;
879
+ // eslint-disable-next-line
880
+ basicISOString = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/;
881
+ if ((!extISOString.test(valueString) && !basicISOString.test(valueString))
882
+ || ((/^[a-zA-Z0-9- ]*$/).test(value)) || isNaN(+new Date('' + valueString))) {
883
+ isInvalid = true;
884
+ } else {
885
+ valueExpression = new Date('' + valueString);
886
+ }
887
+ }
888
+ if (isInvalid) {
889
+ if (!this.strictMode) {
890
+ this.invalidValueString = valueString;
891
+ }
892
+ this.setProperties({ value: null }, true);
893
+ this.initValue = null;
894
+ } else {
895
+ this.setProperties({ value: valueExpression }, true);
896
+ this.initValue = this.value;
897
+ }
898
+ }
899
+ }
900
+ public requiredModules(): ModuleDeclaration[] {
901
+ const modules: ModuleDeclaration[] = [];
902
+ if (this.enableMask)
903
+ {
904
+ modules.push({args: [this], member: 'MaskedDateTime'});
905
+ }
906
+ return modules;
907
+ }
908
+ protected getAmPmValue(date: string | null | undefined): string {
909
+ try {
910
+ if (typeof date === 'string' && date.trim() !== '') {
911
+ // Replace am/pm variants with uppercase AM/PM
912
+ return date.replace(/(am|pm|Am|aM|pM|Pm)/g, (match: string) => match.toLocaleUpperCase());
913
+ }
914
+ // If date is null, undefined, or an empty string, return a default value or empty string
915
+ return '';
916
+ } catch (error) {
917
+ console.error('Error occurred while processing date:', error);
918
+ return ''; // Return a default value in case of an error
919
+ }
920
+ }
921
+ private cldrFormat(type: string): string {
922
+ let cldrDateTimeString: string;
923
+ if (this.locale === 'en' || this.locale === 'en-US') {
924
+ cldrDateTimeString = <string>(getValue('timeFormats.short', getDefaultDateObject()));
925
+ } else {
926
+ cldrDateTimeString = <string>(this.getCultureTimeObject(cldrData, '' + this.locale));
927
+ }
928
+ return cldrDateTimeString;
929
+ }
930
+ // destroy function
931
+ public destroy(): void {
932
+ this.hide();
933
+ if (this.showClearButton) {
934
+ this.clearButton = document.getElementsByClassName('e-clear-icon')[0] as HTMLElement;
935
+ }
936
+ this.unBindEvents();
937
+ const ariaAttribute: object = {
938
+ 'aria-autocomplete': 'list', 'tabindex': '0',
939
+ 'aria-expanded': 'false', 'role': 'combobox', 'autocomplete': 'off',
940
+ 'autocorrect': 'off', 'autocapitalize': 'off', 'spellcheck': 'false',
941
+ 'aria-disabled': 'true', 'aria-invalid': 'false'
942
+ };
943
+ if (this.inputElement) {
944
+ Input.removeAttributes(<{ [key: string]: string }>ariaAttribute, this.inputElement);
945
+ if (this.angularTag === null) {
946
+ this.inputWrapper.container.parentElement.appendChild(this.inputElement);
947
+ }
948
+ if (!isNullOrUndefined(this.cloneElement.getAttribute('tabindex'))) {
949
+ this.inputElement.setAttribute('tabindex', this.tabIndex);
950
+ } else {
951
+ this.inputElement.removeAttribute('tabindex');
952
+ }
953
+ this.ensureInputAttribute();
954
+ this.enableElement([this.inputElement]);
955
+ this.inputElement.classList.remove('e-input');
956
+ if (isNullOrUndefined(this.cloneElement.getAttribute('disabled'))) {
957
+ Input.setEnabled(true, this.inputElement, this.floatLabelType);
958
+ }
959
+ }
960
+ if (this.inputWrapper.container) {
961
+ detach(this.inputWrapper.container);
962
+ }
963
+ this.inputWrapper = this.popupWrapper = this.cloneElement = undefined;
964
+ this.liCollections = this.timeCollections = this.disableItemCollection = [];
965
+ if (!isNullOrUndefined(this.rippleFn)) {
966
+ this.rippleFn();
967
+ }
968
+ Input.destroy({
969
+ element: this.inputElement,
970
+ floatLabelType: this.floatLabelType,
971
+ properties: this.properties
972
+ }, this.clearButton);
973
+ super.destroy();
974
+ if (this.formElement) {
975
+ EventHandler.remove(this.formElement, 'reset', this.formResetHandler);
976
+ }
977
+ this.rippleFn = null;
978
+ this.openPopupEventArgs = null;
979
+ this.selectedElement = null;
980
+ this.listTag = null;
981
+ this.liCollections = null;
982
+ }
983
+ protected ensureInputAttribute(): void {
984
+ const propertyList: string[] = [];
985
+ for (let i: number = 0; i < this.inputElement.attributes.length; i++) {
986
+ propertyList[i as number] = this.inputElement.attributes[i as number].name;
987
+ }
988
+ for (let i: number = 0; i < propertyList.length; i++) {
989
+ if (!isNullOrUndefined(this.cloneElement.getAttribute(propertyList[i as number]))) {
990
+ this.inputElement.setAttribute(propertyList[i as number], this.cloneElement.getAttribute(propertyList[i as number]));
991
+ if (propertyList[i as number].toLowerCase() === 'value') {
992
+ this.inputElement.value = this.cloneElement.getAttribute(propertyList[i as number]);
993
+ }
994
+ } else {
995
+ this.inputElement.removeAttribute(propertyList[i as number]);
996
+ if (propertyList[i as number].toLowerCase() === 'value') {
997
+ this.inputElement.value = '';
998
+ }
999
+ }
1000
+ }
1001
+ }
1002
+ //popup creation
1003
+ private popupCreation(): void {
1004
+ this.popupWrapper = this.createElement('div', {
1005
+ className: ROOT + ' ' + POPUP,
1006
+ attrs: { 'id': this.element.id + '_popup' }
1007
+ });
1008
+ this.popupWrapper.style.visibility = 'hidden';
1009
+ this.popupWrapper.setAttribute( 'aria-label', this.element.id );
1010
+ this.popupWrapper.setAttribute( 'role', 'dialog' );
1011
+ if (!isNullOrUndefined(this.cssClass)) {
1012
+ this.popupWrapper.className += ' ' + this.cssClass;
1013
+ }
1014
+ if (!isNullOrUndefined(this.step) && this.step > 0) {
1015
+ this.generateList();
1016
+ append([this.listWrapper], this.popupWrapper);
1017
+ }
1018
+ this.addSelection();
1019
+ this.renderPopup();
1020
+ detach(this.popupWrapper);
1021
+ }
1022
+ protected getPopupHeight(): number {
1023
+ const height: number = parseInt(<string>POPUPDIMENSION, 10);
1024
+ const popupHeight: number = this.popupWrapper.getBoundingClientRect().height;
1025
+ return popupHeight > height ? height : popupHeight;
1026
+ }
1027
+ private generateList(): void {
1028
+ this.createListItems();
1029
+ this.wireListEvents();
1030
+ const rippleModel: RippleOptions = { duration: 300, selector: '.' + LISTCLASS };
1031
+ this.rippleFn = rippleEffect(this.listWrapper, rippleModel);
1032
+ this.liCollections = <HTMLElement[] & NodeListOf<Element>>this.listWrapper.querySelectorAll('.' + LISTCLASS);
1033
+ }
1034
+ private renderPopup(): void {
1035
+ this.containerStyle = this.inputWrapper.container.getBoundingClientRect();
1036
+ this.popupObj = new Popup(this.popupWrapper as HTMLElement, {
1037
+ width: this.setPopupWidth(this.width),
1038
+ zIndex: this.zIndex,
1039
+ targetType: 'relative',
1040
+ position: Browser.isDevice ? { X: 'center', Y: 'center' } : { X: 'left', Y: 'bottom' },
1041
+ collision: Browser.isDevice ? { X: 'fit', Y: 'fit' } : { X: 'flip', Y: 'flip' },
1042
+ enableRtl: this.enableRtl,
1043
+ relateTo: Browser.isDevice ? document.body : this.inputWrapper.container,
1044
+ offsetY: OFFSETVAL,
1045
+ open: () => {
1046
+ this.popupWrapper.style.visibility = 'visible';
1047
+ addClass([this.inputWrapper.buttons[0]], SELECTED);
1048
+ }, close: () => {
1049
+ removeClass([this.inputWrapper.buttons[0]], SELECTED);
1050
+ this.unWireListEvents();
1051
+ this.inputElement.removeAttribute('aria-activedescendant');
1052
+ remove(this.popupObj.element);
1053
+ this.popupObj.destroy();
1054
+ this.popupWrapper.innerHTML = '';
1055
+ this.listWrapper = this.popupWrapper = this.listTag = undefined;
1056
+ }, targetExitViewport: () => {
1057
+ if (!Browser.isDevice) {
1058
+ this.hide();
1059
+ }
1060
+ }
1061
+ });
1062
+ if (!Browser.isDevice) {
1063
+ this.popupObj.collision = { X: 'none', Y: 'flip' };
1064
+ }
1065
+ if (Browser.isDevice && this.fullScreenMode){
1066
+ this.popupObj.element.style.maxHeight = '100%';
1067
+ this.popupObj.element.style.width = '100%';
1068
+ } else {
1069
+ this.popupObj.element.style.maxHeight = POPUPDIMENSION;
1070
+ }
1071
+
1072
+ if (Browser.isDevice && this.fullScreenMode) {
1073
+ const modelHeader: HTMLElement = this.createElement('div', { className: 'e-model-header' });
1074
+ const modelTitleSpan: HTMLElement = this.createElement('span', { className: 'e-model-title' });
1075
+ modelTitleSpan.textContent = 'Select time';
1076
+ const modelCloseIcon: HTMLElement = this.createElement('span', { className: 'e-popup-close' });
1077
+ EventHandler.add(modelCloseIcon, 'mousedown touchstart', this.timePopupCloseHandler, this);
1078
+ modelHeader.appendChild(modelCloseIcon);
1079
+ modelHeader.appendChild(modelTitleSpan);
1080
+ this.popupWrapper.insertBefore(modelHeader, this.popupWrapper.firstElementChild);
1081
+ }
1082
+ }
1083
+
1084
+ private timePopupCloseHandler(e: MouseEvent| TouchEvent): void {
1085
+ this.hide();
1086
+ }
1087
+
1088
+ //util function
1089
+ private getFormattedValue(value: Date): string {
1090
+ if (isNullOrUndefined(this.checkDateValue(value))) {
1091
+ return null;
1092
+ } else {
1093
+ return this.globalize.formatDate(value, { skeleton: 'medium', type: 'time' });
1094
+ }
1095
+ }
1096
+ private getDateObject(text: string | Date): Date {
1097
+ if (!this.isNullOrEmpty(text)) {
1098
+ const dateValue: Date = this.createDateObj(text);
1099
+ const value: boolean = !this.isNullOrEmpty(this.initValue);
1100
+ if (this.checkDateValue(dateValue)) {
1101
+ const date: number = value ? this.initValue.getDate() : DAY;
1102
+ const month: number = value ? this.initValue.getMonth() : MONTH;
1103
+ const year: number = value ? this.initValue.getFullYear() : YEAR;
1104
+ return new Date(year, month, date, dateValue.getHours(), dateValue.getMinutes(), dateValue.getSeconds());
1105
+ }
1106
+ }
1107
+ return null;
1108
+ }
1109
+
1110
+ private updateHtmlAttributeToWrapper(): void {
1111
+ if ( !isNullOrUndefined(this.htmlAttributes)) {
1112
+ for (const key of Object.keys(this.htmlAttributes)) {
1113
+ if (wrapperAttributes.indexOf(key) > -1 ) {
1114
+ if (key === 'class') {
1115
+ const updatedClassesValue: string = (this.htmlAttributes[`${key}`].replace(/\s+/g, ' ')).trim();
1116
+ if (updatedClassesValue !== '') {
1117
+ addClass([this.inputWrapper.container], updatedClassesValue.split(' '));
1118
+ }
1119
+ } else if (key === 'style') {
1120
+ let timeStyle: string = this.inputWrapper.container.getAttribute(key);
1121
+ timeStyle = !isNullOrUndefined(timeStyle) ? (timeStyle + this.htmlAttributes[`${key}`]) :
1122
+ this.htmlAttributes[`${key}`];
1123
+ this.inputWrapper.container.setAttribute(key, timeStyle);
1124
+ } else {
1125
+ this.inputWrapper.container.setAttribute(key, this.htmlAttributes[`${key}`]);
1126
+ }
1127
+ }
1128
+ }
1129
+ }
1130
+ }
1131
+
1132
+ private updateHtmlAttributeToElement(): void {
1133
+ if ( !isNullOrUndefined(this.htmlAttributes)) {
1134
+ for (const key of Object.keys(this.htmlAttributes)) {
1135
+ if (wrapperAttributes.indexOf(key) < 0 ) {
1136
+ this.inputElement.setAttribute(key, this.htmlAttributes[`${key}`]);
1137
+ }
1138
+ }
1139
+ }
1140
+ }
1141
+ private updateCssClass(cssClassNew : string, cssClassOld : string) : void {
1142
+ if (!isNullOrUndefined(cssClassOld)) {
1143
+ cssClassOld = (cssClassOld.replace(/\s+/g, ' ')).trim();
1144
+ }
1145
+ if (!isNullOrUndefined(cssClassNew)) {
1146
+ cssClassNew = (cssClassNew.replace(/\s+/g, ' ')).trim();
1147
+ }
1148
+ Input.setCssClass(cssClassNew, [this.inputWrapper.container], cssClassOld);
1149
+ if (this.popupWrapper) {
1150
+ Input.setCssClass(cssClassNew, [this.popupWrapper], cssClassOld);
1151
+ }
1152
+ }
1153
+ private removeErrorClass(): void {
1154
+ removeClass([this.inputWrapper.container], ERROR);
1155
+ attributes(this.inputElement, { 'aria-invalid': 'false' });
1156
+ }
1157
+ private checkErrorState(val: Date | string): void {
1158
+ const value: Date = this.getDateObject(val);
1159
+ if ((this.validateState(value) && !this.invalidValueString ) ||
1160
+ (this.enableMask && this.inputElement.value === this.maskedDateValue)) {
1161
+ this.removeErrorClass();
1162
+ } else {
1163
+ addClass([this.inputWrapper.container], ERROR);
1164
+ attributes(this.inputElement, { 'aria-invalid': 'true' });
1165
+ }
1166
+ }
1167
+ private validateInterval(): void {
1168
+ if (!isNullOrUndefined(this.step) && this.step > 0) {
1169
+ this.enableElement([this.inputWrapper.buttons[0]]);
1170
+ } else {
1171
+ this.disableTimeIcon();
1172
+ }
1173
+ }
1174
+ private disableTimeIcon(): void {
1175
+ this.disableElement([this.inputWrapper.buttons[0]]);
1176
+ this.hide();
1177
+ }
1178
+ private disableElement(element: HTMLElement[]): void {
1179
+ addClass(element, DISABLED);
1180
+ }
1181
+ private enableElement(element: HTMLElement[]): void {
1182
+ removeClass(element, DISABLED);
1183
+ }
1184
+ private selectInputText(): void {
1185
+ this.inputElement.setSelectionRange(0, (this.inputElement).value.length);
1186
+ }
1187
+ private setCursorToEnd(): void {
1188
+ this.inputElement.setSelectionRange((this.inputElement).value.length, (this.inputElement).value.length);
1189
+ }
1190
+ private getMeridianText(): MeridianText {
1191
+ let meridian: MeridianText;
1192
+ if (this.locale === 'en' || this.locale === 'en-US') {
1193
+ meridian = getValue('dayPeriods.format.wide', getDefaultDateObject());
1194
+ } else {
1195
+ const gregorianFormat: string = '.dates.calendars.gregorian.dayPeriods.format.abbreviated';
1196
+ const mainVal: string = 'main.';
1197
+ meridian = getValue(mainVal + '' + this.locale + gregorianFormat, cldrData);
1198
+ }
1199
+ return meridian;
1200
+ }
1201
+ private getCursorSelection(): CursorPositionDetails {
1202
+ const input: HTMLInputElement = (this.inputElement);
1203
+ let start: number = 0;
1204
+ let end: number = 0;
1205
+ if (!isNaN(input.selectionStart)) {
1206
+ start = input.selectionStart;
1207
+ end = input.selectionEnd;
1208
+ }
1209
+ return { start: Math.abs(start), end: Math.abs(end) };
1210
+ }
1211
+ private getActiveElement(): HTMLElement[] {
1212
+ if (!isNullOrUndefined(this.popupWrapper)) {
1213
+ return <NodeListOf<HTMLElement> & HTMLElement[]>this.popupWrapper.querySelectorAll('.' + SELECTED);
1214
+ } else {
1215
+ return null;
1216
+ }
1217
+ }
1218
+ private isNullOrEmpty(value: Date | string): boolean {
1219
+ if (isNullOrUndefined(value) || (typeof value === 'string' && value.trim() === '')) {
1220
+ return true;
1221
+ } else {
1222
+ return false;
1223
+ }
1224
+ }
1225
+ private setWidth(width: number | string): string {
1226
+ if (typeof width === 'number') {
1227
+ width = formatUnit(width);
1228
+ } else if (typeof width === 'string') {
1229
+ width = (width.match(/px|%|em/)) ? width : formatUnit(width);
1230
+ } else {
1231
+ width = '100%';
1232
+ }
1233
+ return width;
1234
+ }
1235
+ private setPopupWidth(width: string | number): string {
1236
+ width = this.setWidth(width);
1237
+ if (width.indexOf('%') > -1) {
1238
+ const inputWidth: number = this.containerStyle.width * parseFloat(width) / 100;
1239
+ width = inputWidth.toString() + 'px';
1240
+ }
1241
+ return width;
1242
+ }
1243
+ private setScrollPosition(): void {
1244
+ const element: HTMLElement = this.selectedElement;
1245
+ if (!isNullOrUndefined(element)) {
1246
+ this.findScrollTop(element);
1247
+ } else if (this.popupWrapper && this.checkDateValue(this.scrollTo)) {
1248
+ this.setScrollTo();
1249
+ }
1250
+ }
1251
+ private findScrollTop(element: HTMLElement): void {
1252
+ const listHeight: number = this.getPopupHeight();
1253
+ const nextEle: Element = element.nextElementSibling;
1254
+ const height: number = nextEle ? (<HTMLElement>nextEle).offsetTop : element.offsetTop;
1255
+ const liHeight: number = element.getBoundingClientRect().height;
1256
+ if ((height + element.offsetTop) > listHeight) {
1257
+ this.popupWrapper.scrollTop = nextEle ? (height - (listHeight / HALFPOSITION + liHeight / HALFPOSITION)) : height;
1258
+ } else {
1259
+ this.popupWrapper.scrollTop = 0;
1260
+ }
1261
+ }
1262
+ private setScrollTo(): void {
1263
+ let element: HTMLElement;
1264
+ if (!isNullOrUndefined(this.popupWrapper)) {
1265
+ const items: HTMLElement[] = <NodeListOf<HTMLLIElement> & HTMLElement[]>this.popupWrapper.querySelectorAll('.' + LISTCLASS);
1266
+ if (items.length) {
1267
+ const initialTime: number = this.timeCollections[0];
1268
+ const scrollTime: number = this.getDateObject(this.checkDateValue(this.scrollTo)).getTime();
1269
+ element = items[Math.round((scrollTime - initialTime) / (this.step * 60000))];
1270
+ }
1271
+ } else {
1272
+ this.popupWrapper.scrollTop = 0;
1273
+ }
1274
+ if (!isNullOrUndefined(element)) {
1275
+ this.findScrollTop(element);
1276
+ } else {
1277
+ this.popupWrapper.scrollTop = 0;
1278
+ }
1279
+ }
1280
+ private getText(): string {
1281
+ return (isNullOrUndefined(this.checkDateValue(this.value))) ? '' : this.getValue(this.value);
1282
+ }
1283
+ private getValue(value: Date): string {
1284
+ return (isNullOrUndefined(this.checkDateValue(value))) ? null : this.globalize.formatDate(value, {
1285
+ format: this.cldrTimeFormat(), type: 'time'
1286
+ });
1287
+ }
1288
+ private cldrDateFormat(): string {
1289
+ let cldrDate: string;
1290
+ if (this.locale === 'en' || this.locale === 'en-US') {
1291
+ cldrDate = <string>(getValue('dateFormats.short', getDefaultDateObject()));
1292
+ } else {
1293
+ cldrDate = <string>(this.getCultureDateObject(cldrData, '' + this.locale));
1294
+ }
1295
+ return cldrDate;
1296
+ }
1297
+ private cldrTimeFormat(): string {
1298
+ let cldrTime: string;
1299
+ if (this.isNullOrEmpty(this.formatString)) {
1300
+ if (this.locale === 'en' || this.locale === 'en-US') {
1301
+ cldrTime = <string>(getValue('timeFormats.short', getDefaultDateObject()));
1302
+ } else {
1303
+ cldrTime = <string>(this.getCultureTimeObject(cldrData, '' + this.locale));
1304
+ }
1305
+ } else {
1306
+ cldrTime = this.formatString;
1307
+ }
1308
+ return cldrTime;
1309
+ }
1310
+ private dateToNumeric(): string {
1311
+ let cldrTime: string;
1312
+ if (this.locale === 'en' || this.locale === 'en-US') {
1313
+ cldrTime = <string>(getValue('timeFormats.medium', getDefaultDateObject()));
1314
+ } else {
1315
+ cldrTime = <string>(getValue('main.' + '' + this.locale + '.dates.calendars.gregorian.timeFormats.medium', cldrData));
1316
+ }
1317
+ return cldrTime;
1318
+ }
1319
+ private getExactDateTime(value: Date): string {
1320
+ if (isNullOrUndefined(this.checkDateValue(value))) {
1321
+ return null;
1322
+ } else {
1323
+ return this.globalize.formatDate(value, { format: this.dateToNumeric(), type: 'time' });
1324
+ }
1325
+ }
1326
+ private setValue(value: Date | string): void {
1327
+ const time: string = this.checkValue(value);
1328
+ if (!this.strictMode && !this.validateState(time)) {
1329
+ if (this.checkDateValue(this.valueWithMinutes) === null) {
1330
+ this.initValue = this.valueWithMinutes = null;
1331
+ }
1332
+ this.validateMinMax(this.value, this.min, this.max);
1333
+ } else {
1334
+ if (this.isNullOrEmpty(time)) {
1335
+ this.initValue = null;
1336
+ this.validateMinMax(this.value, this.min, this.max);
1337
+ } else {
1338
+ this.initValue = this.compareFormatChange(time);
1339
+ }
1340
+ }
1341
+ this.updateInput(true, this.initValue);
1342
+ }
1343
+ private compareFormatChange(value: string): Date {
1344
+ if (isNullOrUndefined(value)) {
1345
+ return null;
1346
+ }
1347
+ return (value !== this.getText()) ? this.getDateObject(value) : this.getDateObject(this.value);
1348
+ }
1349
+ private updatePlaceHolder(): void {
1350
+ Input.setPlaceholder(this.l10n.getConstant('placeholder'), this.inputElement);
1351
+ }
1352
+
1353
+ //event related functions
1354
+
1355
+ private updateInputValue(value?: string): void {
1356
+ Input.setValue(value, this.inputElement, this.floatLabelType, this.showClearButton);
1357
+ }
1358
+
1359
+ private preventEventBubbling(e?: MouseEvent): void {
1360
+ e.preventDefault();
1361
+ (this as any).interopAdaptor.invokeMethodAsync('OnTimeIconClick');
1362
+ }
1363
+
1364
+ private popupHandler(e: MouseEvent): void {
1365
+ if (!this.enabled) {
1366
+ return;
1367
+ }
1368
+ if (Browser.isDevice) {
1369
+ this.inputElement.setAttribute('readonly', '');
1370
+ }
1371
+ e.preventDefault();
1372
+ if (this.isPopupOpen()) {
1373
+ this.closePopup(0, e);
1374
+ } else {
1375
+ (<HTMLElement>this.inputElement).focus();
1376
+ this.show(e);
1377
+ }
1378
+ }
1379
+ private mouseDownHandler(): void {
1380
+ if (!this.enabled) {
1381
+ return;
1382
+ }
1383
+ if (!this.readonly) {
1384
+ this.inputElement.setSelectionRange(0, 0);
1385
+ EventHandler.add(this.inputElement, 'mouseup', this.mouseUpHandler, this);
1386
+ }
1387
+ }
1388
+ private mouseUpHandler(event: MouseEvent): void {
1389
+
1390
+ if (!this.readonly) {
1391
+ event.preventDefault();
1392
+ if (this.enableMask)
1393
+ {
1394
+ event.preventDefault();
1395
+ this.notify('setMaskSelection', {
1396
+ module: 'MaskedDateTime'
1397
+ });
1398
+ return;
1399
+ }
1400
+ else
1401
+ {
1402
+ EventHandler.remove(this.inputElement, 'mouseup', this.mouseUpHandler);
1403
+ const curPos: CursorPositionDetails = this.getCursorSelection();
1404
+ if (!(curPos.start === 0 && curPos.end === this.inputElement.value.length)) {
1405
+ if (this.inputElement.value.length > 0) {
1406
+ this.cursorDetails = this.focusSelection();
1407
+ }
1408
+ this.inputElement.setSelectionRange(this.cursorDetails.start, this.cursorDetails.end);
1409
+ }
1410
+ }
1411
+ }
1412
+ }
1413
+ private focusSelection(): CursorPositionDetails {
1414
+ const regex: RegExp = new RegExp('^[a-zA-Z0-9]+$');
1415
+ const split: string[] = this.inputElement.value.split('');
1416
+ split.push(' ');
1417
+ const curPos: CursorPositionDetails = this.getCursorSelection();
1418
+ let start: number = 0;
1419
+ let end: number = 0;
1420
+ let isSeparator: boolean = false;
1421
+ if (!this.isTextSelected) {
1422
+ for (let i: number = 0; i < split.length; i++) {
1423
+ if (!regex.test(split[i as number])) {
1424
+ end = i;
1425
+ isSeparator = true;
1426
+ }
1427
+ if (isSeparator) {
1428
+ if (curPos.start >= start && curPos.end <= end) {
1429
+ // eslint-disable-next-line no-self-assign
1430
+ end = end;
1431
+ this.isTextSelected = true;
1432
+ break;
1433
+ } else {
1434
+ start = i + 1;
1435
+ isSeparator = false;
1436
+ }
1437
+ }
1438
+ }
1439
+ } else {
1440
+ start = curPos.start;
1441
+ end = curPos.end;
1442
+ this.isTextSelected = false;
1443
+ }
1444
+ return { start: start, end: end };
1445
+ }
1446
+ private inputHandler(event: KeyboardEventArgs): void {
1447
+ if (!this.readonly && this.enabled) {
1448
+ if (!((event.action === 'right' || event.action === 'left' || event.action === 'tab') || ((event.action === 'home' || event.action === 'end' || event.action === 'up' || event.action === 'down') && !this.isPopupOpen() && !this.enableMask))) {
1449
+ event.preventDefault();
1450
+ }
1451
+ switch (event.action) {
1452
+ case 'home':
1453
+ case 'end':
1454
+ case 'up':
1455
+ case 'down':
1456
+ if (!this.isPopupOpen()) {
1457
+ this.popupCreation();
1458
+ this.popupObj.destroy();
1459
+ this.popupObj = this.popupWrapper = null;
1460
+ }
1461
+ if (this.enableMask && !this.readonly && !this.isPopupOpen())
1462
+ {
1463
+ event.preventDefault();
1464
+ this.notify('keyDownHandler', {
1465
+ module: 'MaskedDateTime' ,
1466
+ e: event
1467
+ });
1468
+ }
1469
+ if (this.isPopupOpen())
1470
+ {
1471
+ this.keyHandler(event);
1472
+ }
1473
+ break;
1474
+ case 'enter':
1475
+ if (this.isNavigate) {
1476
+ this.selectedElement = this.liCollections[this.activeIndex];
1477
+ this.valueWithMinutes = new Date(this.timeCollections[this.activeIndex]);
1478
+ this.updateValue(this.valueWithMinutes, event);
1479
+ } else {
1480
+ this.updateValue(this.inputElement.value, event);
1481
+ }
1482
+ this.hide();
1483
+ this.isNavigate = false;
1484
+ if (this.isPopupOpen()) {
1485
+ event.stopPropagation();
1486
+ }
1487
+ break;
1488
+ case 'open':
1489
+ this.show(event);
1490
+ break;
1491
+ case 'escape':
1492
+ this.updateInputValue(this.objToString(this.value));
1493
+ if (this.enableMask)
1494
+ {
1495
+ if (!this.value)
1496
+ {
1497
+ this.updateInputValue(this.maskedDateValue);
1498
+ }
1499
+ this.createMask();
1500
+ }
1501
+ this.previousState(this.value);
1502
+ this.hide();
1503
+ break;
1504
+ case 'close':
1505
+ this.hide();
1506
+ break;
1507
+ case 'right':
1508
+ case 'left':
1509
+ case 'tab':
1510
+ case 'shiftTab':
1511
+ if (!this.isPopupOpen() && this.enableMask && !this.readonly)
1512
+ {
1513
+ if ((this.inputElement.selectionStart === 0 && this.inputElement.selectionEnd === this.inputElement.value.length) ||
1514
+ (this.inputElement.selectionEnd !== this.inputElement.value.length && event.action === 'tab') ||
1515
+ (this.inputElement.selectionStart !== 0 && event.action === 'shiftTab') || (event.action === 'left' || event.action === 'right'))
1516
+ {
1517
+ event.preventDefault();
1518
+ }
1519
+ this.notify('keyDownHandler', { module: 'MaskedDateTime' ,
1520
+ e: event
1521
+ });
1522
+ }
1523
+ break;
1524
+ default:
1525
+ this.isNavigate = false;
1526
+ break;
1527
+ }
1528
+ }
1529
+ }
1530
+ private onMouseClick(event: MouseEvent): void {
1531
+ const target: Element = <Element>event.target;
1532
+ const li: HTMLElement = this.selectedElement = <HTMLElement>closest(target, '.' + LISTCLASS);
1533
+ this.setSelection(li, event);
1534
+ if (li && li.classList.contains(LISTCLASS)) {
1535
+ this.hide();
1536
+ }
1537
+ }
1538
+ private closePopup(delay?: number, e?: MouseEvent | KeyboardEvent | Event): void {
1539
+ if (this.isPopupOpen() && this.popupWrapper) {
1540
+ const args: PopupEventArgs = {
1541
+ popup: this.popupObj,
1542
+ event: e || null,
1543
+ cancel: false,
1544
+ name: 'open'
1545
+ };
1546
+
1547
+ removeClass([document.body], OVERFLOW);
1548
+ this.trigger('close', args, (args: PopupEventArgs) => {
1549
+ if (!args.cancel) {
1550
+ const animModel: AnimationModel = {
1551
+ name: 'FadeOut',
1552
+ duration: ANIMATIONDURATION,
1553
+ delay: delay ? delay : 0
1554
+ };
1555
+ this.popupObj.hide(new Animation(animModel));
1556
+ removeClass([this.inputWrapper.container], [ICONANIMATION]);
1557
+ attributes(this.inputElement, { 'aria-expanded': 'false' });
1558
+ this.inputElement.removeAttribute('aria-owns');
1559
+ this.inputElement.removeAttribute('aria-controls');
1560
+ this.inputElement.removeAttribute('aria-activedescendant');
1561
+ EventHandler.remove(document, 'mousedown touchstart', this.documentClickHandler);
1562
+ }
1563
+ if (Browser.isDevice && this.modal) {
1564
+ this.modal.style.display = 'none';
1565
+ this.modal.outerHTML = '';
1566
+ this.modal = null;
1567
+ }
1568
+ if (Browser.isDevice) {
1569
+ if (!isNullOrUndefined(this.mobileTimePopupWrap)) {
1570
+ this.mobileTimePopupWrap.remove();
1571
+ this.mobileTimePopupWrap = null;
1572
+ }
1573
+ }
1574
+ if (Browser.isDevice && this.allowEdit && !this.readonly) {
1575
+ this.inputElement.removeAttribute('readonly');
1576
+ }
1577
+ });
1578
+ } else {
1579
+ if (Browser.isDevice && this.allowEdit && !this.readonly) {
1580
+ this.inputElement.removeAttribute('readonly');
1581
+ }
1582
+ }
1583
+ }
1584
+
1585
+ private disposeServerPopup() : void {
1586
+ if (this.popupWrapper) {
1587
+ this.popupWrapper.style.visibility = 'hidden';
1588
+ this.popupWrapper.style.top = '-9999px';
1589
+ this.popupWrapper.style.left = '-9999px';
1590
+ this.popupWrapper.style.width = '0px';
1591
+ this.popupWrapper.style.height = '0px';
1592
+ }
1593
+ }
1594
+ private checkValueChange(event: KeyboardEventArgs | FocusEvent | MouseEvent, isNavigation: boolean): void {
1595
+ if (!this.strictMode && !this.validateState(this.valueWithMinutes)) {
1596
+ if (this.checkDateValue(this.valueWithMinutes) === null) {
1597
+ this.initValue = this.valueWithMinutes = null;
1598
+ }
1599
+ this.setProperties({ value: this.compareFormatChange(this.inputElement.value) }, true);
1600
+ this.initValue = this.valueWithMinutes = this.compareFormatChange(this.inputElement.value);
1601
+ this.prevValue = this.inputElement.value;
1602
+ if (+this.prevDate !== +this.value) {
1603
+ this.changeEvent(event);
1604
+ }
1605
+ } else {
1606
+ if (!isNavigation) {
1607
+ const value: Date = this.compareFormatChange(this.inputElement.value);
1608
+ if ((+this.prevDate !== +value) || isNullOrUndefined(this.checkDateValue(this.value))) {
1609
+ this.valueProcess(event, value);
1610
+ }
1611
+ } else {
1612
+ const value: Date = this.getDateObject(new Date(this.timeCollections[this.activeIndex]));
1613
+ if (+this.prevDate !== +value) {
1614
+ this.valueProcess(event, value);
1615
+ }
1616
+ }
1617
+ }
1618
+ }
1619
+ private onMouseOver(event: MouseEvent): void {
1620
+ const currentLi: HTMLElement = <HTMLElement>closest(<Element>event.target, '.' + LISTCLASS);
1621
+ this.setHover(currentLi, HOVER);
1622
+ }
1623
+ private setHover(li: HTMLElement, className: string): void {
1624
+ if (this.enabled && this.isValidLI(li) && !li.classList.contains(className)) {
1625
+ this.removeHover(className);
1626
+ addClass([li], className);
1627
+ if (className === NAVIGATION) {
1628
+ li.setAttribute('aria-selected', 'true');
1629
+ }
1630
+ }
1631
+ }
1632
+ private setSelection(li: HTMLElement, event: MouseEvent): void {
1633
+ if (this.isValidLI(li)) {
1634
+ this.checkValue(li.getAttribute('data-value'));
1635
+ if (this.enableMask)
1636
+ {
1637
+ this.createMask();
1638
+ }
1639
+ this.selectedElement = li;
1640
+ this.activeIndex = Array.prototype.slice.call(this.liCollections).indexOf(li);
1641
+ this.valueWithMinutes = new Date(this.timeCollections[this.activeIndex]);
1642
+ addClass([this.selectedElement], SELECTED);
1643
+ this.selectedElement.setAttribute('aria-selected', 'true');
1644
+ this.checkValueChange(event, true);
1645
+ }
1646
+ }
1647
+ private onMouseLeave(): void {
1648
+ this.removeHover(HOVER);
1649
+ }
1650
+ private scrollHandler(): void {
1651
+ if (this.getModuleName() === 'timepicker' && Browser.isDevice) {
1652
+ return;
1653
+ } else {
1654
+ this.hide();
1655
+ }
1656
+ }
1657
+ private setMinMax(minVal: Date, maxVal: Date): void {
1658
+ if (isNullOrUndefined(this.checkDateValue(minVal))) {
1659
+ this.initMin = this.getDateObject('12:00:00 AM');
1660
+ }
1661
+ if (isNullOrUndefined(this.checkDateValue(maxVal))) {
1662
+ this.initMax = this.getDateObject('11:59:59 PM');
1663
+ }
1664
+ }
1665
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1666
+ protected validateMinMax(dateVal: Date | string, minVal: Date, maxVal: Date): Date | string {
1667
+ const value: Date = dateVal instanceof Date ? dateVal : this.getDateObject(dateVal);
1668
+ if (!isNullOrUndefined(this.checkDateValue(value))) {
1669
+ dateVal = this.strictOperation(this.initMin, this.initMax, dateVal, value);
1670
+ } else if (+(this.createDateObj(this.getFormattedValue(this.initMin))) >
1671
+ +(this.createDateObj(this.getFormattedValue(this.initMax)))) {
1672
+ this.disableTimeIcon();
1673
+ }
1674
+ if (this.strictMode) {
1675
+ dateVal = this.valueIsDisable(dateVal) ? dateVal : null;
1676
+ }
1677
+ this.checkErrorState(dateVal);
1678
+ return dateVal;
1679
+ }
1680
+ private valueIsDisable(value: string | Date): boolean {
1681
+ if (!isNullOrUndefined(this.disableItemCollection) && this.disableItemCollection.length > 0) {
1682
+ if (this.disableItemCollection.length === this.timeCollections.length) {
1683
+ return false;
1684
+ }
1685
+ const time: string = value instanceof Date ? this.objToString(value) : value;
1686
+ for (let index: number = 0; index < this.disableItemCollection.length; index++) {
1687
+ if (time === this.disableItemCollection[index as number]) {
1688
+ return false;
1689
+ }
1690
+ }
1691
+ }
1692
+ return true;
1693
+ }
1694
+ protected validateState(val: string | Date): boolean {
1695
+ if (!this.strictMode) {
1696
+ if (this.valueIsDisable(val)) {
1697
+ const value: Date = typeof val === 'string' ? this.setCurrentDate(this.getDateObject(val)) :
1698
+ this.setCurrentDate(this.getDateObject(val));
1699
+ const maxValue: Date = this.setCurrentDate(this.getDateObject(this.initMax));
1700
+ const minValue: Date = this.setCurrentDate(this.getDateObject(this.initMin));
1701
+ if (!isNullOrUndefined(this.checkDateValue(value))) {
1702
+ if ((+(value) > +(maxValue)) || (+(value) < +(minValue))) {
1703
+ return false;
1704
+ }
1705
+ } else {
1706
+ if ((+(maxValue) < +(minValue)) || this.inputElement.value !== '') {
1707
+ return false;
1708
+ }
1709
+ }
1710
+ } else {
1711
+ return false;
1712
+ }
1713
+ }
1714
+ return true;
1715
+ }
1716
+ protected strictOperation(minimum: Date, maximum: Date, dateVal: Date | string, val: Date): Date | string {
1717
+ const maxValue: Date = this.createDateObj(this.getFormattedValue(maximum));
1718
+ const minValue: Date = this.createDateObj(this.getFormattedValue(minimum));
1719
+ const value: Date = this.createDateObj(this.getFormattedValue(val));
1720
+ if (this.strictMode) {
1721
+ if (+minValue > +maxValue) {
1722
+ this.disableTimeIcon();
1723
+ this.initValue = this.getDateObject(maxValue);
1724
+ this.updateInputValue(this.getValue(this.initValue));
1725
+ if (this.enableMask)
1726
+ {
1727
+ this.createMask();
1728
+ }
1729
+ return this.inputElement.value;
1730
+ } else if (+minValue >= +value) {
1731
+ return this.getDateObject(minValue);
1732
+ } else if (+value >= +maxValue || +minValue === +maxValue) {
1733
+ return this.getDateObject(maxValue);
1734
+ }
1735
+ } else {
1736
+ if (+minValue > +maxValue) {
1737
+ this.disableTimeIcon();
1738
+ if (!isNaN(+this.createDateObj(dateVal))) {
1739
+ return dateVal;
1740
+ }
1741
+ }
1742
+ }
1743
+ return dateVal;
1744
+ }
1745
+ protected bindEvents(): void {
1746
+ EventHandler.add(this.inputWrapper.buttons[0], 'mousedown', this.popupHandler, this);
1747
+ EventHandler.add(this.inputElement, 'blur', this.inputBlurHandler, this);
1748
+ EventHandler.add(this.inputElement, 'focus', this.inputFocusHandler, this);
1749
+ EventHandler.add(this.inputElement, 'change', this.inputChangeHandler, this);
1750
+ EventHandler.add(this.inputElement, 'input', this.inputEventHandler, this);
1751
+ if (this.enableMask){
1752
+ EventHandler.add(this.inputElement, 'keydown', this.keydownHandler, this);
1753
+ EventHandler.add(this.inputElement, 'keyup', this.keyupHandler, this);
1754
+ }
1755
+ if (this.showClearButton && this.inputWrapper.clearButton) {
1756
+ EventHandler.add(this.inputWrapper.clearButton, 'mousedown', this.clearHandler, this);
1757
+ }
1758
+ if (this.formElement) {
1759
+ EventHandler.add(this.formElement, 'reset', this.formResetHandler, this);
1760
+ }
1761
+ if (!Browser.isDevice) {
1762
+ this.keyConfigure = (extend(this.keyConfigure, this.keyConfigs) as { [key: string]: string });
1763
+ this.inputEvent = new KeyboardEvents(
1764
+ this.inputWrapper.container, {
1765
+ keyAction: this.inputHandler.bind(this),
1766
+ keyConfigs: this.keyConfigure,
1767
+ eventName: 'keydown'
1768
+ });
1769
+ if (this.inputElement && (this.showClearButton || this.enableMask)) {
1770
+ EventHandler.add(this.inputElement, 'mousedown', this.mouseDownHandler, this);
1771
+ }
1772
+ }
1773
+ }
1774
+ private keydownHandler(e: KeyboardEventArgs): void{
1775
+ switch (e.code)
1776
+ {
1777
+ case 'Delete':
1778
+ if (this.enableMask && !this.popupObj && !this.readonly)
1779
+ {
1780
+ this.notify('keyDownHandler', {
1781
+ module: 'MaskedDateTime' ,
1782
+ e: e
1783
+ });
1784
+ }
1785
+ break;
1786
+ default:
1787
+ break;
1788
+ }
1789
+ }
1790
+ private keyupHandler(e: KeyboardEventArgs): void{
1791
+ if ((e.code === 'Backspace' || e.code === 'Delete') && (this.enableMask && this.showClearButton && this.inputElement && this.inputElement.value === this.maskedDateValue && this.inputWrapper && this.inputWrapper.clearButton && !this.inputWrapper.clearButton.classList.contains('e-clear-icon-hide'))) {
1792
+ this.inputWrapper.clearButton.classList.add('e-clear-icon-hide');
1793
+ }
1794
+ }
1795
+ protected formResetHandler(): void {
1796
+ if (!this.enabled) {
1797
+ return;
1798
+ }
1799
+ if (!this.inputElement.disabled) {
1800
+ let timeValue: string = this.inputElement.getAttribute('value');
1801
+ let val: Date = this.checkDateValue(this.inputEleValue);
1802
+ if (this.element.tagName === 'EJS-TIMEPICKER') {
1803
+ val = null;
1804
+ timeValue = '';
1805
+ this.inputElement.setAttribute('value', '');
1806
+ }
1807
+ this.setProperties({ value: val }, true);
1808
+ this.prevDate = this.value;
1809
+ this.valueWithMinutes = this.value;
1810
+ this.initValue = this.value;
1811
+ if (this.inputElement) {
1812
+ this.updateInputValue(timeValue);
1813
+ if (this.enableMask)
1814
+ {
1815
+ if (!timeValue)
1816
+ {
1817
+ this.updateInputValue(this.maskedDateValue);
1818
+ }
1819
+ this.createMask();
1820
+ }
1821
+ this.checkErrorState(timeValue);
1822
+ this.prevValue = this.inputElement.value;
1823
+ }
1824
+ }
1825
+ }
1826
+ private inputChangeHandler(e: MouseEvent): void {
1827
+ if (!this.enabled) {
1828
+ return;
1829
+ }
1830
+ e.stopPropagation();
1831
+ }
1832
+ private inputEventHandler(): void {
1833
+ if (this.enableMask)
1834
+ {
1835
+ this.notify('inputHandler', {
1836
+ module: 'MaskedDateTime'
1837
+ });
1838
+ }
1839
+ }
1840
+ protected unBindEvents(): void {
1841
+ if (this.inputWrapper) {
1842
+ EventHandler.remove(this.inputWrapper.buttons[0], 'mousedown touchstart', this.popupHandler);
1843
+ }
1844
+ EventHandler.remove(this.inputElement, 'blur', this.inputBlurHandler);
1845
+ EventHandler.remove(this.inputElement, 'focus', this.inputFocusHandler);
1846
+ EventHandler.remove(this.inputElement, 'change', this.inputChangeHandler);
1847
+ EventHandler.remove(this.inputElement, 'input', this.inputEventHandler);
1848
+ if (this.enableMask) {
1849
+ EventHandler.remove(this.inputElement, 'keyup', this.keyupHandler);
1850
+ }
1851
+ if (this.inputEvent) {
1852
+ this.inputEvent.destroy();
1853
+ }
1854
+ EventHandler.remove(this.inputElement, 'mousedown touchstart', this.mouseDownHandler);
1855
+ if (this.showClearButton && !isNullOrUndefined(this.inputWrapper) && !isNullOrUndefined(this.inputWrapper.clearButton)) {
1856
+ EventHandler.remove(this.inputWrapper.clearButton, 'mousedown touchstart', this.clearHandler);
1857
+ }
1858
+ if (this.formElement) {
1859
+ EventHandler.remove(this.formElement, 'reset', this.formResetHandler);
1860
+ }
1861
+
1862
+ }
1863
+ private bindClearEvent(): void {
1864
+ if (this.showClearButton && this.inputWrapper.clearButton) {
1865
+ EventHandler.add(this.inputWrapper.clearButton, 'mousedown', this.clearHandler, this);
1866
+ }
1867
+ }
1868
+ private raiseClearedEvent(e: MouseEvent): void {
1869
+ const clearedArgs: ClearedEventArgs = {
1870
+ event: e
1871
+ };
1872
+ this.trigger('cleared', clearedArgs);
1873
+ }
1874
+ protected clearHandler(e: MouseEvent): void {
1875
+ if (!this.enabled) {
1876
+ return;
1877
+ }
1878
+ e.preventDefault();
1879
+ if (!isNullOrUndefined(this.value)) {
1880
+ this.clear(e);
1881
+ } else {
1882
+ this.resetState();
1883
+ this.raiseClearedEvent(e);
1884
+ }
1885
+ if (this.popupWrapper) {
1886
+ this.popupWrapper.scrollTop = 0;
1887
+ }
1888
+ if (this.enableMask)
1889
+ {
1890
+ this.notify('clearHandler', {
1891
+ module: 'MaskedDateTime'
1892
+ });
1893
+ }
1894
+ if (closest(this.element, 'form')) {
1895
+ const element: Element = this.element;
1896
+ const keyupEvent: KeyboardEvent = document.createEvent('KeyboardEvent');
1897
+ keyupEvent.initEvent('keyup', false, true);
1898
+ element.dispatchEvent(keyupEvent);
1899
+ }
1900
+ }
1901
+ private clear(event: MouseEvent): void {
1902
+ this.setProperties({ value: null }, true);
1903
+ this.initValue = null;
1904
+ this.resetState();
1905
+ this.raiseClearedEvent(event);
1906
+ this.changeEvent(event);
1907
+ }
1908
+ protected setZIndex(): void {
1909
+ if (this.popupObj) {
1910
+ this.popupObj.zIndex = this.zIndex;
1911
+ this.popupObj.dataBind();
1912
+ }
1913
+ }
1914
+ protected checkAttributes(isDynamic: boolean): void {
1915
+ const attributes: string[] = isDynamic ? isNullOrUndefined(this.htmlAttributes) ? [] : Object.keys(this.htmlAttributes) :
1916
+ ['step', 'disabled', 'readonly', 'style', 'name', 'value', 'min', 'max', 'placeholder'];
1917
+ let value: Date;
1918
+ for (const prop of attributes) {
1919
+ if (!isNullOrUndefined(this.inputElement.getAttribute(prop))) {
1920
+ switch (prop) {
1921
+ case 'disabled':
1922
+ if (( isNullOrUndefined(this.timeOptions) || (this.timeOptions['enabled'] === undefined)) || isDynamic) {
1923
+ const enabled: boolean = this.inputElement.getAttribute(prop) === 'disabled' ||
1924
+ this.inputElement.getAttribute(prop) === '' || this.inputElement.getAttribute(prop) === 'true' ? false : true;
1925
+ this.setProperties({ enabled: enabled }, !isDynamic);
1926
+ }
1927
+ break;
1928
+ case 'style':
1929
+ this.inputStyle = this.inputElement.getAttribute(prop);
1930
+ break;
1931
+ case 'readonly':
1932
+ if (( isNullOrUndefined(this.timeOptions) || (this.timeOptions['readonly'] === undefined)) || isDynamic) {
1933
+ const readonly: boolean = this.inputElement.getAttribute(prop) === 'readonly' ||
1934
+ this.inputElement.getAttribute(prop) === '' || this.inputElement.getAttribute(prop) === 'true' ? true : false;
1935
+ this.setProperties({ readonly: readonly }, !isDynamic);
1936
+ }
1937
+ break;
1938
+ case 'name':
1939
+ this.inputElement.setAttribute('name', this.inputElement.getAttribute(prop));
1940
+ break;
1941
+ case 'step':
1942
+ this.step = parseInt(this.inputElement.getAttribute(prop), 10);
1943
+ break;
1944
+ case 'placeholder':
1945
+ if (( isNullOrUndefined(this.timeOptions) || (this.timeOptions['placeholder'] === undefined)) || isDynamic) {
1946
+ this.setProperties({ placeholder: this.inputElement.getAttribute(prop) }, !isDynamic);
1947
+ }
1948
+ break;
1949
+ case 'min':
1950
+ if ((isNullOrUndefined(this.timeOptions) || (this.timeOptions['min'] === undefined)) || isDynamic) {
1951
+ value = new Date(this.inputElement.getAttribute(prop));
1952
+ if (!isNullOrUndefined(this.checkDateValue(value))) {
1953
+ this.setProperties({ min: value }, !isDynamic);
1954
+ }
1955
+ }
1956
+ break;
1957
+ case 'max':
1958
+ if ((isNullOrUndefined(this.timeOptions) || (this.timeOptions['max'] === undefined)) || isDynamic) {
1959
+ value = new Date(this.inputElement.getAttribute(prop));
1960
+ if (!isNullOrUndefined(this.checkDateValue(value))) {
1961
+ this.setProperties({ max: value }, !isDynamic);
1962
+ }
1963
+ }
1964
+ break;
1965
+ case 'value':
1966
+ if ((isNullOrUndefined(this.timeOptions) || (this.timeOptions['value'] === undefined)) || isDynamic) {
1967
+ value = new Date(this.inputElement.getAttribute(prop));
1968
+ if (!isNullOrUndefined(this.checkDateValue(value))) {
1969
+ this.initValue = value;
1970
+ this.updateInput(false, this.initValue);
1971
+ this.setProperties({ value: value }, !isDynamic);
1972
+ }
1973
+ }
1974
+ break;
1975
+ }
1976
+ }
1977
+ }
1978
+ }
1979
+ protected setCurrentDate(value: Date): Date {
1980
+ if (isNullOrUndefined(this.checkDateValue(value))) {
1981
+ return null;
1982
+ }
1983
+ return new Date(YEAR, MONTH, DAY, value.getHours(), value.getMinutes(), value.getSeconds());
1984
+ }
1985
+
1986
+ protected getTextFormat(): number {
1987
+ let time: number = 0;
1988
+ if (this.cldrTimeFormat().split(' ')[0] === 'a' || this.cldrTimeFormat().indexOf('a') === 0) {
1989
+ time = 1;
1990
+ } else if (this.cldrTimeFormat().indexOf('a') < 0) {
1991
+ const strArray: string[] = this.cldrTimeFormat().split(' ');
1992
+ for (let i: number = 0; i < strArray.length; i++) {
1993
+ if (strArray[i as number].toLowerCase().indexOf('h') >= 0) {
1994
+ time = i;
1995
+ break;
1996
+ }
1997
+ }
1998
+ }
1999
+ return time;
2000
+ }
2001
+ protected updateValue(value: string | Date, event: KeyboardEventArgs | FocusEvent | MouseEvent): void {
2002
+ let val: string;
2003
+ if (this.isNullOrEmpty(value)) {
2004
+ this.resetState();
2005
+ } else {
2006
+ val = this.checkValue(value);
2007
+ if (this.strictMode) {
2008
+ // this case set previous value to the text box when set invalid date
2009
+ const inputVal: string = (val === null && (<string>value).trim().length > 0) ?
2010
+ this.previousState(this.prevDate) : this.inputElement.value;
2011
+ this.updateInputValue(inputVal);
2012
+ if (this.enableMask)
2013
+ {
2014
+ if (!inputVal)
2015
+ {
2016
+ this.updateInputValue(this.maskedDateValue);
2017
+ }
2018
+ if (isNullOrUndefined(val) && value !== this.maskedDateValue) {
2019
+ this.createMask();
2020
+ }
2021
+ if (isNullOrUndefined(val) && value === this.maskedDateValue) {
2022
+ this.updateInputValue(this.maskedDateValue);
2023
+ }
2024
+ }
2025
+ }
2026
+ }
2027
+ this.checkValueChange(event, typeof value === 'string' ? false : true);
2028
+ }
2029
+ protected previousState(date: Date): string {
2030
+ const value: Date = this.getDateObject(date);
2031
+ for (let i: number = 0; i < this.timeCollections.length; i++) {
2032
+ if (+value === this.timeCollections[i as number]) {
2033
+ this.activeIndex = i;
2034
+ this.selectedElement = this.liCollections[i as number];
2035
+ this.valueWithMinutes = new Date(this.timeCollections[i as number]);
2036
+ break;
2037
+ }
2038
+ }
2039
+ return this.getValue(date);
2040
+ }
2041
+ protected resetState(): void {
2042
+ this.removeSelection();
2043
+ Input.setValue('', this.inputElement, this.floatLabelType, false);
2044
+ this.valueWithMinutes = this.activeIndex = null;
2045
+ if (!this.strictMode) {
2046
+ this.checkErrorState(null);
2047
+ }
2048
+ }
2049
+ protected objToString(val: Date): string {
2050
+ if (isNullOrUndefined(this.checkDateValue(val))) {
2051
+ return null;
2052
+ } else {
2053
+ return this.globalize.formatDate(val, { format: this.cldrTimeFormat(), type: 'time' });
2054
+ }
2055
+ }
2056
+ protected checkValue(value: string | Date): string {
2057
+ if (!this.isNullOrEmpty(value)) {
2058
+ const date: Date = value instanceof Date ? value : this.getDateObject(value);
2059
+ return this.validateValue(date, value);
2060
+ }
2061
+ this.resetState();
2062
+ return this.valueWithMinutes = null;
2063
+ }
2064
+
2065
+ protected validateValue(date: Date, value: string | Date): string {
2066
+ let time: string;
2067
+ const val: string | Date = this.validateMinMax(value, this.min, this.max);
2068
+ const newval: Date = this.getDateObject(val);
2069
+ if (this.getFormattedValue(newval) !== this.getFormattedValue(this.value)) {
2070
+ this.valueWithMinutes = isNullOrUndefined(newval) ? null : newval;
2071
+ time = this.objToString(this.valueWithMinutes);
2072
+ } else {
2073
+ if (this.strictMode) {
2074
+ //for strict mode case, when value not present within a range. Reset the nearest range value.
2075
+ date = newval;
2076
+ }
2077
+ this.valueWithMinutes = this.checkDateValue(date);
2078
+ time = this.objToString(this.valueWithMinutes);
2079
+ }
2080
+ if (!this.strictMode && isNullOrUndefined(time)) {
2081
+ const value: string = (<string>val).trim().length > 0 ? (<string>val) : '';
2082
+ this.updateInputValue(value);
2083
+ if (this.enableMask)
2084
+ {
2085
+ if (!value)
2086
+ {
2087
+ this.updateInputValue(this.maskedDateValue);
2088
+ }
2089
+ }
2090
+ } else {
2091
+ this.updateInputValue(time);
2092
+ if (this.enableMask)
2093
+ {
2094
+ if (time === '')
2095
+ {
2096
+ this.updateInputValue(this.maskedDateValue);
2097
+ }
2098
+ if (isNullOrUndefined(time) && value !== this.maskedDateValue){
2099
+ this.createMask();
2100
+ }
2101
+ if (isNullOrUndefined(time) && value === this.maskedDateValue){
2102
+ this.updateInputValue(this.maskedDateValue);
2103
+ }
2104
+ }
2105
+ }
2106
+ return time;
2107
+ }
2108
+ private createMask(): void {
2109
+ this.notify('createMask', {
2110
+ module: 'MaskedDateTime'
2111
+ });
2112
+ }
2113
+ protected findNextElement(event: KeyboardEventArgs): void {
2114
+ const textVal: string = (this.inputElement).value;
2115
+ const value: Date = isNullOrUndefined(this.valueWithMinutes) ? this.createDateObj(textVal) :
2116
+ this.getDateObject(this.valueWithMinutes);
2117
+ let timeVal: number = null;
2118
+ const count: number = this.liCollections.length;
2119
+ const collections: string[] | number[] = this.timeCollections;
2120
+ if (!isNullOrUndefined(this.checkDateValue(value)) || !isNullOrUndefined(this.activeIndex)) {
2121
+ if (event.action === 'home') {
2122
+ const index: number = this.validLiElement(0);
2123
+ timeVal = +(this.createDateObj(new Date(this.timeCollections[index as number])));
2124
+ this.activeIndex = index;
2125
+ } else if (event.action === 'end') {
2126
+ const index: number = this.validLiElement(collections.length - 1, true);
2127
+ timeVal = +(this.createDateObj(new Date(this.timeCollections[index as number])));
2128
+ this.activeIndex = index;
2129
+ } else {
2130
+ if (event.action === 'down') {
2131
+ for (let i: number = 0; i < count; i++) {
2132
+ if (+value < this.timeCollections[i as number]) {
2133
+ const index: number = this.validLiElement(i);
2134
+ timeVal = +(this.createDateObj(new Date(this.timeCollections[index as number])));
2135
+ this.activeIndex = index;
2136
+ break;
2137
+ } else if (i === count - 1) {
2138
+ const index: number = this.validLiElement(0);
2139
+ timeVal = +(this.createDateObj(new Date(this.timeCollections[index as number])));
2140
+ this.activeIndex = index;
2141
+ break;
2142
+ }
2143
+ }
2144
+ } else {
2145
+ for (let i: number = count - 1; i >= 0; i--) {
2146
+ if (+value > this.timeCollections[i as number]) {
2147
+ const index: number = this.validLiElement(i, true);
2148
+ timeVal = +(this.createDateObj(new Date(this.timeCollections[index as number])));
2149
+ this.activeIndex = index;
2150
+ break;
2151
+ } else if (i === 0) {
2152
+ const index: number = this.validLiElement(count - 1);
2153
+ timeVal = +(this.createDateObj(new Date(this.timeCollections[index as number])));
2154
+ this.activeIndex = index;
2155
+ break;
2156
+ }
2157
+ }
2158
+ }
2159
+ }
2160
+ this.selectedElement = this.liCollections[this.activeIndex];
2161
+ this.elementValue(isNullOrUndefined(timeVal) ? null : new Date(timeVal));
2162
+ } else {
2163
+ this.selectNextItem(event);
2164
+ }
2165
+ }
2166
+ protected selectNextItem(event: KeyboardEventArgs): void {
2167
+ const index: number = this.validLiElement(0, event.action === 'down' ? false : true);
2168
+ this.activeIndex = index;
2169
+ this.selectedElement = this.liCollections[index as number];
2170
+ this.elementValue(new Date(this.timeCollections[index as number]));
2171
+ }
2172
+ protected elementValue(value: Date): void {
2173
+ if (!isNullOrUndefined(this.checkDateValue(value))) {
2174
+ this.checkValue(value);
2175
+ }
2176
+ }
2177
+ private validLiElement(index: number, backward?: boolean): number {
2178
+ let elementIndex: number = null;
2179
+ const items: HTMLElement[] = isNullOrUndefined(this.popupWrapper) ? this.liCollections :
2180
+ <NodeListOf<HTMLLIElement> & HTMLElement[]>this.popupWrapper.querySelectorAll('.' + LISTCLASS);
2181
+ let isCheck: boolean = true;
2182
+ if (items.length) {
2183
+ if (backward) {
2184
+ for (let i: number = index; i >= 0; i--) {
2185
+ if (!items[i as number].classList.contains(DISABLED)) {
2186
+ elementIndex = i;
2187
+ break;
2188
+ } else if (i === 0) {
2189
+ if (isCheck) {
2190
+ index = i = items.length;
2191
+ isCheck = false;
2192
+ }
2193
+ }
2194
+ }
2195
+ } else {
2196
+ for (let i: number = index; i <= items.length - 1; i++) {
2197
+ if (!items[i as number].classList.contains(DISABLED)) {
2198
+ elementIndex = i;
2199
+ break;
2200
+ } else if (i === items.length - 1) {
2201
+ if (isCheck) {
2202
+ index = i = -1;
2203
+ isCheck = false;
2204
+ }
2205
+ }
2206
+ }
2207
+ }
2208
+ }
2209
+ return elementIndex;
2210
+ }
2211
+ protected keyHandler(event: KeyboardEventArgs): void {
2212
+ if (isNullOrUndefined(this.step) || this.step <= 0 || !isNullOrUndefined(this.inputWrapper)
2213
+ && this.inputWrapper.buttons[0].classList.contains(DISABLED)) { return; }
2214
+ const count: number = this.timeCollections.length;
2215
+ if (isNullOrUndefined(this.getActiveElement()) || this.getActiveElement().length === 0) {
2216
+ if (this.liCollections.length > 0) {
2217
+ if (isNullOrUndefined(this.value) && isNullOrUndefined(this.activeIndex)) {
2218
+ const index: number = this.validLiElement(0, event.action === 'down' ? false : true);
2219
+ this.activeIndex = index;
2220
+ this.selectedElement = this.liCollections[index as number];
2221
+ this.elementValue(new Date(this.timeCollections[index as number]));
2222
+ } else { this.findNextElement(event); }
2223
+ } else { this.findNextElement(event); }
2224
+ } else {
2225
+ let nextItem: number;
2226
+ if ((event.keyCode >= 37) && (event.keyCode <= 40)) {
2227
+ let index: number = (event.keyCode === 40 || event.keyCode === 39) ? ++this.activeIndex : --this.activeIndex;
2228
+ this.activeIndex = index = this.activeIndex === (count) ? 0 : this.activeIndex;
2229
+ this.activeIndex = index = this.activeIndex < 0 ? (count - 1) : this.activeIndex;
2230
+ this.activeIndex = index = this.validLiElement(this.activeIndex, (event.keyCode === 40 || event.keyCode === 39) ?
2231
+ false : true);
2232
+ nextItem = isNullOrUndefined(this.timeCollections[index as number]) ?
2233
+ this.timeCollections[0] : this.timeCollections[index as number];
2234
+ } else if (event.action === 'home') {
2235
+ const index: number = this.validLiElement(0);
2236
+ this.activeIndex = index;
2237
+ nextItem = this.timeCollections[index as number];
2238
+ } else if (event.action === 'end') {
2239
+ const index: number = this.validLiElement(count - 1, true);
2240
+ this.activeIndex = index;
2241
+ nextItem = this.timeCollections[index as number];
2242
+ }
2243
+ this.selectedElement = this.liCollections[this.activeIndex];
2244
+ this.elementValue(new Date(nextItem));
2245
+ }
2246
+ this.isNavigate = true;
2247
+ this.setHover(this.selectedElement, NAVIGATION);
2248
+ this.setActiveDescendant();
2249
+ if (this.enableMask) {
2250
+ this.selectInputText();
2251
+ }
2252
+ if (this.isPopupOpen() && this.selectedElement !== null && (!event || event.type !== 'click')) { this.setScrollPosition(); }
2253
+ }
2254
+ protected getCultureTimeObject(ld: Object, c: string): Object {
2255
+ return getValue('main.' + c + '.dates.calendars.gregorian.timeFormats.short', ld);
2256
+ }
2257
+ protected getCultureDateObject(ld: Object, c: string): Object {
2258
+ return getValue('main.' + c + '.dates.calendars.gregorian.dateFormats.short', ld);
2259
+ }
2260
+ protected wireListEvents(): void {
2261
+ EventHandler.add(this.listWrapper, 'click', this.onMouseClick, this);
2262
+ if (!Browser.isDevice) {
2263
+ EventHandler.add(this.listWrapper, 'mouseover', this.onMouseOver, this);
2264
+ EventHandler.add(this.listWrapper, 'mouseout', this.onMouseLeave, this);
2265
+ }
2266
+ }
2267
+ protected unWireListEvents(): void {
2268
+ if (this.listWrapper) {
2269
+ EventHandler.remove(this.listWrapper, 'click', this.onMouseClick);
2270
+ if (!Browser.isDevice) {
2271
+ EventHandler.remove(this.listWrapper, 'mouseover', this.onMouseOver);
2272
+ EventHandler.remove(this.listWrapper, 'mouseout', this.onMouseLeave);
2273
+ }
2274
+ }
2275
+ }
2276
+ protected valueProcess(event: KeyboardEventArgs | FocusEvent | MouseEvent, value: Date): void {
2277
+ const result: Date = (isNullOrUndefined(this.checkDateValue(value))) ? null : value;
2278
+ if (+ this.prevDate !== +result) {
2279
+ this.initValue = result;
2280
+ this.changeEvent(event);
2281
+ }
2282
+ }
2283
+ protected changeEvent(e: KeyboardEventArgs | FocusEvent | MouseEvent): void {
2284
+ this.addSelection();
2285
+ this.updateInput(true, this.initValue);
2286
+ const eventArgs: ChangeEventArgs = {
2287
+ event: (e || null),
2288
+ value: this.value,
2289
+ text: (this.inputElement).value,
2290
+ isInteracted: !isNullOrUndefined(e),
2291
+ element: this.element
2292
+ };
2293
+ eventArgs.value = this.valueWithMinutes || this.getDateObject(this.inputElement.value);
2294
+ this.prevDate = this.valueWithMinutes || this.getDateObject(this.inputElement.value);
2295
+ if (this.isAngular && this.preventChange) {
2296
+ this.preventChange = false;
2297
+ } else {
2298
+ this.trigger('change', eventArgs);
2299
+ }
2300
+ this.invalidValueString = null;
2301
+ this.checkErrorState(this.value);
2302
+
2303
+ }
2304
+ protected updateInput(isUpdate: boolean, date: Date): void {
2305
+ if (isUpdate) {
2306
+ this.prevValue = this.getValue(this.prevDate);
2307
+ }
2308
+ this.prevDate = this.valueWithMinutes = date;
2309
+ if ((typeof date !== 'number') || (this.value && +new Date(+this.value).setMilliseconds(0)) !== +date) {
2310
+ this.setProperties({ value: date }, true);
2311
+ if (this.enableMask && this.value)
2312
+ {
2313
+ this.createMask();
2314
+ }
2315
+ }
2316
+ if (!this.strictMode && isNullOrUndefined(this.value) && this.invalidValueString) {
2317
+ this.checkErrorState(this.invalidValueString);
2318
+ this.updateInputValue(this.invalidValueString);
2319
+ }
2320
+ this.clearIconState();
2321
+
2322
+ }
2323
+ protected setActiveDescendant(): void {
2324
+ if (!isNullOrUndefined(this.selectedElement) && this.value) {
2325
+ attributes(this.inputElement, { 'aria-activedescendant': this.selectedElement.getAttribute('id') });
2326
+ } else {
2327
+ this.inputElement.removeAttribute('aria-activedescendant');
2328
+ }
2329
+ }
2330
+ protected removeSelection(): void {
2331
+ this.removeHover(HOVER);
2332
+ if (!isNullOrUndefined(this.popupWrapper)) {
2333
+ const items: Element[] = <NodeListOf<Element> & Element[]>this.popupWrapper.querySelectorAll('.' + SELECTED);
2334
+ if (items.length) {
2335
+ removeClass(items, SELECTED);
2336
+ items[0].removeAttribute('aria-selected');
2337
+ }
2338
+ }
2339
+ }
2340
+ protected removeHover(className: string): void {
2341
+ const hoveredItem: Element[] = this.getHoverItem(className);
2342
+ if (hoveredItem && hoveredItem.length) {
2343
+ removeClass(hoveredItem, className);
2344
+ if (className === NAVIGATION) {
2345
+ hoveredItem[0].removeAttribute('aria-selected');
2346
+ }
2347
+ }
2348
+ }
2349
+ protected getHoverItem(className: string): Element[] {
2350
+ let hoveredItem: Element[];
2351
+ if (!isNullOrUndefined(this.popupWrapper)) {
2352
+ hoveredItem = <NodeListOf<HTMLLIElement> & Element[]>this.popupWrapper.querySelectorAll('.' + className);
2353
+ }
2354
+ return hoveredItem;
2355
+ }
2356
+ private setActiveClass(): void {
2357
+ if (!isNullOrUndefined(this.popupWrapper)) {
2358
+ const items: HTMLElement[] = <NodeListOf<HTMLLIElement> & HTMLElement[]>this.popupWrapper.querySelectorAll('.' + LISTCLASS);
2359
+ if (items.length) {
2360
+ for (let i: number = 0; i < items.length; i++) {
2361
+ if ((this.timeCollections[i as number] === +this.getDateObject(this.valueWithMinutes))) {
2362
+ items[i as number].setAttribute('aria-selected', 'true');
2363
+ this.selectedElement = items[i as number];
2364
+ this.activeIndex = i;
2365
+ break;
2366
+ }
2367
+ }
2368
+ }
2369
+ }
2370
+ }
2371
+ protected addSelection(): void {
2372
+ this.selectedElement = null;
2373
+ this.removeSelection();
2374
+ this.setActiveClass();
2375
+ if (!isNullOrUndefined(this.selectedElement)) {
2376
+ addClass([this.selectedElement], SELECTED);
2377
+ this.selectedElement.setAttribute('aria-selected', 'true');
2378
+ }
2379
+ }
2380
+ protected isValidLI(li: Element | HTMLElement): boolean {
2381
+ return (li && li.classList.contains(LISTCLASS) && !li.classList.contains(DISABLED));
2382
+ }
2383
+ protected createDateObj(val: Date | string): Date {
2384
+ const formatStr: string = null;
2385
+ let today: string = this.globalize.formatDate(new Date(), { format: formatStr, skeleton: 'short', type: 'date' });
2386
+ let value: Date = null;
2387
+ if (typeof val === 'string' && !(this.enableMask && this.maskedDateValue &&
2388
+ this.inputElement.value === this.maskedDateValue && !this.value)) {
2389
+ if (val.toUpperCase().indexOf('AM') > -1 || val.toUpperCase().indexOf('PM') > -1) {
2390
+ today = this.defaultCulture.formatDate(new Date(), { format: formatStr, skeleton: 'short', type: 'date' });
2391
+ value = isNaN(+new Date(today + ' ' + val)) ? null : new Date(new Date(today + ' ' + val).setMilliseconds(0));
2392
+ if (isNullOrUndefined(value)) {
2393
+ value = this.timeParse(today, val);
2394
+ }
2395
+ } else {
2396
+ value = this.timeParse(today, val);
2397
+ }
2398
+ } else if (val instanceof Date) {
2399
+ value = val;
2400
+ }
2401
+ return value;
2402
+ }
2403
+ protected timeParse(today: string, val: Date | string): Date {
2404
+ let value: Date;
2405
+ value = this.globalize.parseDate(this.getAmPmValue(today + ' ' + val), {
2406
+ format: this.cldrDateFormat() + ' ' + this.cldrTimeFormat(), type: 'datetime'
2407
+ });
2408
+ value = isNullOrUndefined(value) ? this.globalize.parseDate(this.getAmPmValue(today + ' ' + val), {
2409
+ format: this.cldrDateFormat() + ' ' + this.dateToNumeric(), type: 'datetime'
2410
+ }) : value;
2411
+ value = isNullOrUndefined(value) ? value : new Date(value.setMilliseconds(0));
2412
+ return value;
2413
+ }
2414
+ protected createListItems(): void {
2415
+ this.listWrapper = this.createElement('div', { className: CONTENT, attrs: { 'tabindex': '-1' } });
2416
+ let start: number;
2417
+ const interval: number = this.step * 60000;
2418
+ const listItems: string[] = [];
2419
+ this.timeCollections = [];
2420
+ this.disableItemCollection = [];
2421
+ start = +(this.getDateObject(this.initMin).setMilliseconds(0));
2422
+ const end: number = +(this.getDateObject(this.initMax).setMilliseconds(0));
2423
+ while (end >= start) {
2424
+ this.timeCollections.push(start);
2425
+ listItems.push(this.globalize.formatDate(new Date(start), { format: this.cldrTimeFormat(), type: 'time' }));
2426
+ start += interval;
2427
+ }
2428
+ const listBaseOptions: ListBaseOptions = {
2429
+ itemCreated: (args: { item: Element; text: string }): void => {
2430
+ const eventArgs: ItemEventArgs = {
2431
+ element: args.item as HTMLElement,
2432
+ text: args.text, value: this.getDateObject(args.text), isDisabled: false
2433
+ };
2434
+ this.trigger('itemRender', eventArgs, (eventArgs: ItemEventArgs) => {
2435
+ if (eventArgs.isDisabled) {
2436
+ eventArgs.element.classList.add(DISABLED);
2437
+ }
2438
+ if (eventArgs.element.classList.contains(DISABLED)) {
2439
+ this.disableItemCollection.push(eventArgs.element.getAttribute('data-value'));
2440
+ }
2441
+ });
2442
+ }
2443
+ };
2444
+ this.listTag = ListBase.createList(this.createElement, listItems, listBaseOptions, true);
2445
+ attributes(this.listTag, { 'role': 'listbox', 'aria-hidden': 'false', 'id': this.element.id + '_options', 'tabindex': '0' });
2446
+ append([this.listTag], this.listWrapper);
2447
+ }
2448
+ private documentClickHandler(event: MouseEvent): void {
2449
+ const target: HTMLElement = <HTMLElement>event.target;
2450
+ if ((!isNullOrUndefined(this.popupObj) && !isNullOrUndefined(this.inputWrapper) && (this.inputWrapper.container.contains(target) && event.type !== 'mousedown' ||
2451
+ (this.popupObj.element && this.popupObj.element.contains(target)))) && event.type !== 'touchstart') {
2452
+ event.preventDefault();
2453
+ }
2454
+ if (!(closest(target, '[id="' + this.popupObj.element.id + '"]')) && target !== this.inputElement
2455
+ && target !== (this.inputWrapper && this.inputWrapper.buttons[0]) &&
2456
+ target !== (this.inputWrapper && this.inputWrapper.clearButton) &&
2457
+ target !== (this.inputWrapper && this.inputWrapper.container)) {
2458
+ if (this.isPopupOpen()) {
2459
+ this.hide();
2460
+ if (target.classList.contains('e-dlg-overlay')) {
2461
+ event.preventDefault();
2462
+ } else {
2463
+ this.focusOut();
2464
+ }
2465
+ }
2466
+ } else if (target !== this.inputElement) {
2467
+ if (!Browser.isDevice) {
2468
+ this.isPreventBlur = (Browser.isIE || Browser.info.name === 'edge') && (document.activeElement === this.inputElement)
2469
+ && (target === this.popupWrapper);
2470
+ }
2471
+ }
2472
+ }
2473
+ protected setEnableRtl(): void {
2474
+ Input.setEnableRtl(this.enableRtl, [this.inputWrapper.container]);
2475
+ if (this.popupObj) {
2476
+ this.popupObj.enableRtl = this.enableRtl;
2477
+ this.popupObj.dataBind();
2478
+ }
2479
+ }
2480
+
2481
+ protected setEnable(): void {
2482
+ Input.setEnabled(this.enabled, this.inputElement, this.floatLabelType);
2483
+ if (this.enabled) {
2484
+ removeClass([this.inputWrapper.container], DISABLED);
2485
+ attributes(this.inputElement, { 'aria-disabled': 'false' });
2486
+ this.inputElement.setAttribute('tabindex', this.tabIndex);
2487
+ } else {
2488
+ this.hide();
2489
+ addClass([this.inputWrapper.container], DISABLED);
2490
+ attributes(this.inputElement, { 'aria-disabled': 'true' });
2491
+ this.inputElement.tabIndex = -1;
2492
+ }
2493
+ }
2494
+
2495
+ protected getProperty(date: TimePickerModel, val: string): void {
2496
+ if (val === 'min') {
2497
+ this.initMin = this.checkDateValue(new Date(this.checkInValue(date.min)));
2498
+ this.setProperties({ min: this.initMin }, true);
2499
+ } else {
2500
+ this.initMax = this.checkDateValue(new Date(this.checkInValue(date.max)));
2501
+ this.setProperties({ max: this.initMax }, true);
2502
+ }
2503
+ if (this.inputElement.value === '') {
2504
+ this.validateMinMax(this.value, this.min, this.max);
2505
+ } else {
2506
+ this.checkValue((<HTMLInputElement>this.inputElement).value);
2507
+ }
2508
+ this.checkValueChange(null, false);
2509
+ }
2510
+ protected inputBlurHandler(e: MouseEvent): void {
2511
+ if (!this.enabled) {
2512
+ return;
2513
+ }
2514
+ // IE popup closing issue when click over the scrollbar
2515
+ if (this.isPreventBlur && this.isPopupOpen()) {
2516
+ this.inputElement.focus();
2517
+ return;
2518
+ }
2519
+ this.closePopup(0, e);
2520
+ if (this.enableMask && this.maskedDateValue && this.placeholder && this.floatLabelType !== 'Always')
2521
+ {
2522
+ if (this.inputElement.value === this.maskedDateValue && !this.value && (this.floatLabelType === 'Auto' || this.floatLabelType === 'Never' || this.placeholder))
2523
+ {
2524
+ this.updateInputValue('');
2525
+ }
2526
+ }
2527
+ removeClass([this.inputWrapper.container], [FOCUS]);
2528
+ if (this.getText() !== this.inputElement.value) {
2529
+ this.updateValue((this.inputElement).value, e);
2530
+ } else if (this.inputElement.value.trim().length === 0) {
2531
+ this.resetState();
2532
+ }
2533
+ this.cursorDetails = null;
2534
+ this.isNavigate = false;
2535
+
2536
+ if (this.inputElement.value === '') {
2537
+ this.invalidValueString = null;
2538
+ }
2539
+ const blurArguments: BlurEventArgs = {
2540
+ model: this
2541
+ };
2542
+ this.trigger('blur', blurArguments);
2543
+
2544
+ }
2545
+ /**
2546
+ * Focuses out the TimePicker textbox element.
2547
+ *
2548
+ * @returns {void}
2549
+ */
2550
+ public focusOut(): void {
2551
+ if (document.activeElement === this.inputElement) {
2552
+ this.inputElement.blur();
2553
+ removeClass([this.inputWrapper.container], [FOCUS]);
2554
+ const blurArguments: BlurEventArgs = {
2555
+ model: this
2556
+ };
2557
+ this.trigger('blur', blurArguments);
2558
+ }
2559
+ }
2560
+ private isPopupOpen(): boolean {
2561
+ if (this.popupWrapper && this.popupWrapper.classList.contains('' + ROOT)) {
2562
+ return true;
2563
+ }
2564
+ return false;
2565
+ }
2566
+ private inputFocusHandler(): void {
2567
+ if (!this.enabled) {
2568
+ return;
2569
+ }
2570
+ const focusArguments: FocusEventArgs = {
2571
+ model: this
2572
+ };
2573
+ if (!this.readonly && !Browser.isDevice && !this.enableMask) {
2574
+ this.selectInputText();
2575
+ }
2576
+ if (this.enableMask && !this.inputElement.value && this.placeholder)
2577
+ {
2578
+ if (this.maskedDateValue && !this.value && (this.floatLabelType === 'Auto' || this.floatLabelType === 'Never' || this.placeholder))
2579
+ {
2580
+ this.updateInputValue(this.maskedDateValue);
2581
+ this.inputElement.selectionStart = 0;
2582
+ this.inputElement.selectionEnd = this.inputElement.value.length;
2583
+ }
2584
+ }
2585
+ if (this.enableMask && this.showClearButton && this.inputElement && this.inputElement.value === this.maskedDateValue && this.inputWrapper && this.inputWrapper.clearButton && !this.inputWrapper.clearButton.classList.contains('e-clear-icon-hide')) {
2586
+ this.inputWrapper.clearButton.classList.add('e-clear-icon-hide');
2587
+ }
2588
+ this.trigger('focus', focusArguments);
2589
+ this.clearIconState();
2590
+ if (this.openOnFocus) {
2591
+ this.show();
2592
+ }
2593
+ }
2594
+ /**
2595
+ * Focused the TimePicker textbox element.
2596
+ *
2597
+ * @returns {void}
2598
+ */
2599
+ public focusIn(): void {
2600
+ if (document.activeElement !== this.inputElement && this.enabled) {
2601
+ this.inputElement.focus();
2602
+ }
2603
+ }
2604
+ /**
2605
+ * Hides the TimePicker popup.
2606
+ *
2607
+ * @returns {void}
2608
+ * @deprecated
2609
+ */
2610
+ public hide(): void {
2611
+ this.closePopup(100, null);
2612
+ this.clearIconState();
2613
+ }
2614
+ /* eslint-disable valid-jsdoc, jsdoc/require-param */
2615
+ /**
2616
+ * Opens the popup to show the list items.
2617
+ *
2618
+ * @returns {void}
2619
+ * @deprecated
2620
+ */
2621
+ public show(event?: KeyboardEvent | MouseEvent | Event): void {
2622
+ if ((this.enabled && this.readonly) || !this.enabled || this.popupWrapper) {
2623
+ return;
2624
+ } else {
2625
+ this.popupCreation();
2626
+ if (Browser.isDevice && this.listWrapper) {
2627
+ this.modal = this.createElement('div');
2628
+ this.modal.className = '' + ROOT + ' e-time-modal';
2629
+ document.body.className += ' ' + OVERFLOW;
2630
+ document.body.appendChild(this.modal);
2631
+ }
2632
+ if (Browser.isDevice) {
2633
+ this.mobileTimePopupWrap = this.createElement('div', { className: 'e-timepicker-mob-popup-wrap'});
2634
+ document.body.appendChild(this.mobileTimePopupWrap);
2635
+ }
2636
+ this.openPopupEventArgs = {
2637
+ popup: this.popupObj || null,
2638
+ cancel: false,
2639
+ event: event || null,
2640
+ name: 'open',
2641
+ appendTo: Browser.isDevice ? this.mobileTimePopupWrap : document.body
2642
+ };
2643
+ const eventArgs: PopupEventArgs = this.openPopupEventArgs;
2644
+ this.trigger('open', eventArgs, (eventArgs: PopupEventArgs) => {
2645
+ this.openPopupEventArgs = eventArgs;
2646
+ if (!this.openPopupEventArgs.cancel && !this.inputWrapper.buttons[0].classList.contains(DISABLED)) {
2647
+ this.openPopupEventArgs.appendTo.appendChild(this.popupWrapper);
2648
+ this.popupAlignment(this.openPopupEventArgs);
2649
+ this.setScrollPosition();
2650
+ if (!Browser.isDevice) {
2651
+ this.inputElement.focus();
2652
+ }
2653
+ const openAnimation: AnimationModel = {
2654
+ name: 'FadeIn',
2655
+ duration: ANIMATIONDURATION
2656
+ };
2657
+ this.popupObj.refreshPosition(this.anchor);
2658
+ if (this.zIndex === 1000) {
2659
+ this.popupObj.show(new Animation(openAnimation), this.element);
2660
+ } else {
2661
+ this.popupObj.show(new Animation(openAnimation), null);
2662
+ }
2663
+ this.setActiveDescendant();
2664
+ attributes(this.inputElement, { 'aria-expanded': 'true' });
2665
+ attributes(this.inputElement, { 'aria-owns': this.inputElement.id + '_options'});
2666
+ attributes(this.inputElement, { 'aria-controls': this.inputElement.id});
2667
+ addClass([this.inputWrapper.container], FOCUS);
2668
+ EventHandler.add(document, 'mousedown touchstart', this.documentClickHandler, this);
2669
+ this.setOverlayIndex(this.mobileTimePopupWrap, this.popupObj.element, this.modal, Browser.isDevice as any);
2670
+ if (Browser.isDevice) {
2671
+ const dlgOverlay: HTMLElement = this.createElement('div', { className: 'e-dlg-overlay'});
2672
+ dlgOverlay.style.zIndex = (this.zIndex - 1).toString();
2673
+ this.mobileTimePopupWrap.appendChild(dlgOverlay);
2674
+ }
2675
+ } else {
2676
+ this.popupObj.destroy();
2677
+ this.popupWrapper = this.listTag = undefined;
2678
+ this.liCollections = this.timeCollections = this.disableItemCollection = [];
2679
+ this.popupObj = null;
2680
+ }
2681
+ });
2682
+ }
2683
+ }
2684
+ /* eslint-enable valid-jsdoc, jsdoc/require-param */
2685
+ private setOverlayIndex(popupWrapper: HTMLElement, timePopupElement: HTMLElement, modal: HTMLElement, isDevice: boolean ): void {
2686
+ if (isDevice && !isNullOrUndefined(timePopupElement) && !isNullOrUndefined(modal) && !isNullOrUndefined(popupWrapper)) {
2687
+ const index: number = parseInt(timePopupElement.style.zIndex, 10) ? parseInt(timePopupElement.style.zIndex, 10) : 1000;
2688
+ modal.style.zIndex = (index - 1).toString();
2689
+ popupWrapper.style.zIndex = index.toString();
2690
+ }
2691
+ }
2692
+ private formatValues(type: string | number): string {
2693
+ let value: string;
2694
+ if (typeof type === 'number') {
2695
+ value = formatUnit(type);
2696
+ } else if (typeof type === 'string') {
2697
+ value = (type.match(/px|%|em/)) ? type : isNaN(parseInt(type, 10)) ? type : formatUnit(type);
2698
+ }
2699
+ return value;
2700
+ }
2701
+
2702
+ private popupAlignment(args: PopupEventArgs): void {
2703
+ args.popup.position.X = this.formatValues(args.popup.position.X);
2704
+ args.popup.position.Y = this.formatValues(args.popup.position.Y);
2705
+ if (!isNaN(parseFloat(args.popup.position.X)) || !isNaN(parseFloat(args.popup.position.Y))) {
2706
+ this.popupObj.relateTo = this.anchor = document.body;
2707
+ this.popupObj.targetType = 'container';
2708
+ }
2709
+ if (!isNaN(parseFloat(args.popup.position.X))) {
2710
+ this.popupObj.offsetX = parseFloat(args.popup.position.X);
2711
+ }
2712
+ if (!isNaN(parseFloat(args.popup.position.Y))) {
2713
+ this.popupObj.offsetY = parseFloat(args.popup.position.Y);
2714
+ }
2715
+ if (!Browser.isDevice) {
2716
+ switch (args.popup.position.X) {
2717
+ case 'left':
2718
+ break;
2719
+ case 'right':
2720
+ args.popup.offsetX = this.containerStyle.width;
2721
+ break;
2722
+ case 'center':
2723
+ args.popup.offsetX = -(this.containerStyle.width / 2);
2724
+ break;
2725
+ }
2726
+ switch (args.popup.position.Y) {
2727
+ case 'top':
2728
+ break;
2729
+ case 'bottom':
2730
+ break;
2731
+ case 'center':
2732
+ args.popup.offsetY = -(this.containerStyle.height / 2);
2733
+ break;
2734
+ }
2735
+ if (args.popup.position.X === 'center' && args.popup.position.Y === 'center') {
2736
+ this.popupObj.relateTo = this.inputWrapper.container;
2737
+ this.anchor = this.inputElement;
2738
+ this.popupObj.targetType = 'relative';
2739
+ }
2740
+ } else {
2741
+ if (args.popup.position.X === 'center' && args.popup.position.Y === 'center') {
2742
+ this.popupObj.relateTo = this.anchor = document.body;
2743
+ this.popupObj.offsetY = 0;
2744
+ this.popupObj.targetType = 'container';
2745
+ this.popupObj.collision = { X: 'fit', Y: 'fit' };
2746
+ }
2747
+ }
2748
+ }
2749
+ /* eslint-disable valid-jsdoc, jsdoc/require-returns-description */
2750
+ /**
2751
+ * Gets the properties to be maintained upon browser refresh.
2752
+ *
2753
+ * @returns {string}
2754
+ */
2755
+ public getPersistData(): string {
2756
+ const keyEntity: string[] = ['value'];
2757
+ return this.addOnPersist(keyEntity);
2758
+ }
2759
+ /* eslint-enable valid-jsdoc, jsdoc/require-returns-description */
2760
+ /**
2761
+ * To get component name
2762
+ *
2763
+ * @returns {string} Returns the component name.
2764
+ * @private
2765
+ */
2766
+ protected getModuleName(): string {
2767
+ return 'timepicker';
2768
+ }
2769
+ /**
2770
+ * Called internally if any of the property value changed.
2771
+ *
2772
+ * @param {TimePickerModel} newProp - Returns the dynamic property value of the component.
2773
+ * @param {TimePickerModel} oldProp - Returns the previous property value of the component.
2774
+ * @returns {void}
2775
+ * @private
2776
+ */
2777
+ public onPropertyChanged(newProp: TimePickerModel, oldProp: TimePickerModel): void {
2778
+ for (const prop of Object.keys(newProp)) {
2779
+ switch (prop) {
2780
+ case 'placeholder':
2781
+ Input.setPlaceholder(newProp.placeholder, this.inputElement);
2782
+ break;
2783
+ case 'readonly':
2784
+ Input.setReadonly(this.readonly, this.inputElement, this.floatLabelType);
2785
+ if (this.readonly) {
2786
+ this.hide();
2787
+ }
2788
+ this.setTimeAllowEdit();
2789
+ break;
2790
+ case 'enabled':
2791
+ this.setProperties({ enabled: newProp.enabled }, true);
2792
+ this.setEnable();
2793
+ break;
2794
+ case 'allowEdit':
2795
+ this.setTimeAllowEdit();
2796
+ break;
2797
+ case 'enableRtl':
2798
+ this.setProperties({ enableRtl: newProp.enableRtl }, true);
2799
+ this.setEnableRtl();
2800
+ break;
2801
+ case 'cssClass':
2802
+ this.updateCssClass(newProp.cssClass, oldProp.cssClass);
2803
+ break;
2804
+ case 'zIndex':
2805
+ this.setProperties({ zIndex: newProp.zIndex }, true);
2806
+ this.setZIndex();
2807
+ break;
2808
+ case 'htmlAttributes':
2809
+ this.updateHtmlAttributeToElement();
2810
+ this.updateHtmlAttributeToWrapper();
2811
+ this.checkAttributes(true);
2812
+ break;
2813
+ case 'min':
2814
+ case 'max':
2815
+ this.getProperty(newProp, prop);
2816
+ break;
2817
+ case 'showClearButton':
2818
+ Input.setClearButton(this.showClearButton, this.inputElement, this.inputWrapper);
2819
+ this.bindClearEvent();
2820
+ break;
2821
+ case 'locale':
2822
+ this.setProperties({ locale: newProp.locale }, true);
2823
+ this.globalize = new Internationalization(this.locale);
2824
+ this.l10n.setLocale(this.locale);
2825
+ if (this.timeOptions && this.timeOptions.placeholder == null) {
2826
+ this.updatePlaceHolder();
2827
+ }
2828
+ this.setValue(this.value);
2829
+ if (this.enableMask) {
2830
+ this.notify('createMask', {
2831
+ module: 'MaskedDateTime'
2832
+ });
2833
+ }
2834
+ break;
2835
+ case 'width':
2836
+ setStyleAttribute(this.inputWrapper.container, { 'width': this.setWidth(newProp.width) });
2837
+ this.containerStyle = this.inputWrapper.container.getBoundingClientRect();
2838
+ Input.calculateWidth(this.inputElement, this.inputWrapper.container);
2839
+ if (!isNullOrUndefined(this.inputWrapper.buttons[0]) && !isNullOrUndefined(this.inputWrapper.container.getElementsByClassName('e-float-text-overflow')[0]) && this.floatLabelType !== 'Never') {
2840
+ this.inputWrapper.container.getElementsByClassName('e-float-text-overflow')[0].classList.add('e-icon');
2841
+ }
2842
+ break;
2843
+ case 'format':
2844
+ this.setProperties({ format: newProp.format }, true);
2845
+ this.checkTimeFormat();
2846
+ this.setValue(this.value);
2847
+ if (this.enableMask) {
2848
+ this.createMask();
2849
+ if (!this.value)
2850
+ {
2851
+ this.updateInputValue(this.maskedDateValue);
2852
+ }
2853
+ }
2854
+ break;
2855
+ case 'value':
2856
+ this.invalidValueString = null;
2857
+ this.checkInvalidValue(newProp.value);
2858
+ newProp.value = this.value;
2859
+ if (!this.invalidValueString) {
2860
+ if (typeof newProp.value === 'string') {
2861
+ this.setProperties({ value: this.checkDateValue(new Date(newProp.value)) }, true);
2862
+ newProp.value = this.value;
2863
+ } else {
2864
+ if ((newProp.value && +new Date(+newProp.value).setMilliseconds(0)) !== +this.value) {
2865
+ newProp.value = this.checkDateValue(new Date('' + newProp.value));
2866
+ }
2867
+ }
2868
+ this.initValue = newProp.value;
2869
+ newProp.value = this.compareFormatChange(this.checkValue(newProp.value));
2870
+ } else {
2871
+ this.updateInputValue(this.invalidValueString);
2872
+ this.checkErrorState(this.invalidValueString);
2873
+ }
2874
+ if (this.enableMask && isNullOrUndefined(newProp.value)) {
2875
+ this.updateInputValue(this.maskedDateValue);
2876
+ this.checkErrorState(this.maskedDateValue);
2877
+ }
2878
+ this.checkValueChange(null, false);
2879
+ if (this.isPopupOpen()) {
2880
+ this.setScrollPosition();
2881
+ }
2882
+ if (this.isAngular && this.preventChange) {
2883
+ this.preventChange = false;
2884
+ }
2885
+ if (this.enableMask) {
2886
+ this.notify('createMask', {
2887
+ module: 'MaskedDateTime'
2888
+ });
2889
+ }
2890
+ break;
2891
+ case 'floatLabelType':
2892
+ this.floatLabelType = newProp.floatLabelType;
2893
+ Input.removeFloating(this.inputWrapper);
2894
+ Input.addFloating(this.inputElement, this.floatLabelType, this.placeholder);
2895
+ if (!isNullOrUndefined(this.inputWrapper.buttons[0]) && !isNullOrUndefined(this.inputWrapper.container.getElementsByClassName('e-float-text-overflow')[0]) && this.floatLabelType !== 'Never') {
2896
+ this.inputWrapper.container.getElementsByClassName('e-float-text-overflow')[0].classList.add('e-icon');
2897
+ }
2898
+ break;
2899
+ case 'strictMode':
2900
+ this.invalidValueString = null;
2901
+ if (newProp.strictMode) {
2902
+ this.checkErrorState(null);
2903
+ }
2904
+ this.setProperties({ strictMode: newProp.strictMode }, true);
2905
+ this.checkValue((this.inputElement).value);
2906
+ this.checkValueChange(null, false);
2907
+ break;
2908
+ case 'scrollTo':
2909
+ if (this.checkDateValue(new Date(this.checkInValue(newProp.scrollTo)))) {
2910
+ if (this.popupWrapper) {
2911
+ this.setScrollTo();
2912
+ }
2913
+ this.setProperties({ scrollTo: this.checkDateValue(new Date(this.checkInValue(newProp.scrollTo))) }, true);
2914
+ } else {
2915
+ this.setProperties({ scrollTo: null }, true);
2916
+ }
2917
+ break;
2918
+ case 'enableMask':
2919
+ if (this.enableMask) {
2920
+ this.notify('createMask', {
2921
+ module: 'MaskedDateTime'
2922
+ });
2923
+ this.updateInputValue(this.maskedDateValue);
2924
+ }
2925
+ else
2926
+ {
2927
+ if (this.inputElement.value === this.maskedDateValue)
2928
+ {
2929
+ this.updateInputValue('');
2930
+ }
2931
+ }
2932
+ break;
2933
+ }
2934
+ }
2935
+ }
2936
+ protected checkInValue(inValue: string | Date | number): string {
2937
+ if (inValue instanceof Date) {
2938
+ return (inValue.toUTCString());
2939
+ } else {
2940
+ return ('' + inValue);
2941
+ }
2942
+ }
2943
+ }
2944
+ /* eslint-enable @typescript-eslint/no-explicit-any */