35
35
* @function
36
36
*
37
37
* @description
38
- * Formats a number as a currency (ie $1,234.56).
38
+ * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
39
+ * symbol for current locale is used.
39
40
*
40
41
* @param {number } amount Input to filter.
42
+ * @param {string= } symbol Currency symbol or identifier to be displayed.
41
43
* @returns {string } Formated number.
42
44
*
43
45
* @css ng-format-negative
47
49
<doc:example>
48
50
<doc:source>
49
51
<input type="text" name="amount" value="1234.56"/> <br/>
50
- {{amount | currency}}
52
+ default currency symbol ($): {{amount | currency}}<br/>
53
+ custom currency identifier (USD$): {{amount | currency:"USD$"}}
51
54
</doc:source>
52
55
<doc:scenario>
53
56
it('should init with 1234.56', function(){
54
57
expect(binding('amount | currency')).toBe('$1,234.56');
58
+ expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56');
55
59
});
56
60
it('should update', function(){
57
61
input('amount').enter('-1234');
58
- expect(binding('amount | currency')).toBe('$-1,234.00');
62
+ expect(binding('amount | currency')).toBe('($1,234.00)');
63
+ expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)');
59
64
expect(element('.doc-example-live .ng-binding').attr('className')).
60
65
toMatch(/ng-format-negative/);
61
66
});
62
67
</doc:scenario>
63
68
</doc:example>
64
69
*/
65
- angularFilter . currency = function ( amount ) {
70
+ angularFilter . currency = function ( amount , currencySymbol ) {
66
71
this . $element . toggleClass ( 'ng-format-negative' , amount < 0 ) ;
67
- return '$' + angularFilter . number . apply ( this , [ amount , 2 ] ) ;
72
+ if ( isUndefined ( currencySymbol ) ) currencySymbol = NUMBER_FORMATS . CURRENCY_SYM ;
73
+ return formatNumber ( amount , 2 , 1 ) . replace ( / \u00A4 / g, currencySymbol ) ;
68
74
} ;
69
75
70
76
/**
@@ -74,9 +80,9 @@ angularFilter.currency = function(amount){
74
80
* @function
75
81
*
76
82
* @description
77
- * Formats a number as text.
83
+ * Formats a number as text.
78
84
*
79
- * If the input is not a number empty string is returned.
85
+ * If the input is not a number empty string is returned.
80
86
*
81
87
* @param {number|string } number Number to format.
82
88
* @param {(number|string)= } [fractionSize=2] Number of decimal places to round the number to.
@@ -92,59 +98,104 @@ angularFilter.currency = function(amount){
92
98
</doc:source>
93
99
<doc:scenario>
94
100
it('should format numbers', function(){
95
- expect(binding('val | number')).toBe('1,234.57 ');
101
+ expect(binding('val | number')).toBe('1,234.568 ');
96
102
expect(binding('val | number:0')).toBe('1,235');
97
103
expect(binding('-val | number:4')).toBe('-1,234.5679');
98
104
});
99
105
100
106
it('should update', function(){
101
107
input('val').enter('3374.333');
102
- expect(binding('val | number')).toBe('3,374.33 ');
108
+ expect(binding('val | number')).toBe('3,374.333 ');
103
109
expect(binding('val | number:0')).toBe('3,374');
104
110
expect(binding('-val | number:4')).toBe('-3,374.3330');
105
111
});
106
112
</doc:scenario>
107
113
</doc:example>
108
114
*/
109
- angularFilter . number = function ( number , fractionSize ) {
110
- if ( isNaN ( number ) || ! isFinite ( number ) ) {
111
- return '' ;
112
- }
113
- fractionSize = isUndefined ( fractionSize ) ? 2 : fractionSize ;
114
115
116
+ // PATTERNS[0] is an array for Decimal Pattern, PATTERNS[1] is an array Currency Pattern
117
+ // Following is the order in each pattern array:
118
+ // 0: minInteger,
119
+ // 1: minFraction,
120
+ // 2: maxFraction,
121
+ // 3: positivePrefix,
122
+ // 4: positiveSuffix,
123
+ // 5: negativePrefix,
124
+ // 6: negativeSuffix,
125
+ // 7: groupSize,
126
+ // 8: lastGroupSize
127
+ var NUMBER_FORMATS = {
128
+ DECIMAL_SEP : '.' ,
129
+ GROUP_SEP : ',' ,
130
+ PATTERNS : [ [ 1 , 0 , 3 , '' , '' , '-' , '' , 3 , 3 ] , [ 1 , 2 , 2 , '\u00A4' , '' , '(\u00A4' , ')' , 3 , 3 ] ] ,
131
+ CURRENCY_SYM : '$'
132
+ } ;
133
+ var DECIMAL_SEP = '.' ;
134
+
135
+ angularFilter . number = function ( number , fractionSize ) {
136
+ if ( isNaN ( number ) || ! isFinite ( number ) ) return '' ;
137
+ return formatNumber ( number , fractionSize , 0 ) ;
138
+ }
139
+
140
+ function formatNumber ( number , fractionSize , type ) {
115
141
var isNegative = number < 0 ,
116
- pow = Math . pow ( 10 , fractionSize ) ,
117
- whole = '' + number ,
142
+ type = type || 0 , // 0 is decimal pattern, 1 is currency pattern
143
+ pattern = NUMBER_FORMATS . PATTERNS [ type ] ;
144
+
145
+ number = Math . abs ( number ) ;
146
+ var numStr = number + '' ,
118
147
formatedText = '' ,
119
- i , fraction ;
148
+ parts = [ ] ;
120
149
121
- if ( whole . indexOf ( 'e' ) > - 1 ) return whole ;
150
+ if ( numStr . indexOf ( 'e' ) !== - 1 ) {
151
+ var formatedText = numStr ;
152
+ } else {
153
+ var fractionLen = ( numStr . split ( DECIMAL_SEP ) [ 1 ] || '' ) . length ;
122
154
123
- number = Math . round ( number * pow ) / pow ;
124
- fraction = ( '' + number ) . split ( '.' ) ;
125
- whole = fraction [ 0 ] ;
126
- fraction = fraction [ 1 ] || '' ;
127
- if ( isNegative ) {
128
- formatedText = '-' ;
129
- whole = whole . substring ( 1 ) ;
130
- }
155
+ //determine fractionSize if it is not specified
156
+ if ( isUndefined ( fractionSize ) ) {
157
+ fractionSize = Math . min ( Math . max ( pattern [ 1 ] , fractionLen ) , pattern [ 2 ] ) ;
158
+ }
131
159
160
+ var pow = Math . pow ( 10 , fractionSize ) ;
161
+ number = Math . round ( number * pow ) / pow ;
162
+ var fraction = ( '' + number ) . split ( DECIMAL_SEP ) ;
163
+ var whole = fraction [ 0 ] ;
164
+ fraction = fraction [ 1 ] || '' ;
165
+
166
+ var pos = 0 ,
167
+ lgroup = pattern [ 8 ] ,
168
+ group = pattern [ 7 ] ;
169
+
170
+ if ( whole . length >= ( lgroup + group ) ) {
171
+ pos = whole . length - lgroup ;
172
+ for ( var i = 0 ; i < pos ; i ++ ) {
173
+ if ( ( pos - i ) % group === 0 && i !== 0 ) {
174
+ formatedText += NUMBER_FORMATS . GROUP_SEP ;
175
+ }
176
+ formatedText += whole . charAt ( i ) ;
177
+ }
178
+ }
132
179
133
- for ( i = 0 ; i < whole . length ; i ++ ) {
134
- if ( ( whole . length - i ) % 3 === 0 && i !== 0 ) {
135
- formatedText += ',' ;
180
+ for ( i = pos ; i < whole . length ; i ++ ) {
181
+ if ( ( whole . length - i ) % lgroup === 0 && i !== 0 ) {
182
+ formatedText += NUMBER_FORMATS . GROUP_SEP ;
183
+ }
184
+ formatedText += whole . charAt ( i ) ;
136
185
}
137
- formatedText += whole . charAt ( i ) ;
138
- }
139
- if ( fractionSize ) {
186
+
187
+ // format fraction part.
140
188
while ( fraction . length < fractionSize ) {
141
189
fraction += '0' ;
142
190
}
143
- formatedText += '.' + fraction . substring ( 0 , fractionSize ) ;
191
+ if ( fractionSize ) formatedText += NUMBER_FORMATS . DECIMAL_SEP + fraction . substr ( 0 , fractionSize ) ;
144
192
}
145
- return formatedText ;
146
- } ;
147
193
194
+ parts . push ( isNegative ? pattern [ 5 ] : pattern [ 3 ] ) ;
195
+ parts . push ( formatedText ) ;
196
+ parts . push ( isNegative ? pattern [ 6 ] : pattern [ 4 ] ) ;
197
+ return parts . join ( '' ) ;
198
+ }
148
199
149
200
function padNumber ( num , digits , trim ) {
150
201
var neg = '' ;
0 commit comments