├── .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 [![Build Status](https://travis-ci.org/Recras/angular-jquery-timepicker.png?branch=master)](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 | 12 | 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 | --------------------------------------------------------------------------------