]>
Commit | Line | Data |
---|---|---|
2f01fe57 | 1 | /* |
81bea17a | 2 | Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. |
2f01fe57 AD |
3 | Available via Academic Free License >= 2.1 OR the modified BSD license. |
4 | see: http://dojotoolkit.org/license for details | |
5 | */ | |
6 | ||
7 | ||
a089699c AD |
8 | if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
9 | dojo._hasResource["dojo.number"] = true; | |
2f01fe57 AD |
10 | dojo.provide("dojo.number"); |
11 | dojo.require("dojo.i18n"); | |
81bea17a | 12 | dojo.requireLocalization("dojo.cldr", "number", null, "ROOT,ar,ca,cs,da,de,el,en,en-au,en-gb,es,fi,fr,fr-ch,he,hu,it,ja,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-hant,zh-hk"); |
2f01fe57 AD |
13 | dojo.require("dojo.string"); |
14 | dojo.require("dojo.regexp"); | |
a089699c | 15 | |
81bea17a | 16 | dojo.getObject("number", true, dojo); |
a089699c AD |
17 | |
18 | /*===== | |
19 | dojo.number = { | |
20 | // summary: localized formatting and parsing routines for Number | |
2f01fe57 | 21 | } |
a089699c AD |
22 | |
23 | dojo.number.__FormatOptions = function(){ | |
24 | // pattern: String? | |
25 | // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) | |
26 | // with this string. Default value is based on locale. Overriding this property will defeat | |
27 | // localization. Literal characters in patterns are not supported. | |
28 | // type: String? | |
29 | // choose a format type based on the locale from the following: | |
30 | // decimal, scientific (not yet supported), percent, currency. decimal by default. | |
31 | // places: Number? | |
32 | // fixed number of decimal places to show. This overrides any | |
33 | // information in the provided pattern. | |
34 | // round: Number? | |
35 | // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1 | |
36 | // means do not round. | |
37 | // locale: String? | |
38 | // override the locale used to determine formatting rules | |
39 | // fractional: Boolean? | |
40 | // If false, show no decimal places, overriding places and pattern settings. | |
41 | this.pattern = pattern; | |
42 | this.type = type; | |
43 | this.places = places; | |
44 | this.round = round; | |
45 | this.locale = locale; | |
46 | this.fractional = fractional; | |
47 | } | |
48 | =====*/ | |
49 | ||
50 | dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){ | |
51 | // summary: | |
52 | // Format a Number as a String, using locale-specific settings | |
53 | // description: | |
54 | // Create a string from a Number using a known localized pattern. | |
55 | // Formatting patterns appropriate to the locale are chosen from the | |
56 | // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and | |
57 | // delimiters. | |
58 | // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null. | |
59 | // value: | |
60 | // the number to be formatted | |
61 | ||
62 | options = dojo.mixin({}, options || {}); | |
63 | var locale = dojo.i18n.normalizeLocale(options.locale), | |
64 | bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale); | |
65 | options.customs = bundle; | |
66 | var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"]; | |
67 | if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null | |
68 | return dojo.number._applyPattern(value, pattern, options); // String | |
2f01fe57 | 69 | }; |
a089699c AD |
70 | |
71 | //dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough | |
72 | dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough | |
73 | ||
74 | dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){ | |
75 | // summary: | |
76 | // Apply pattern to format value as a string using options. Gives no | |
77 | // consideration to local customs. | |
78 | // value: | |
79 | // the number to be formatted. | |
80 | // pattern: | |
81 | // a pattern string as described by | |
82 | // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) | |
83 | // options: dojo.number.__FormatOptions? | |
84 | // _applyPattern is usually called via `dojo.number.format()` which | |
85 | // populates an extra property in the options parameter, "customs". | |
86 | // The customs object specifies group and decimal parameters if set. | |
87 | ||
88 | //TODO: support escapes | |
89 | options = options || {}; | |
90 | var group = options.customs.group, | |
91 | decimal = options.customs.decimal, | |
92 | patternList = pattern.split(';'), | |
93 | positivePattern = patternList[0]; | |
94 | pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern); | |
95 | ||
96 | //TODO: only test against unescaped | |
97 | if(pattern.indexOf('%') != -1){ | |
98 | value *= 100; | |
99 | }else if(pattern.indexOf('\u2030') != -1){ | |
100 | value *= 1000; // per mille | |
101 | }else if(pattern.indexOf('\u00a4') != -1){ | |
102 | group = options.customs.currencyGroup || group;//mixins instead? | |
103 | decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead? | |
104 | pattern = pattern.replace(/\u00a4{1,3}/, function(match){ | |
105 | var prop = ["symbol", "currency", "displayName"][match.length-1]; | |
106 | return options[prop] || options.currency || ""; | |
107 | }); | |
108 | }else if(pattern.indexOf('E') != -1){ | |
109 | throw new Error("exponential notation not supported"); | |
110 | } | |
111 | ||
112 | //TODO: support @ sig figs? | |
113 | var numberPatternRE = dojo.number._numberPatternRE; | |
114 | var numberPattern = positivePattern.match(numberPatternRE); | |
115 | if(!numberPattern){ | |
116 | throw new Error("unable to find a number expression in pattern: "+pattern); | |
117 | } | |
118 | if(options.fractional === false){ options.places = 0; } | |
119 | return pattern.replace(numberPatternRE, | |
120 | dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round})); | |
81bea17a | 121 | }; |
a089699c AD |
122 | |
123 | dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){ | |
124 | // summary: | |
125 | // Rounds to the nearest value with the given number of decimal places, away from zero | |
126 | // description: | |
127 | // Rounds to the nearest value with the given number of decimal places, away from zero if equal. | |
128 | // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by | |
129 | // fractional increments also, such as the nearest quarter. | |
130 | // NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround. | |
131 | // value: | |
132 | // The number to round | |
133 | // places: | |
134 | // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding. | |
135 | // Must be non-negative. | |
136 | // increment: | |
137 | // Rounds next place to nearest value of increment/10. 10 by default. | |
138 | // example: | |
139 | // >>> dojo.number.round(-0.5) | |
140 | // -1 | |
141 | // >>> dojo.number.round(162.295, 2) | |
142 | // 162.29 // note floating point error. Should be 162.3 | |
143 | // >>> dojo.number.round(10.71, 0, 2.5) | |
144 | // 10.75 | |
145 | var factor = 10 / (increment || 10); | |
146 | return (factor * +value).toFixed(places) / factor; // Number | |
81bea17a | 147 | }; |
a089699c AD |
148 | |
149 | if((0.9).toFixed() == 0){ | |
150 | // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit | |
151 | // is just after the rounding place and is >=5 | |
152 | (function(){ | |
153 | var round = dojo.number.round; | |
154 | dojo.number.round = function(v, p, m){ | |
155 | var d = Math.pow(10, -p || 0), a = Math.abs(v); | |
156 | if(!v || a >= d || a * Math.pow(10, p + 1) < 5){ | |
157 | d = 0; | |
158 | } | |
159 | return round(v, p, m) + (v > 0 ? d : -d); | |
81bea17a | 160 | }; |
a089699c | 161 | })(); |
2f01fe57 | 162 | } |
a089699c AD |
163 | |
164 | /*===== | |
165 | dojo.number.__FormatAbsoluteOptions = function(){ | |
166 | // decimal: String? | |
167 | // the decimal separator | |
168 | // group: String? | |
169 | // the group separator | |
170 | // places: Number?|String? | |
171 | // number of decimal places. the range "n,m" will format to m places. | |
172 | // round: Number? | |
173 | // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1 | |
174 | // means don't round. | |
175 | this.decimal = decimal; | |
176 | this.group = group; | |
177 | this.places = places; | |
178 | this.round = round; | |
179 | } | |
180 | =====*/ | |
181 | ||
182 | dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){ | |
81bea17a | 183 | // summary: |
a089699c AD |
184 | // Apply numeric pattern to absolute value using options. Gives no |
185 | // consideration to local customs. | |
186 | // value: | |
187 | // the number to be formatted, ignores sign | |
188 | // pattern: | |
189 | // the number portion of a pattern (e.g. `#,##0.00`) | |
190 | options = options || {}; | |
191 | if(options.places === true){options.places=0;} | |
192 | if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit | |
193 | ||
194 | var patternParts = pattern.split("."), | |
195 | comma = typeof options.places == "string" && options.places.indexOf(","), | |
196 | maxPlaces = options.places; | |
197 | if(comma){ | |
198 | maxPlaces = options.places.substring(comma + 1); | |
199 | }else if(!(maxPlaces >= 0)){ | |
200 | maxPlaces = (patternParts[1] || []).length; | |
201 | } | |
202 | if(!(options.round < 0)){ | |
203 | value = dojo.number.round(value, maxPlaces, options.round); | |
204 | } | |
205 | ||
206 | var valueParts = String(Math.abs(value)).split("."), | |
207 | fractional = valueParts[1] || ""; | |
208 | if(patternParts[1] || options.places){ | |
209 | if(comma){ | |
210 | options.places = options.places.substring(0, comma); | |
211 | } | |
212 | // Pad fractional with trailing zeros | |
213 | var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1); | |
214 | if(pad > fractional.length){ | |
215 | valueParts[1] = dojo.string.pad(fractional, pad, '0', true); | |
216 | } | |
217 | ||
218 | // Truncate fractional | |
219 | if(maxPlaces < fractional.length){ | |
220 | valueParts[1] = fractional.substr(0, maxPlaces); | |
221 | } | |
222 | }else{ | |
223 | if(valueParts[1]){ valueParts.pop(); } | |
224 | } | |
225 | ||
226 | // Pad whole with leading zeros | |
227 | var patternDigits = patternParts[0].replace(',', ''); | |
228 | pad = patternDigits.indexOf("0"); | |
229 | if(pad != -1){ | |
230 | pad = patternDigits.length - pad; | |
231 | if(pad > valueParts[0].length){ | |
232 | valueParts[0] = dojo.string.pad(valueParts[0], pad); | |
233 | } | |
234 | ||
235 | // Truncate whole | |
236 | if(patternDigits.indexOf("#") == -1){ | |
237 | valueParts[0] = valueParts[0].substr(valueParts[0].length - pad); | |
238 | } | |
239 | } | |
240 | ||
241 | // Add group separators | |
242 | var index = patternParts[0].lastIndexOf(','), | |
243 | groupSize, groupSize2; | |
244 | if(index != -1){ | |
245 | groupSize = patternParts[0].length - index - 1; | |
246 | var remainder = patternParts[0].substr(0, index); | |
247 | index = remainder.lastIndexOf(','); | |
248 | if(index != -1){ | |
249 | groupSize2 = remainder.length - index - 1; | |
250 | } | |
251 | } | |
252 | var pieces = []; | |
253 | for(var whole = valueParts[0]; whole;){ | |
254 | var off = whole.length - groupSize; | |
255 | pieces.push((off > 0) ? whole.substr(off) : whole); | |
256 | whole = (off > 0) ? whole.slice(0, off) : ""; | |
257 | if(groupSize2){ | |
258 | groupSize = groupSize2; | |
259 | delete groupSize2; | |
260 | } | |
261 | } | |
262 | valueParts[0] = pieces.reverse().join(options.group || ","); | |
263 | ||
264 | return valueParts.join(options.decimal || "."); | |
2f01fe57 | 265 | }; |
a089699c AD |
266 | |
267 | /*===== | |
268 | dojo.number.__RegexpOptions = function(){ | |
269 | // pattern: String? | |
270 | // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) | |
271 | // with this string. Default value is based on locale. Overriding this property will defeat | |
272 | // localization. | |
273 | // type: String? | |
274 | // choose a format type based on the locale from the following: | |
275 | // decimal, scientific (not yet supported), percent, currency. decimal by default. | |
276 | // locale: String? | |
277 | // override the locale used to determine formatting rules | |
278 | // strict: Boolean? | |
279 | // strict parsing, false by default. Strict parsing requires input as produced by the format() method. | |
280 | // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators | |
281 | // places: Number|String? | |
282 | // number of decimal places to accept: Infinity, a positive number, or | |
283 | // a range "n,m". Defined by pattern or Infinity if pattern not provided. | |
284 | this.pattern = pattern; | |
285 | this.type = type; | |
286 | this.locale = locale; | |
287 | this.strict = strict; | |
288 | this.places = places; | |
289 | } | |
290 | =====*/ | |
291 | dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){ | |
292 | // summary: | |
293 | // Builds the regular needed to parse a number | |
294 | // description: | |
295 | // Returns regular expression with positive and negative match, group | |
296 | // and decimal separators | |
297 | return dojo.number._parseInfo(options).regexp; // String | |
81bea17a | 298 | }; |
a089699c AD |
299 | |
300 | dojo.number._parseInfo = function(/*Object?*/options){ | |
301 | options = options || {}; | |
302 | var locale = dojo.i18n.normalizeLocale(options.locale), | |
303 | bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale), | |
304 | pattern = options.pattern || bundle[(options.type || "decimal") + "Format"], | |
305 | //TODO: memoize? | |
306 | group = bundle.group, | |
307 | decimal = bundle.decimal, | |
308 | factor = 1; | |
309 | ||
310 | if(pattern.indexOf('%') != -1){ | |
311 | factor /= 100; | |
312 | }else if(pattern.indexOf('\u2030') != -1){ | |
313 | factor /= 1000; // per mille | |
314 | }else{ | |
315 | var isCurrency = pattern.indexOf('\u00a4') != -1; | |
316 | if(isCurrency){ | |
317 | group = bundle.currencyGroup || group; | |
318 | decimal = bundle.currencyDecimal || decimal; | |
319 | } | |
320 | } | |
321 | ||
322 | //TODO: handle quoted escapes | |
323 | var patternList = pattern.split(';'); | |
324 | if(patternList.length == 1){ | |
325 | patternList.push("-" + patternList[0]); | |
326 | } | |
327 | ||
328 | var re = dojo.regexp.buildGroupRE(patternList, function(pattern){ | |
329 | pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")"; | |
330 | return pattern.replace(dojo.number._numberPatternRE, function(format){ | |
331 | var flags = { | |
332 | signed: false, | |
333 | separator: options.strict ? group : [group,""], | |
334 | fractional: options.fractional, | |
335 | decimal: decimal, | |
336 | exponent: false | |
337 | }, | |
338 | ||
339 | parts = format.split('.'), | |
340 | places = options.places; | |
341 | ||
342 | // special condition for percent (factor != 1) | |
343 | // allow decimal places even if not specified in pattern | |
344 | if(parts.length == 1 && factor != 1){ | |
345 | parts[1] = "###"; | |
346 | } | |
347 | if(parts.length == 1 || places === 0){ | |
348 | flags.fractional = false; | |
349 | }else{ | |
350 | if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; } | |
351 | if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified | |
352 | if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; } | |
353 | flags.places = places; | |
354 | } | |
355 | var groups = parts[0].split(','); | |
356 | if(groups.length > 1){ | |
357 | flags.groupSize = groups.pop().length; | |
358 | if(groups.length > 1){ | |
359 | flags.groupSize2 = groups.pop().length; | |
360 | } | |
361 | } | |
362 | return "("+dojo.number._realNumberRegexp(flags)+")"; | |
363 | }); | |
364 | }, true); | |
365 | ||
366 | if(isCurrency){ | |
367 | // substitute the currency symbol for the placeholder in the pattern | |
368 | re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){ | |
369 | var prop = ["symbol", "currency", "displayName"][target.length-1], | |
370 | symbol = dojo.regexp.escapeString(options[prop] || options.currency || ""); | |
371 | before = before ? "[\\s\\xa0]" : ""; | |
372 | after = after ? "[\\s\\xa0]" : ""; | |
373 | if(!options.strict){ | |
374 | if(before){before += "*";} | |
375 | if(after){after += "*";} | |
376 | return "(?:"+before+symbol+after+")?"; | |
377 | } | |
378 | return before+symbol+after; | |
379 | }); | |
380 | } | |
381 | ||
382 | //TODO: substitute localized sign/percent/permille/etc.? | |
383 | ||
384 | // normalize whitespace and return | |
385 | return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object | |
81bea17a | 386 | }; |
a089699c AD |
387 | |
388 | /*===== | |
389 | dojo.number.__ParseOptions = function(){ | |
390 | // pattern: String? | |
391 | // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) | |
392 | // with this string. Default value is based on locale. Overriding this property will defeat | |
393 | // localization. Literal characters in patterns are not supported. | |
394 | // type: String? | |
395 | // choose a format type based on the locale from the following: | |
396 | // decimal, scientific (not yet supported), percent, currency. decimal by default. | |
397 | // locale: String? | |
398 | // override the locale used to determine formatting rules | |
399 | // strict: Boolean? | |
400 | // strict parsing, false by default. Strict parsing requires input as produced by the format() method. | |
401 | // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators | |
402 | // fractional: Boolean?|Array? | |
403 | // Whether to include the fractional portion, where the number of decimal places are implied by pattern | |
404 | // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional. | |
405 | this.pattern = pattern; | |
406 | this.type = type; | |
407 | this.locale = locale; | |
408 | this.strict = strict; | |
409 | this.fractional = fractional; | |
410 | } | |
411 | =====*/ | |
412 | dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){ | |
413 | // summary: | |
414 | // Convert a properly formatted string to a primitive Number, using | |
415 | // locale-specific settings. | |
416 | // description: | |
417 | // Create a Number from a string using a known localized pattern. | |
418 | // Formatting patterns are chosen appropriate to the locale | |
419 | // and follow the syntax described by | |
420 | // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) | |
421 | // Note that literal characters in patterns are not supported. | |
422 | // expression: | |
423 | // A string representation of a Number | |
424 | var info = dojo.number._parseInfo(options), | |
425 | results = (new RegExp("^"+info.regexp+"$")).exec(expression); | |
426 | if(!results){ | |
427 | return NaN; //NaN | |
428 | } | |
429 | var absoluteMatch = results[1]; // match for the positive expression | |
430 | if(!results[1]){ | |
431 | if(!results[2]){ | |
432 | return NaN; //NaN | |
433 | } | |
434 | // matched the negative pattern | |
435 | absoluteMatch =results[2]; | |
436 | info.factor *= -1; | |
437 | } | |
438 | ||
439 | // Transform it to something Javascript can parse as a number. Normalize | |
440 | // decimal point and strip out group separators or alternate forms of whitespace | |
441 | absoluteMatch = absoluteMatch. | |
442 | replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), ""). | |
443 | replace(info.decimal, "."); | |
444 | // Adjust for negative sign, percent, etc. as necessary | |
445 | return absoluteMatch * info.factor; //Number | |
2f01fe57 | 446 | }; |
a089699c AD |
447 | |
448 | /*===== | |
449 | dojo.number.__RealNumberRegexpFlags = function(){ | |
450 | // places: Number? | |
451 | // The integer number of decimal places or a range given as "n,m". If | |
452 | // not given, the decimal part is optional and the number of places is | |
453 | // unlimited. | |
454 | // decimal: String? | |
455 | // A string for the character used as the decimal point. Default | |
456 | // is ".". | |
457 | // fractional: Boolean?|Array? | |
458 | // Whether decimal places are used. Can be true, false, or [true, | |
459 | // false]. Default is [true, false] which means optional. | |
460 | // exponent: Boolean?|Array? | |
461 | // Express in exponential notation. Can be true, false, or [true, | |
462 | // false]. Default is [true, false], (i.e. will match if the | |
463 | // exponential part is present are not). | |
464 | // eSigned: Boolean?|Array? | |
465 | // The leading plus-or-minus sign on the exponent. Can be true, | |
466 | // false, or [true, false]. Default is [true, false], (i.e. will | |
467 | // match if it is signed or unsigned). flags in regexp.integer can be | |
468 | // applied. | |
469 | this.places = places; | |
470 | this.decimal = decimal; | |
471 | this.fractional = fractional; | |
472 | this.exponent = exponent; | |
473 | this.eSigned = eSigned; | |
474 | } | |
475 | =====*/ | |
476 | ||
477 | dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){ | |
478 | // summary: | |
479 | // Builds a regular expression to match a real number in exponential | |
480 | // notation | |
481 | ||
482 | // assign default values to missing parameters | |
483 | flags = flags || {}; | |
484 | //TODO: use mixin instead? | |
485 | if(!("places" in flags)){ flags.places = Infinity; } | |
486 | if(typeof flags.decimal != "string"){ flags.decimal = "."; } | |
487 | if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; } | |
488 | if(!("exponent" in flags)){ flags.exponent = [true, false]; } | |
489 | if(!("eSigned" in flags)){ flags.eSigned = [true, false]; } | |
490 | ||
491 | var integerRE = dojo.number._integerRegexp(flags), | |
492 | decimalRE = dojo.regexp.buildGroupRE(flags.fractional, | |
493 | function(q){ | |
494 | var re = ""; | |
495 | if(q && (flags.places!==0)){ | |
496 | re = "\\" + flags.decimal; | |
81bea17a AD |
497 | if(flags.places == Infinity){ |
498 | re = "(?:" + re + "\\d+)?"; | |
a089699c | 499 | }else{ |
81bea17a | 500 | re += "\\d{" + flags.places + "}"; |
a089699c AD |
501 | } |
502 | } | |
503 | return re; | |
504 | }, | |
505 | true | |
506 | ); | |
507 | ||
508 | var exponentRE = dojo.regexp.buildGroupRE(flags.exponent, | |
81bea17a | 509 | function(q){ |
a089699c | 510 | if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; } |
81bea17a | 511 | return ""; |
a089699c AD |
512 | } |
513 | ); | |
514 | ||
515 | var realRE = integerRE + decimalRE; | |
516 | // allow for decimals without integers, e.g. .25 | |
517 | if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";} | |
518 | return realRE + exponentRE; // String | |
2f01fe57 | 519 | }; |
a089699c AD |
520 | |
521 | /*===== | |
522 | dojo.number.__IntegerRegexpFlags = function(){ | |
523 | // signed: Boolean? | |
524 | // The leading plus-or-minus sign. Can be true, false, or `[true,false]`. | |
525 | // Default is `[true, false]`, (i.e. will match if it is signed | |
526 | // or unsigned). | |
527 | // separator: String? | |
528 | // The character used as the thousands separator. Default is no | |
529 | // separator. For more than one symbol use an array, e.g. `[",", ""]`, | |
530 | // makes ',' optional. | |
531 | // groupSize: Number? | |
532 | // group size between separators | |
533 | // groupSize2: Number? | |
534 | // second grouping, where separators 2..n have a different interval than the first separator (for India) | |
535 | this.signed = signed; | |
536 | this.separator = separator; | |
537 | this.groupSize = groupSize; | |
538 | this.groupSize2 = groupSize2; | |
539 | } | |
540 | =====*/ | |
541 | ||
542 | dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){ | |
81bea17a | 543 | // summary: |
a089699c AD |
544 | // Builds a regular expression that matches an integer |
545 | ||
546 | // assign default values to missing parameters | |
547 | flags = flags || {}; | |
548 | if(!("signed" in flags)){ flags.signed = [true, false]; } | |
549 | if(!("separator" in flags)){ | |
550 | flags.separator = ""; | |
551 | }else if(!("groupSize" in flags)){ | |
552 | flags.groupSize = 3; | |
553 | } | |
554 | ||
555 | var signRE = dojo.regexp.buildGroupRE(flags.signed, | |
556 | function(q){ return q ? "[-+]" : ""; }, | |
557 | true | |
558 | ); | |
559 | ||
560 | var numberRE = dojo.regexp.buildGroupRE(flags.separator, | |
561 | function(sep){ | |
562 | if(!sep){ | |
563 | return "(?:\\d+)"; | |
564 | } | |
565 | ||
566 | sep = dojo.regexp.escapeString(sep); | |
567 | if(sep == " "){ sep = "\\s"; } | |
568 | else if(sep == "\xa0"){ sep = "\\s\\xa0"; } | |
569 | ||
570 | var grp = flags.groupSize, grp2 = flags.groupSize2; | |
571 | //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933 | |
572 | if(grp2){ | |
573 | var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})"; | |
574 | return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE; | |
575 | } | |
576 | return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)"; | |
577 | }, | |
578 | true | |
579 | ); | |
580 | ||
581 | return signRE + numberRE; // String | |
81bea17a | 582 | }; |
a089699c | 583 | |
2f01fe57 | 584 | } |