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