@@ -13,6 +13,7 @@ var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-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
15
var DATETIMELOCAL_REGEXP = / ^ ( \d { 4 } ) - ( \d \d ) - ( \d \d ) T ( \d \d ) : ( \d \d ) : ( \d \d ) $ / ;
16
+ var WEEK_REGEXP = / ^ ( \d { 4 } ) - W ( \d \d ) $ / ;
16
17
17
18
var inputType = {
18
19
@@ -187,18 +188,18 @@ var inputType = {
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:ss " min="2001 -01-01T00:00:00" max="2013-12-31T00:00: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>
@@ -222,6 +223,73 @@ var inputType = {
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
0 commit comments