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