493e0cb23778502237c441357ef79b0c0080a918
[sdc/sdc-workflow-designer.git] /
1 /**
2  * picker.component
3  */
4
5 import {
6     AfterViewInit,
7     Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, OnInit, Output, Renderer2,
8     ViewChild
9 } from '@angular/core';
10 import { animate, state, style, transition, trigger } from '@angular/animations';
11 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
12 import {
13     parse,
14     isValid,
15     startOfMonth,
16     getDate,
17     getDay,
18     addDays,
19     addMonths,
20     isSameDay,
21     isSameMonth,
22     isToday,
23     getMonth,
24     setMonth,
25     getYear,
26     addYears,
27     differenceInCalendarDays,
28     setYear,
29     getHours,
30     setHours,
31     getMinutes,
32     setMinutes,
33     getSeconds,
34     setSeconds,
35     isBefore,
36     isAfter,
37     compareAsc,
38     startOfDay,
39     format,
40     endOfDay,
41 } from 'date-fns';
42 import { NumberFixedLenPipe } from './numberedFixLen.pipe';
43
44 export interface LocaleSettings {
45     firstDayOfWeek?: number;
46     dayNames: string[];
47     dayNamesShort: string[];
48     monthNames: string[];
49     monthNamesShort: string[];
50     dateFns: any;
51     confirm: string;
52 }
53
54 export enum DialogType {
55     Time,
56     Date,
57     Month,
58     Year,
59 }
60
61 export const DATETIMEPICKER_VALUE_ACCESSOR: any = {
62     provide: NG_VALUE_ACCESSOR,
63     useExisting: forwardRef(() => DateTimePickerComponent),
64     multi: true
65 };
66
67 @Component({
68     selector: 'plx-datepicker',
69     templateUrl: './picker.component.html',
70     styleUrls: ['./picker.component.less'],
71     providers: [NumberFixedLenPipe, DATETIMEPICKER_VALUE_ACCESSOR],
72     animations: [
73         trigger('fadeInOut', [
74             state('hidden', style({
75                 opacity: 0,
76                 display: 'none'
77             })),
78             state('visible', style({
79                 opacity: 1,
80                 display: 'block'
81             })),
82             transition('visible => hidden', animate('200ms ease-in')),
83             transition('hidden => visible', animate('400ms ease-out'))
84         ])
85     ],
86 })
87
88 export class DateTimePickerComponent implements OnInit, AfterViewInit, OnDestroy, ControlValueAccessor {
89
90     @ViewChild('timepicker') public timepicker;
91
92     /**
93      * Type of the value to write back to ngModel
94      * @default 'date' -- Javascript Date type
95      * @type {'string' | 'date'}
96      * */
97     @Input() dataType: 'string' | 'date' = 'date';
98
99     /**
100      * Format of the date
101      * @default 'y/MM/dd'
102      * @type {String}
103      * */
104     @Input() dateFormat: string = 'YYYY-MM-DD HH:mm';
105     /**
106      * When present, it specifies that the component should be disabled
107      * @default false
108      * @type {Boolean}
109      * */
110     @Input() disabled: boolean;
111     /**
112      * @default false
113      * @type {Boolean}
114      * */
115     @Input() supportKeyboardInput: boolean = false;
116     /**
117      * Array with dates that should be disabled (not selectable)
118      * @default null
119      * @type {Date[]}
120      * */
121     @Input() disabledDates: Date[] = [];
122
123     /**
124      * Array with weekday numbers that should be disabled (not selectable)
125      * @default null
126      * @type {number[]}
127      * */
128     @Input() disabledDays: number[];
129
130     /**
131      * When enabled, displays the calendar as inline
132      * @default false -- popup mode
133      * @type {boolean}
134      * */
135     @Input() inline: boolean;
136
137     /**
138      * Identifier of the focus input to match a label defined for the component
139      * @default null
140      * @type {String}
141      * */
142     @Input() inputId: string;
143
144     /**
145      * Inline style of the picker text input
146      * @default null
147      * @type {any}
148      * */
149     @Input() inputStyle: any;
150
151     /**
152      * Style class of the picker text input
153      * @default null
154      * @type {String}
155      * */
156     @Input() inputStyleClass: string;
157
158     /**
159      * Maximum number of selectable dates in multiple mode
160      * @default null
161      * @type {number}
162      * */
163     @Input() maxDateCount: number;
164
165     /**
166      * The minimum selectable date time
167      * @default null
168      * @type {Date | string}
169      * */
170     private _max: Date;
171     @Input()
172     get max() {
173         return this._max;
174     }
175
176     set max(val: Date | string) {
177         this._max = this.parseToDate(val);
178     }
179
180     @Input()
181     get maxDate() {
182         return this._max;
183     }
184
185     set maxDate(val: Date | string) {
186         this._max = this.parseToDate(val);
187     }
188
189     /**
190      * The maximum selectable date time
191      * @default null
192      * @type {Date | string }
193      * */
194     private _min: Date;
195     @Input()
196     get min() {
197         return this._min;
198     }
199
200     set min(val: Date | string) {
201         this._min = this.parseToDate(val);
202     }
203     @Input()
204     get minDate() {
205         return this._min;
206     }
207
208     set minDate(val: Date | string) {
209         this._min = this.parseToDate(val);
210     }
211
212     @Input()
213     get dateValue() {
214         return this.value;
215     }
216
217     set dateValue(val: Date | string) {
218         let newvalue =  this.parseToDate(val);
219         if(newvalue!==undefined)
220         {
221             this.updateModel(newvalue);
222             this.updateCalendar(newvalue);
223             this.updateTimer(newvalue);
224             this.updateFormattedValue();
225         }
226     }
227
228
229     /**
230      * Picker input placeholder value
231      * @default
232      * @type {String}
233      * */
234     @Input() placeHolder: string = 'yyyy-mm-dd hh:mm';
235
236     /**
237      * When present, it specifies that an input field must be filled out before submitting the form
238      * @default false
239      * @type {Boolean}
240      * */
241     @Input() required: boolean;
242
243     /**
244      * Defines the quantity of the selection
245      *      'single' -- allow only a date value to be selected
246      *      'multiple' -- allow multiple date value to be selected
247      *      'range' -- allow to select an range ot date values
248      * @default 'single'
249      * @type {string}
250      * */
251     @Input() selectionMode: 'single' | 'multiple' | 'range' = 'single';
252
253     /**
254      * Whether to show the picker dialog header
255      * @default false
256      * @type {Boolean}
257      * */
258     @Input() showHeader: boolean;
259
260     @Input() canClear: boolean = true;
261
262     /**
263      * Whether to show the second's timer
264      * @default false
265      * @type {Boolean}
266      * */
267     @Input() showSeconds: boolean;
268
269     /**
270      * Inline style of the element
271      * @default null
272      * @type {any}
273      * */
274     @Input() style: any;
275
276     /**
277      * Style class of the element
278      * @default null
279      * @type {String}
280      * */
281     @Input() styleClass: string;
282
283     /**
284      * Index of the element in tabbing order
285      * @default null
286      * @type {Number}
287      * */
288     @Input() tabIndex: number;
289
290     /**
291      * Set the type of the dateTime picker
292      *      'both' -- show both calendar and timer
293      *      'calendar' -- show only calendar
294      *      'timer' -- show only timer
295      * @default 'both'
296      * @type {'both' | 'calendar' | 'timer'}
297      * */
298     @Input() type: 'both' | 'calendar' | 'timer' = 'calendar';
299
300     //附加方法
301     @Input()
302     set timeOnly(value: boolean) {
303         if (value) {
304             this.type = 'timer';
305         }
306         else {
307             this.type = "both";
308         }
309     }
310
311     @Input()
312     set showTime(value: boolean) {
313         if (value) {
314             this.type = 'both';
315         }
316         else {
317             this.type = "calendar";
318         }
319     }
320
321     /**
322      * An object having regional configuration properties for the dateTimePicker
323      * */
324     @Input()
325     get locale(): any {
326         return this._locale;
327     }
328     set locale(val: any) {
329         if (val !== undefined) {
330             this._locale = val;
331             this._userlocale = true;
332         }
333     }
334
335     @Input() localePrefab: 'Zh' | 'En' = 'En';
336     /**
337      * Determine the hour format (12 or 24)
338      * @default '24'
339      * @type {'24'| '12'}
340      * */
341     @Input() hourFormat: '12' | '24' = '24';
342
343
344     /**
345      * When it is set to false, only show current month's days in calendar
346      * @default true
347      * @type {boolean}
348      * */
349     @Input() showOtherMonths: boolean = true;
350
351     /**
352      * Callback to invoke when dropdown gets focus.
353      * */
354     @Output() onFocus: any = new EventEmitter<any>();
355
356     /**
357      * Callback to invoke when dropdown gets focus.
358      * */
359     @Output() onConfirm: any = new EventEmitter<any>();
360
361     /**
362      * Callback to invoke when dropdown loses focus.
363      * */
364     @Output() onBlur: any = new EventEmitter<any>();
365
366     /**
367      * Callback to invoke when a invalid date is selected.
368      * */
369     @Output() onInvalid: any = new EventEmitter<any>();
370
371
372
373     @ViewChild('container') containerElm: ElementRef;
374     @ViewChild('textInput') textInputElm: ElementRef;
375     @ViewChild('dialog') dialogElm: ElementRef;
376
377     public calendarDays: Array<any[]>;
378     public calendarWeekdays: string[];
379     public calendarMonths: Array<string[]>;
380     public calendarYears: Array<string[]> = [];
381     public dialogType: DialogType = DialogType.Date;
382     public dialogVisible: boolean;
383     public focus: boolean;
384     public formattedValue: string = '';
385     public value: any;
386     public pickerMoment: Date;
387     public pickerMonth: string;
388     public pickerYear: string;
389
390     public hourValue: number;
391     public minValue: number;
392     public secValue: number;
393     public meridianValue: string = 'AM';
394     private _userlocale: boolean = false;
395     private _locale: LocaleSettings = {
396         firstDayOfWeek: 0,
397         dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
398         dayNamesShort: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
399         //dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
400         monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
401         monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
402         dateFns: null,
403         confirm: 'OK'
404     };
405     private _localeEn: LocaleSettings = {
406         firstDayOfWeek: 0,
407         dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
408         dayNamesShort: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
409         monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
410         monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
411         dateFns: null,
412         confirm: 'OK'
413     };
414     private _localeZh: LocaleSettings = {
415         firstDayOfWeek: 0,
416         dayNames: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
417         dayNamesShort: ['日', '一', '二', '三', '四', '五', '六'],
418         monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
419         monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
420         dateFns: null,
421         confirm: '确定'
422     };
423     private dialogClick: boolean;
424     private documentClickListener: Function;
425     private valueIndex: number = 0;
426     private onModelChange: Function = () => {
427         //
428     }
429     private onModelTouched: Function = () => {
430         //
431     }
432
433     constructor(private renderer: Renderer2,
434         private numFixedLenPipe: NumberFixedLenPipe) {
435     }
436     private updateDate(newvalue : Date)
437     {
438         if(newvalue!==undefined&&newvalue!==null)
439         {
440             if(this.min)
441             {
442             newvalue = this._min.getTime()<=newvalue.getTime()?newvalue:new Date(this._min);
443             }
444             if(this.max)
445             {
446             newvalue = this._max.getTime()>=newvalue.getTime()?newvalue:new Date(this._max);
447             }
448             this.updateModel(newvalue);
449             this.updateCalendar(newvalue);
450             this.updateTimer(newvalue);
451             this.updateFormattedValue();
452             return;
453         }
454     }
455     public onInputChange(event:any): void {
456         let newvalue =  this.parseToDate(event.target.value);
457         if(newvalue!==undefined&&newvalue!==null)
458         {
459             if(this.min)
460             {
461             newvalue = this._min.getTime()<=newvalue.getTime()?newvalue:new Date(this._min);
462             }
463             if(this.max)
464             {
465             newvalue = this._max.getTime()>=newvalue.getTime()?newvalue:new Date(this._max);
466             }
467             this.updateModel(newvalue);
468             this.updateCalendar(newvalue);
469             this.updateTimer(newvalue);
470             this.updateFormattedValue();
471             return;
472         }
473         this.updateModel(null);
474         this.updateCalendar(null);
475         this.updateTimer(null);
476         this.updateFormattedValue();
477     }
478     public ngOnInit(): void {
479
480         if ((!this._userlocale) || this.locale === null && this.locale === undefined) {
481             switch (this.localePrefab) {
482
483                 case 'Zh': {
484                     this._locale = this._localeZh;
485                     break;
486                 }
487                 case 'En': {
488                     this._locale = this._localeEn;
489                     break;
490                 }
491                 default:
492                     {
493                         this._locale = this._localeEn;
494                         break;
495                     }
496             }
497         }
498         this.pickerMoment = new Date();
499
500         if (this.type === 'both' || this.type === 'calendar') {
501             this.generateWeekDays();
502             this.generateMonthList();
503         }
504         this.updateTimer(this.value);
505     }
506
507     public ngAfterViewInit(): void {
508         this.updateCalendar(this.value);
509         this.updateTimer(this.value);
510     }
511
512     public ngOnDestroy(): void {
513         this.unbindDocumentClickListener();
514     }
515
516     public writeValue(obj: any): void {
517
518         if (obj instanceof Array) {
519             this.value = [];
520             for (let o of obj) {
521                 let v = this.parseToDate(o);
522                 this.value.push(v);
523             }
524             this.updateCalendar(this.value[0]);
525             this.updateTimer(this.value[0]);
526         } else {
527             this.value = this.parseToDate(obj);
528             this.updateCalendar(this.value);
529             this.updateTimer(this.value);
530         }
531         this.updateFormattedValue();
532     }
533
534     public registerOnChange(fn: any): void {
535         this.onModelChange = fn;
536     }
537
538     public registerOnTouched(fn: any): void {
539         this.onModelTouched = fn;
540     }
541
542     public setDisabledState(isDisabled: boolean): void {
543         this.disabled = isDisabled;
544     }
545
546
547     private initflag = true;
548     /**
549      * Handle click event on the text input
550      * @param {any} event
551      * @return {void}
552      * */
553     public onInputClick(event: any): void {
554
555         if (this.timepicker !== undefined && this.initflag) {
556             this.initflag = false;
557             if (this.value !== undefined && this.value !== null) {
558                 this.timepicker.updateHour(this.value.getHours());
559                 this.timepicker.updateMinute(this.value.getMinutes());
560                 this.timepicker.updateSecond(this.value.getSeconds());
561             }
562             else {
563                 this.timepicker.updateHour(0);
564                 this.timepicker.updateMinute(0);
565                 this.timepicker.updateSecond(0);
566                 this.updateModel(null);
567                 this.updateFormattedValue();
568             }
569         }
570         if (this.disabled) {
571             event.preventDefault();
572             return;
573         }
574
575         this.dialogClick = true;
576         if (!this.dialogVisible) {
577             this.show();
578         }
579         event.preventDefault();
580         return;
581     }
582
583     /**
584      * Set the element on focus
585      * @param {any} event
586      * @return {void}
587      * */
588     public onInputFocus(event: any): void {
589         this.focus = true;
590         this.onFocus.emit(event);
591         event.preventDefault();
592         return;
593     }
594
595     /**
596      * Set the element on blur
597      * @param {any} event
598      * @return {void}
599      * */
600     public onInputBlur(event: any): void {
601         this.focus = false;
602         this.onModelTouched();
603         this.onBlur.emit(event);
604         event.preventDefault();
605         return;
606     }
607
608     /**
609      * Handle click event on the dialog
610      * @param {any} event
611      * @return {void}
612      * */
613     public onDialogClick(event: any): void {
614         this.dialogClick = true;
615     }
616
617     /**
618      * Go to previous month
619      * @param {any} event
620      * @return {void}
621      * */
622     public prevMonth(event: any): void {
623
624         if (this.disabled) {
625             event.preventDefault();
626             return;
627         }
628
629         this.pickerMoment = addMonths(this.pickerMoment, -1);
630         this.generateCalendar();
631         if(this.value!==undefined&&this.value!==null)
632         {
633         let nowvalue = new Date(this.value);
634         nowvalue.setMonth(this.pickerMoment.getMonth());
635         this.updateDate(nowvalue);
636         }
637         event.preventDefault();
638         return;
639     }
640
641     /**
642      * Go to next month
643      * @param {any} event
644      * @return {void}
645      * */
646     public nextMonth(event: any): void {
647
648         if (this.disabled) {
649             event.preventDefault();
650             return;
651         }
652
653         this.pickerMoment = addMonths(this.pickerMoment, 1);
654         this.generateCalendar();
655         if(this.value!==undefined&&this.value!==null)
656         {
657         let nowvalue = new Date(this.value);
658         nowvalue.setMonth(this.pickerMoment.getMonth());
659         this.updateDate(nowvalue);
660         }
661         event.preventDefault();
662         return;
663     }
664
665     /**
666      * Select a date
667      * @param {any} event
668      * @param {Date} date
669      * @return {void}
670      * */
671     public selectDate(event: any, date: Date): void {
672
673         if (this.disabled || !date) {
674             event.preventDefault();
675             return;
676         }
677
678         let temp: Date;
679         // check if the selected date is valid
680         if (this.isValidValue(date)) {
681             temp = date;
682         } else {
683             if (isSameDay(date, this._min)) {
684                 temp = new Date(this._min);
685             } else if (isSameDay(date, this._max)) {
686                 temp = new Date(this._max);
687             } else {
688                 this.onInvalid.emit({ originalEvent: event, value: date });
689                 return;
690             }
691         }
692         if (this.minValue !== undefined) {
693             temp.setMinutes(this.minValue);
694         }
695         if (this.secValue !== undefined) {
696             temp.setSeconds(this.secValue);
697         }
698         if (this.hourValue !== undefined) {
699             temp.setHours(this.hourValue);
700         }
701         let selected;
702         if (this.isSingleSelection()) {
703             if (!isSameDay(this.value, temp)) {
704                 selected = temp;
705             }
706         } else if (this.isRangeSelection()) {
707             if (this.value && this.value.length) {
708                 let startDate = this.value[0];
709                 let endDate = this.value[1];
710
711                 if (!endDate && temp.getTime() > startDate.getTime()) {
712                     endDate = temp;
713                     this.valueIndex = 1;
714                 } else {
715                     startDate = temp;
716                     endDate = null;
717                     this.valueIndex = 0;
718                 }
719                 selected = [startDate, endDate];
720             } else {
721                 selected = [temp, null];
722                 this.valueIndex = 0;
723             }
724         } else if (this.isMultiSelection()) {
725
726             // check if it exceeds the maxDateCount limit
727             if (this.maxDateCount && this.value &&
728                 this.value.length && this.value.length >= this.maxDateCount) {
729                 this.onInvalid.emit({ originalEvent: event, value: 'Exceed max date count.' });
730                 return;
731             }
732
733             if (this.isSelectedDay(temp)) {
734                 selected = this.value.filter((d: Date) => {
735                     return !isSameDay(d, temp);
736                 });
737             } else {
738                 selected = this.value ? [...this.value, temp] : [temp];
739                 this.valueIndex = selected.length - 1;
740             }
741         }
742         if (selected) {
743             this.updateModel(selected);
744             if (this.value instanceof Array) {
745                 this.updateCalendar(this.value[this.valueIndex]);
746                 this.updateTimer(this.value[this.valueIndex]);
747             } else {
748                 this.updateCalendar(this.value);
749                 this.updateTimer(this.value);
750             }
751             this.updateFormattedValue();
752         }
753     }
754
755     /**
756      * Set a pickerMoment's month
757      * @param {Number} monthNum
758      * @return {void}
759      * */
760     public selectMonth(monthNum: number): void {
761         this.pickerMoment = setMonth(this.pickerMoment, monthNum);
762         this.generateCalendar();
763         if(this.value!==undefined&&this.value!==null)
764         {
765         let nowvalue = new Date(this.value);
766         nowvalue.setMonth(monthNum);
767         this.updateDate(nowvalue);
768         }
769         this.changeDialogType(DialogType.Month);
770     }
771
772     /**
773      * Set a pickerMoment's year
774      * @param {Number} yearNum
775      * @return {void}
776      * */
777     public selectYear(yearNum: number): void {
778         this.pickerMoment = setYear(this.pickerMoment, yearNum);
779         this.generateCalendar();
780         if(this.value!==undefined&&this.value!==null)
781         {
782         let nowvalue = new Date(this.value);
783         nowvalue.setFullYear(yearNum);
784         this.updateDate(nowvalue);
785         }
786         this.changeDialogType(DialogType.Year);
787     }
788
789     /**
790      * Set the selected moment's meridian
791      * @param {any} event
792      * @return {void}
793      * */
794     public toggleMeridian(event: any): void {
795
796         let value = this.value ? (this.value.length ? this.value[this.valueIndex] : this.value) : null;
797
798         if (this.disabled) {
799             event.preventDefault();
800             return;
801         }
802
803         if (!value) {
804             this.meridianValue = this.meridianValue === 'AM' ? 'PM' : 'AM';
805             return;
806         }
807
808         let hours = getHours(value);
809         if (this.meridianValue === 'AM') {
810             hours += 12;
811         } else if (this.meridianValue === 'PM') {
812             hours -= 12;
813         }
814
815         let selectedTime = setHours(value, hours);
816         this.setSelectedTime(selectedTime);
817         event.preventDefault();
818         return;
819     }
820
821     /**
822      * Set the selected moment's hour
823      * @param {any} event
824      * @param {'increase' | 'decrease' | number} val
825      *      'increase' -- increase hour value by 1
826      *      'decrease' -- decrease hour value by 1
827      *      number -- set hour value to val
828      * @param {HTMLInputElement} input -- optional
829      * @return {boolean}
830      * */
831     public setHours(event: any, val: 'increase' | 'decrease' | number, input?: HTMLInputElement): boolean {
832
833         let value;
834         if (this.value) {
835             if (this.value.length) {
836                 value = this.value[this.valueIndex];
837             } else {
838                 value = this.value;
839             }
840         } else {
841             if (this.type === 'timer') {
842                 value = new Date();
843             } else {
844                 value = null;
845             }
846         }
847
848         if (this.disabled || !value) {
849             event.preventDefault();
850             return false;
851         }
852
853         let hours = getHours(value);
854         if (val === 'increase') {
855             hours += 1;
856         } else if (val === 'decrease') {
857             hours -= 1;
858         } else {
859             hours = val;
860         }
861
862         if (hours > 23) {
863             hours = 0;
864         } else if (hours < 0) {
865             hours = 23;
866         }
867
868         let selectedTime = setHours(value, hours);
869         let done = this.setSelectedTime(selectedTime);
870
871         // Focus the input and select its value when model updated
872         if (input) {
873             setTimeout(() => {
874                 input.focus();
875             }, 0);
876         }
877
878         event.preventDefault();
879         return done;
880     }
881
882     /**
883      * Set the selected moment's minute
884      * @param {any} event
885      * @param {'increase' | 'decrease' | number} val
886      *      'increase' -- increase minute value by 1
887      *      'decrease' -- decrease minute value by 1
888      *      number -- set minute value to val
889      * @param {HTMLInputElement} input -- optional
890      * @return {boolean}
891      * */
892     public setMinutes(event: any, val: 'increase' | 'decrease' | number, input?: HTMLInputElement): boolean {
893
894         let value;
895         if (this.value) {
896             if (this.value.length) {
897                 value = this.value[this.valueIndex];
898             } else {
899                 value = this.value;
900             }
901         } else {
902             if (this.type === 'timer') {
903                 value = new Date();
904             } else {
905                 value = null;
906             }
907         }
908
909         if (this.disabled || !value) {
910             event.preventDefault();
911             return false;
912         }
913
914         let minutes = getMinutes(value);
915         if (val === 'increase') {
916             minutes += 1;
917         } else if (val === 'decrease') {
918             minutes -= 1;
919         } else {
920             minutes = val;
921         }
922
923         if (minutes > 59) {
924             minutes = 0;
925         } else if (minutes < 0) {
926             minutes = 59;
927         }
928
929         let selectedTime = setMinutes(value, minutes);
930         let done = this.setSelectedTime(selectedTime);
931
932         // Focus the input and select its value when model updated
933         if (input) {
934             setTimeout(() => {
935                 input.focus();
936             }, 0);
937         }
938
939         event.preventDefault();
940         return done;
941     }
942
943     /**
944      * Set the selected moment's second
945      * @param {any} event
946      * @param {'increase' | 'decrease' | number} val
947      *      'increase' -- increase second value by 1
948      *      'decrease' -- decrease second value by 1
949      *      number -- set second value to val
950      * @param {HTMLInputElement} input -- optional
951      * @return {boolean}
952      * */
953     public setSeconds(event: any, val: 'increase' | 'decrease' | number, input?: HTMLInputElement): boolean {
954
955         let value;
956         if (this.value) {
957             if (this.value.length) {
958                 value = this.value[this.valueIndex];
959             } else {
960                 value = this.value;
961             }
962         } else {
963             if (this.type === 'timer') {
964                 value = new Date();
965             } else {
966                 value = null;
967             }
968         }
969
970         if (this.disabled || !value) {
971             event.preventDefault();
972             return false;
973         }
974
975         let seconds = getSeconds(value);
976         if (val === 'increase') {
977             seconds = this.secValue + 1;
978         } else if (val === 'decrease') {
979             seconds = this.secValue - 1;
980         } else {
981             seconds = val;
982         }
983
984         if (seconds > 59) {
985             seconds = 0;
986         } else if (seconds < 0) {
987             seconds = 59;
988         }
989
990         let selectedTime = setSeconds(value, seconds);
991         let done = this.setSelectedTime(selectedTime);
992
993         // Focus the input and select its value when model updated
994         if (input) {
995             setTimeout(() => {
996                 input.focus();
997             }, 0);
998         }
999
1000         event.preventDefault();
1001         return done;
1002     }
1003
1004     /**
1005      * Check if the date is selected
1006      * @param {Date} date
1007      * @return {Boolean}
1008      * */
1009     public isSelectedDay(date: Date): boolean {
1010         if (this.isSingleSelection()) {
1011             return this.value && isSameDay(this.value, date);
1012         } else if (this.isRangeSelection() && this.value && this.value.length) {
1013             if (this.value[1]) {
1014                 return (isSameDay(this.value[0], date) || isSameDay(this.value[1], date) ||
1015                     this.isDayBetween(this.value[0], this.value[1], date)) && this.isValidDay(date);
1016             } else {
1017                 return isSameDay(this.value[0], date);
1018             }
1019         } else if (this.isMultiSelection() && this.value && this.value.length) {
1020             let selected;
1021             for (let d of this.value) {
1022                 selected = isSameDay(d, date);
1023                 if (selected) {
1024                     break;
1025                 }
1026             }
1027             return selected;
1028         }
1029         return false;
1030     }
1031
1032     /**
1033      * Check if a day is between two specific days
1034      * @param {Date} start
1035      * @param {Date} end
1036      * @param {Date} day
1037      * @return {boolean}
1038      * */
1039     public isDayBetween(start: Date, end: Date, day: Date): boolean {
1040         if (start && end) {
1041             return isAfter(day, start) && isBefore(day, end);
1042         } else {
1043             return false;
1044         }
1045     }
1046
1047     /**
1048      * Check if the calendar day is a valid day
1049      * @param {Date}  date
1050      * @return {Boolean}
1051      * */
1052     public isValidDay(date: Date): boolean {
1053         let isValid = true;
1054         if (this.disabledDates && this.disabledDates.length) {
1055             for (let disabledDate of this.disabledDates) {
1056                 if (isSameDay(disabledDate, date)) {
1057                     return false;
1058                 }
1059             }
1060         }
1061
1062         if (isValid && this.disabledDays && this.disabledDays.length) {
1063             let weekdayNum = getDay(date);
1064             isValid = this.disabledDays.indexOf(weekdayNum) === -1;
1065         }
1066
1067         if (isValid && this.min) {
1068             isValid = isValid && !isBefore(date, startOfDay(this.min));
1069         }
1070
1071         if (isValid && this.max) {
1072             isValid = isValid && !isAfter(date, endOfDay(this.max));
1073         }
1074         return isValid;
1075     }
1076
1077     /**
1078      * Check if the month is current pickerMoment's month
1079      * @param {Number} monthNum
1080      * @return {Boolean}
1081      * */
1082     public isCurrentMonth(monthNum: number): boolean {
1083         return getMonth(this.pickerMoment) === monthNum;
1084     }
1085
1086     /**
1087      * Check if the year is current pickerMoment's year
1088      * @param {Number} yearNum
1089      * @return {Boolean}
1090      * */
1091     public isCurrentYear(yearNum: any): boolean {
1092         return getYear(this.pickerMoment) === yearNum||(getYear(this.pickerMoment)+"") === yearNum;
1093     }
1094
1095     /**
1096      * Change the dialog type
1097      * @param {DialogType} type
1098      * @return {void}
1099      * */
1100     public changeDialogType(type: DialogType): void {
1101         if (this.dialogType === type) {
1102             this.dialogType = DialogType.Date;
1103             return;
1104         } else {
1105             this.dialogType = type;
1106         }
1107
1108         if (this.dialogType === DialogType.Year) {
1109             this.generateYearList();
1110         }
1111     }
1112
1113     /**
1114      * Handle blur event on timer input
1115      * @param {any} event
1116      * @param {HTMLInputElement} input
1117      * @param {string} type
1118      * @param {number} modelValue
1119      * @return {void}
1120      * */
1121     public onTimerInputBlur(event: any, input: HTMLInputElement, type: string, modelValue: number): void {
1122         let val = +input.value;
1123
1124         if (this.disabled || val === modelValue) {
1125             event.preventDefault();
1126             return;
1127         }
1128
1129         let done;
1130         if (!isNaN(val)) {
1131             switch (type) {
1132                 case 'hours':
1133                     if (this.hourFormat === '24' &&
1134                         val >= 0 && val <= 23) {
1135                         done = this.setHours(event, val);
1136                     } else if (this.hourFormat === '12'
1137                         && val >= 1 && val <= 12) {
1138                         if (this.meridianValue === 'AM' && val === 12) {
1139                             val = 0;
1140                         } else if (this.meridianValue === 'PM' && val < 12) {
1141                             val = val + 12;
1142                         }
1143                         done = this.setHours(event, val);
1144                     }
1145                     break;
1146                 case 'minutes':
1147                     if (val >= 0 && val <= 59) {
1148                         done = this.setMinutes(event, val);
1149                     }
1150                     break;
1151                 case 'seconds':
1152                     if (val >= 0 && val <= 59) {
1153                         done = this.setSeconds(event, val);
1154                     }
1155                     break;
1156             }
1157         }
1158
1159         if (!done) {
1160             input.value = this.numFixedLenPipe.transform(modelValue, 2);
1161             input.focus();
1162             return;
1163         }
1164         event.preventDefault();
1165         return;
1166     }
1167
1168     /**
1169      * Set value to null
1170      * @param {any} event
1171      * @return {void}
1172      * */
1173     public clearValue(event: any): void {
1174         this.dialogClick = true;
1175         this.updateModel(null);
1176         this.updateTimer(this.value);
1177         if (this.timepicker!==undefined) {
1178             this.timepicker.settime(undefined);
1179         }
1180         this.updateFormattedValue();
1181         if(this.value!==null)
1182         {
1183         event.date=new Date(this.value);
1184         }
1185         this.onConfirm.emit(event);
1186         event.preventDefault();
1187     }
1188
1189     /**
1190      * Show the dialog
1191      * @return {void}
1192      * */
1193     private show(): void {
1194         this.alignDialog();
1195         this.dialogVisible = true;
1196         this.dialogType = DialogType.Date;
1197         this.bindDocumentClickListener();
1198         return;
1199     }
1200     private nextNav(event : any):void {
1201         if( this.dialogType===DialogType.Date|| this.dialogType===DialogType.Month)
1202         {
1203             this.nextMonth(event);
1204         }
1205         else if(this.dialogType===DialogType.Year){
1206             this.generateYearList('next');
1207         }
1208     }
1209     private prevNav(event : any):void {
1210         if( this.dialogType===DialogType.Date|| this.dialogType===DialogType.Month)
1211         {
1212             this.prevMonth(event);
1213         }
1214         else if(this.dialogType===DialogType.Year){
1215             this.generateYearList('prev');
1216         }
1217     }
1218     /**
1219      * Hide the dialog
1220      * @return {void}
1221      * */
1222     private hide(): void {
1223         this.dialogVisible = false;
1224         this.timepicker ? this.timepicker.closeProp() : 0;
1225         this.unbindDocumentClickListener();
1226         if(this.value!==null)
1227         {
1228         event["date"]=new Date(this.value);
1229         }
1230         this.onConfirm.emit(event);
1231         return;
1232     }
1233
1234     /**
1235      * Set the dialog position
1236      * @return {void}
1237      * */
1238     private alignDialog(): void {
1239         let element = this.dialogElm.nativeElement;
1240         let target = this.containerElm.nativeElement;
1241         let elementDimensions = element.offsetParent ? {
1242             width: element.offsetWidth,
1243             height: element.offsetHeight
1244         } : this.getHiddenElementDimensions(element);
1245         let targetHeight = target.offsetHeight;
1246         let targetWidth = target.offsetWidth;
1247         let targetOffset = target.getBoundingClientRect();
1248         let viewport = this.getViewport();
1249         let top, left;
1250
1251         if ((targetOffset.top + targetHeight + elementDimensions.height) > viewport.height) {
1252             top = -1 * (elementDimensions.height);
1253             if (targetOffset.top + top < 0) {
1254                 top = 0;
1255             }
1256         } else {
1257             top = targetHeight;
1258         }
1259
1260
1261         if ((targetOffset.left + elementDimensions.width) > viewport.width) {
1262             left = targetWidth - elementDimensions.width;
1263         } else {
1264             left = 0;
1265         }
1266
1267         element.style.top = top + 'px';
1268         element.style.left = left + 'px';
1269     }
1270
1271     /**
1272      * Bind click event on document
1273      * @return {void}
1274      * */
1275     private bindDocumentClickListener(): void {
1276         let firstClick = true;
1277         if (!this.documentClickListener) {
1278             this.documentClickListener = this.renderer.listen('document', 'click', () => {
1279                 if (!firstClick && !this.dialogClick) {
1280                     this.hide();
1281                 }
1282
1283                 firstClick = false;
1284                 this.dialogClick = false;
1285             });
1286         }
1287         return;
1288     }
1289
1290     /**
1291      * Unbind click event on document
1292      * @return {void}
1293      * */
1294     private unbindDocumentClickListener(): void {
1295         if (this.documentClickListener) {
1296             this.documentClickListener();
1297             this.documentClickListener = null;
1298         }
1299         return;
1300     }
1301
1302     /**
1303      * Parse a object to Date object
1304      * @param {any} val
1305      * @return {Date}
1306      * */
1307     private parseToDate(val: any): Date {
1308         if (!val) {
1309             return;
1310         }
1311
1312         let parsedVal;
1313         if (typeof val === 'string') {
1314             parsedVal = parse(val);
1315         } else {
1316             parsedVal = val;
1317         }
1318
1319         return isValid(parsedVal) ? parsedVal : null;
1320     }
1321
1322     /**
1323      * Generate the calendar days array
1324      * @return {void}
1325      * */
1326     private generateCalendar(): void {
1327
1328         if (!this.pickerMoment) {
1329             return;
1330         }
1331
1332         this.calendarDays = [];
1333         let startDateOfMonth = startOfMonth(this.pickerMoment);
1334         let startWeekdayOfMonth = getDay(startDateOfMonth);
1335
1336         let dayDiff = 0 - (startWeekdayOfMonth + (7 - this.locale.firstDayOfWeek)) % 7;
1337
1338         for (let i = 1; i < 7; i++) {
1339             let week = [];
1340             for (let j = 0; j < 7; j++) {
1341                 let date = addDays(startDateOfMonth, dayDiff);
1342                 let inOtherMonth = !isSameMonth(date, this.pickerMoment);
1343                 week.push({
1344                     date,
1345                     num: getDate(date),
1346                     today: isToday(date),
1347                     otherMonth: inOtherMonth,
1348                     hide: !this.showOtherMonths && inOtherMonth,
1349                 });
1350                 dayDiff += 1;
1351             }
1352             this.calendarDays.push(week);
1353         }
1354
1355         this.pickerMonth = this.locale.monthNames[getMonth(this.pickerMoment)];
1356         this.pickerYear = getYear(this.pickerMoment).toString();
1357     }
1358
1359     /**
1360      * Generate the calendar weekdays array
1361      * */
1362     private generateWeekDays(): void {
1363         this.calendarWeekdays = [];
1364         let dayIndex = this.locale.firstDayOfWeek;
1365         for (let i = 0; i < 7; i++) {
1366             this.calendarWeekdays.push(this.locale.dayNamesShort[dayIndex]);
1367             dayIndex = (dayIndex === 6) ? 0 : ++dayIndex;
1368         }
1369     }
1370
1371     /**
1372      * Generate the calendar month array
1373      * @return {void}
1374      * */
1375     private generateMonthList(): void {
1376         this.calendarMonths = [];
1377         let monthIndex = 0;
1378         for (let i = 0; i < 4; i++) {
1379             let months = [];
1380             for (let j = 0; j < 3; j++) {
1381                 months.push(this.locale.monthNamesShort[monthIndex]);
1382                 monthIndex += 1;
1383             }
1384             this.calendarMonths.push(months);
1385         }
1386     }
1387
1388     /**
1389      * Generate the calendar year array
1390      * @return {void}
1391      * */
1392     public generateYearList(dir?: string): void {
1393
1394         if (!this.pickerMoment) {
1395             return;
1396         }
1397         let start;
1398
1399         if (dir === 'prev') {
1400             start = +this.calendarYears[0][0] - 12;
1401             if(start<0)
1402             {
1403                 start=0;
1404             }
1405         } else if (dir === 'next') {
1406             start = +this.calendarYears[3][2] + 1;
1407         } else {
1408             start = getYear(addYears(this.pickerMoment, -4));
1409         }
1410
1411         for (let i = 0; i < 4; i++) {
1412             let years = [];
1413             for (let j = 0; j < 3; j++) {
1414                 let year = (start + i * 3 + j).toString();
1415                 years.push(year);
1416             }
1417             this.calendarYears[i] = years;
1418         }
1419         return;
1420     }
1421
1422     /**
1423      * Update the calendar
1424      * @param {Date} value
1425      * @return {void}
1426      * */
1427     private updateCalendar(value: Date): void {
1428
1429         // if the dateTime picker is only the timer,
1430         // no need to update the update Calendar.
1431         if (this.type === 'timer') {
1432             return;
1433         }
1434
1435         if (value && (!this.calendarDays || !isSameMonth(value, this.pickerMoment))) {
1436             this.pickerMoment = setMonth(this.pickerMoment, getMonth(value));
1437             this.pickerMoment = setYear(this.pickerMoment, getYear(value));
1438             this.generateCalendar();
1439         } else if (!value && !this.calendarDays) {
1440             this.generateCalendar();
1441         }
1442         return;
1443     }
1444
1445     /**
1446      * Update the timer
1447      * @param {Date} value
1448      * @return {boolean}
1449      * */
1450     private updateTimer(value: Date): boolean {
1451
1452         // if the dateTime picker is only the calendar,
1453         // no need to update the timer
1454         if (this.type === 'calendar') {
1455             return false;
1456         }
1457
1458         if (!value) {
1459             this.hourValue = null;
1460             this.minValue = null;
1461             this.secValue = null;
1462             this.mtime.hour = 0;
1463             this.mtime.minute = 0;
1464             this.mtime.second = 0;
1465             return true;
1466         }
1467         this.mtime.hour = value.getHours();
1468         this.mtime.minute = value.getMinutes();
1469         this.mtime.second = value.getSeconds();;
1470
1471         let time = value;
1472         let hours = getHours(time);
1473         if (this.hourFormat === '12') {
1474             if (hours < 12 && hours > 0) {
1475                 this.hourValue = hours;
1476                 this.meridianValue = 'AM';
1477             } else if (hours > 12) {
1478                 this.hourValue = hours - 12;
1479                 this.meridianValue = 'PM';
1480             } else if (hours === 12) {
1481                 this.hourValue = 12;
1482                 this.meridianValue = 'PM';
1483             } else if (hours === 0) {
1484                 this.hourValue = 12;
1485                 this.meridianValue = 'AM';
1486             }
1487         } else if (this.hourFormat === '24') {
1488             this.hourValue = hours;
1489         }
1490
1491         this.minValue = getMinutes(time);
1492         this.secValue = getSeconds(time);
1493         if(this.value!==undefined&&this.timepicker!==undefined)
1494         {
1495         this.timepicker.settime(new Date(this.value));
1496         }
1497         return true;
1498     }
1499
1500     /**
1501      * Update ngModel
1502      * @param {Date} value
1503      * @return {Boolean}
1504      * */
1505     private updateModel(value: Date | Date[]): boolean {
1506         this.value = value;
1507         if (this.dataType === 'date') {
1508             this.onModelChange(this.value);
1509         } else if (this.dataType === 'string') {
1510             if (this.value && this.value.length) {
1511                 let formatted = [];
1512                 for (let v of this.value) {
1513                     if (v) {
1514                         formatted.push(format(v, this.dateFormat, { locale: this.locale.dateFns }));
1515                     } else {
1516                         formatted.push(null);
1517                     }
1518                 }
1519                 this.onModelChange(formatted);
1520             } else {
1521                 this.onModelChange(format(this.value, this.dateFormat, { locale: this.locale.dateFns }));
1522             }
1523         }
1524         return true;
1525     }
1526
1527     /**
1528      * Update variable formattedValue
1529      * @return {void}
1530      * */
1531     private updateFormattedValue(): void {
1532         let formattedValue = '';
1533
1534         if (this.value) {
1535             if (this.isSingleSelection()) {
1536                 formattedValue = format(this.value, this.dateFormat, { locale: this.locale.dateFns });
1537             } else if (this.isRangeSelection()) {
1538                 let startDate = this.value[0];
1539                 let endDate = this.value[1];
1540
1541                 formattedValue = format(startDate, this.dateFormat, { locale: this.locale.dateFns });
1542
1543                 if (endDate) {
1544                     formattedValue += ' - ' + format(endDate, this.dateFormat, { locale: this.locale.dateFns });
1545                 } else {
1546                     formattedValue += ' - ' + this.dateFormat;
1547                 }
1548             } else if (this.isMultiSelection()) {
1549                 for (let i = 0; i < this.value.length; i++) {
1550                     let dateAsString = format(this.value[i], this.dateFormat, { locale: this.locale.dateFns });
1551                     formattedValue += dateAsString;
1552                     if (i !== (this.value.length - 1)) {
1553                         formattedValue += ', ';
1554                     }
1555                 }
1556             }
1557         }
1558
1559         this.formattedValue = formattedValue;
1560
1561         return;
1562     }
1563
1564     /**
1565      * Set the time
1566      * @param {Date} val
1567      * @return {boolean}
1568      * */
1569     public setSelectedTime(val: Date): boolean {
1570         let done;
1571         if (this.isValidValue(val)) {
1572             if (this.value instanceof Array) {
1573                 this.value[this.valueIndex] = val;
1574                 done = this.updateModel(this.value);
1575                 done = done && this.updateTimer(this.value[this.valueIndex]);
1576             } else {
1577                 done = this.updateModel(val);
1578                 done = done && this.updateTimer(this.value);
1579             }
1580             this.updateFormattedValue();
1581         } else {
1582             this.onInvalid.emit({ originalEvent: event, value: val });
1583             done = false;
1584         }
1585         return done;
1586     }
1587
1588     private isValidValue(value: Date): boolean {
1589         let isValid = true;
1590
1591         if (this.disabledDates && this.disabledDates.length) {
1592             for (let disabledDate of this.disabledDates) {
1593                 if (isSameDay(disabledDate, value)) {
1594                     return false;
1595                 }
1596             }
1597         }
1598
1599         if (isValid && this.disabledDays && this.disabledDays.length) {
1600             let weekdayNum = getDay(value);
1601             isValid = this.disabledDays.indexOf(weekdayNum) === -1;
1602         }
1603
1604         if (isValid && this.min) {
1605             isValid = isValid && !isBefore(value, this.min);
1606         }
1607
1608         if (isValid && this.max) {
1609             isValid = isValid && !isAfter(value, this.max);
1610         }
1611
1612         return isValid;
1613     }
1614
1615     /**
1616      * Check if the selection mode is 'single'
1617      * @return {boolean}
1618      * */
1619     private isSingleSelection(): boolean {
1620         return this.selectionMode === 'single';
1621     }
1622
1623     /**
1624      * Check if the selection mode is 'range'
1625      * @return {boolean}
1626      * */
1627     private isRangeSelection(): boolean {
1628         return this.selectionMode === 'range';
1629     }
1630
1631     /**
1632      * Check if the selection mode is 'multiple'
1633      * @return {boolean}
1634      * */
1635     private isMultiSelection(): boolean {
1636         return this.selectionMode === 'multiple';
1637     }
1638
1639     private getHiddenElementDimensions(element: any): any {
1640         let dimensions: any = {};
1641         element.style.visibility = 'hidden';
1642         element.style.display = 'block';
1643         dimensions.width = element.offsetWidth;
1644         dimensions.height = element.offsetHeight;
1645         element.style.display = 'none';
1646         element.style.visibility = 'visible';
1647
1648         return dimensions;
1649     }
1650
1651     private getViewport(): any {
1652         let win = window,
1653             d = document,
1654             e = d.documentElement,
1655             g = d.getElementsByTagName('body')[0],
1656             w = win.innerWidth || e.clientWidth || g.clientWidth,
1657             h = win.innerHeight || e.clientHeight || g.clientHeight;
1658
1659         return { width: w, height: h };
1660     }
1661     public confirm() {
1662         this.hide();
1663     }
1664     public seconds = false;
1665     public mtime: any = { hour: 0, minute: 0, second: 0 };
1666     public TimerChange(time: any) {
1667         let value;
1668         if (this.value) {
1669             if (this.value.length) {
1670                 value = this.value[this.valueIndex];
1671             } else {
1672                 value = this.value;
1673             }
1674         } else {
1675             if (this.type === 'timer') {
1676                 value = new Date();
1677             } else {
1678                 value = new Date();
1679             }
1680         }
1681
1682         if (this.disabled || !value) {
1683             event.preventDefault();
1684             return false;
1685         }
1686
1687         let minute = time.minute;
1688         let hour = time.hour;
1689         let second = time.second;
1690         this.minValue = minute;
1691         this.hourValue = hour;
1692         this.secValue = second;
1693         let selectedTime = setMinutes(value, minute);
1694         selectedTime = setHours(selectedTime, hour);
1695         selectedTime = setSeconds(selectedTime, second);
1696         let done = this.setSelectedTime(selectedTime);
1697
1698         // Focus the input and select its value when model updated
1699
1700         event.preventDefault();
1701         return done;
1702     }
1703     private mouseIn :boolean = false; 
1704     private Mouseout(event:any)
1705     {
1706         this.mouseIn = false;
1707     }
1708     private Mouseover(event:any)
1709     {
1710         this.mouseIn = true;
1711     }
1712 }