From 7bb24cbd82ee153e80eb4d72685ebd9ae572bcf0 Mon Sep 17 00:00:00 2001 From: Chris Gat Date: Sat, 24 Oct 2015 20:27:15 -0700 Subject: [PATCH] feat(timepicker): add support for seconds - Adds support for seconds option Closes #4725 --- src/timepicker/docs/readme.md | 8 + src/timepicker/test/timepicker.spec.js | 1008 +++++++++++++++--------- src/timepicker/timepicker.js | 147 +++- template/timepicker/timepicker.html | 8 + 4 files changed, 784 insertions(+), 387 deletions(-) diff --git a/src/timepicker/docs/readme.md b/src/timepicker/docs/readme.md index 4e2a231b7c..5f9b299254 100644 --- a/src/timepicker/docs/readme.md +++ b/src/timepicker/docs/readme.md @@ -19,6 +19,10 @@ All settings can be provided as attributes in the `` or globally _(Defaults: 1)_ : Number of minutes to increase or decrease when using a button. + * `second-step` + _(Defaults: 1)_ : + Number of seconds to increase or decrease when using a button. + * `show-meridian` _(Defaults: true)_ : Whether to display 12H or 24H mode. @@ -43,6 +47,10 @@ All settings can be provided as attributes in the `` or globally _(Defaults: true)_ : Shows spinner arrows above and below the inputs + * `show_seconds` + _(Defaults: false)_ : + Shows seconds input + * `min` _(Defaults: undefined)_ : Minimum time a user can select diff --git a/src/timepicker/test/timepicker.spec.js b/src/timepicker/test/timepicker.spec.js index c30a2063df..822f451999 100644 --- a/src/timepicker/test/timepicker.spec.js +++ b/src/timepicker/test/timepicker.spec.js @@ -6,24 +6,31 @@ describe('timepicker directive', function() { beforeEach(inject(function(_$compile_, _$rootScope_, _$templateCache_) { $compile = _$compile_; $rootScope = _$rootScope_; - $rootScope.time = newTime(14, 40); + $rootScope.time = newTime(14, 40, 25); $templateCache = _$templateCache_; element = $compile('')($rootScope); $rootScope.$digest(); })); - function newTime(hours, minutes) { + function newTime(hours, minutes, seconds) { + seconds = seconds ? seconds : 0; var time = new Date(); - time.setHours(hours, minutes, 0, 0); + time.setHours(hours); + time.setMinutes(minutes); + time.setSeconds(seconds); + //this is required, otherwise rollover edges cases tests will have + //time reset to dates that are off by milliseconds. + time.setMilliseconds(0); return time; } - function getTimeState(withoutMeridian) { + function getTimeState(withoutMeridian, withoutSeconds) { var inputs = element.find('input'); + var limit = withoutSeconds ? 2 : 3; var state = []; - for (var i = 0; i < 2; i ++) { + for (var i = 0; i < limit; i ++) { state.push(inputs.eq(i).val()); } if (withoutMeridian !== true) { @@ -32,8 +39,12 @@ describe('timepicker directive', function() { return state; } - function getModelState() { - return [ $rootScope.time.getHours(), $rootScope.time.getMinutes() ]; + function getModelState(withoutSeconds) { + if (withoutSeconds) { + return [$rootScope.time.getHours(), $rootScope.time.getMinutes()]; + } + + return [$rootScope.time.getHours(), $rootScope.time.getMinutes(), $rootScope.time.getSeconds()]; } function getArrow(isUp, tdIndex) { @@ -48,6 +59,10 @@ describe('timepicker directive', function() { return getArrow(isUp, 2); } + function getSecondsButton(isUp) { + return getArrow(isUp, 4); + } + function getMeridianButton() { return element.find('button').eq(0); } @@ -90,15 +105,15 @@ describe('timepicker directive', function() { return e; } - it('contains three row & three input elements', function() { + it('contains three row & four input elements', function() { expect(element.find('tr').length).toBe(3); - expect(element.find('input').length).toBe(2); + expect(element.find('input').length).toBe(3); expect(element.find('button').length).toBe(1); }); it('has initially the correct time & meridian', function() { - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); }); it('should be pristine', function() { @@ -111,19 +126,19 @@ describe('timepicker directive', function() { $rootScope.$digest(); expect($rootScope.time).toBe(null); - expect(getTimeState()).not.toEqual(['', '', '']); + expect(getTimeState()).not.toEqual(['', '', '' , '']); }); it('changes inputs when model changes value', function() { - $rootScope.time = newTime(11, 50); + $rootScope.time = newTime(11, 50, 20); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '50', 'AM']); - expect(getModelState()).toEqual([11, 50]); + expect(getTimeState()).toEqual(['11', '50', '20', 'AM']); + expect(getModelState()).toEqual([11, 50, 20]); - $rootScope.time = newTime(16, 40); + $rootScope.time = newTime(16, 40, 45); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '40', 'PM']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState()).toEqual(['04', '40', '45', 'PM']); + expect(getModelState()).toEqual([16, 40, 45]); }); it('increases / decreases hours when arrows are clicked', function() { @@ -131,16 +146,16 @@ describe('timepicker directive', function() { var down = getHoursButton(false); doClick(up); - expect(getTimeState()).toEqual(['03', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState()).toEqual(['03', '40', '25', 'PM']); + expect(getModelState()).toEqual([15, 40, 25]); doClick(down); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); doClick(down); - expect(getTimeState()).toEqual(['01', '40', 'PM']); - expect(getModelState()).toEqual([13, 40]); + expect(getTimeState()).toEqual(['01', '40', '25', 'PM']); + expect(getModelState()).toEqual([13, 40, 25]); }); it('increase / decreases minutes by default step when arrows are clicked', function() { @@ -148,16 +163,33 @@ describe('timepicker directive', function() { var down = getMinutesButton(false); doClick(up); - expect(getTimeState()).toEqual(['02', '41', 'PM']); - expect(getModelState()).toEqual([14, 41]); + expect(getTimeState()).toEqual(['02', '41', '25', 'PM']); + expect(getModelState()).toEqual([14, 41, 25]); doClick(down); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); doClick(down); - expect(getTimeState()).toEqual(['02', '39', 'PM']); - expect(getModelState()).toEqual([14, 39]); + expect(getTimeState()).toEqual(['02', '39', '25', 'PM']); + expect(getModelState()).toEqual([14, 39, 25]); + }); + + it('increase / decreases seconds by default step when arrows are clicked', function() { + var up = getSecondsButton(true); + var down = getSecondsButton(false); + + doClick(up); + expect(getTimeState()).toEqual(['02', '40', '26', 'PM']); + expect(getModelState()).toEqual([14, 40, 26]); + + doClick(down); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); + + doClick(down); + expect(getTimeState()).toEqual(['02', '40', '24', 'PM']); + expect(getModelState()).toEqual([14, 40, 24]); }); it('meridian button has correct type', function() { @@ -169,16 +201,16 @@ describe('timepicker directive', function() { var button = getMeridianButton(); doClick(button); - expect(getTimeState()).toEqual(['02', '40', 'AM']); - expect(getModelState()).toEqual([2, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'AM']); + expect(getModelState()).toEqual([2, 40, 25]); doClick(button); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); doClick(button); - expect(getTimeState()).toEqual(['02', '40', 'AM']); - expect(getModelState()).toEqual([2, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'AM']); + expect(getModelState()).toEqual([2, 40, 25]); }); it('has minutes "connected" to hours', function() { @@ -186,27 +218,55 @@ describe('timepicker directive', function() { var down = getMinutesButton(false); doClick(up, 10); - expect(getTimeState()).toEqual(['02', '50', 'PM']); - expect(getModelState()).toEqual([14, 50]); + expect(getTimeState()).toEqual(['02', '50', '25', 'PM']); + expect(getModelState()).toEqual([14, 50, 25]); doClick(up, 10); - expect(getTimeState()).toEqual(['03', '00', 'PM']); - expect(getModelState()).toEqual([15, 0]); + expect(getTimeState()).toEqual(['03', '00', '25', 'PM']); + expect(getModelState()).toEqual([15, 0, 25]); doClick(up, 10); $rootScope.$digest(); - expect(getTimeState()).toEqual(['03', '10', 'PM']); - expect(getModelState()).toEqual([15, 10]); + expect(getTimeState()).toEqual(['03', '10', '25', 'PM']); + expect(getModelState()).toEqual([15, 10, 25]); doClick(down, 10); $rootScope.$digest(); - expect(getTimeState()).toEqual(['03', '00', 'PM']); - expect(getModelState()).toEqual([15, 0]); + expect(getTimeState()).toEqual(['03', '00', '25', 'PM']); + expect(getModelState()).toEqual([15, 0, 25]); doClick(down, 10); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '50', 'PM']); - expect(getModelState()).toEqual([14, 50]); + expect(getTimeState()).toEqual(['02', '50', '25', 'PM']); + expect(getModelState()).toEqual([14, 50, 25]); + }); + + it('has seconds "connected" to minutes', function() { + var up = getSecondsButton(true); + var down = getSecondsButton(false); + + doClick(up, 15); + expect(getTimeState()).toEqual(['02', '40', '40', 'PM']); + expect(getModelState()).toEqual([14, 40, 40]); + + doClick(up, 15); + expect(getTimeState()).toEqual(['02', '40', '55', 'PM']); + expect(getModelState()).toEqual([14, 40, 55]); + + doClick(up, 15); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '41', '10', 'PM']); + expect(getModelState()).toEqual([14, 41, 10]); + + doClick(down, 15); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', '55', 'PM']); + expect(getModelState()).toEqual([14, 40, 55]); + + doClick(down, 15); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', '40', 'PM']); + expect(getModelState()).toEqual([14, 40, 40]); }); it('has hours "connected" to meridian', function() { @@ -214,261 +274,330 @@ describe('timepicker directive', function() { var down = getHoursButton(false); // AM -> PM - $rootScope.time = newTime(11, 0); + $rootScope.time = newTime(11, 0, 25); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '00', 'AM']); - expect(getModelState()).toEqual([11, 0]); + expect(getTimeState()).toEqual(['11', '00', '25', 'AM']); + expect(getModelState()).toEqual([11, 0, 25]); doClick(up); - expect(getTimeState()).toEqual(['12', '00', 'PM']); - expect(getModelState()).toEqual([12, 0]); + expect(getTimeState()).toEqual(['12', '00', '25', 'PM']); + expect(getModelState()).toEqual([12, 0, 25]); doClick(up); - expect(getTimeState()).toEqual(['01', '00', 'PM']); - expect(getModelState()).toEqual([13, 0]); + expect(getTimeState()).toEqual(['01', '00', '25', 'PM']); + expect(getModelState()).toEqual([13, 0, 25]); doClick(down); - expect(getTimeState()).toEqual(['12', '00', 'PM']); - expect(getModelState()).toEqual([12, 0]); + expect(getTimeState()).toEqual(['12', '00', '25', 'PM']); + expect(getModelState()).toEqual([12, 0, 25]); doClick(down); - expect(getTimeState()).toEqual(['11', '00', 'AM']); - expect(getModelState()).toEqual([11, 0]); + expect(getTimeState()).toEqual(['11', '00', '25', 'AM']); + expect(getModelState()).toEqual([11, 0, 25]); // PM -> AM - $rootScope.time = newTime(23, 0); + $rootScope.time = newTime(23, 0, 25); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '00', 'PM']); - expect(getModelState()).toEqual([23, 0]); + expect(getTimeState()).toEqual(['11', '00', '25', 'PM']); + expect(getModelState()).toEqual([23, 0, 25]); doClick(up); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState()).toEqual(['12', '00', '25', 'AM']); + expect(getModelState()).toEqual([0, 0, 25]); doClick(up); - expect(getTimeState()).toEqual(['01', '00', 'AM']); - expect(getModelState()).toEqual([01, 0]); + expect(getTimeState()).toEqual(['01', '00', '25', 'AM']); + expect(getModelState()).toEqual([01, 0, 25]); doClick(down); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState()).toEqual(['12', '00', '25', 'AM']); + expect(getModelState()).toEqual([0, 0, 25]); doClick(down); - expect(getTimeState()).toEqual(['11', '00', 'PM']); - expect(getModelState()).toEqual([23, 0]); + expect(getTimeState()).toEqual(['11', '00', '25', 'PM']); + expect(getModelState()).toEqual([23, 0, 25]); }); it('changes only the time part when hours change', function() { - $rootScope.time = newTime(23, 50); + $rootScope.time = newTime(23, 50, 20); $rootScope.$digest(); var date = $rootScope.time.getDate(); var up = getHoursButton(true); doClick(up); - expect(getTimeState()).toEqual(['12', '50', 'AM']); - expect(getModelState()).toEqual([0, 50]); + expect(getTimeState()).toEqual(['12', '50', '20', 'AM']); + expect(getModelState()).toEqual([0, 50, 20]); expect(date).toEqual($rootScope.time.getDate()); }); it('changes only the time part when minutes change', function() { element = $compile('')($rootScope); - $rootScope.time = newTime(0, 0); + $rootScope.time = newTime(0, 0, 0); $rootScope.$digest(); var date = $rootScope.time.getDate(); var up = getMinutesButton(true); doClick(up, 2); - expect(getTimeState()).toEqual(['12', '30', 'AM']); - expect(getModelState()).toEqual([0, 30]); + expect(getTimeState()).toEqual(['12', '30', '00', 'AM']); + expect(getModelState()).toEqual([0, 30, 0]); expect(date).toEqual($rootScope.time.getDate()); var down = getMinutesButton(false); doClick(down, 2); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState()).toEqual(['12', '00', '00', 'AM']); + expect(getModelState()).toEqual([0, 0, 0]); expect(date).toEqual($rootScope.time.getDate()); doClick(down, 2); - expect(getTimeState()).toEqual(['11', '30', 'PM']); - expect(getModelState()).toEqual([23, 30]); + expect(getTimeState()).toEqual(['11', '30', '00', 'PM']); + expect(getModelState()).toEqual([23, 30, 0]); expect(date).toEqual($rootScope.time.getDate()); }); it('responds properly on "mousewheel" events', function() { var inputs = element.find('input'); - var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1); + var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1), secondsEl = inputs.eq(2); var upMouseWheelEvent = wheelThatMouse(1); var downMouseWheelEvent = wheelThatMouse(-1); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); // UP hoursEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['03', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState()).toEqual(['03', '40', '25', 'PM']); + expect(getModelState()).toEqual([15, 40, 25]); hoursEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '40', 'PM']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState()).toEqual(['04', '40', '25', 'PM']); + expect(getModelState()).toEqual([16, 40, 25]); minutesEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '41', 'PM']); - expect(getModelState()).toEqual([16, 41]); + expect(getTimeState()).toEqual(['04', '41', '25', 'PM']); + expect(getModelState()).toEqual([16, 41, 25]); minutesEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '42', 'PM']); - expect(getModelState()).toEqual([16, 42]); + expect(getTimeState()).toEqual(['04', '42', '25', 'PM']); + expect(getModelState()).toEqual([16, 42, 25]); + + secondsEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '26', 'PM']); + expect(getModelState()).toEqual([16, 42, 26]); + + secondsEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '27', 'PM']); + expect(getModelState()).toEqual([16, 42, 27]); // DOWN + secondsEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '26', 'PM']); + expect(getModelState()).toEqual([16, 42, 26]); + + secondsEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '25', 'PM']); + expect(getModelState()).toEqual([16, 42, 25]); + minutesEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '41', 'PM']); - expect(getModelState()).toEqual([16, 41]); + expect(getTimeState()).toEqual(['04', '41', '25', 'PM']); + expect(getModelState()).toEqual([16, 41, 25]); minutesEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '40', 'PM']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState()).toEqual(['04', '40', '25', 'PM']); + expect(getModelState()).toEqual([16, 40, 25]); hoursEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['03', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState()).toEqual(['03', '40', '25', 'PM']); + expect(getModelState()).toEqual([15, 40, 25]); hoursEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); + + }); it('responds properly on "wheel" events', function() { var inputs = element.find('input'); - var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1); + var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1), secondsEl = inputs.eq(2); var upMouseWheelEvent = wheelThatOtherMouse(-1); var downMouseWheelEvent = wheelThatOtherMouse(1); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); // UP hoursEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['03', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState()).toEqual(['03', '40', '25', 'PM']); + expect(getModelState()).toEqual([15, 40, 25]); hoursEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '40', 'PM']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState()).toEqual(['04', '40', '25', 'PM']); + expect(getModelState()).toEqual([16, 40, 25]); minutesEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '41', 'PM']); - expect(getModelState()).toEqual([16, 41]); + expect(getTimeState()).toEqual(['04', '41', '25', 'PM']); + expect(getModelState()).toEqual([16, 41, 25]); minutesEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '42', 'PM']); - expect(getModelState()).toEqual([16, 42]); + expect(getTimeState()).toEqual(['04', '42', '25', 'PM']); + expect(getModelState()).toEqual([16, 42, 25]); + + secondsEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '26', 'PM']); + expect(getModelState()).toEqual([16, 42, 26]); + + secondsEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '27', 'PM']); + expect(getModelState()).toEqual([16, 42, 27]); // DOWN + secondsEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '26', 'PM']); + expect(getModelState()).toEqual([16, 42, 26]); + + secondsEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '25', 'PM']); + expect(getModelState()).toEqual([16, 42, 25]); + minutesEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '41', 'PM']); - expect(getModelState()).toEqual([16, 41]); + expect(getTimeState()).toEqual(['04', '41', '25', 'PM']); + expect(getModelState()).toEqual([16, 41, 25]); minutesEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '40', 'PM']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState()).toEqual(['04', '40', '25', 'PM']); + expect(getModelState()).toEqual([16, 40, 25]); hoursEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['03', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState()).toEqual(['03', '40', '25', 'PM']); + expect(getModelState()).toEqual([15, 40, 25]); hoursEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); }); it('responds properly on "keydown" events', function() { var inputs = element.find('input'); - var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1); + var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1), + secondsEl = inputs.eq(2); var upKeydownEvent = keydown('up'); var downKeydownEvent = keydown('down'); var leftKeydownEvent = keydown('left'); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); // UP hoursEl.trigger( upKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['03', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState()).toEqual(['03', '40', '25', 'PM']); + expect(getModelState()).toEqual([15, 40, 25]); hoursEl.trigger( upKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '40', 'PM']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState()).toEqual(['04', '40', '25', 'PM']); + expect(getModelState()).toEqual([16, 40, 25]); minutesEl.trigger( upKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '41', 'PM']); - expect(getModelState()).toEqual([16, 41]); + expect(getTimeState()).toEqual(['04', '41', '25', 'PM']); + expect(getModelState()).toEqual([16, 41, 25]); minutesEl.trigger( upKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '42', 'PM']); - expect(getModelState()).toEqual([16, 42]); + expect(getTimeState()).toEqual(['04', '42', '25', 'PM']); + expect(getModelState()).toEqual([16, 42, 25]); + + secondsEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '26', 'PM']); + expect(getModelState()).toEqual([16, 42, 26]); + + secondsEl.trigger( upKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '27', 'PM']); + expect(getModelState()).toEqual([16, 42, 27]); // DOWN + secondsEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '26', 'PM']); + expect(getModelState()).toEqual([16, 42, 26]); + + secondsEl.trigger( downKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '42', '25', 'PM']); + expect(getModelState()).toEqual([16, 42, 25]); + minutesEl.trigger( downKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '41', 'PM']); - expect(getModelState()).toEqual([16, 41]); + expect(getTimeState()).toEqual(['04', '41', '25', 'PM']); + expect(getModelState()).toEqual([16, 41, 25]); minutesEl.trigger( downKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '40', 'PM']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState()).toEqual(['04', '40', '25', 'PM']); + expect(getModelState()).toEqual([16, 40, 25]); hoursEl.trigger( downKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['03', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState()).toEqual(['03', '40', '25', 'PM']); + expect(getModelState()).toEqual([15, 40, 25]); hoursEl.trigger( downKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); // Other keydown hoursEl.trigger( leftKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); minutesEl.trigger( leftKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); + + secondsEl.trigger( leftKeydownEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); }); describe('attributes', function() { beforeEach(function() { $rootScope.hstep = 2; $rootScope.mstep = 30; - $rootScope.time = newTime(14, 0); - element = $compile('')($rootScope); + $rootScope.sstep = 30; + $rootScope.time = newTime(14, 0 , 0); + element = $compile('')($rootScope); $rootScope.$digest(); }); @@ -476,32 +605,32 @@ describe('timepicker directive', function() { var up = getHoursButton(true); var down = getHoursButton(false); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); doClick(up); - expect(getTimeState()).toEqual(['04', '00', 'PM']); - expect(getModelState()).toEqual([16, 0]); + expect(getTimeState()).toEqual(['04', '00', '00', 'PM']); + expect(getModelState()).toEqual([16, 0, 0]); doClick(down); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); doClick(down); - expect(getTimeState()).toEqual(['12', '00', 'PM']); - expect(getModelState()).toEqual([12, 0]); + expect(getTimeState()).toEqual(['12', '00', '00', 'PM']); + expect(getModelState()).toEqual([12, 0, 0]); // Change step $rootScope.hstep = 3; $rootScope.$digest(); doClick(up); - expect(getTimeState()).toEqual(['03', '00', 'PM']); - expect(getModelState()).toEqual([15, 0]); + expect(getTimeState()).toEqual(['03', '00', '00', 'PM']); + expect(getModelState()).toEqual([15, 0, 0]); doClick(down); - expect(getTimeState()).toEqual(['12', '00', 'PM']); - expect(getModelState()).toEqual([12, 0]); + expect(getTimeState()).toEqual(['12', '00', '00', 'PM']); + expect(getModelState()).toEqual([12, 0, 0]); }); it('increases / decreases minutes by configurable step', function() { @@ -509,120 +638,223 @@ describe('timepicker directive', function() { var down = getMinutesButton(false); doClick(up); - expect(getTimeState()).toEqual(['02', '30', 'PM']); - expect(getModelState()).toEqual([14, 30]); + expect(getTimeState()).toEqual(['02', '30', '00', 'PM']); + expect(getModelState()).toEqual([14, 30, 0]); doClick(up); - expect(getTimeState()).toEqual(['03', '00', 'PM']); - expect(getModelState()).toEqual([15, 0]); + expect(getTimeState()).toEqual(['03', '00', '00', 'PM']); + expect(getModelState()).toEqual([15, 0, 0]); doClick(down); - expect(getTimeState()).toEqual(['02', '30', 'PM']); - expect(getModelState()).toEqual([14, 30]); + expect(getTimeState()).toEqual(['02', '30', '00', 'PM']); + expect(getModelState()).toEqual([14, 30, 0]); doClick(down); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); // Change step $rootScope.mstep = 15; $rootScope.$digest(); doClick(up); - expect(getTimeState()).toEqual(['02', '15', 'PM']); - expect(getModelState()).toEqual([14, 15]); + expect(getTimeState()).toEqual(['02', '15', '00', 'PM']); + expect(getModelState()).toEqual([14, 15, 0]); doClick(down); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); doClick(down); - expect(getTimeState()).toEqual(['01', '45', 'PM']); - expect(getModelState()).toEqual([13, 45]); + expect(getTimeState()).toEqual(['01', '45', '00', 'PM']); + expect(getModelState()).toEqual([13, 45, 0]); }); it('responds properly on "mousewheel" events with configurable steps', function() { var inputs = element.find('input'); - var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1); + var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1), secondsEl = inputs.eq(2); var upMouseWheelEvent = wheelThatMouse(1); var downMouseWheelEvent = wheelThatMouse(-1); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); // UP hoursEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '00', 'PM']); - expect(getModelState()).toEqual([16, 0]); + expect(getTimeState()).toEqual(['04', '00', '00', 'PM']); + expect(getModelState()).toEqual([16, 0, 0]); minutesEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '30', 'PM']); - expect(getModelState()).toEqual([16, 30]); + expect(getTimeState()).toEqual(['04', '30', '00', 'PM']); + expect(getModelState()).toEqual([16, 30, 0]); + + secondsEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '30', '30', 'PM']); + expect(getModelState()).toEqual([16, 30, 30]); // DOWN + + secondsEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '30', '00', 'PM']); + expect(getModelState()).toEqual([16, 30, 0]); + minutesEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '00', 'PM']); - expect(getModelState()).toEqual([16, 0]); + expect(getTimeState()).toEqual(['04', '00', '00', 'PM']); + expect(getModelState()).toEqual([16, 0, 0]); hoursEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); }); it('responds properly on "wheel" events with configurable steps', function() { var inputs = element.find('input'); - var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1); + var hoursEl = inputs.eq(0), minutesEl = inputs.eq(1), secondsEl = inputs.eq(2); var upMouseWheelEvent = wheelThatOtherMouse(-1); var downMouseWheelEvent = wheelThatOtherMouse(1); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); // UP hoursEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '00', 'PM']); - expect(getModelState()).toEqual([16, 0]); + expect(getTimeState()).toEqual(['04', '00', '00', 'PM']); + expect(getModelState()).toEqual([16, 0, 0]); minutesEl.trigger( upMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '30', 'PM']); - expect(getModelState()).toEqual([16, 30]); + expect(getTimeState()).toEqual(['04', '30', '00', 'PM']); + expect(getModelState()).toEqual([16, 30, 0]); + + secondsEl.trigger( upMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '30', '30', 'PM']); + expect(getModelState()).toEqual([16, 30, 30]); // DOWN + + secondsEl.trigger( downMouseWheelEvent ); + $rootScope.$digest(); + expect(getTimeState()).toEqual(['04', '30', '00', 'PM']); + expect(getModelState()).toEqual([16, 30, 0]); + minutesEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '00', 'PM']); - expect(getModelState()).toEqual([16, 0]); + expect(getTimeState()).toEqual(['04', '00', '00', 'PM']); + expect(getModelState()).toEqual([16, 0, 0]); hoursEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); }); it('can handle strings as steps', function() { var upHours = getHoursButton(true); var upMinutes = getMinutesButton(true); + var upSeconds = getSecondsButton(true); - expect(getTimeState()).toEqual(['02', '00', 'PM']); - expect(getModelState()).toEqual([14, 0]); + expect(getTimeState()).toEqual(['02', '00', '00', 'PM']); + expect(getModelState()).toEqual([14, 0, 0]); $rootScope.hstep = '4'; $rootScope.mstep = '20'; + $rootScope.sstep = '20'; $rootScope.$digest(); doClick(upHours); - expect(getTimeState()).toEqual(['06', '00', 'PM']); - expect(getModelState()).toEqual([18, 0]); + expect(getTimeState()).toEqual(['06', '00', '00', 'PM']); + expect(getModelState()).toEqual([18, 0, 0]); doClick(upMinutes); - expect(getTimeState()).toEqual(['06', '20', 'PM']); - expect(getModelState()).toEqual([18, 20]); + expect(getTimeState()).toEqual(['06', '20', '00', 'PM']); + expect(getModelState()).toEqual([18, 20, 0]); + + doClick(upSeconds); + expect(getTimeState()).toEqual(['06', '20', '20', 'PM']); + expect(getModelState()).toEqual([18, 20, 20]); + + }); + + }); + + describe('without seconds mode',function(){ + beforeEach(function(){ + $rootScope.displaysSeconds = false; + $rootScope.time = newTime(14,40,35); + element = $compile('')($rootScope); + $rootScope.$digest(); + }); + + it('increases / decreases hours when arrows are clicked', function() { + var up = getHoursButton(true); + var down = getHoursButton(false); + + doClick(up); + expect(getTimeState(false, true)).toEqual(['03', '40', 'PM']); + expect(getModelState(true)).toEqual([15, 40]); + + doClick(down); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); + + doClick(down); + expect(getTimeState(false, true)).toEqual(['01', '40', 'PM']); + expect(getModelState(true)).toEqual([13, 40]); + + }); + + it('increase / decreases minutes by default step when arrows are clicked', function() { + var up = getMinutesButton(true); + var down = getMinutesButton(false); + + doClick(up); + expect(getTimeState(false, true)).toEqual(['02', '41', 'PM']); + expect(getModelState(true)).toEqual([14, 41]); + + doClick(down); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); + + doClick(down); + expect(getTimeState(false, true)).toEqual(['02', '39', 'PM']); + expect(getModelState(true)).toEqual([14, 39]); + }); + + it('has minutes "connected" to hours', function() { + + var up = getMinutesButton(true); + var down = getMinutesButton(false); + + doClick(up, 10); + expect(getTimeState(false, true)).toEqual(['02', '50', 'PM']); + expect(getModelState(true)).toEqual([14, 50]); + + doClick(up, 10); + expect(getTimeState(false, true)).toEqual(['03', '00', 'PM']); + expect(getModelState(true)).toEqual([15, 0]); + + doClick(up, 10); + $rootScope.$digest(); + expect(getTimeState(false, true)).toEqual(['03', '10', 'PM']); + expect(getModelState(true)).toEqual([15, 10]); + + doClick(down, 10); + $rootScope.$digest(); + expect(getTimeState(false, true)).toEqual(['03', '00', 'PM']); + expect(getModelState(true)).toEqual([15, 0]); + + doClick(down, 10); + $rootScope.$digest(); + expect(getTimeState(false,true)).toEqual(['02', '50', 'PM']); + expect(getModelState(true)).toEqual([14, 50]); }); }); @@ -630,34 +862,34 @@ describe('timepicker directive', function() { describe('12 / 24 hour mode', function() { beforeEach(function() { $rootScope.meridian = false; - $rootScope.time = newTime(14, 10); + $rootScope.time = newTime(14, 10, 20); element = $compile('')($rootScope); $rootScope.$digest(); }); function getMeridianTd() { - return element.find('tr').eq(1).find('td').eq(3); + return element.find('tr').eq(1).find('td').eq(5); } it('initially displays correct time when `show-meridian` is false', function() { - expect(getTimeState(true)).toEqual(['14', '10']); - expect(getModelState()).toEqual([14, 10]); + expect(getTimeState(true)).toEqual(['14', '10', '20']); + expect(getModelState()).toEqual([14, 10, 20]); expect(getMeridianTd()).toBeHidden(); }); it('toggles correctly between different modes', function() { - expect(getTimeState(true)).toEqual(['14', '10']); + expect(getTimeState(true)).toEqual(['14', '10', '20']); $rootScope.meridian = true; $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '10', 'PM']); - expect(getModelState()).toEqual([14, 10]); + expect(getTimeState()).toEqual(['02', '10', '20', 'PM']); + expect(getModelState()).toEqual([14, 10, 20]); expect(getMeridianTd()).not.toBeHidden(); $rootScope.meridian = false; $rootScope.$digest(); - expect(getTimeState(true)).toEqual(['14', '10']); - expect(getModelState()).toEqual([14, 10]); + expect(getTimeState(true)).toEqual(['14', '10', '20']); + expect(getModelState()).toEqual([14, 10, 20]); expect(getMeridianTd()).toBeHidden(); }); @@ -678,13 +910,13 @@ describe('timepicker directive', function() { })); it('displays correctly', function() { - expect(getTimeState()[2]).toBe('pm'); + expect(getTimeState()[3]).toBe('pm'); }); it('toggles correctly', function() { - $rootScope.time = newTime(2, 40); + $rootScope.time = newTime(2, 40, 20); $rootScope.$digest(); - expect(getTimeState()[2]).toBe('am'); + expect(getTimeState()[3]).toBe('am'); }); }); @@ -699,15 +931,17 @@ describe('timepicker directive', function() { var inputs = element.find('input'); expect(inputs.eq(0).attr('readonly')).toBe('readonly'); expect(inputs.eq(1).attr('readonly')).toBe('readonly'); + expect(inputs.eq(2).attr('readonly')).toBe('readonly'); }); }); - describe('setting timepickerConfig steps', function() { + describe('setting uibTimepickerConfig steps', function() { var originalConfig = {}; beforeEach(inject(function(_$compile_, _$rootScope_, uibTimepickerConfig) { angular.extend(originalConfig, uibTimepickerConfig); uibTimepickerConfig.hourStep = 2; uibTimepickerConfig.minuteStep = 10; + uibTimepickerConfig.secondStep = 10; uibTimepickerConfig.showMeridian = false; element = $compile('')($rootScope); $rootScope.$digest(); @@ -718,8 +952,8 @@ describe('timepicker directive', function() { })); it('does not affect the initial value', function() { - expect(getTimeState(true)).toEqual(['14', '40']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(true)).toEqual(['14', '40', '25']); + expect(getModelState()).toEqual([14, 40, 25]); }); it('increases / decreases hours with configured step', function() { @@ -727,12 +961,12 @@ describe('timepicker directive', function() { var down = getHoursButton(false); doClick(up, 2); - expect(getTimeState(true)).toEqual(['18', '40']); - expect(getModelState()).toEqual([18, 40]); + expect(getTimeState(true)).toEqual(['18', '40', '25']); + expect(getModelState()).toEqual([18, 40, 25]); doClick(down, 3); - expect(getTimeState(true)).toEqual(['12', '40']); - expect(getModelState()).toEqual([12, 40]); + expect(getTimeState(true)).toEqual(['12', '40', '25']); + expect(getModelState()).toEqual([12, 40, 25]); }); it('increases / decreases minutes with configured step', function() { @@ -740,16 +974,30 @@ describe('timepicker directive', function() { var down = getMinutesButton(false); doClick(up); - expect(getTimeState(true)).toEqual(['14', '50']); - expect(getModelState()).toEqual([14, 50]); + expect(getTimeState(true)).toEqual(['14', '50', '25']); + expect(getModelState()).toEqual([14, 50, 25]); doClick(down, 3); - expect(getTimeState(true)).toEqual(['14', '20']); - expect(getModelState()).toEqual([14, 20]); + expect(getTimeState(true)).toEqual(['14', '20' , '25']); + expect(getModelState()).toEqual([14, 20, 25]); }); + + it('increases / decreases seconds with configured step', function() { + var up = getSecondsButton(true); + var down = getSecondsButton(false); + + doClick(up); + expect(getTimeState(true)).toEqual(['14', '40', '35']); + expect(getModelState()).toEqual([14, 40, 35]); + + doClick(down, 3); + expect(getTimeState(true)).toEqual(['14', '40', '05']); + expect(getModelState()).toEqual([14, 40, 5]); + }); + }); - describe('setting timepickerConfig meridian labels', function() { + describe('setting uibTimepickerConfig meridian labels', function() { var originalConfig = {}; beforeEach(inject(function(_$compile_, _$rootScope_, uibTimepickerConfig) { angular.extend(originalConfig, uibTimepickerConfig); @@ -764,16 +1012,16 @@ describe('timepicker directive', function() { })); it('displays correctly', function() { - expect(getTimeState()).toEqual(['02', '40', 'μ.μ.']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'μ.μ.']); + expect(getModelState()).toEqual([14, 40, 25]); }); it('toggles correctly', function() { - $rootScope.time = newTime(2, 40); + $rootScope.time = newTime(2, 40, 20); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'π.μ.']); - expect(getModelState()).toEqual([2, 40]); + expect(getTimeState()).toEqual(['02', '40', '20', 'π.μ.']); + expect(getModelState()).toEqual([2, 40, 20]); }); }); @@ -826,33 +1074,50 @@ describe('timepicker directive', function() { return element.find('input').eq(1); } + function getSecondsInputEl() { + return element.find('input').eq(2); + } + it('has initially the correct time & meridian', function() { - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState()).toEqual(['02', '40', '25', 'PM']); + expect(getModelState()).toEqual([14, 40, 25]); }); it('updates hours & pads on input change & pads on blur', function() { var el = getHoursInputEl(); changeInputValueTo(el, 5); - expect(getTimeState()).toEqual(['5', '40', 'PM']); - expect(getModelState()).toEqual([17, 40]); + expect(getTimeState()).toEqual(['5', '40', '25', 'PM']); + expect(getModelState()).toEqual([17, 40, 25]); el.blur(); - expect(getTimeState()).toEqual(['05', '40', 'PM']); - expect(getModelState()).toEqual([17, 40]); + expect(getTimeState()).toEqual(['05', '40', '25', 'PM']); + expect(getModelState()).toEqual([17, 40, 25]); }); it('updates minutes & pads on input change & pads on blur', function() { var el = getMinutesInputEl(); changeInputValueTo(el, 9); - expect(getTimeState()).toEqual(['02', '9', 'PM']); - expect(getModelState()).toEqual([14, 9]); + expect(getTimeState()).toEqual(['02', '9', '25', 'PM']); + expect(getModelState()).toEqual([14, 9, 25]); el.blur(); - expect(getTimeState()).toEqual(['02', '09', 'PM']); - expect(getModelState()).toEqual([14, 9]); + expect(getTimeState()).toEqual(['02', '09', '25', 'PM']); + expect(getModelState()).toEqual([14, 9, 25]); + }); + + + it('updates seconds & pads on input change & pads on blur', function() { + var el = getSecondsInputEl(); + + changeInputValueTo(el, 4); + expect(getTimeState()).toEqual(['02', '40', '4', 'PM']); + expect(getModelState()).toEqual([14, 40, 4]); + + el.blur(); + expect(getTimeState()).toEqual(['02', '40', '04', 'PM']); + expect(getModelState()).toEqual([14, 40, 4]); }); it('clears model when input hours is invalid & alerts the UI', function() { @@ -866,8 +1131,8 @@ describe('timepicker directive', function() { changeInputValueTo(el, 8); el.blur(); $rootScope.$digest(); - expect(getTimeState()).toEqual(['08', '40', 'PM']); - expect(getModelState()).toEqual([20, 40]); + expect(getTimeState()).toEqual(['08', '40', '25', 'PM']); + expect(getModelState()).toEqual([20, 40, 25]); expect(el.parent().hasClass('has-error')).toBe(false); expect(element.hasClass('ng-invalid-time')).toBe(false); }); @@ -881,25 +1146,41 @@ describe('timepicker directive', function() { expect(element.hasClass('ng-invalid-time')).toBe(true); changeInputValueTo(el, 22); - expect(getTimeState()).toEqual(['02', '22', 'PM']); - expect(getModelState()).toEqual([14, 22]); + expect(getTimeState()).toEqual(['02', '22', '25', 'PM']); + expect(getModelState()).toEqual([14, 22, 25]); + expect(el.parent().hasClass('has-error')).toBe(false); + expect(element.hasClass('ng-invalid-time')).toBe(false); + }); + + + it('clears model when input seconds is invalid & alerts the UI', function() { + var el = getSecondsInputEl(); + + changeInputValueTo(el, 'pizza'); + expect($rootScope.time).toBe(null); + expect(el.parent().hasClass('has-error')).toBe(true); + expect(element.hasClass('ng-invalid-time')).toBe(true); + + changeInputValueTo(el, 13); + expect(getTimeState()).toEqual(['02', '40', '13', 'PM']); + expect(getModelState()).toEqual([14, 40, 13]); expect(el.parent().hasClass('has-error')).toBe(false); expect(element.hasClass('ng-invalid-time')).toBe(false); }); - it('leaves view alone when hours are invalid and minutes are updated', function() { + it('timepicker1 leaves view alone when hours are invalid and minutes are updated', function() { var hoursEl = getHoursInputEl(), minutesEl = getMinutesInputEl(); changeInputValueTo(hoursEl, '25'); hoursEl.blur(); $rootScope.$digest(); - expect(getTimeState()).toEqual(['25', '40', 'PM']); + expect(getTimeState()).toEqual(['25', '40', '25', 'PM']); changeInputValueTo(minutesEl, '2'); minutesEl.blur(); $rootScope.$digest(); - expect(getTimeState()).toEqual(['25', '2', 'PM']); + expect(getTimeState()).toEqual(['25', '2', '25', 'PM']); }); it('leaves view alone when minutes are invalid and hours are updated', function() { @@ -910,13 +1191,13 @@ describe('timepicker directive', function() { minutesEl.blur(); $rootScope.$digest(); expect($rootScope.time).toBe(null); - expect(getTimeState()).toEqual(['02', '61', 'PM']); + expect(getTimeState()).toEqual(['02', '61', '25', 'PM']); changeInputValueTo(hoursEl, '2'); hoursEl.blur(); $rootScope.$digest(); expect($rootScope.time).toBe(null); - expect(getTimeState()).toEqual(['2', '61', 'PM']); + expect(getTimeState()).toEqual(['2', '61', '25', 'PM']); }); it('handles 12/24H mode change', function() { @@ -933,8 +1214,8 @@ describe('timepicker directive', function() { $rootScope.meridian = false; $rootScope.$digest(); - expect(getTimeState(true)).toEqual(['16', '40']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState(true)).toEqual(['16', '40', '25']); + expect(getModelState()).toEqual([16, 40, 25]); expect(element.hasClass('ng-invalid-time')).toBe(false); }); @@ -972,10 +1253,10 @@ describe('timepicker directive', function() { }); it('should not be invalid when the model is a valid string date representation', function() { - $rootScope.time = 'September 30, 2010 15:30:00'; + $rootScope.time = 'September 30, 2010 15:30:10'; $rootScope.$digest(); expect(element.hasClass('ng-invalid-time')).toBe(false); - expect(getTimeState()).toEqual(['03', '30', 'PM']); + expect(getTimeState()).toEqual(['03', '30', '10','PM']); }); it('should be invalid when the model is not a valid string date representation', function() { @@ -1038,11 +1319,14 @@ describe('timepicker directive', function() { it('should be called when hours / minutes buttons clicked', function() { var btn1 = getHoursButton(true); var btn2 = getMinutesButton(false); + var btn3 = getSecondsButton(false); doClick(btn1, 2); doClick(btn2, 3); + doClick(btn3, 1); $rootScope.$digest(); - expect($rootScope.changeHandler.calls.count()).toBe(5); + + expect($rootScope.changeHandler.calls.count()).toBe(6); }); it('should not be called when model changes programatically', function() { @@ -1075,20 +1359,19 @@ describe('timepicker directive', function() { $rootScope.$digest(); expect(down.hasClass('disabled')).toBe(true); - doClick(down); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false,true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); hoursEl.trigger( downMouseWheelEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); hoursEl.trigger( downKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); }); it('should decrease hours when it would not result in a time earlier than min', function() { @@ -1104,18 +1387,18 @@ describe('timepicker directive', function() { expect(down.hasClass('disabled')).toBe(false); doClick(down); - expect(getTimeState()).toEqual(['01', '40', 'PM']); - expect(getModelState()).toEqual([13, 40]); + expect(getTimeState(false, true)).toEqual(['01', '40', 'PM']); + expect(getModelState(true)).toEqual([13, 40]); hoursEl.trigger(downMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '40', 'PM']); - expect(getModelState()).toEqual([12, 40]); + expect(getTimeState(false, true)).toEqual(['12', '40', 'PM']); + expect(getModelState(true)).toEqual([12, 40]); hoursEl.trigger(downKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '40', 'AM']); - expect(getModelState()).toEqual([11, 40]); + expect(getTimeState(false, true)).toEqual(['11', '40', 'AM']); + expect(getModelState(true)).toEqual([11, 40]); }); it('should not decrease minutes when it would result in a time ealier than min', function() { @@ -1131,18 +1414,18 @@ describe('timepicker directive', function() { expect(down.hasClass('disabled')).toBe(true); doClick(down); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); minutesEl.trigger(downMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); minutesEl.trigger(downKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); }); it('should decrease minutes when it would not result in a time ealier than min', function() { @@ -1158,18 +1441,18 @@ describe('timepicker directive', function() { expect(down.hasClass('disabled')).toBe(false); doClick(down); - expect(getTimeState()).toEqual(['02', '39', 'PM']); - expect(getModelState()).toEqual([14, 39]); + expect(getTimeState(false, true)).toEqual(['02', '39', 'PM']); + expect(getModelState(true)).toEqual([14, 39]); minutesEl.trigger(downMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '38', 'PM']); - expect(getModelState()).toEqual([14, 38]); + expect(getTimeState(false, true)).toEqual(['02', '38', 'PM']); + expect(getModelState(true)).toEqual([14, 38]); minutesEl.trigger(downKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '37', 'PM']); - expect(getModelState()).toEqual([14, 37]); + expect(getTimeState(false, true)).toEqual(['02', '37', 'PM']); + expect(getModelState(true)).toEqual([14, 37]); }); it('should not increase hours when time would rollover to a time earlier than min', function() { @@ -1186,18 +1469,18 @@ describe('timepicker directive', function() { expect(up.hasClass('disabled')).toBe(true); doClick(up); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); hoursEl.trigger(upMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); hoursEl.trigger(upKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); }); it('should increase hours when time would rollover to a time not earlier than min', function() { @@ -1215,24 +1498,24 @@ describe('timepicker directive', function() { expect(up.hasClass('disabled')).toBe(false); doClick(up); - expect(getTimeState()).toEqual(['12', '59', 'AM']); - expect(getModelState()).toEqual([0, 59]); + expect(getTimeState(false, true)).toEqual(['12', '59', 'AM']); + expect(getModelState(true)).toEqual([0, 59]); $rootScope.time = newTime(23, 59); $rootScope.$digest(); hoursEl.trigger(upMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '59', 'AM']); - expect(getModelState()).toEqual([0, 59]); + expect(getTimeState(false, true)).toEqual(['12', '59', 'AM']); + expect(getModelState(true)).toEqual([0, 59]); $rootScope.time = newTime(23, 59); $rootScope.$digest(); hoursEl.trigger(upKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '59', 'AM']); - expect(getModelState()).toEqual([0, 59]); + expect(getTimeState(false, true)).toEqual(['12', '59', 'AM']); + expect(getModelState(true)).toEqual([0, 59]); }); @@ -1250,18 +1533,18 @@ describe('timepicker directive', function() { expect(up.hasClass('disabled')).toBe(true); doClick(up); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); minutesEl.trigger(upMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); minutesEl.trigger(upKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); }); it('should increase minutes when time would rollover to a time not earlier than min', function() { @@ -1279,24 +1562,24 @@ describe('timepicker directive', function() { expect(up.hasClass('disabled')).toBe(false); doClick(up); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); $rootScope.time = newTime(23, 59); $rootScope.$digest(); minutesEl.trigger(upMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); $rootScope.time = newTime(23, 59); $rootScope.$digest(); minutesEl.trigger(upKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); }); it('should not change meridian when it would result a in time earlier than min', function() { @@ -1308,8 +1591,8 @@ describe('timepicker directive', function() { expect(button.hasClass('disabled')).toBe(true); doClick(button); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); }); it('should change meridian when it would not result in a time earlier than min', function() { @@ -1321,8 +1604,8 @@ describe('timepicker directive', function() { expect(button.hasClass('disabled')).toBe(false); doClick(button); - expect(getTimeState()).toEqual(['02', '40', 'AM']); - expect(getModelState()).toEqual([2, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'AM']); + expect(getModelState(true)).toEqual([2, 40]); }); it('should return invalid when the hours are changes such that the time is earlier than min', function() { @@ -1346,8 +1629,8 @@ describe('timepicker directive', function() { $rootScope.$digest(); changeInputValueTo(hoursEl, 3); - expect(getTimeState()).toEqual(['3', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState(false, true)).toEqual(['3', '40', 'PM']); + expect(getModelState(true)).toEqual([15, 40]); expect(hoursEl.parent().hasClass('has-error')).toBe(false); expect(element.hasClass('ng-invalid-time')).toBe(false); }); @@ -1373,8 +1656,8 @@ describe('timepicker directive', function() { $rootScope.$digest(); changeInputValueTo(minutesEl, 42); - expect(getTimeState()).toEqual(['02', '42', 'PM']); - expect(getModelState()).toEqual([14, 42]); + expect(getTimeState(false, true)).toEqual(['02', '42', 'PM']); + expect(getModelState(true)).toEqual([14, 42]); expect(minutesEl.parent().hasClass('has-error')).toBe(false); expect(element.hasClass('ng-invalid-time')).toBe(false); }); @@ -1385,7 +1668,7 @@ describe('timepicker directive', function() { beforeEach(inject(function($sniffer) { element = $compile('')($rootScope); $rootScope.$digest(); - changeInputValueTo = function (inputEl, value) { + changeInputValueTo = function(inputEl, value) { inputEl.val(value); inputEl.trigger($sniffer.hasEvent('input') ? 'input' : 'change'); $rootScope.$digest(); @@ -1405,18 +1688,18 @@ describe('timepicker directive', function() { expect(up.hasClass('disabled')).toBe(true); doClick(up); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); hoursEl.trigger(upMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); hoursEl.trigger(upKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); }); it('should increase hours when it would not result in a time later than max', function() { @@ -1432,18 +1715,18 @@ describe('timepicker directive', function() { expect(up.hasClass('disabled')).toBe(false); doClick(up); - expect(getTimeState()).toEqual(['03', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState(false, true)).toEqual(['03', '40', 'PM']); + expect(getModelState(true)).toEqual([15, 40]); hoursEl.trigger(upMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['04', '40', 'PM']); - expect(getModelState()).toEqual([16, 40]); + expect(getTimeState(false, true)).toEqual(['04', '40', 'PM']); + expect(getModelState(true)).toEqual([16, 40]); hoursEl.trigger(upKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['05', '40', 'PM']); - expect(getModelState()).toEqual([17, 40]); + expect(getTimeState(false, true)).toEqual(['05', '40', 'PM']); + expect(getModelState(true)).toEqual([17, 40]); }); it('should not increase minutes when it would result in a time later than max', function() { @@ -1459,18 +1742,18 @@ describe('timepicker directive', function() { expect(up.hasClass('disabled')).toBe(true); doClick(up); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); minutesEl.trigger(upMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); minutesEl.trigger(upKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); }); it('should increase minutes when it would not result in a time later than max', function() { @@ -1486,18 +1769,18 @@ describe('timepicker directive', function() { expect(up.hasClass('disabled')).toBe(false); doClick(up); - expect(getTimeState()).toEqual(['02', '41', 'PM']); - expect(getModelState()).toEqual([14, 41]); + expect(getTimeState(false, true)).toEqual(['02', '41', 'PM']); + expect(getModelState(true)).toEqual([14, 41]); minutesEl.trigger(upMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '42', 'PM']); - expect(getModelState()).toEqual([14, 42]); + expect(getTimeState(false, true)).toEqual(['02', '42', 'PM']); + expect(getModelState(true)).toEqual([14, 42]); minutesEl.trigger(upKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['02', '43', 'PM']); - expect(getModelState()).toEqual([14, 43]); + expect(getTimeState(false, true)).toEqual(['02', '43', 'PM']); + expect(getModelState(true)).toEqual([14, 43]); }); it('should not decrease hours when time would rollover to a time later than max', function() { @@ -1514,18 +1797,18 @@ describe('timepicker directive', function() { expect(down.hasClass('disabled')).toBe(true); doClick(down); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); hoursEl.trigger(downMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); hoursEl.trigger(downKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); }); it('should decrease hours when time would rollover to a time not later than max', function() { @@ -1543,24 +1826,24 @@ describe('timepicker directive', function() { expect(down.hasClass('disabled')).toBe(false); doClick(down); - expect(getTimeState()).toEqual(['11', '00', 'PM']); - expect(getModelState()).toEqual([23, 0]); + expect(getTimeState(false, true)).toEqual(['11', '00', 'PM']); + expect(getModelState(true)).toEqual([23, 0]); $rootScope.time = newTime(0, 0); $rootScope.$digest(); hoursEl.trigger(downMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '00', 'PM']); - expect(getModelState()).toEqual([23, 0]); + expect(getTimeState(false, true)).toEqual(['11', '00', 'PM']); + expect(getModelState(true)).toEqual([23, 0]); $rootScope.time = newTime(0, 0); $rootScope.$digest(); hoursEl.trigger(downKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '00', 'PM']); - expect(getModelState()).toEqual([23, 0]); + expect(getTimeState(false, true)).toEqual(['11', '00', 'PM']); + expect(getModelState(true)).toEqual([23, 0]); }); it('should not decrease minutes when time would rollover to a time later than max', function() { @@ -1577,18 +1860,18 @@ describe('timepicker directive', function() { expect(down.hasClass('disabled')).toBe(true); doClick(down); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); minutesEl.trigger(downMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); minutesEl.trigger(downKeydownEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['12', '00', 'AM']); - expect(getModelState()).toEqual([0, 0]); + expect(getTimeState(false, true)).toEqual(['12', '00', 'AM']); + expect(getModelState(true)).toEqual([0, 0]); }); it('should decrease minutes when time would rollover to a time not later than max', function() { @@ -1606,24 +1889,23 @@ describe('timepicker directive', function() { expect(down.hasClass('disabled')).toBe(false); doClick(down); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); $rootScope.time = newTime(0, 0); $rootScope.$digest(); - minutesEl.trigger(downMouseWheelEvent); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); $rootScope.time = newTime(0, 0); $rootScope.$digest(); minutesEl.trigger( downKeydownEvent ); $rootScope.$digest(); - expect(getTimeState()).toEqual(['11', '59', 'PM']); - expect(getModelState()).toEqual([23, 59]); + expect(getTimeState(false, true)).toEqual(['11', '59', 'PM']); + expect(getModelState(true)).toEqual([23, 59]); }); it('should not change meridian when it would result a in time later than max', function() { @@ -1636,8 +1918,8 @@ describe('timepicker directive', function() { expect(button.hasClass('disabled')).toBe(true); doClick(button); - expect(getTimeState()).toEqual(['02', '40', 'AM']); - expect(getModelState()).toEqual([2, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'AM']); + expect(getModelState(true)).toEqual([2, 40]); }); it('should change meridian when it would not result in a time later than max', function() { @@ -1650,8 +1932,8 @@ describe('timepicker directive', function() { expect(button.hasClass('disabled')).toBe(false); doClick(button); - expect(getTimeState()).toEqual(['02', '40', 'PM']); - expect(getModelState()).toEqual([14, 40]); + expect(getTimeState(false, true)).toEqual(['02', '40', 'PM']); + expect(getModelState(true)).toEqual([14, 40]); }); it('should return invalid when the hours are changes such that the time is later than max', function() { @@ -1675,8 +1957,8 @@ describe('timepicker directive', function() { $rootScope.$digest(); changeInputValueTo(hoursEl, 3); - expect(getTimeState()).toEqual(['3', '40', 'PM']); - expect(getModelState()).toEqual([15, 40]); + expect(getTimeState(false, true)).toEqual(['3', '40', 'PM']); + expect(getModelState(true)).toEqual([15, 40]); expect(hoursEl.parent().hasClass('has-error')).toBe(false); expect(element.hasClass('ng-invalid-time')).toBe(false); }); @@ -1702,8 +1984,8 @@ describe('timepicker directive', function() { $rootScope.$digest(); changeInputValueTo(minutesEl, 41); - expect(getTimeState()).toEqual(['02', '41', 'PM']); - expect(getModelState()).toEqual([14, 41]); + expect(getTimeState(false, true)).toEqual(['02', '41', 'PM']); + expect(getModelState(true)).toEqual([14, 41]); expect(minutesEl.parent().hasClass('has-error')).toBe(false); expect(element.hasClass('ng-invalid-time')).toBe(false); }); diff --git a/src/timepicker/timepicker.js b/src/timepicker/timepicker.js index 62bd7ccc3b..97786ec332 100644 --- a/src/timepicker/timepicker.js +++ b/src/timepicker/timepicker.js @@ -3,7 +3,9 @@ angular.module('ui.bootstrap.timepicker', []) .constant('uibTimepickerConfig', { hourStep: 1, minuteStep: 1, + secondStep: 1, showMeridian: true, + showSeconds:false, meridians: null, readonlyInput: false, mousewheel: true, @@ -28,20 +30,22 @@ angular.module('ui.bootstrap.timepicker', []) }); var hoursInputEl = inputs.eq(0), - minutesInputEl = inputs.eq(1); + minutesInputEl = inputs.eq(1), + secondsInputEl = inputs.eq(2); var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel; + if (mousewheel) { - this.setupMousewheelEvents(hoursInputEl, minutesInputEl); + this.setupMousewheelEvents(hoursInputEl, minutesInputEl,secondsInputEl); } var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys; if (arrowkeys) { - this.setupArrowkeyEvents(hoursInputEl, minutesInputEl); + this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl); } $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput; - this.setupInputEvents(hoursInputEl, minutesInputEl); + this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl); }; var hourStep = timepickerConfig.hourStep; @@ -94,6 +98,18 @@ angular.module('ui.bootstrap.timepicker', []) (decrementedSelected > selected && decrementedSelected > max); }; + $scope.noIncrementSeconds = function() { + var incrementedSelected = addSeconds(selected, secondStep); + return incrementedSelected > max || + (incrementedSelected < selected && incrementedSelected < min); + }; + + $scope.noDecrementSeconds = function() { + var decrementedSelected = addSeconds(selected, -secondStep); + return decrementedSelected < min || + (decrementedSelected > selected && decrementedSelected > max); + }; + $scope.noToggleMeridian = function() { if (selected.getHours() < 13) { return addMinutes(selected, 12 * 60) > max; @@ -102,6 +118,21 @@ angular.module('ui.bootstrap.timepicker', []) } }; + var secondStep = timepickerConfig.secondStep; + if ($attrs.secondStep) { + $scope.$parent.$watch($parse($attrs.secondStep), function(value) { + secondStep = parseInt(value, 10); + }); + } + + + $scope.showSeconds = timepickerConfig.showSeconds; + if ($attrs.showSeconds) { + $scope.$parent.$watch($parse($attrs.showSeconds), function(value) { + $scope.showSeconds = !!value; + }); + } + // 12H / 24H mode $scope.showMeridian = timepickerConfig.showMeridian; if ($attrs.showMeridian) { @@ -145,12 +176,17 @@ angular.module('ui.bootstrap.timepicker', []) return (minutes >= 0 && minutes < 60) ? minutes : undefined; } - function pad(value) { + function getSecondsFromTemplate() { + var seconds = parseInt($scope.seconds, 10); + return (seconds >= 0 && seconds < 60) ? seconds : undefined; + } + + function pad( value ) { return (angular.isDefined(value) && value.toString().length < 2) ? '0' + value : value.toString(); } // Respond on mousewheel spin - this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl) { + this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { var isScrollingUp = function(e) { if (e.originalEvent) { e = e.originalEvent; @@ -170,10 +206,15 @@ angular.module('ui.bootstrap.timepicker', []) e.preventDefault(); }); + secondsInputEl.bind('mousewheel wheel', function(e) { + $scope.$apply((isScrollingUp(e)) ? $scope.incrementSeconds() : $scope.decrementSeconds()); + e.preventDefault(); + }); + }; // Respond on up/down arrowkeys - this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl) { + this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { hoursInputEl.bind('keydown', function(e) { if (e.which === 38) { // up e.preventDefault(); @@ -197,24 +238,42 @@ angular.module('ui.bootstrap.timepicker', []) $scope.$apply(); } }); + + secondsInputEl.bind('keydown', function(e) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementSeconds(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementSeconds(); + $scope.$apply(); + } + }); }; - this.setupInputEvents = function(hoursInputEl, minutesInputEl) { + this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { if ($scope.readonlyInput) { $scope.updateHours = angular.noop; $scope.updateMinutes = angular.noop; + $scope.updateSeconds = angular.noop; return; } - var invalidate = function(invalidHours, invalidMinutes) { + var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) { ngModelCtrl.$setViewValue(null); ngModelCtrl.$setValidity('time', false); if (angular.isDefined(invalidHours)) { $scope.invalidHours = invalidHours; } + if (angular.isDefined(invalidMinutes)) { $scope.invalidMinutes = invalidMinutes; } + + if (angular.isDefined(invalidSeconds)) { + $scope.invalidSeconds = invalidSeconds; + } }; $scope.updateHours = function() { @@ -265,8 +324,29 @@ angular.module('ui.bootstrap.timepicker', []) } }); + $scope.updateSeconds = function() { + var seconds = getSecondsFromTemplate(); + + if (angular.isDefined(seconds)) { + selected.setSeconds(seconds); + refresh('s'); + } else { + invalidate(undefined, undefined, true); + } + }; + + secondsInputEl.bind('blur', function(e) { + if (!$scope.invalidSeconds && $scope.seconds < 10) { + $scope.$apply( function() { + $scope.seconds = pad($scope.seconds); + }); + } + }); + }; + + this.render = function() { var date = ngModelCtrl.$viewValue; @@ -300,10 +380,11 @@ angular.module('ui.bootstrap.timepicker', []) ngModelCtrl.$setValidity('time', true); $scope.invalidHours = false; $scope.invalidMinutes = false; + $scope.invalidSeconds = false; } function updateTemplate(keyboardChange) { - var hours = selected.getHours(), minutes = selected.getMinutes(); + var hours = selected.getHours(), minutes = selected.getMinutes(), seconds = selected.getSeconds(); if ($scope.showMeridian) { hours = (hours === 0 || hours === 12) ? 12 : hours % 12; // Convert 24 to 12 hour system @@ -313,19 +394,26 @@ angular.module('ui.bootstrap.timepicker', []) if (keyboardChange !== 'm') { $scope.minutes = pad(minutes); } + if (keyboardChange !== 's') { + $scope.seconds = pad(seconds); + } $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; } - function addMinutes(date, minutes) { - var dt = new Date(date.getTime() + minutes * 60000); - var newDate = new Date(date); - newDate.setHours(dt.getHours(), dt.getMinutes()); - return newDate; + function addSecondsToSelected(seconds) { + selected = addSeconds(selected, seconds); + refresh(); } - function addMinutesToSelected(minutes) { - selected = addMinutes(selected, minutes); - refresh(); + function addMinutes(selected, minutes) { + return addSeconds(selected, minutes*60); + } + + function addSeconds(date, seconds) { + var dt = new Date(date.getTime() + seconds * 1000); + var newDate = new Date(date); + newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds()); + return newDate; } $scope.showSpinners = angular.isDefined($attrs.showSpinners) ? @@ -333,38 +421,49 @@ angular.module('ui.bootstrap.timepicker', []) $scope.incrementHours = function() { if (!$scope.noIncrementHours()) { - addMinutesToSelected(hourStep * 60); + addSecondsToSelected(hourStep * 60 * 60); } }; $scope.decrementHours = function() { if (!$scope.noDecrementHours()) { - addMinutesToSelected(-hourStep * 60); + addSecondsToSelected(-hourStep * 60 * 60); } }; $scope.incrementMinutes = function() { if (!$scope.noIncrementMinutes()) { - addMinutesToSelected(minuteStep); + addSecondsToSelected(minuteStep * 60); } }; $scope.decrementMinutes = function() { if (!$scope.noDecrementMinutes()) { - addMinutesToSelected(-minuteStep); + addSecondsToSelected(-minuteStep * 60); + } + }; + + $scope.incrementSeconds = function() { + if (!$scope.noIncrementSeconds()) { + addSecondsToSelected(secondStep); + } + }; + + $scope.decrementSeconds = function() { + if (!$scope.noDecrementSeconds()) { + addSecondsToSelected(-secondStep); } }; $scope.toggleMeridian = function() { if (!$scope.noToggleMeridian()) { - addMinutesToSelected(12 * 60 * (selected.getHours() < 12 ? 1 : -1)); + addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60)); } }; }]) .directive('uibTimepicker', function() { return { - restrict: 'EA', require: ['uibTimepicker', '?^ngModel'], controller: 'UibTimepickerController', controllerAs: 'timepicker', diff --git a/template/timepicker/timepicker.html b/template/timepicker/timepicker.html index 1873841f49..802e5474ad 100644 --- a/template/timepicker/timepicker.html +++ b/template/timepicker/timepicker.html @@ -4,6 +4,8 @@   +   + @@ -14,12 +16,18 @@ + : + + +   +   +