@@ -12,7 +12,8 @@ var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\
12
12
var EMAIL_REGEXP = / ^ [ A - Z a - z 0 - 9 . _ % + - ] + @ [ A - Z a - z 0 - 9 . - ] + \. [ A - Z a - z ] { 2 , 6 } $ / ;
13
13
var NUMBER_REGEXP = / ^ \s * ( \- | \+ ) ? ( \d + | ( \d * ( \. \d * ) ) ) \s * $ / ;
14
14
var DATE_REGEXP = / ^ ( \d { 4 } ) - ( \d { 2 } ) - ( \d { 2 } ) $ / ;
15
- var DATETIMELOCAL_REGEXP = / ^ ( \d { 4 } ) - ( \d \d ) - ( \d \d ) T ( \d \d ) : ( \d \d ) : ( \d \d ) $ / ;
15
+ var DATETIMELOCAL_REGEXP = / ^ ( \d { 4 } ) - ( \d \d ) - ( \d \d ) T ( \d \d ) : ( \d \d ) $ / ;
16
+ var WEEK_REGEXP = / ^ ( \d { 4 } ) - W ( \d \d ) $ / ;
16
17
17
18
var inputType = {
18
19
@@ -164,7 +165,7 @@ var inputType = {
164
165
* @description
165
166
* HTML5 or text input with datetime validation and transformation. In browsers that do not yet support
166
167
* the HTML5 date input, a text element will be used. The text must be entered in a valid ISO-8601
167
- * local datetime format (yyyy-MM-ddTHH:mm:ss ), for example: `2010-12-28T14:57:12 `. Will also accept a valid ISO
168
+ * local datetime format (yyyy-MM-ddTHH:mm), for example: `2010-12-28T14:57`. Will also accept a valid ISO
168
169
* datetime string or Date object as model input, but will always output a Date object to the model.
169
170
*
170
171
* @param {string } ngModel Assignable angular expression to data-bind to.
@@ -183,27 +184,27 @@ var inputType = {
183
184
<doc:source>
184
185
<script>
185
186
function Ctrl($scope) {
186
- $scope.value = '2010-12-28T14:57:12 ';
187
+ $scope.value = new Date(2010, 11, 28, 14, 57); // or '2010-12-28T14:57';
187
188
}
188
189
</script>
189
190
<form name="myForm" ng-controller="Ctrl as dateCtrl">
190
- Pick a date between in 2013:
191
- <input type="date " name="input" ng-model="value"
192
- placeholder="yyyy-MM-dd " min="2013 -01-01T00:00:00 " max="2013-12-31T00:00 :00" required />
193
- <span class="error" ng-show="myForm.input.$error.required">
194
- Required!</span>
195
- <span class="error" ng-show="myForm.input.$error.datetimelocal">
196
- Not a valid date!</span>
197
- <tt>value = {{value}}</tt><br/>
198
- <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
199
- <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
200
- <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
201
- <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
191
+ Pick a date between in 2013:
192
+ <input type="datetime-local " name="input" ng-model="value"
193
+ placeholder="yyyy-MM-ddTHH:mm " min="2001 -01-01T00:00" max="2013-12-31T00:00" required />
194
+ <span class="error" ng-show="myForm.input.$error.required">
195
+ Required!</span>
196
+ <span class="error" ng-show="myForm.input.$error.datetimelocal">
197
+ Not a valid date!</span>
198
+ <tt>value = {{value}}</tt><br/>
199
+ <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
200
+ <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
201
+ <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
202
+ <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
202
203
</form>
203
204
</doc:source>
204
205
<doc:scenario>
205
206
it('should initialize to model', function() {
206
- expect(binding('value')).toEqual('2010-12-28T14:57:12 ');
207
+ expect(binding('value')).toEqual('2010-12-28T14:57');
207
208
expect(binding('myForm.input.$valid')).toEqual('true');
208
209
});
209
210
@@ -214,14 +215,81 @@ var inputType = {
214
215
});
215
216
216
217
it('should be invalid if over max', function() {
217
- input('value').enter('2015-01-01T23:59:59 ');
218
+ input('value').enter('2015-01-01T23:59');
218
219
expect(binding('value')).toEqual('');
219
220
expect(binding('myForm.input.$valid')).toEqual('false');
220
221
});
221
222
</doc:scenario>
222
223
</doc:example>
223
224
*/
224
225
'datetime-local' : dateTimeLocalInputType ,
226
+
227
+ /**
228
+ * @ngdoc inputType
229
+ * @name ng.directive:input.week
230
+ *
231
+ * @description
232
+ * HTML5 or text input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
233
+ * the HTML5 week input, a text element will be used. The text must be entered in a valid ISO-8601
234
+ * week format (yyyy-W##), for example: `2013-W02`. Will also accept a valid ISO
235
+ * week string or Date object as model input, but will always output a Date object to the model.
236
+ *
237
+ * @param {string } ngModel Assignable angular expression to data-bind to.
238
+ * @param {string= } name Property name of the form under which the control is published.
239
+ * @param {string= } min Sets the `min` validation error key if the value entered is less than `min`.
240
+ * @param {string= } max Sets the `max` validation error key if the value entered is greater than `max`.
241
+ * @param {string= } required Sets `required` validation error key if the value is not entered.
242
+ * @param {string= } ngRequired Adds `required` attribute and `required` validation constraint to
243
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
244
+ * `required` when you want to data-bind to the `required` attribute.
245
+ * @param {string= } ngChange Angular expression to be executed when input changes due to user
246
+ * interaction with the input element.
247
+ *
248
+ * @example
249
+ <doc:example>
250
+ <doc:source>
251
+ <script>
252
+ function Ctrl($scope) {
253
+ $scope.value = '2013-W01';
254
+ }
255
+ </script>
256
+ <form name="myForm" ng-controller="Ctrl as dateCtrl">
257
+ Pick a date between in 2013:
258
+ <input type="week" name="input" ng-model="value"
259
+ placeholder="YYYY-W##" min="2012-W32" max="2013-W52" required />
260
+ <span class="error" ng-show="myForm.input.$error.required">
261
+ Required!</span>
262
+ <span class="error" ng-show="myForm.input.$error.week">
263
+ Not a valid date!</span>
264
+ <tt>value = {{value}}</tt><br/>
265
+ <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
266
+ <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
267
+ <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
268
+ <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
269
+ </form>
270
+ </doc:source>
271
+ <doc:scenario>
272
+ it('should initialize to model', function() {
273
+ expect(binding('value')).toEqual('2013-W01');
274
+ expect(binding('myForm.input.$valid')).toEqual('true');
275
+ });
276
+
277
+ it('should be invalid if empty', function() {
278
+ input('value').enter('');
279
+ expect(binding('value')).toEqual('');
280
+ expect(binding('myForm.input.$valid')).toEqual('false');
281
+ });
282
+
283
+ it('should be invalid if over max', function() {
284
+ input('value').enter('2015-W01');
285
+ expect(binding('value')).toEqual('');
286
+ expect(binding('myForm.input.$valid')).toEqual('false');
287
+ });
288
+ </doc:scenario>
289
+ </doc:example>
290
+ */
291
+ 'week' : weekInputType ,
292
+
225
293
/**
226
294
* @ngdoc inputType
227
295
* @name ng.directive:input.number
@@ -669,6 +737,118 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
669
737
}
670
738
}
671
739
740
+
741
+ function weekInputType ( scope , element , attr , ctrl , $sniffer , $browser , $filter ) {
742
+ textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) ;
743
+
744
+ ctrl . $parsers . push ( function ( value ) {
745
+ if ( ctrl . $isEmpty ( value ) ) {
746
+ ctrl . $setValidity ( 'week' , true ) ;
747
+ return value ;
748
+ }
749
+
750
+ if ( WEEK_REGEXP . test ( value ) ) {
751
+ ctrl . $setValidity ( 'week' , true ) ;
752
+ return new Date ( getTime ( value ) . time ) ;
753
+ }
754
+
755
+ ctrl . $setValidity ( 'week' , false ) ;
756
+ return undefined ;
757
+ } ) ;
758
+
759
+ ctrl . $formatters . push ( function ( value ) {
760
+ if ( isDate ( value ) ) {
761
+ return $filter ( 'date' ) ( value , 'yyyy-Www' ) ;
762
+ }
763
+ return ctrl . $isEmpty ( value ) ? '' : '' + value ;
764
+ } ) ;
765
+
766
+ if ( attr . min ) {
767
+ var minValidator = function ( value ) {
768
+ var valTime = getTime ( value ) ,
769
+ minTime = getTime ( attr . min ) ;
770
+
771
+ var valid = ctrl . $isEmpty ( value ) ||
772
+ valTime . time >= minTime . time ;
773
+
774
+ ctrl . $setValidity ( 'min' , valid ) ;
775
+ return valid ? value : undefined ;
776
+ } ;
777
+
778
+ ctrl . $parsers . push ( minValidator ) ;
779
+ ctrl . $formatters . push ( minValidator ) ;
780
+ }
781
+
782
+ if ( attr . max ) {
783
+ var maxValidator = function ( value ) {
784
+ var valTime = getTime ( value ) ,
785
+ maxTime = getTime ( attr . max ) ;
786
+
787
+ var valid = ctrl . $isEmpty ( value ) ||
788
+ valTime . time <= maxTime . time ;
789
+
790
+ ctrl . $setValidity ( 'max' , valid ) ;
791
+ return valid ? value : undefined ;
792
+ } ;
793
+
794
+ ctrl . $parsers . push ( maxValidator ) ;
795
+ ctrl . $formatters . push ( maxValidator ) ;
796
+ }
797
+
798
+ function getFirstThursday ( year ) {
799
+ var d = 1 , date ;
800
+ while ( true ) {
801
+ date = new Date ( year , 0 , d ++ ) ;
802
+ if ( date . getDay ( ) === 4 ) {
803
+ return date ;
804
+ }
805
+ }
806
+ }
807
+
808
+ function getThisThursday ( date ) {
809
+ return new Date ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) + ( 4 - date . getDay ( ) ) ) ;
810
+ }
811
+
812
+ var MILLISECONDS_PER_WEEK = 6.048e8 ;
813
+
814
+ function getWeek ( date ) {
815
+ var firstThurs = getFirstThursday ( date . getFullYear ( ) ) ,
816
+ thisThurs = getThisThursday ( date ) ,
817
+ diff = + thisThurs - + firstThurs ;
818
+
819
+ return 1 + Math . round ( diff / MILLISECONDS_PER_WEEK ) ;
820
+ }
821
+
822
+ function getTime ( isoWeek ) {
823
+ if ( isDate ( isoWeek ) ) {
824
+ return {
825
+ year : isoWeek . getFullYear ( ) ,
826
+ week : getWeek ( isoWeek ) ,
827
+ time : + isoWeek
828
+ } ;
829
+ }
830
+
831
+ if ( isString ( isoWeek ) ) {
832
+ WEEK_REGEXP . lastIndex = 0 ;
833
+ var parts = WEEK_REGEXP . exec ( isoWeek ) ;
834
+ if ( parts ) {
835
+ var year = + parts [ 1 ] ,
836
+ week = + parts [ 2 ] ,
837
+ firstThurs = getFirstThursday ( year ) ,
838
+ addDays = ( week - 1 ) * 7 ;
839
+
840
+ return {
841
+ time : + new Date ( year , 0 , firstThurs . getDate ( ) + addDays ) ,
842
+ week : week ,
843
+ year : year
844
+ } ;
845
+ }
846
+ }
847
+
848
+ return NaN ;
849
+ }
850
+ }
851
+
672
852
function dateTimeLocalInputType ( scope , element , attr , ctrl , $sniffer , $browser , $filter ) {
673
853
textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) ;
674
854
@@ -689,7 +869,7 @@ function dateTimeLocalInputType(scope, element, attr, ctrl, $sniffer, $browser,
689
869
690
870
ctrl . $formatters . push ( function ( value ) {
691
871
if ( isDate ( value ) ) {
692
- return $filter ( 'date' ) ( value , 'yyyy-MM-ddTHH:mm:ss ' ) ;
872
+ return $filter ( 'date' ) ( value , 'yyyy-MM-ddTHH:mm' ) ;
693
873
}
694
874
return ctrl . $isEmpty ( value ) ? '' : '' + value ;
695
875
} ) ;
@@ -730,10 +910,9 @@ function dateTimeLocalInputType(scope, element, attr, ctrl, $sniffer, $browser,
730
910
MM = + parts [ 2 ] - 1 ,
731
911
dd = + parts [ 3 ] ,
732
912
HH = + parts [ 4 ] ,
733
- mm = + parts [ 5 ] ,
734
- ss = + parts [ 6 ] ;
913
+ mm = + parts [ 5 ] ;
735
914
736
- return + new Date ( yyyy , MM , dd , HH , mm , ss ) ;
915
+ return + new Date ( yyyy , MM , dd , HH , mm ) ;
737
916
}
738
917
739
918
return NaN ;
0 commit comments