├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── bower.json
├── demo
├── demo.html
└── demo.js
├── gruntFile.js
├── package.json
├── src
├── timepickerdirective.js
└── timepickerdirective.min.js
└── test
├── test.conf.js
└── timepicker.spec.js
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | before_install:
5 | - export DISPLAY=:99.0
6 | - sh -e /etc/init.d/xvfb start
7 | - npm install -g bower grunt-cli
8 | - npm install
9 | - bower install
10 | script: grunt
11 | notifications:
12 | slack:
13 | secure: E/a6I1jYCQf1OPtXSOgw35z1ehP+tA/zyd5qOx+9ItbtDj0RlQdu3R4QZrn/8SWFY9KepAZFXnYvGl69TL8kJfbLLQUbnzyZ54ZYJWqgXXA1/OkHkrCbd0c3M7cdT/XgMpmP9zl+h8o6K0Vs3TYhoT+QBDmEcqEqaD9ouU4AHE8=
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Recras
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | angular-jquery-timepicker [](https://travis-ci.org/Recras/angular-jquery-timepicker)
2 | =====================
3 |
4 | An AngularJS directive for [jquery-timepicker](https://github.com/jonthornton/jquery-timepicker)
5 |
6 | [See a demo here](http://recras.github.io/angular-jquery-timepicker/)
7 |
8 | # Requirements
9 |
10 | - AngularJS
11 | - JQuery
12 | - [jquery-timepicker](https://github.com/jonthornton/jquery-timepicker)
13 |
14 | # Usage
15 |
16 | You can use Bower or NPM to install this directive.
17 |
18 | bower install angular-jquery-timepicker
19 |
20 | or for NPM:
21 |
22 | npm install angular-jquery-timepicker
23 |
24 | Add the timepicker module as a dependency to your applicatin module:
25 |
26 | var myAppModule = angular.module('MyApp', ['ui.timepicker'])
27 |
28 |
29 | Apply the directive to your form elements. This directive expects ng-model to be a valid javascript Date object (or null).
30 |
31 |
32 |
33 | You can specify a base-date that will be used to initialize the ng-model when it is null
34 |
35 |
36 |
37 | Configure timepicker at a global level. Use the 'asMoment' to use moment.js instead of Date as the ng-model. Note: moment.js timezones will be discarded.
38 |
39 | angular.module('ui.timepicker').value('uiTimepickerConfig',{
40 | step: 5,
41 | asMoment: true
42 | });
43 |
44 |
45 | Adding custom options to timepicker.
46 |
47 | $scope.timePickerOptions = {
48 | step: 20,
49 | timeFormat: 'g:ia',
50 | appendTo: 'body'
51 | };
52 |
53 |
54 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-jquery-timepicker",
3 | "version": "0.13.1",
4 | "homepage": "https://github.com/Recras/angular-jquery-timepicker",
5 | "description": "An AngularJS directive for jQuery Timepicker",
6 | "main": "./src/timepickerdirective.js",
7 | "authors": [
8 | "https://github.com/Recras/angular-jquery-timepicker/graphs/contributors"
9 | ],
10 | "license": "MIT",
11 | "ignore": [
12 | "**/.*",
13 | "node_modules",
14 | "bower_components",
15 | "test",
16 | "tests"
17 | ],
18 | "dependencies": {
19 | "angular": ">= 1.3",
20 | "jquery-timepicker-jt": "1.2 - 1.8"
21 | },
22 | "devDependencies": {
23 | "angular-mocks": "~1.x",
24 | "moment": "~2.9.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/demo/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | click me
9 |
10 | {{ date | date:'medium' }}
11 | Increase date
12 | Increase Hour
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/demo/demo.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('testApp', ['ui.timepicker']);
2 |
3 | app.controller('TimepickerCtrl', function ($scope) {
4 | $scope.date = new Date('2014-01-01T18:00:00');
5 | $scope.options = {
6 | step: 5,
7 | timeFormat: 'H:i'
8 | };
9 |
10 | $scope.increaseDate = function(date) {
11 | date.setDate(date.getDate() + 1);
12 | };
13 |
14 | $scope.increaseHour = function(date) {
15 | date.setHours(date.getHours() + 1);
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/gruntFile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 |
3 | grunt.loadNpmTasks('grunt-karma');
4 | grunt.loadNpmTasks('grunt-contrib-jshint');
5 | grunt.loadNpmTasks('grunt-contrib-uglify');
6 |
7 | // Default task.
8 | grunt.registerTask('default', ['jshint', 'uglify', 'karma']);
9 |
10 | var testConfig = function(configFile, customOptions) {
11 | var options = { configFile: configFile, keepalive: true };
12 | var travisOptions = process.env.TRAVIS && { browsers: ['Firefox'], reporters: 'dots' };
13 | return grunt.util._.extend(options, customOptions, travisOptions);
14 | };
15 |
16 |
17 | // Project configuration.
18 | grunt.initConfig({
19 | karma: {
20 | unit: {
21 | options: testConfig('test/test.conf.js')
22 | }
23 | },
24 | jshint:{
25 | files:['src/timepickerdirective.js', 'test/**/*.js'],
26 | options:{
27 | curly:true,
28 | eqeqeq:true,
29 | immed:true,
30 | latedef:true,
31 | newcap:true,
32 | noarg:true,
33 | sub:true,
34 | boss:true,
35 | eqnull:true,
36 | globals:{}
37 | }
38 | },
39 | uglify: {
40 | dist: {
41 | files: {
42 | 'src/timepickerdirective.min.js': ['src/timepickerdirective.js']
43 | }
44 | }
45 | }
46 | });
47 |
48 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-jquery-timepicker",
3 | "version": "0.13.1",
4 | "description": "An AngularJS directive for jQuery Timepicker",
5 | "main": "./src/timepickerdirective.js",
6 | "devDependencies": {
7 | "grunt": "~0.4.2",
8 | "grunt-contrib-jshint": "~0.8.0",
9 | "grunt-contrib-uglify": "~0.3.3",
10 | "grunt-karma": "~0.6.2"
11 | },
12 | "dependencies": {},
13 | "scripts": {
14 | "test": "grunt karma"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git@github.com:Recras/angular-jquery-timepicker.git"
19 | },
20 | "author": "https://github.com/Recras/angular-jquery-timepicker/graphs/contributors",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/Recras/angular-jquery-timepicker/issues"
24 | },
25 | "homepage": "https://github.com/Recras/angular-jquery-timepicker"
26 | }
27 |
--------------------------------------------------------------------------------
/src/timepickerdirective.js:
--------------------------------------------------------------------------------
1 | /*global angular */
2 | /*
3 | Directive for jQuery UI timepicker (http://jonthornton.github.io/jquery-timepicker/)
4 |
5 | */
6 | var m = angular.module('ui.timepicker', []);
7 |
8 |
9 | m.value('uiTimepickerConfig', {
10 | 'step': 15
11 | });
12 |
13 | m.directive('uiTimepicker', ['uiTimepickerConfig', '$parse', '$window', function(uiTimepickerConfig, $parse, $window) {
14 | var moment = $window.moment;
15 |
16 | var isAMoment = function(date) {
17 | return moment !== undefined && moment.isMoment(date) && date.isValid();
18 | };
19 | var isDateOrMoment = function(date) {
20 | return date !== null && (angular.isDate(date) || isAMoment(date));
21 | };
22 |
23 | return {
24 | restrict: 'A',
25 | require: 'ngModel',
26 | scope: {
27 | ngModel: '=',
28 | baseDate: '=',
29 | uiTimepicker: '=',
30 | },
31 | priority: 1,
32 | link: function(scope, element, attrs, ngModel) {
33 | 'use strict';
34 | var config = angular.copy(uiTimepickerConfig);
35 | var asMoment = config.asMoment || false;
36 | delete config.asMoment;
37 |
38 | ngModel.$render = function() {
39 | var date = ngModel.$modelValue;
40 | if (!angular.isDefined(date)) {
41 | return;
42 | }
43 | if (date !== null && date !== '' && !isDateOrMoment(date)) {
44 | throw new Error('ng-Model value must be a Date or Moment object - currently it is a ' + typeof date + '.');
45 | }
46 | if (isAMoment(date)) {
47 | date = date.toDate();
48 | }
49 | if (!element.is(':focus') && !invalidInput()) {
50 | element.timepicker('setTime', date);
51 | }
52 | if(date === null){
53 | resetInput();
54 | }
55 | };
56 |
57 | scope.$watch('ngModel', function() {
58 | ngModel.$render();
59 | }, true);
60 |
61 | scope.$watch('uiTimepicker', function() {
62 | element.timepicker(
63 | 'option',
64 | angular.extend(
65 | config, scope.uiTimepicker ?
66 | scope.uiTimepicker :
67 | {}
68 | )
69 | );
70 | ngModel.$render();
71 | }, true);
72 |
73 | config.appendTo = config.appendTo || element.parent();
74 |
75 | element.timepicker(
76 | angular.extend(
77 | config, scope.uiTimepicker ?
78 | scope.uiTimepicker :
79 | {}
80 | )
81 | );
82 |
83 | var resetInput = function(){
84 | element.timepicker('setTime', null);
85 | };
86 |
87 | var userInput = function() {
88 | return element.val().trim();
89 | };
90 |
91 | var invalidInput = function() {
92 | return userInput() && ngModel.$modelValue === null;
93 | };
94 |
95 | element.on('$destroy', function() {
96 | element.timepicker('remove');
97 | });
98 |
99 | var asDate = function() {
100 | var baseDate = ngModel.$modelValue ? ngModel.$modelValue : scope.baseDate;
101 | return isAMoment(baseDate) ? baseDate.toDate() : baseDate;
102 | };
103 |
104 | var asMomentOrDate = function(date) {
105 | return asMoment ? moment(date) : date;
106 | };
107 |
108 | if (element.is('input')) {
109 | if(element.attr('type') === 'time') {
110 | ngModel.$parsers.unshift(function(viewValue) {
111 | var date = element.timepicker('getTime', asDate());
112 | return ("0" + date.getUTCHours()).slice(-2) + ':' + ("0" + date.getUTCMinutes()).slice(-2);
113 | });
114 | } else {
115 | ngModel.$parsers.unshift(function(viewValue) {
116 | var date = element.timepicker('getTime', asDate());
117 | return date ? asMomentOrDate(date) : date;
118 | });
119 | }
120 | ngModel.$validators.time = function(modelValue) {
121 | return (!attrs.required && !userInput()) ? true : isDateOrMoment(modelValue);
122 | };
123 | } else {
124 | element.on('changeTime', function() {
125 | scope.$evalAsync(function() {
126 | var date = element.timepicker('getTime', asDate());
127 | ngModel.$setViewValue(date);
128 | });
129 | });
130 | }
131 | }
132 | };
133 | }]);
134 |
--------------------------------------------------------------------------------
/src/timepickerdirective.min.js:
--------------------------------------------------------------------------------
1 | var m=angular.module("ui.timepicker",[]);m.value("uiTimepickerConfig",{step:15}),m.directive("uiTimepicker",["uiTimepickerConfig","$parse","$window",function(a,b,c){var d=c.moment,e=function(a){return void 0!==d&&d.isMoment(a)&&a.isValid()},f=function(a){return null!==a&&(angular.isDate(a)||e(a))};return{restrict:"A",require:"ngModel",scope:{ngModel:"=",baseDate:"=",uiTimepicker:"="},priority:1,link:function(b,c,g,h){"use strict";var i=angular.copy(a),j=i.asMoment||!1;delete i.asMoment,h.$render=function(){var a=h.$modelValue;if(angular.isDefined(a)){if(null!==a&&""!==a&&!f(a))throw new Error("ng-Model value must be a Date or Moment object - currently it is a "+typeof a+".");e(a)&&(a=a.toDate()),c.is(":focus")||m()||c.timepicker("setTime",a),null===a&&k()}},b.$watch("ngModel",function(){h.$render()},!0),b.$watch("uiTimepicker",function(){c.timepicker("option",angular.extend(i,b.uiTimepicker?b.uiTimepicker:{})),h.$render()},!0),i.appendTo=i.appendTo||c.parent(),c.timepicker(angular.extend(i,b.uiTimepicker?b.uiTimepicker:{}));var k=function(){c.timepicker("setTime",null)},l=function(){return c.val().trim()},m=function(){return l()&&null===h.$modelValue};c.on("$destroy",function(){c.timepicker("remove")});var n=function(){var a=h.$modelValue?h.$modelValue:b.baseDate;return e(a)?a.toDate():a},o=function(a){return j?d(a):a};c.is("input")?("time"===c.attr("type")?h.$parsers.unshift(function(a){var b=c.timepicker("getTime",n());return("0"+b.getUTCHours()).slice(-2)+":"+("0"+b.getUTCMinutes()).slice(-2)}):h.$parsers.unshift(function(a){var b=c.timepicker("getTime",n());return b?o(b):b}),h.$validators.time=function(a){return g.required||l()?f(a):!0}):c.on("changeTime",function(){b.$evalAsync(function(){var a=c.timepicker("getTime",n());h.$setViewValue(a)})})}}}]);
--------------------------------------------------------------------------------
/test/test.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['jasmine'],
4 | basePath : '..',
5 | files : [
6 | 'bower_components/jquery/dist/jquery.min.js',
7 | 'bower_components/angular/angular.min.js',
8 | 'bower_components/angular-mocks/angular-mocks.js',
9 | 'bower_components/jquery-timepicker-jt/jquery.timepicker.min.js',
10 | 'bower_components/moment/moment.js',
11 | 'src/timepickerdirective.min.js',
12 | 'test/*.spec.js'
13 | ],
14 | singleRun : true,
15 | browsers: ['Chrome', 'Firefox']
16 | });
17 | };
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/test/timepicker.spec.js:
--------------------------------------------------------------------------------
1 |
2 | /*global describe, beforeEach, afterEach, it, inject, expect, module, dump, $*/
3 | describe('uiTimepicker', function() {
4 | 'use strict';
5 | beforeEach(module('ui.timepicker'));
6 | describe('simple use on span element', function() {
7 | it('should not work without ngModel', function() {
8 | inject(function($compile, $rootScope) {
9 | expect(function() { $compile(" ")($rootScope); }).toThrow();
10 | });
11 | });
12 |
13 | it('should be able to get the date from the model', function() {
14 | inject(function($compile, $rootScope) {
15 | var element, aDate;
16 | aDate = new Date(2010, 12, 1, 14, 15);
17 | element = $compile(" ")($rootScope);
18 | $rootScope.$apply(function() {
19 | $rootScope.x = aDate;
20 | });
21 | expect(element.timepicker('getTime', aDate)).toEqual(aDate);
22 | });
23 | });
24 |
25 | it('should put the date in the model', function() {
26 | inject(function($compile, $rootScope) {
27 | var element, aDate;
28 | aDate = new Date(2010, 12, 1, 14, 15);
29 | element = $compile(" ")($rootScope);
30 | $rootScope.$apply(function() {
31 | $rootScope.x = aDate;
32 | });
33 | element.timepicker('setTime', aDate);
34 | expect($rootScope.x.hour).toEqual(aDate.hour);
35 | expect($rootScope.x.minute).toEqual(aDate.minute);
36 | });
37 | });
38 |
39 | });
40 |
41 | describe('simple use on input element', function() {
42 | it('should not work without ngModel', function() {
43 | inject(function($compile, $rootScope) {
44 | expect(function() { $compile(" ")($rootScope); }).toThrow();
45 | });
46 | });
47 |
48 | it('should be able to get the date from the model', function() {
49 | inject(function($compile, $rootScope) {
50 | var element, aDate;
51 | aDate = new Date(2010, 12, 1, 14, 15);
52 | element = $compile(" ")($rootScope);
53 | $rootScope.$apply(function() {
54 | $rootScope.x = aDate;
55 | });
56 | expect(element.timepicker('getTime', aDate)).toEqual(aDate);
57 | });
58 | });
59 |
60 | it('should put the date in the model', function() {
61 | inject(function($compile, $rootScope) {
62 | var element, aDate;
63 | aDate = new Date(2010, 12, 1, 14, 15);
64 | element = $compile(" ")($rootScope);
65 | $rootScope.$apply(function() {
66 | $rootScope.x = aDate;
67 | });
68 | element.timepicker('setTime', aDate);
69 | expect($rootScope.x.hour).toEqual(aDate.hour);
70 | expect($rootScope.x.minute).toEqual(aDate.minute);
71 | });
72 | });
73 |
74 | it('should mark model valid for valid user input', function() {
75 | inject(function($compile, $rootScope) {
76 | var element, aDate;
77 | aDate = new Date(2010, 12, 1, 14, 15);
78 | element = $compile(" ")($rootScope);
79 | $rootScope.$apply(function() {
80 | $rootScope.x = aDate;
81 | });
82 |
83 | element.val('05:30').trigger('input');
84 | expect(element.hasClass('ng-invalid-time')).toBe(false);
85 | });
86 | });
87 |
88 | it('should mark model invalid for invalid user input', function() {
89 | inject(function($compile, $rootScope) {
90 | var element, aDate;
91 | aDate = new Date(2010, 12, 1, 14, 15);
92 | element = $compile(" ")($rootScope);
93 | $rootScope.$apply(function() {
94 | $rootScope.x = aDate;
95 | });
96 |
97 | element.val('abcd').trigger('input');
98 | expect(element.hasClass('ng-invalid-time')).toBe(true);
99 | });
100 | });
101 |
102 | it('should mark model invalid for blank input when required', function() {
103 | inject(function($compile, $rootScope) {
104 | var element, aDate;
105 | aDate = new Date(2010, 12, 1, 14, 15);
106 | element = $compile(" ")($rootScope);
107 | $rootScope.$apply(function() {
108 | $rootScope.x = aDate;
109 | });
110 |
111 | element.val('').trigger('input');
112 | expect(element.hasClass('ng-invalid-time')).toBe(true);
113 | });
114 | });
115 |
116 | it('should mark model valid for blank input when not required', function() {
117 | inject(function($compile, $rootScope) {
118 | var element, aDate;
119 | aDate = new Date(2010, 12, 1, 14, 15);
120 | element = $compile(" ")($rootScope);
121 | $rootScope.$apply(function() {
122 | $rootScope.x = aDate;
123 | });
124 |
125 | element.val('').trigger('input');
126 | expect(element.hasClass('ng-invalid-time')).toBe(false);
127 | });
128 | });
129 |
130 | it('should be able to reset input value when model is reset to null', function() {
131 | inject(function($compile, $rootScope) {
132 | var element, aDate;
133 | aDate = new Date(2010, 12, 1, 14, 15);
134 | element = $compile(" ")($rootScope);
135 | $rootScope.$apply(function() {
136 | $rootScope.x = aDate;
137 | });
138 |
139 | $rootScope.$apply(function() {
140 | $rootScope.x = null;
141 | });
142 |
143 | expect(element.val()).toEqual('');
144 | });
145 | });
146 | });
147 |
148 | describe('when ngModel is a moment', function() {
149 | beforeEach(function () {
150 | angular.module('ui.timepicker').value('uiTimepickerConfig', {
151 | step: 5,
152 | asMoment: true
153 | });
154 | });
155 |
156 | it('should be able to get the date from the model', function() {
157 | inject(function($compile, $rootScope) {
158 | var element, aMoment;
159 | aMoment = moment("2010-12-01 07:14:00");
160 | element = $compile(" ")($rootScope);
161 | $rootScope.$apply(function() {
162 | $rootScope.x = aMoment;
163 | });
164 | expect(element.timepicker('getTime', aMoment)).toEqual(aMoment.toDate());
165 | $rootScope.$apply(function() {
166 | expect(moment.isMoment($rootScope.x)).toBeTruthy();
167 | });
168 | });
169 | });
170 | });
171 |
172 | describe('using custom options', function() {
173 | it('should work with custom options', function() {
174 | inject(function($compile, $rootScope) {
175 | var element, aDate, opts;
176 | aDate = new Date(2010, 12, 1, 14, 15);
177 | opts = {
178 | timeFormat: 'H:i',
179 | };
180 | $rootScope.$apply(function() {
181 | $rootScope.opts = opts;
182 | });
183 | element = $compile(" ")($rootScope);
184 | $rootScope.$apply(function() {
185 | $rootScope.x = aDate;
186 | });
187 | expect(element.val()).toEqual('14:15');
188 | });
189 | });
190 |
191 | it('should update the custom options when they change', function() {
192 | inject(function($compile, $rootScope) {
193 | var element, aDate, opts;
194 | aDate = new Date(2010, 12, 1, 14, 15);
195 | opts = {
196 | timeFormat: 'H:i'
197 | };
198 | element = $compile(" ")($rootScope);
199 | $rootScope.$apply(function() {
200 | $rootScope.opts = opts;
201 | });
202 | $rootScope.$apply(function() {
203 | $rootScope.x = aDate;
204 | });
205 | expect(element.val()).toEqual('14:15');
206 | opts = {
207 | timeFormat: 'H:i A'
208 | };
209 | $rootScope.$apply(function() {
210 | $rootScope.opts = opts;
211 | });
212 | expect(element.val()).toEqual('14:15 PM');
213 | });
214 | });
215 | });
216 |
217 | describe('using required', function() {
218 | it('should be valid if not required and blank', function() {
219 | inject(function($compile, $rootScope) {
220 | var element, aDate;
221 | element = $compile(" ")($rootScope);
222 | $rootScope.$apply(function() {
223 | $rootScope.x = '';
224 | });
225 | expect(element.hasClass('ng-valid')).toBe(true);
226 | expect(element.hasClass('ng-invalid')).toBe(false);
227 | });
228 | });
229 |
230 | it('should be invalid if required and blank', function() {
231 | inject(function($compile, $rootScope) {
232 | var element, aDate;
233 | element = $compile(" ")($rootScope);
234 | $rootScope.$apply(function() {
235 | $rootScope.x = '';
236 | });
237 | expect(element.hasClass('ng-valid')).toBe(false);
238 | expect(element.hasClass('ng-invalid')).toBe(true);
239 | });
240 | });
241 |
242 | it('should be valid if not required and input is cleared', function() {
243 | inject(function($compile, $rootScope) {
244 | var element, aDate;
245 | aDate = new Date(2010, 12, 1, 14, 15);
246 | element = $compile(" ")($rootScope);
247 | $rootScope.$apply(function() {
248 | $rootScope.x = aDate;
249 | });
250 |
251 | element.val('').trigger('input');
252 | expect(element.hasClass('ng-valid')).toBe(true);
253 | expect(element.hasClass('ng-invalid')).toBe(false);
254 | });
255 | });
256 |
257 | it('should be invalid if required and input is cleared', function() {
258 | inject(function($compile, $rootScope) {
259 | var element, aDate;
260 | aDate = new Date(2010, 12, 1, 14, 15);
261 | element = $compile(" ")($rootScope);
262 | $rootScope.$apply(function() {
263 | $rootScope.x = aDate;
264 | });
265 |
266 | element.val('').trigger('input');
267 | expect(element.hasClass('ng-valid')).toBe(false);
268 | expect(element.hasClass('ng-invalid')).toBe(true);
269 | });
270 | });
271 | });
272 |
273 | describe('when ngModel is undefined', function() {
274 | it('should be able to get the date from the model', function() {
275 | inject(function($compile, $rootScope) {
276 | var element, aDate;
277 | element = $compile(" ")($rootScope);
278 | $rootScope.$apply(function() {
279 | $rootScope.x = aDate;
280 | });
281 | expect(aDate).toBeUndefined();
282 | expect(element.timepicker('getTime', aDate)).toEqual(aDate);
283 | });
284 | });
285 |
286 | it('should put a new date in the model', function() {
287 | inject(function($compile, $rootScope) {
288 | var element, aDate, bDate;
289 | bDate = new Date(2010, 12, 1, 14, 15);
290 | element = $compile(" ")($rootScope);
291 |
292 | $rootScope.$apply(function() {
293 | $rootScope.x = aDate;
294 | });
295 | expect(aDate).toBeUndefined();
296 | expect(element.timepicker('getTime', aDate)).toEqual(aDate);
297 |
298 | $rootScope.$apply(function() {
299 | $rootScope.x = bDate;
300 | });
301 | element.timepicker('setTime', bDate);
302 | expect($rootScope.x.hour).toEqual(bDate.hour);
303 | expect($rootScope.x.minute).toEqual(bDate.minute);
304 | });
305 | });
306 | });
307 | });
308 |
--------------------------------------------------------------------------------