@@ -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 = '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,117 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
669
737
}
670
738
}
671
739
740
+ function weekInputType ( scope , element , attr , ctrl , $sniffer , $browser , $filter ) {
741
+ textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) ;
742
+
743
+ ctrl . $parsers . push ( function ( value ) {
744
+ if ( ctrl . $isEmpty ( value ) ) {
745
+ ctrl . $setValidity ( 'week' , true ) ;
746
+ return value ;
747
+ }
748
+
749
+ if ( WEEK_REGEXP . test ( value ) ) {
750
+ ctrl . $setValidity ( 'week' , true ) ;
751
+ return new Date ( getTime ( value ) . time ) ;
752
+ }
753
+
754
+ ctrl . $setValidity ( 'week' , false ) ;
755
+ return undefined ;
756
+ } ) ;
757
+
758
+ ctrl . $formatters . push ( function ( value ) {
759
+ if ( isDate ( value ) ) {
760
+ return $filter ( 'date' ) ( value , 'yyyy-Www' ) ;
761
+ }
762
+ return ctrl . $isEmpty ( value ) ? '' : '' + value ;
763
+ } ) ;
764
+
765
+ if ( attr . min ) {
766
+ var minValidator = function ( value ) {
767
+ var valTime = getTime ( value ) ,
768
+ minTime = getTime ( attr . min ) ;
769
+
770
+ var valid = ctrl . $isEmpty ( value ) ||
771
+ valTime . time >= minTime . time ;
772
+
773
+ ctrl . $setValidity ( 'min' , valid ) ;
774
+ return valid ? value : undefined ;
775
+ } ;
776
+
777
+ ctrl . $parsers . push ( minValidator ) ;
778
+ ctrl . $formatters . push ( minValidator ) ;
779
+ }
780
+
781
+ if ( attr . max ) {
782
+ var maxValidator = function ( value ) {
783
+ var valTime = getTime ( value ) ,
784
+ maxTime = getTime ( attr . max ) ;
785
+
786
+ var valid = ctrl . $isEmpty ( value ) ||
787
+ valTime . time <= maxTime . time ;
788
+
789
+ ctrl . $setValidity ( 'max' , valid ) ;
790
+ return valid ? value : undefined ;
791
+ } ;
792
+
793
+ ctrl . $parsers . push ( maxValidator ) ;
794
+ ctrl . $formatters . push ( maxValidator ) ;
795
+ }
796
+
797
+ function getFirstThursday ( year ) {
798
+ var d = 1 , date ;
799
+ while ( true ) {
800
+ date = new Date ( year , 0 , d ++ ) ;
801
+ if ( date . getDay ( ) === 4 ) {
802
+ return date ;
803
+ }
804
+ }
805
+ }
806
+
807
+ function getThisThursday ( date ) {
808
+ return new Date ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) + ( 4 - date . getDay ( ) ) ) ;
809
+ }
810
+
811
+ var MILLISECONDS_PER_WEEK = 6.048e8 ;
812
+
813
+ function getWeek ( date ) {
814
+ var firstThurs = getFirstThursday ( date . getFullYear ( ) ) ,
815
+ thisThurs = getThisThursday ( date ) ,
816
+ diff = + thisThurs - + firstThurs ;
817
+
818
+ return 1 + Math . round ( diff / MILLISECONDS_PER_WEEK ) ;
819
+ }
820
+
821
+ function getTime ( isoWeek ) {
822
+ if ( isDate ( isoWeek ) ) {
823
+ return {
824
+ year : isoWeek . getFullYear ( ) ,
825
+ week : getWeek ( isoWeek ) ,
826
+ time : + isoWeek
827
+ } ;
828
+ }
829
+
830
+ if ( isString ( isoWeek ) ) {
831
+ WEEK_REGEXP . lastIndex = 0 ;
832
+ var parts = WEEK_REGEXP . exec ( isoWeek ) ;
833
+ if ( parts ) {
834
+ var year = + parts [ 1 ] ,
835
+ week = + parts [ 2 ] ,
836
+ firstThurs = getFirstThursday ( year ) ,
837
+ addDays = ( week - 1 ) * 7 ;
838
+
839
+ return {
840
+ time : + new Date ( year , 0 , firstThurs . getDate ( ) + addDays ) ,
841
+ week : week ,
842
+ year : year
843
+ } ;
844
+ }
845
+ }
846
+
847
+ return NaN ;
848
+ }
849
+ }
850
+
672
851
function dateTimeLocalInputType ( scope , element , attr , ctrl , $sniffer , $browser , $filter ) {
673
852
textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) ;
674
853
@@ -689,7 +868,7 @@ function dateTimeLocalInputType(scope, element, attr, ctrl, $sniffer, $browser,
689
868
690
869
ctrl . $formatters . push ( function ( value ) {
691
870
if ( isDate ( value ) ) {
692
- return $filter ( 'date' ) ( value , 'yyyy-MM-ddTHH:mm:ss ' ) ;
871
+ return $filter ( 'date' ) ( value , 'yyyy-MM-ddTHH:mm' ) ;
693
872
}
694
873
return ctrl . $isEmpty ( value ) ? '' : '' + value ;
695
874
} ) ;
@@ -730,10 +909,9 @@ function dateTimeLocalInputType(scope, element, attr, ctrl, $sniffer, $browser,
730
909
MM = + parts [ 2 ] - 1 ,
731
910
dd = + parts [ 3 ] ,
732
911
HH = + parts [ 4 ] ,
733
- mm = + parts [ 5 ] ,
734
- ss = + parts [ 6 ] ;
912
+ mm = + parts [ 5 ] ;
735
913
736
- return + new Date ( yyyy , MM , dd , HH , mm , ss ) ;
914
+ return + new Date ( yyyy , MM , dd , HH , mm ) ;
737
915
}
738
916
739
917
return NaN ;
0 commit comments