@@ -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
@@ -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="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');
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
@@ -662,6 +730,119 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
662
730
}
663
731
}
664
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
+
665
846
function dateTimeLocalInputType ( scope , element , attr , ctrl , $sniffer , $browser , $filter ) {
666
847
textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) ;
667
848
0 commit comments