@@ -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 ) $ / ;
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
@@ -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), for example: `2010-12-28T14:57`. Will also accept a valid ISO
168
+ * local datetime format (yyyy-MM-ddTHH:mm:ss ), for example: `2010-12-28T14:57:12 `. 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.
@@ -180,49 +181,115 @@ var inputType = {
180
181
*
181
182
* @example
182
183
<doc:example>
183
- <doc:source>
184
- <script>
185
- function Ctrl($scope) {
186
- $scope.value = '2010-12-28T14:57';
187
- }
188
- </script>
189
- <form name="myForm" ng-controller="Ctrl as dateCtrl">
190
- Pick a date in 2013:
191
- <input type="datetime-local" name="input" ng-model="value"
192
- placeholder="yyyy-MM-ddTHH:mm" min="2013-01-01T00:00" max="2013-12-31T00: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/>
202
- </form>
203
- </doc:source>
204
- <doc:scenario>
205
- it('should initialize to model', function() {
206
- expect(binding('value')).toEqual('2010-12-28T14:57');
184
+ <doc:source>
185
+ <script>
186
+ function Ctrl($scope) {
187
+ $scope.value = '2010-12-28T14:57:12';
188
+ }
189
+ </script>
190
+ <form name="myForm" ng-controller="Ctrl as dateCtrl">
191
+ Pick a date between in 2013:
192
+ <input type="datetime-local" name="input" ng-model="value"
193
+ placeholder="yyyy-MM-dd" min="2013-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/>
203
+ </form>
204
+ </doc:source>
205
+ <doc:scenario>
206
+ it('should initialize to model', function() {
207
+ expect(binding('value')).toEqual('2010-12-28T14:57');
208
+ expect(binding('myForm.input.$valid')).toEqual('true');
209
+ });
210
+
211
+ it('should be invalid if empty', function() {
212
+ input('value').enter('');
213
+ expect(binding('value')).toEqual('');
214
+ expect(binding('myForm.input.$valid')).toEqual('false');
215
+ });
216
+
217
+ it('should be invalid if over max', function() {
218
+ input('value').enter('2015-01-01T23:59');
219
+ expect(binding('value')).toEqual('');
220
+ expect(binding('myForm.input.$valid')).toEqual('false');
221
+ });
222
+ </doc:scenario>
223
+ </doc:example>
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="date" name="input" ng-model="value"
259
+ placeholder="yyyy-MM-dd" 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');
207
274
expect(binding('myForm.input.$valid')).toEqual('true');
208
275
});
209
276
210
- it('should be invalid if empty', function() {
277
+ it('should be invalid if empty', function() {
211
278
input('value').enter('');
212
279
expect(binding('value')).toEqual('');
213
280
expect(binding('myForm.input.$valid')).toEqual('false');
214
281
});
215
282
216
- it('should be invalid if over max', function() {
217
- input('value').enter('2015-01-01T23:59 ');
283
+ it('should be invalid if over max', function() {
284
+ input('value').enter('2015-W01 ');
218
285
expect(binding('value')).toEqual('');
219
286
expect(binding('myForm.input.$valid')).toEqual('false');
220
287
});
221
- </doc:scenario>
288
+ </doc:scenario>
222
289
</doc:example>
223
290
*/
224
- 'datetime-local ' : dateTimeLocalInputType ,
225
-
291
+ 'week ' : weekInputType ,
292
+
226
293
/**
227
294
* @ngdoc inputType
228
295
* @name ng.directive:input.number
@@ -663,6 +730,119 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
663
730
}
664
731
}
665
732
733
+
734
+ function weekInputType ( scope , element , attr , ctrl , $sniffer , $browser , $filter ) {
735
+ textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) ;
736
+
737
+ ctrl . $parsers . push ( function ( value ) {
738
+ if ( ctrl . $isEmpty ( value ) ) {
739
+ ctrl . $setValidity ( 'week' , true ) ;
740
+ return value ;
741
+ }
742
+
743
+ if ( WEEK_REGEXP . test ( value ) ) {
744
+ ctrl . $setValidity ( 'week' , true ) ;
745
+ return new Date ( getTime ( value ) . time ) ;
746
+ }
747
+
748
+ ctrl . $setValidity ( 'week' , false ) ;
749
+ return undefined ;
750
+ } ) ;
751
+
752
+ ctrl . $formatters . push ( function ( value ) {
753
+ if ( isDate ( value ) ) {
754
+ return $filter ( 'date' ) ( value , 'yyyy-Www' ) ;
755
+ }
756
+ return ctrl . $isEmpty ( value ) ? '' : '' + value ;
757
+ } ) ;
758
+
759
+ if ( attr . min ) {
760
+ var minValidator = function ( value ) {
761
+ var valTime = getTime ( value ) ,
762
+ minTime = getTime ( attr . min ) ;
763
+
764
+ var valid = ctrl . $isEmpty ( value ) ||
765
+ valTime . time >= minTime . time ;
766
+
767
+ ctrl . $setValidity ( 'min' , valid ) ;
768
+ return valid ? value : undefined ;
769
+ } ;
770
+
771
+ ctrl . $parsers . push ( minValidator ) ;
772
+ ctrl . $formatters . push ( minValidator ) ;
773
+ }
774
+
775
+ if ( attr . max ) {
776
+ var maxValidator = function ( value ) {
777
+ debugger ;
778
+ var valTime = getTime ( value ) ,
779
+ maxTime = getTime ( attr . max ) ;
780
+
781
+ var valid = ctrl . $isEmpty ( value ) ||
782
+ valTime . time <= maxTime . time ;
783
+
784
+ ctrl . $setValidity ( 'max' , valid ) ;
785
+ return valid ? value : undefined ;
786
+ } ;
787
+
788
+ ctrl . $parsers . push ( maxValidator ) ;
789
+ ctrl . $formatters . push ( maxValidator ) ;
790
+ }
791
+
792
+ function getFirstThursday ( year ) {
793
+ var d = 1 , date ;
794
+ while ( true ) {
795
+ date = new Date ( year , 0 , d ++ ) ;
796
+ if ( date . getDay ( ) === 4 ) {
797
+ return date ;
798
+ }
799
+ }
800
+ }
801
+
802
+ function getThisThursday ( date ) {
803
+ return new Date ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) + ( 4 - date . getDay ( ) ) ) ;
804
+ }
805
+
806
+ var MILLISECONDS_PER_WEEK = 6.048e8 ;
807
+
808
+ function getWeek ( date ) {
809
+ var firstThurs = getFirstThursday ( date . getFullYear ( ) ) ,
810
+ thisThurs = getThisThursday ( date ) ,
811
+ diff = + thisThurs - + firstThurs ;
812
+
813
+ return 1 + Math . round ( diff / MILLISECONDS_PER_WEEK ) ;
814
+ }
815
+
816
+ function getTime ( isoWeek ) {
817
+ if ( isDate ( isoWeek ) ) {
818
+ return {
819
+ year : isoWeek . getFullYear ( ) ,
820
+ week : getWeek ( isoWeek ) ,
821
+ time : + isoWeek
822
+ } ;
823
+ }
824
+
825
+ if ( isString ( isoWeek ) ) {
826
+ WEEK_REGEXP . lastIndex = 0 ;
827
+ var parts = WEEK_REGEXP . exec ( isoWeek ) ;
828
+ if ( parts ) {
829
+ var year = + parts [ 1 ] ,
830
+ week = + parts [ 2 ] ,
831
+ firstThurs = getFirstThursday ( year ) ,
832
+ addDays = ( week - 1 ) * 7 ;
833
+
834
+ return {
835
+ time : + new Date ( year , 0 , firstThurs . getDate ( ) + addDays ) ,
836
+ week : week ,
837
+ year : year
838
+ } ;
839
+ }
840
+ }
841
+
842
+ return NaN ;
843
+ }
844
+ }
845
+
666
846
function dateTimeLocalInputType ( scope , element , attr , ctrl , $sniffer , $browser , $filter ) {
667
847
textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) ;
668
848
@@ -683,7 +863,7 @@ function dateTimeLocalInputType(scope, element, attr, ctrl, $sniffer, $browser,
683
863
684
864
ctrl . $formatters . push ( function ( value ) {
685
865
if ( isDate ( value ) ) {
686
- return $filter ( 'date' ) ( value , 'yyyy-MM-ddTHH:mm' ) ;
866
+ return $filter ( 'date' ) ( value , 'yyyy-MM-ddTHH:mm:ss ' ) ;
687
867
}
688
868
return ctrl . $isEmpty ( value ) ? '' : '' + value ;
689
869
} ) ;
@@ -724,9 +904,10 @@ function dateTimeLocalInputType(scope, element, attr, ctrl, $sniffer, $browser,
724
904
MM = + parts [ 2 ] - 1 ,
725
905
dd = + parts [ 3 ] ,
726
906
HH = + parts [ 4 ] ,
727
- mm = + parts [ 5 ] ;
907
+ mm = + parts [ 5 ] ,
908
+ ss = + parts [ 6 ] ;
728
909
729
- return + new Date ( yyyy , MM , dd , HH , mm ) ;
910
+ return + new Date ( yyyy , MM , dd , HH , mm , ss ) ;
730
911
}
731
912
732
913
return NaN ;
0 commit comments