@progress/kendo-dateinputs-common 0.1.0 → 0.2.0-dev.202301061353

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 (55) hide show
  1. package/README.md +34 -3
  2. package/dist/cdn/js/kendo-dateinputs-common.js +1 -0
  3. package/dist/cdn/main.js +1 -1
  4. package/dist/es/common/constants.js +6 -0
  5. package/dist/es/common/dateobject.js +1159 -0
  6. package/dist/es/common/key.js +16 -0
  7. package/dist/es/common/keycode.js +16 -0
  8. package/dist/es/common/mask.js +8 -0
  9. package/dist/es/common/observable.js +32 -0
  10. package/dist/es/common/utils.js +128 -0
  11. package/dist/es/dateinput/dateinput.js +1011 -0
  12. package/dist/es/dateinput/interaction-mode.js +6 -0
  13. package/dist/es/dateinput/utils.js +93 -0
  14. package/dist/es/main.js +1 -1
  15. package/dist/es2015/common/constants.js +6 -0
  16. package/dist/es2015/common/dateobject.js +1137 -0
  17. package/dist/es2015/common/key.js +16 -0
  18. package/dist/es2015/common/keycode.js +16 -0
  19. package/dist/es2015/common/mask.js +6 -0
  20. package/dist/es2015/common/observable.js +29 -0
  21. package/dist/es2015/common/utils.js +117 -0
  22. package/dist/es2015/dateinput/dateinput.js +969 -0
  23. package/dist/es2015/dateinput/interaction-mode.js +6 -0
  24. package/dist/es2015/dateinput/utils.js +92 -0
  25. package/dist/es2015/main.js +1 -1
  26. package/dist/npm/common/constants.d.ts +6 -0
  27. package/dist/npm/common/constants.js +8 -0
  28. package/dist/npm/common/dateobject.d.ts +172 -0
  29. package/dist/npm/common/dateobject.js +1161 -0
  30. package/dist/npm/common/key.d.ts +16 -0
  31. package/dist/npm/common/key.js +18 -0
  32. package/dist/npm/common/keycode.d.ts +16 -0
  33. package/dist/npm/common/keycode.js +18 -0
  34. package/dist/npm/common/mask.d.ts +4 -0
  35. package/dist/npm/common/mask.js +10 -0
  36. package/dist/npm/common/observable.d.ts +9 -0
  37. package/dist/npm/common/observable.js +34 -0
  38. package/dist/npm/common/utils.d.ts +60 -0
  39. package/dist/npm/common/utils.js +130 -0
  40. package/dist/npm/dateinput/dateinput.d.ts +204 -0
  41. package/dist/npm/dateinput/dateinput.js +1013 -0
  42. package/dist/npm/dateinput/interaction-mode.d.ts +5 -0
  43. package/dist/npm/dateinput/interaction-mode.js +8 -0
  44. package/dist/npm/dateinput/utils.d.ts +27 -0
  45. package/dist/npm/dateinput/utils.js +95 -0
  46. package/dist/npm/main.d.ts +1 -1
  47. package/dist/npm/main.js +2 -2
  48. package/dist/systemjs/kendo-dateinputs-common.js +1 -0
  49. package/package.json +10 -8
  50. package/dist/cdn/js/kendo-typescript-package-base.js +0 -1
  51. package/dist/es/my-class.js +0 -15
  52. package/dist/es2015/my-class.js +0 -11
  53. package/dist/npm/my-class.d.ts +0 -9
  54. package/dist/npm/my-class.js +0 -17
  55. package/dist/systemjs/kendo-typescript-package-base.js +0 -1
@@ -0,0 +1,969 @@
1
+ import { DateObject } from '../common/dateobject';
2
+ import { approximateStringMatching } from './utils';
3
+ import { KeyCode } from '../common/keycode';
4
+ import { Key } from '../common/key';
5
+ import { extend, isPresent, isDocumentAvailable, millisecondDigitsInFormat, millisecondStepFor, isDate } from '../common/utils';
6
+ import { Observable } from '../common/observable';
7
+ import { DateInputInteractionMode } from './interaction-mode';
8
+ import { isEqual, cloneDate } from '@progress/kendo-date-math';
9
+ import { Constants } from '../common/constants';
10
+ const DEFAULT_SEGMENT_STEP = 1;
11
+ const DRAG_START = "dragStart";
12
+ const DROP = "drop";
13
+ const TOUCH_START = "touchstart";
14
+ const MOUSE_DOWN = "mousedown";
15
+ const MOUSE_UP = "mouseup";
16
+ const CLICK = "click";
17
+ const INPUT = "input";
18
+ const KEY_DOWN = "keydown";
19
+ const FOCUS = "focus";
20
+ const BLUR = "blur";
21
+ const PASTE = "paste";
22
+ const MOUSE_SCROLL = "DOMMouseScroll";
23
+ const MOUSE_WHEEL = "mousewheel";
24
+ const VALUE_CHANGE = "valueChange";
25
+ const INPUT_END = "inputEnd";
26
+ const BLUR_END = "blurEnd";
27
+ const FOCUS_END = "focusEnd";
28
+ const CHANGE = "change";
29
+ const defaultDateInputOptions = {
30
+ format: "d",
31
+ allowNulls: false,
32
+ placeholder: null,
33
+ cycleTime: true,
34
+ locale: null,
35
+ steps: {
36
+ millisecond: DEFAULT_SEGMENT_STEP,
37
+ second: DEFAULT_SEGMENT_STEP,
38
+ minute: DEFAULT_SEGMENT_STEP,
39
+ hour: DEFAULT_SEGMENT_STEP,
40
+ day: DEFAULT_SEGMENT_STEP,
41
+ month: DEFAULT_SEGMENT_STEP,
42
+ year: DEFAULT_SEGMENT_STEP
43
+ },
44
+ formatPlaceholder: null,
45
+ events: {
46
+ [VALUE_CHANGE]: null,
47
+ [INPUT]: null,
48
+ [INPUT_END]: null,
49
+ [FOCUS]: null,
50
+ [FOCUS_END]: null,
51
+ [BLUR]: null,
52
+ [BLUR_END]: null,
53
+ [KEY_DOWN]: null,
54
+ [MOUSE_WHEEL]: null,
55
+ [CHANGE]: null
56
+ },
57
+ selectNearestSegmentOnFocus: false,
58
+ enableMouseWheel: false,
59
+ allowCaretMode: false,
60
+ autoSwitchParts: true,
61
+ autoSwitchKeys: [],
62
+ twoDigitYearMax: Constants.twoDigitYearMax,
63
+ autoCorrectParts: true
64
+ };
65
+ export class DateInput extends Observable {
66
+ constructor(element, options) {
67
+ super(options);
68
+ this.dateObject = null;
69
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
70
+ // @ts-ignore
71
+ this.currentText = '';
72
+ this.currentFormat = '';
73
+ this.interactionMode = DateInputInteractionMode.None;
74
+ this.localeId = Constants.defaultLocaleId;
75
+ this.init(element, options);
76
+ }
77
+ get value() {
78
+ return this.dateObject && this.dateObject.getValue();
79
+ }
80
+ init(element, options) {
81
+ let dateValue = isDate(this.options.value) ? cloneDate(this.options.value) : new Date(options.formattedValue);
82
+ if (!isDate(dateValue)) {
83
+ dateValue = null;
84
+ }
85
+ this.element = element;
86
+ // this.element._kendoWidget = this;
87
+ this.options = extend({}, defaultDateInputOptions, options);
88
+ this.intl = this.options.intlService;
89
+ this.formatPlaceholder = this.options.formatPlaceholder ? this.options.formatPlaceholder : 'formatPattern';
90
+ this.dateObject = this.createDateObject();
91
+ this.dateObject.setValue(dateValue);
92
+ this.setTextAndFormat();
93
+ this.bindEvents();
94
+ this.resetSegmentValue = true;
95
+ this.forceUpdate();
96
+ }
97
+ destroy() {
98
+ this.unbindEvents();
99
+ this.dateObject = null;
100
+ super.destroy();
101
+ }
102
+ bindEvents() {
103
+ this.onElementDragStart = this.onElementDragStart.bind(this);
104
+ this.element.addEventListener(DRAG_START, this.onElementDragStart);
105
+ this.onElementDrop = this.onElementDrop.bind(this);
106
+ this.element.addEventListener(DROP, this.onElementDrop);
107
+ this.onElementClick = this.onElementClick.bind(this);
108
+ this.element.addEventListener(CLICK, this.onElementClick);
109
+ this.onElementMouseDown = this.onElementMouseDown.bind(this);
110
+ this.element.addEventListener(MOUSE_DOWN, this.onElementMouseDown);
111
+ this.element.addEventListener(TOUCH_START, this.onElementMouseDown);
112
+ this.onElementMouseUp = this.onElementMouseUp.bind(this);
113
+ this.element.addEventListener(MOUSE_UP, this.onElementMouseUp);
114
+ this.onElementInput = this.onElementInput.bind(this);
115
+ this.element.addEventListener(INPUT, this.onElementInput);
116
+ this.onElementKeyDown = this.onElementKeyDown.bind(this);
117
+ this.element.addEventListener(KEY_DOWN, this.onElementKeyDown);
118
+ this.onElementFocus = this.onElementFocus.bind(this);
119
+ this.element.addEventListener(FOCUS, this.onElementFocus);
120
+ this.onElementBlur = this.onElementBlur.bind(this);
121
+ this.element.addEventListener(BLUR, this.onElementBlur);
122
+ this.onElementChange = this.onElementChange.bind(this);
123
+ this.element.addEventListener(CHANGE, this.onElementChange);
124
+ this.onElementPaste = this.onElementPaste.bind(this);
125
+ this.element.addEventListener(PASTE, this.onElementPaste);
126
+ this.onElementMouseWheel = this.onElementMouseWheel.bind(this);
127
+ this.element.addEventListener(MOUSE_SCROLL, this.onElementMouseWheel);
128
+ this.element.addEventListener(MOUSE_WHEEL, this.onElementMouseWheel);
129
+ }
130
+ unbindEvents() {
131
+ this.element.removeEventListener(DRAG_START, this.onElementDragStart);
132
+ this.element.removeEventListener(DROP, this.onElementDrop);
133
+ this.element.removeEventListener(TOUCH_START, this.onElementMouseDown);
134
+ this.element.removeEventListener(MOUSE_DOWN, this.onElementMouseDown);
135
+ this.element.removeEventListener(MOUSE_UP, this.onElementMouseUp);
136
+ this.element.removeEventListener(CLICK, this.onElementClick);
137
+ this.element.removeEventListener(INPUT, this.onElementInput);
138
+ this.element.removeEventListener(KEY_DOWN, this.onElementKeyDown);
139
+ this.element.removeEventListener(FOCUS, this.onElementFocus);
140
+ this.element.removeEventListener(BLUR, this.onElementBlur);
141
+ this.element.removeEventListener(CHANGE, this.onElementChange);
142
+ this.element.removeEventListener(PASTE, this.onElementPaste);
143
+ this.element.removeEventListener(MOUSE_SCROLL, this.onElementMouseWheel);
144
+ this.element.removeEventListener(MOUSE_WHEEL, this.onElementMouseWheel);
145
+ }
146
+ setOptions(options, refresh = false) {
147
+ this.options = extend(this.options, options);
148
+ if (refresh) {
149
+ this.destroy();
150
+ this.init(this.element, options);
151
+ }
152
+ }
153
+ /**
154
+ * @hidden
155
+ */
156
+ resetLocale() {
157
+ this.unbindEvents();
158
+ this.init(this.element, this.options);
159
+ }
160
+ /**
161
+ * @hidden
162
+ */
163
+ isInCaretMode() {
164
+ return this.interactionMode === DateInputInteractionMode.Caret;
165
+ }
166
+ focus() {
167
+ this.element.focus();
168
+ if (this.options.selectNearestSegmentOnFocus) {
169
+ this.selectNearestSegment(0);
170
+ }
171
+ }
172
+ /**
173
+ * @hidden
174
+ */
175
+ onElementDragStart(e) {
176
+ e.preventDefault();
177
+ }
178
+ /**
179
+ * @hidden
180
+ */
181
+ onElementDrop(e) {
182
+ e.preventDefault();
183
+ }
184
+ /**
185
+ * @hidden
186
+ */
187
+ onElementMouseDown() {
188
+ this.mouseDownStarted = true;
189
+ this.focusedPriorToMouseDown = this.isActive;
190
+ }
191
+ /**
192
+ * @hidden
193
+ */
194
+ onElementMouseUp(e) {
195
+ this.mouseDownStarted = false;
196
+ e.preventDefault();
197
+ }
198
+ /**
199
+ * @hidden
200
+ */
201
+ onElementClick(e) {
202
+ this.mouseDownStarted = false;
203
+ const selection = this.selection;
204
+ if (this.isInCaretMode()) {
205
+ // explicitly refresh the input element value
206
+ // caret mode can change the number of symbols in the element
207
+ // thus clicking on a segment can result in incorrect selection
208
+ this.forceUpdate();
209
+ }
210
+ if (e.detail === 3) {
211
+ // when 3 clicks occur, leave the native event to handle the change
212
+ // this results in selecting the whole element value
213
+ }
214
+ else {
215
+ if (this.isActive && this.options.selectNearestSegmentOnFocus) {
216
+ const selectionPresent = this.element.selectionStart !== this.element.selectionEnd;
217
+ const placeholderToggled = isPresent(this.options.placeholder) &&
218
+ !this.dateObject.hasValue() &&
219
+ !this.focusedPriorToMouseDown;
220
+ // focus first segment if the user hasn't selected something during mousedown and if the placeholder was just toggled
221
+ const selectFirstSegment = !selectionPresent && placeholderToggled;
222
+ const index = selectFirstSegment ? 0 : this.caret()[0];
223
+ this.selectNearestSegment(index);
224
+ }
225
+ else {
226
+ this.setSelection(this.selectionByIndex(selection.start));
227
+ }
228
+ }
229
+ }
230
+ /**
231
+ * @hidden
232
+ */
233
+ onElementInput(e) {
234
+ this.triggerInput({ event: e });
235
+ const keyDownEvent = this.keyDownEvent || {};
236
+ const isBackspaceKey = keyDownEvent.keyCode === KeyCode.BACKSPACE || keyDownEvent.key === Key.BACKSPACE;
237
+ const isDeleteKey = keyDownEvent.keyCode === KeyCode.DELETE || keyDownEvent.key === Key.DELETE;
238
+ if (!this.element || !this.dateObject) {
239
+ return;
240
+ }
241
+ if (this.isPasteInProgress) {
242
+ if (this.options.allowCaretMode) {
243
+ // pasting should leave the input with caret
244
+ // thus allow direct input instead of selection mode
245
+ this.resetSegmentValue = false;
246
+ }
247
+ this.updateOnPaste(e);
248
+ this.isPasteInProgress = false;
249
+ return;
250
+ }
251
+ const originalInteractionMode = this.interactionMode;
252
+ if (this.options.allowCaretMode &&
253
+ originalInteractionMode !== DateInputInteractionMode.Caret &&
254
+ !isDeleteKey && !isBackspaceKey) {
255
+ this.resetSegmentValue = true;
256
+ }
257
+ if (this.options.allowCaretMode) {
258
+ this.interactionMode = DateInputInteractionMode.Caret;
259
+ }
260
+ else {
261
+ this.interactionMode = DateInputInteractionMode.Selection;
262
+ }
263
+ const hasCaret = this.isInCaretMode();
264
+ if (hasCaret && this.keyDownEvent.key === Key.SPACE) {
265
+ // do not allow custom "holes" in the date segments
266
+ this.setPreviousInputEventState(this.keyDownEvent);
267
+ return;
268
+ }
269
+ const oldDateObjectValue = this.dateObject && this.dateObject.getValue();
270
+ const { text: currentText, format: currentFormat } = this.dateObject.getTextAndFormat();
271
+ this.currentFormat = currentFormat;
272
+ const text = hasCaret && (isBackspaceKey || isDeleteKey) ? this.previousElementValue : currentText;
273
+ const diff = approximateStringMatching({
274
+ oldText: text,
275
+ newText: this.element.value,
276
+ formatPattern: this.currentFormat,
277
+ selectionStart: this.selection.start,
278
+ isInCaretMode: hasCaret,
279
+ keyEvent: this.keyDownEvent
280
+ });
281
+ if (hasCaret && (!diff || diff.length === 0)) {
282
+ this.setPreviousInputEventState(this.keyDownEvent);
283
+ return;
284
+ }
285
+ else if (hasCaret && diff.length === 1) {
286
+ if (!diff[0] || !diff[0][0]) {
287
+ this.setPreviousInputEventState(this.keyDownEvent);
288
+ return;
289
+ }
290
+ else if (hasCaret && diff[0] &&
291
+ (diff[0][0] === Constants.formatSeparator || diff[0][1] === Constants.formatSeparator)) {
292
+ this.setPreviousInputEventState(this.keyDownEvent);
293
+ return;
294
+ }
295
+ }
296
+ const navigationOnly = (diff.length === 1 && diff[0][1] === Constants.formatSeparator);
297
+ const parsePartsResults = [];
298
+ let switchPart = false;
299
+ if (!navigationOnly) {
300
+ for (let i = 0; i < diff.length; i++) {
301
+ const parsePartResult = this.dateObject.parsePart({
302
+ symbol: diff[i][0],
303
+ currentChar: diff[i][1],
304
+ resetSegmentValue: this.resetSegmentValue,
305
+ cycleSegmentValue: !this.isInCaretMode(),
306
+ rawTextValue: this.element.value,
307
+ isDeleting: isBackspaceKey || isDeleteKey
308
+ });
309
+ parsePartsResults.push(parsePartResult);
310
+ switchPart = parsePartResult.switchToNext;
311
+ }
312
+ }
313
+ if (!this.options.autoSwitchParts) {
314
+ switchPart = false;
315
+ }
316
+ this.resetSegmentValue = false;
317
+ const lastParseResult = parsePartsResults[parsePartsResults.length - 1];
318
+ const lastParseResultHasNoValue = lastParseResult && !lastParseResult.value;
319
+ const parsingFailedOnDelete = (hasCaret && (isBackspaceKey || isDeleteKey) && lastParseResultHasNoValue);
320
+ const resetPart = lastParseResult ? lastParseResult.resetPart : false;
321
+ const hasDateValueChanged = !isEqual(oldDateObjectValue, this.dateObject.value);
322
+ let symbolForSelection;
323
+ const currentSelection = this.selection;
324
+ if (!hasCaret || parsingFailedOnDelete || resetPart) {
325
+ if (resetPart) {
326
+ symbolForSelection = this.currentFormat[currentSelection.start];
327
+ }
328
+ this.forceUpdate();
329
+ }
330
+ if (diff.length && diff[0][0] !== Constants.formatSeparator) {
331
+ if (!hasCaret || parsingFailedOnDelete || resetPart) {
332
+ if (symbolForSelection) {
333
+ this.setSelection(this.selectionBySymbol(symbolForSelection));
334
+ }
335
+ else {
336
+ if (!this.options.autoSwitchParts && diff[0][1] === Constants.formatSeparator) {
337
+ // do not change the selection when a separator is pressed
338
+ // this should happen only if autoSwitchKeys contains the separator explicitly
339
+ }
340
+ else {
341
+ this.setSelection(this.selectionBySymbol(diff[0][0]));
342
+ }
343
+ }
344
+ }
345
+ }
346
+ if (!switchPart && hasCaret && !isBackspaceKey && !isDeleteKey && !resetPart && lastParseResultHasNoValue) {
347
+ if (hasDateValueChanged) {
348
+ if (this.currentFormat.length === this.elementValue.length) {
349
+ // if a full date is entered, do not reset it
350
+ }
351
+ else {
352
+ // the input is not complete, not parsable or not updatable
353
+ if (hasCaret && originalInteractionMode !== DateInputInteractionMode.Caret && hasDateValueChanged) {
354
+ }
355
+ else if (hasCaret && originalInteractionMode !== DateInputInteractionMode.Caret) {
356
+ symbolForSelection = this.currentFormat[currentSelection.start];
357
+ this.forceUpdate();
358
+ this.setSelection(this.selectionBySymbol(symbolForSelection));
359
+ }
360
+ else {
361
+ this.setPreviousInputEventState(this.keyDownEvent);
362
+ }
363
+ }
364
+ }
365
+ }
366
+ else if (this.options.autoSwitchParts && (switchPart || navigationOnly)) {
367
+ if (!hasCaret) {
368
+ this.switchDateSegment(1);
369
+ }
370
+ }
371
+ this.tryTriggerValueChange({
372
+ oldValue: oldDateObjectValue,
373
+ event: e
374
+ });
375
+ this.triggerInputEnd({ event: e });
376
+ }
377
+ /**
378
+ * @hidden
379
+ */
380
+ onElementFocus(e) {
381
+ if (this.triggerFocus({ event: e })) {
382
+ return;
383
+ }
384
+ this.isActive = true;
385
+ this.interactionMode = DateInputInteractionMode.None;
386
+ this.refreshElementValue();
387
+ if (!this.mouseDownStarted) {
388
+ this.caret(0, this.elementValue.length);
389
+ }
390
+ this.mouseDownStarted = false;
391
+ this.triggerFocusEnd({ event: e });
392
+ }
393
+ /**
394
+ * @hidden
395
+ */
396
+ onElementBlur(e) {
397
+ this.resetSegmentValue = true;
398
+ this.isActive = false;
399
+ if (this.triggerBlur({ event: e })) {
400
+ return;
401
+ }
402
+ this.interactionMode = DateInputInteractionMode.None;
403
+ this.refreshElementValue();
404
+ this.triggerBlurEnd({ event: e });
405
+ }
406
+ /**
407
+ * @hidden
408
+ */
409
+ onElementChange(e) {
410
+ this.triggerChange({ event: e });
411
+ }
412
+ /**
413
+ * @hidden
414
+ */
415
+ onElementKeyDown(e) {
416
+ if (this.triggerKeyDown({ event: e })) {
417
+ return;
418
+ }
419
+ this.keyDownEvent = e;
420
+ this.previousElementValue = this.element.value;
421
+ const autoSwitchKeys = (this.options.autoSwitchKeys || [])
422
+ .map(x => x.toString().toLowerCase().trim());
423
+ if (autoSwitchKeys.indexOf(e.keyCode.toString()) >= 0 ||
424
+ autoSwitchKeys.indexOf(e.keyCode) >= 0 ||
425
+ autoSwitchKeys.indexOf(e.key.toLowerCase().trim()) >= 0) {
426
+ const isTabKey = autoSwitchKeys.indexOf(Key.TAB.toLowerCase().trim()) >= 0 ||
427
+ autoSwitchKeys.indexOf(KeyCode.TAB) >= 0 ||
428
+ autoSwitchKeys.indexOf(KeyCode.TAB.toString()) >= 0;
429
+ if (isTabKey) {
430
+ const { start: selectionStart, end: selectionEnd } = this.selection;
431
+ if (e.shiftKey && isTabKey) {
432
+ this.switchDateSegment(-1);
433
+ }
434
+ else {
435
+ this.switchDateSegment(1);
436
+ }
437
+ if (selectionStart !== this.selection.start || selectionEnd !== this.selection.end) {
438
+ // when the selection changes, prevent the default Tab behavior
439
+ e.preventDefault();
440
+ return;
441
+ }
442
+ }
443
+ else {
444
+ // do not allow the "input" event to be triggered
445
+ e.preventDefault();
446
+ this.switchDateSegment(1);
447
+ return;
448
+ }
449
+ }
450
+ const symbol = this.currentFormat[this.selection.start];
451
+ const step = this.getStepFromSymbol(symbol);
452
+ let shouldPreventDefault = false;
453
+ if (e.altKey || e.ctrlKey || e.metaKey || e.keyCode === KeyCode.TAB) {
454
+ return;
455
+ }
456
+ switch (e.keyCode) {
457
+ case KeyCode.ARROW_LEFT:
458
+ this.switchDateSegment(-1);
459
+ shouldPreventDefault = true;
460
+ break;
461
+ case KeyCode.ARROW_UP:
462
+ this.modifyDateSegmentValue(step, symbol, event);
463
+ shouldPreventDefault = true;
464
+ break;
465
+ case KeyCode.ARROW_RIGHT:
466
+ this.switchDateSegment(1);
467
+ shouldPreventDefault = true;
468
+ break;
469
+ case KeyCode.ARROW_DOWN:
470
+ this.modifyDateSegmentValue(-step, symbol, event);
471
+ shouldPreventDefault = true;
472
+ break;
473
+ case KeyCode.ENTER:
474
+ // todo: handle "change" event
475
+ break;
476
+ case KeyCode.DELETE:
477
+ case KeyCode.BACKSPACE:
478
+ if (this.options.allowNulls) {
479
+ const oldValue = this.dateObject.value;
480
+ this.dateObject.setValue(null);
481
+ this.forceUpdate();
482
+ this.tryTriggerValueChange({
483
+ oldValue: oldValue,
484
+ event: e
485
+ });
486
+ }
487
+ shouldPreventDefault = true;
488
+ if (e.keyCode === KeyCode.BACKSPACE &&
489
+ ((this.options.autoSwitchKeys || []).indexOf(KeyCode.BACKSPACE) >= 0 ||
490
+ (this.options.autoSwitchKeys || []).indexOf(Key.BACKSPACE) >= 0)) {
491
+ this.switchDateSegment(-1);
492
+ }
493
+ return;
494
+ case Key.HOME:
495
+ this.selectNearestSegment(0);
496
+ break;
497
+ case Key.END:
498
+ this.selectNearestSegment(this.elementValue.length);
499
+ break;
500
+ default:
501
+ // allow the "input" event to handle the change
502
+ return;
503
+ }
504
+ if (shouldPreventDefault) {
505
+ e.preventDefault();
506
+ }
507
+ }
508
+ /**
509
+ * @hidden
510
+ */
511
+ onElementPaste() {
512
+ this.isPasteInProgress = true;
513
+ }
514
+ /**
515
+ * @hidden
516
+ */
517
+ onElementMouseWheel(e) {
518
+ if (!this.options.enableMouseWheel || this.triggerMouseWheel({ event: e })) {
519
+ return;
520
+ }
521
+ if (!this.isActive) {
522
+ return;
523
+ }
524
+ const event = e;
525
+ if (event.shiftKey) {
526
+ this.switchDateSegment((event.wheelDelta || -event.detail) > 0 ? -1 : 1);
527
+ }
528
+ else {
529
+ this.modifyDateSegmentValue((event.wheelDelta || -event.detail) > 0 ? 1 : -1);
530
+ }
531
+ event.returnValue = false;
532
+ if (event.preventDefault) {
533
+ event.preventDefault();
534
+ }
535
+ if (event.stopPropagation) {
536
+ event.stopPropagation();
537
+ }
538
+ }
539
+ updateOnPaste(e) {
540
+ let value = this.intl.parseDate(this.elementValue, this.inputFormat) || this.value;
541
+ if (isPresent(value) && this.dateObject.shouldNormalizeCentury()) {
542
+ value = this.dateObject.normalizeCentury(value);
543
+ }
544
+ const oldDateObjectValue = this.dateObject && this.dateObject.getValue();
545
+ this.writeValue(value);
546
+ this.tryTriggerValueChange({
547
+ oldValue: oldDateObjectValue,
548
+ event: e
549
+ });
550
+ }
551
+ get elementValue() {
552
+ return (this.element || {}).value || '';
553
+ }
554
+ get inputFormat() {
555
+ if (!this.options.format) {
556
+ return Constants.defaultDateFormat;
557
+ }
558
+ if (typeof this.options.format === 'string') {
559
+ return this.options.format;
560
+ }
561
+ else {
562
+ return this.options.format.inputFormat;
563
+ }
564
+ }
565
+ get displayFormat() {
566
+ if (!this.options.format) {
567
+ return Constants.defaultDateFormat;
568
+ }
569
+ if (typeof this.options.format === 'string') {
570
+ return this.options.format;
571
+ }
572
+ else {
573
+ return this.options.format.displayFormat;
574
+ }
575
+ }
576
+ get selection() {
577
+ let returnValue = { start: 0, end: 0 };
578
+ if (this.element !== null && this.element.selectionStart !== undefined) {
579
+ returnValue = {
580
+ start: this.element.selectionStart,
581
+ end: this.element.selectionEnd
582
+ };
583
+ }
584
+ return returnValue;
585
+ }
586
+ setSelection(selection) {
587
+ // this._lastSelectedSymbol = this.currentFormat[selection.start];
588
+ if (this.element && document.activeElement === this.element) {
589
+ this.element.setSelectionRange(selection.start, selection.end);
590
+ }
591
+ }
592
+ /**
593
+ * @hidden
594
+ */
595
+ selectionBySymbol(symbol) {
596
+ let start = -1;
597
+ let end = 0;
598
+ for (let i = 0; i < this.currentFormat.length; i++) {
599
+ if (this.currentFormat[i] === symbol) {
600
+ end = i + 1;
601
+ if (start === -1) {
602
+ start = i;
603
+ }
604
+ }
605
+ }
606
+ if (start < 0) {
607
+ start = 0;
608
+ }
609
+ return { start, end };
610
+ }
611
+ /**
612
+ * @hidden
613
+ */
614
+ selectionByIndex(index) {
615
+ let selection = { start: index, end: index };
616
+ for (let i = index, j = index - 1; i < this.currentFormat.length || j >= 0; i++, j--) {
617
+ if (i < this.currentFormat.length && this.currentFormat[i] !== Constants.formatSeparator) {
618
+ selection = this.selectionBySymbol(this.currentFormat[i]);
619
+ break;
620
+ }
621
+ if (j >= 0 && this.currentFormat[j] !== Constants.formatSeparator) {
622
+ selection = this.selectionBySymbol(this.currentFormat[j]);
623
+ break;
624
+ }
625
+ }
626
+ return selection;
627
+ }
628
+ switchDateSegment(offset) {
629
+ const selection = this.selection;
630
+ if (this.isInCaretMode()) {
631
+ let start = selection.start;
632
+ let closestNonSeparatorSymbol = this.currentFormat[start];
633
+ for (let i = start; i >= 0; i--) {
634
+ closestNonSeparatorSymbol = this.currentFormat[i];
635
+ if (closestNonSeparatorSymbol !== Constants.formatSeparator) {
636
+ start = i;
637
+ break;
638
+ }
639
+ }
640
+ let symbol;
641
+ for (let i = start; i < this.currentFormat.length; i++) {
642
+ symbol = this.currentFormat[i];
643
+ if (symbol !== Constants.formatSeparator) {
644
+ break;
645
+ }
646
+ }
647
+ if (symbol) {
648
+ this.forceUpdate();
649
+ this.setSelection(this.selectionBySymbol(symbol));
650
+ }
651
+ }
652
+ let { start: selectionStart, end: selectionEnd } = this.selection;
653
+ if (selectionStart < selectionEnd &&
654
+ this.currentFormat[selectionStart] !== this.currentFormat[selectionEnd - 1]) {
655
+ this.setSelection(this.selectionByIndex(offset > 0 ? selectionStart : selectionEnd - 1));
656
+ this.resetSegmentValue = true;
657
+ return;
658
+ }
659
+ const previousFormatSymbol = this.currentFormat[selectionStart];
660
+ let a = selectionStart + offset;
661
+ while (a > 0 && a < this.currentFormat.length) {
662
+ if (this.currentFormat[a] !== previousFormatSymbol &&
663
+ this.currentFormat[a] !== Constants.formatSeparator) {
664
+ break;
665
+ }
666
+ a += offset;
667
+ }
668
+ if (this.currentFormat[a] === Constants.formatSeparator) {
669
+ // no known symbol is found
670
+ return;
671
+ }
672
+ let b = a;
673
+ while (b >= 0 && b < this.currentFormat.length) {
674
+ if (this.currentFormat[b] !== this.currentFormat[a]) {
675
+ break;
676
+ }
677
+ b += offset;
678
+ }
679
+ if (a > b && (b + 1 !== selectionStart || a + 1 !== selectionEnd)) {
680
+ this.setSelection({ start: b + 1, end: a + 1 });
681
+ this.resetSegmentValue = true;
682
+ }
683
+ else if (a < b && (a !== selectionStart || b !== selectionEnd)) {
684
+ this.setSelection({ start: a, end: b });
685
+ this.resetSegmentValue = true;
686
+ }
687
+ }
688
+ modifyDateSegmentValue(offset, symbol = "", event = {}) {
689
+ if (!this.dateObject) {
690
+ return;
691
+ }
692
+ const oldValue = this.value;
693
+ let step = DEFAULT_SEGMENT_STEP * offset;
694
+ const caret = this.caret();
695
+ symbol = symbol || this.currentFormat[caret[0]];
696
+ if (symbol === "S" && !this.options.steps.millisecond) {
697
+ const msDigits = millisecondDigitsInFormat(this.inputFormat);
698
+ step = millisecondStepFor(msDigits);
699
+ }
700
+ this.dateObject.modifyPart(symbol, step);
701
+ this.tryTriggerValueChange({
702
+ oldValue: oldValue,
703
+ event: event
704
+ });
705
+ this.forceUpdate();
706
+ this.setSelection(this.selectionBySymbol(symbol));
707
+ }
708
+ /**
709
+ * @hidden
710
+ */
711
+ tryTriggerValueChange(args = { oldValue: null, event: {} }) {
712
+ if (!isEqual(this.value, args.oldValue)) {
713
+ return this.triggerValueChange(args);
714
+ }
715
+ }
716
+ /**
717
+ * @hidden
718
+ */
719
+ triggerValueChange(args = { oldValue: null, event: {} }) {
720
+ return this.trigger(VALUE_CHANGE, extend(args, {
721
+ value: this.value
722
+ }));
723
+ }
724
+ /**
725
+ * @hidden
726
+ */
727
+ triggerInput(args = { event: {} }) {
728
+ return this.trigger(INPUT, extend(args, {
729
+ value: this.value
730
+ }));
731
+ }
732
+ /**
733
+ * @hidden
734
+ */
735
+ triggerInputEnd(args = { event: {} }) {
736
+ return this.trigger(INPUT_END, extend(args, {
737
+ value: this.value
738
+ }));
739
+ }
740
+ /**
741
+ * @hidden
742
+ */
743
+ triggerFocus(args = { event: {} }) {
744
+ return this.trigger(FOCUS, extend({}, args));
745
+ }
746
+ /**
747
+ * @hidden
748
+ */
749
+ triggerFocusEnd(args = { event: {} }) {
750
+ return this.trigger(FOCUS_END, extend({}, args));
751
+ }
752
+ /**
753
+ * @hidden
754
+ */
755
+ triggerBlur(args = { event: {} }) {
756
+ return this.trigger(BLUR, extend({}, args));
757
+ }
758
+ /**
759
+ * @hidden
760
+ */
761
+ triggerBlurEnd(args = { event: {} }) {
762
+ return this.trigger(BLUR_END, extend({}, args));
763
+ }
764
+ /**
765
+ * @hidden
766
+ */
767
+ triggerChange(args = { event: {} }) {
768
+ return this.trigger(CHANGE, extend(args, {
769
+ value: this.value
770
+ }));
771
+ }
772
+ /**
773
+ * @hidden
774
+ */
775
+ triggerKeyDown(args = { event: {} }) {
776
+ return this.trigger(KEY_DOWN, extend({}, args));
777
+ }
778
+ /**
779
+ * @hidden
780
+ */
781
+ triggerMouseWheel(args = { event: {} }) {
782
+ return this.trigger(MOUSE_WHEEL, extend({}, args));
783
+ }
784
+ /**
785
+ * @hidden
786
+ */
787
+ forceUpdate() {
788
+ this.setTextAndFormat();
789
+ this.refreshElementValue();
790
+ }
791
+ /**
792
+ * @hidden
793
+ */
794
+ setTextAndFormat() {
795
+ const { text: currentText, format: currentFormat } = this.dateObject.getTextAndFormat();
796
+ this.currentFormat = currentFormat;
797
+ this.currentText = currentText;
798
+ }
799
+ /**
800
+ * @hidden
801
+ */
802
+ setElementValue(value) {
803
+ this.previousElementValue = this.element.value;
804
+ this.element.value = value;
805
+ }
806
+ /**
807
+ * @hidden
808
+ */
809
+ getStepFromSymbol(symbol) {
810
+ /* eslint-disable no-fallthrough */
811
+ switch (symbol) {
812
+ case "S":
813
+ return Number(this.options.steps.millisecond);
814
+ case "s":
815
+ return Number(this.options.steps.second);
816
+ case "m":
817
+ return Number(this.options.steps.minute);
818
+ // represents hour as value from 01 through 12
819
+ case "h":
820
+ // represents hour as value from 01 through 23
821
+ case "H":
822
+ return Number(this.options.steps.hour);
823
+ case "M":
824
+ return Number(this.options.steps.month);
825
+ // there is no 'D' format specifier for day
826
+ case "d":
827
+ // used for formats such as "EEEE, MMMM d, yyyy",
828
+ // where "EEEE" stands for full name of the day e.g. Monday
829
+ case "E":
830
+ return Number(this.options.steps.day);
831
+ // there is no 'Y' format specifier for year
832
+ case "y":
833
+ return Number(this.options.steps.year);
834
+ default:
835
+ return DEFAULT_SEGMENT_STEP;
836
+ }
837
+ /* eslint-enable no-fallthrough */
838
+ }
839
+ setPreviousInputEventState(keyDownEvent) {
840
+ const { start: selectionStart, end: selectionEnd } = this.selection;
841
+ let selectionOffset = -1;
842
+ if (keyDownEvent.keyCode === KeyCode.BACKSPACE) {
843
+ selectionOffset = 1;
844
+ }
845
+ else if (keyDownEvent.keyCode === KeyCode.DELETE) {
846
+ selectionOffset = 0;
847
+ }
848
+ else if (keyDownEvent.keyCode === KeyCode.SPACE) {
849
+ selectionOffset = -1;
850
+ }
851
+ else {
852
+ selectionOffset = -1;
853
+ }
854
+ this.setElementValue(this.previousElementValue || '');
855
+ this.setSelection({ start: selectionStart + selectionOffset, end: selectionEnd + selectionOffset });
856
+ }
857
+ writeValue(value) {
858
+ this.verifyValue(value);
859
+ this.dateObject = this.getDateObject(value);
860
+ this.refreshElementValue();
861
+ }
862
+ verifyValue(value) {
863
+ if (value && !isDate(value)) {
864
+ throw new Error("The 'value' should be a valid JavaScript Date instance.");
865
+ }
866
+ }
867
+ refreshElementValue() {
868
+ const start = this.caret()[0];
869
+ const element = this.element;
870
+ const format = this.isActive ? this.inputFormat : this.displayFormat;
871
+ const { text: currentText, format: currentFormat } = this.dateObject.getTextAndFormat(format);
872
+ this.currentFormat = currentFormat;
873
+ this.currentText = currentText;
874
+ const showPlaceholder = !this.isActive &&
875
+ isPresent(this.options.placeholder) &&
876
+ !this.dateObject.hasValue();
877
+ if (isPresent(this.options.placeholder)) {
878
+ element.placeholder = this.options.placeholder;
879
+ }
880
+ const newElementValue = !showPlaceholder ? currentText : "";
881
+ this.setElementValue(newElementValue);
882
+ if (this.isActive && !this.options.allowCaretMode && this.options.selectNearestSegmentOnFocus) {
883
+ this.selectNearestSegment(start);
884
+ }
885
+ }
886
+ /**
887
+ * @hidden
888
+ */
889
+ caret(start, end = start) {
890
+ const isPosition = start !== undefined;
891
+ let returnValue = [start, start];
892
+ const element = this.element;
893
+ if (isPosition && (this.options.disabled || this.options.readonly)) {
894
+ return undefined;
895
+ }
896
+ try {
897
+ if (element.selectionStart !== undefined) {
898
+ if (isPosition) {
899
+ if (isDocumentAvailable() && document.activeElement !== element) {
900
+ element.focus();
901
+ }
902
+ element.setSelectionRange(start, end);
903
+ }
904
+ returnValue = [element.selectionStart, element.selectionEnd];
905
+ }
906
+ }
907
+ catch (e) {
908
+ returnValue = [];
909
+ }
910
+ return returnValue;
911
+ }
912
+ selectNearestSegment(index) {
913
+ // Finds the nearest (in both directions) known part.
914
+ for (let i = index, j = index - 1; i < this.currentFormat.length || j >= 0; i++, j--) {
915
+ if (i < this.currentFormat.length && this.currentFormat[i] !== "_") {
916
+ this.selectDateSegment(this.currentFormat[i]);
917
+ return;
918
+ }
919
+ if (j >= 0 && this.currentFormat[j] !== "_") {
920
+ this.selectDateSegment(this.currentFormat[j]);
921
+ return;
922
+ }
923
+ }
924
+ }
925
+ selectDateSegment(symbol) {
926
+ let begin = -1;
927
+ let end = 0;
928
+ for (let i = 0; i < this.currentFormat.length; i++) {
929
+ if (this.currentFormat[i] === symbol) {
930
+ end = i + 1;
931
+ if (begin === -1) {
932
+ begin = i;
933
+ }
934
+ }
935
+ }
936
+ if (begin < 0) {
937
+ begin = 0;
938
+ }
939
+ this.caret(0, 0);
940
+ this.caret(begin, end);
941
+ }
942
+ /**
943
+ * @hidden
944
+ */
945
+ getDateObject(value) {
946
+ const { leadingZero } = (this.dateObject || {}) || null;
947
+ const dateObject = this.createDateObject({
948
+ value: value
949
+ });
950
+ dateObject.setLeadingZero(this.isActive ? leadingZero : null);
951
+ return dateObject;
952
+ }
953
+ /* tslint:disable:align */
954
+ /**
955
+ * @hidden
956
+ */
957
+ createDateObject(options = {}) {
958
+ const dateObject = new DateObject(extend({
959
+ intlService: this.intl,
960
+ formatPlaceholder: this.formatPlaceholder,
961
+ format: this.inputFormat,
962
+ localeId: this.localeId,
963
+ cycleTime: this.options.cycleTime,
964
+ twoDigitYearMax: this.options.twoDigitYearMax,
965
+ autoCorrectParts: this.options.autoCorrectParts
966
+ }, options));
967
+ return dateObject;
968
+ }
969
+ }