]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @class Ext.util.Format\r | |
3 | * \r | |
4 | * This class is a centralized place for formatting functions. It includes\r | |
5 | * functions to format various different types of data, such as text, dates and numeric values.\r | |
6 | * \r | |
7 | * ## Localization\r | |
8 | *\r | |
9 | * This class contains several options for localization. These can be set once the library has loaded,\r | |
10 | * all calls to the functions from that point will use the locale settings that were specified.\r | |
11 | *\r | |
12 | * Options include:\r | |
13 | *\r | |
14 | * - thousandSeparator\r | |
15 | * - decimalSeparator\r | |
16 | * - currenyPrecision\r | |
17 | * - currencySign\r | |
18 | * - currencyAtEnd\r | |
19 | *\r | |
20 | * This class also uses the default date format defined here: {@link Ext.Date#defaultFormat}.\r | |
21 | *\r | |
22 | * ## Using with renderers\r | |
23 | *\r | |
24 | * There are two helper functions that return a new function that can be used in conjunction with\r | |
25 | * grid renderers:\r | |
26 | * \r | |
27 | * columns: [{\r | |
28 | * dataIndex: 'date',\r | |
29 | * renderer: Ext.util.Format.dateRenderer('Y-m-d')\r | |
30 | * }, {\r | |
31 | * dataIndex: 'time',\r | |
32 | * renderer: Ext.util.Format.numberRenderer('0.000')\r | |
33 | * }]\r | |
34 | * \r | |
35 | * Functions that only take a single argument can also be passed directly:\r | |
36 | *\r | |
37 | * columns: [{\r | |
38 | * dataIndex: 'cost',\r | |
39 | * renderer: Ext.util.Format.usMoney\r | |
40 | * }, {\r | |
41 | * dataIndex: 'productCode',\r | |
42 | * renderer: Ext.util.Format.uppercase\r | |
43 | * }]\r | |
44 | * \r | |
45 | * ## Using with XTemplates\r | |
46 | *\r | |
47 | * XTemplates can also directly use Ext.util.Format functions:\r | |
48 | * \r | |
49 | * new Ext.XTemplate([\r | |
50 | * 'Date: {startDate:date("Y-m-d")}',\r | |
51 | * 'Cost: {cost:usMoney}'\r | |
52 | * ]);\r | |
53 | *\r | |
54 | * @singleton\r | |
55 | */\r | |
56 | Ext.define('Ext.util.Format', function () {\r | |
57 | var me; // holds our singleton instance\r | |
58 | \r | |
59 | return {\r | |
60 | requires: [\r | |
61 | 'Ext.Error',\r | |
62 | 'Ext.Number',\r | |
63 | 'Ext.String',\r | |
64 | 'Ext.Date'\r | |
65 | ],\r | |
66 | \r | |
67 | singleton: true,\r | |
68 | \r | |
69 | /**\r | |
70 | * The global default date format.\r | |
71 | */\r | |
72 | defaultDateFormat: 'm/d/Y',\r | |
73 | \r | |
74 | //<locale>\r | |
75 | /**\r | |
76 | * @property {String} thousandSeparator\r | |
77 | * The character that the {@link #number} function uses as a thousand separator.\r | |
78 | *\r | |
79 | * This may be overridden in a locale file.\r | |
80 | */\r | |
81 | thousandSeparator: ',',\r | |
82 | //</locale>\r | |
83 | \r | |
84 | //<locale>\r | |
85 | /**\r | |
86 | * @property {String} decimalSeparator\r | |
87 | * The character that the {@link #number} function uses as a decimal point.\r | |
88 | *\r | |
89 | * This may be overridden in a locale file.\r | |
90 | */\r | |
91 | decimalSeparator: '.',\r | |
92 | //</locale>\r | |
93 | \r | |
94 | //<locale>\r | |
95 | /**\r | |
96 | * @property {Number} currencyPrecision\r | |
97 | * The number of decimal places that the {@link #currency} function displays.\r | |
98 | *\r | |
99 | * This may be overridden in a locale file.\r | |
100 | */\r | |
101 | currencyPrecision: 2,\r | |
102 | //</locale>\r | |
103 | \r | |
104 | //<locale>\r | |
105 | /**\r | |
106 | * @property {String} currencySign\r | |
107 | * The currency sign that the {@link #currency} function displays.\r | |
108 | *\r | |
109 | * This may be overridden in a locale file.\r | |
110 | */\r | |
111 | currencySign: '$',\r | |
112 | //</locale>\r | |
113 | \r | |
114 | /**\r | |
115 | * @property {String} percentSign\r | |
116 | * The percent sign that the {@link #percent} function displays.\r | |
117 | *\r | |
118 | * This may be overridden in a locale file.\r | |
119 | */\r | |
120 | percentSign: '%',\r | |
121 | \r | |
122 | //<locale>\r | |
123 | /**\r | |
124 | * @property {Boolean} currencyAtEnd\r | |
125 | * This may be set to <code>true</code> to make the {@link #currency} function\r | |
126 | * append the currency sign to the formatted value.\r | |
127 | *\r | |
128 | * This may be overridden in a locale file.\r | |
129 | */\r | |
130 | currencyAtEnd: false,\r | |
131 | //</locale>\r | |
132 | \r | |
133 | stripTagsRe: /<\/?[^>]+>/gi,\r | |
134 | stripScriptsRe: /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,\r | |
135 | nl2brRe: /\r?\n/g,\r | |
136 | hashRe: /#+$/,\r | |
137 | allHashes: /^#+$/,\r | |
138 | \r | |
139 | // Match a format string characters to be able to detect remaining "literal" characters\r | |
140 | formatPattern: /[\d,\.#]+/,\r | |
141 | \r | |
142 | // A RegExp to remove from a number format string, all characters except digits and '.'\r | |
143 | formatCleanRe: /[^\d\.#]/g,\r | |
144 | \r | |
145 | // A RegExp to remove from a number format string, all characters except digits and the local decimal separator.\r | |
146 | // Created on first use. The local decimal separator character must be initialized for this to be created.\r | |
147 | I18NFormatCleanRe: null,\r | |
148 | \r | |
149 | // Cache ofg number formatting functions keyed by format string\r | |
150 | formatFns: {},\r | |
151 | \r | |
152 | constructor: function () {\r | |
153 | me = this; // we are a singleton, so cache our this pointer in scope\r | |
154 | },\r | |
155 | \r | |
156 | /**\r | |
157 | * Checks a reference and converts it to empty string if it is undefined.\r | |
158 | * @param {Object} value Reference to check\r | |
159 | * @return {Object} Empty string if converted, otherwise the original value\r | |
160 | */\r | |
161 | undef : function(value) {\r | |
162 | return value !== undefined ? value : "";\r | |
163 | },\r | |
164 | \r | |
165 | /**\r | |
166 | * Checks a reference and converts it to the default value if it's empty.\r | |
167 | * @param {Object} value Reference to check\r | |
168 | * @param {String} [defaultValue=""] The value to insert of it's undefined.\r | |
169 | * @return {String}\r | |
170 | */\r | |
171 | defaultValue : function(value, defaultValue) {\r | |
172 | return value !== undefined && value !== '' ? value : defaultValue;\r | |
173 | },\r | |
174 | \r | |
175 | /**\r | |
176 | * Returns a substring from within an original string.\r | |
177 | * @param {String} value The original text\r | |
178 | * @param {Number} start The start index of the substring\r | |
179 | * @param {Number} length The length of the substring\r | |
180 | * @return {String} The substring\r | |
181 | * @method\r | |
182 | */\r | |
183 | substr : 'ab'.substr(-1) != 'b'\r | |
184 | ? function (value, start, length) {\r | |
185 | var str = String(value);\r | |
186 | return (start < 0)\r | |
187 | ? str.substr(Math.max(str.length + start, 0), length)\r | |
188 | : str.substr(start, length);\r | |
189 | }\r | |
190 | : function(value, start, length) {\r | |
191 | return String(value).substr(start, length);\r | |
192 | },\r | |
193 | \r | |
194 | /**\r | |
195 | * Converts a string to all lower case letters.\r | |
196 | * @param {String} value The text to convert\r | |
197 | * @return {String} The converted text\r | |
198 | */\r | |
199 | lowercase : function(value) {\r | |
200 | return String(value).toLowerCase();\r | |
201 | },\r | |
202 | \r | |
203 | /**\r | |
204 | * Converts a string to all upper case letters.\r | |
205 | * @param {String} value The text to convert\r | |
206 | * @return {String} The converted text\r | |
207 | */\r | |
208 | uppercase : function(value) {\r | |
209 | return String(value).toUpperCase();\r | |
210 | },\r | |
211 | \r | |
212 | /**\r | |
213 | * Format a number as US currency.\r | |
214 | * @param {Number/String} value The numeric value to format\r | |
215 | * @return {String} The formatted currency string\r | |
216 | */\r | |
217 | usMoney : function(v) {\r | |
218 | return me.currency(v, '$', 2);\r | |
219 | },\r | |
220 | \r | |
221 | /**\r | |
222 | * Format a number as a currency.\r | |
223 | * @param {Number/String} value The numeric value to format\r | |
224 | * @param {String} [sign] The currency sign to use (defaults to {@link #currencySign})\r | |
225 | * @param {Number} [decimals] The number of decimals to use for the currency\r | |
226 | * (defaults to {@link #currencyPrecision})\r | |
227 | * @param {Boolean} [end] True if the currency sign should be at the end of the string\r | |
228 | * (defaults to {@link #currencyAtEnd})\r | |
229 | * @return {String} The formatted currency string\r | |
230 | */\r | |
231 | currency: function(v, currencySign, decimals, end) {\r | |
232 | var negativeSign = '',\r | |
233 | format = ",0",\r | |
234 | i = 0;\r | |
235 | v = v - 0;\r | |
236 | if (v < 0) {\r | |
237 | v = -v;\r | |
238 | negativeSign = '-';\r | |
239 | }\r | |
240 | decimals = Ext.isDefined(decimals) ? decimals : me.currencyPrecision;\r | |
241 | format += (decimals > 0 ? '.' : '');\r | |
242 | for (; i < decimals; i++) {\r | |
243 | format += '0';\r | |
244 | }\r | |
245 | v = me.number(v, format);\r | |
246 | if ((end || me.currencyAtEnd) === true) {\r | |
247 | return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || me.currencySign);\r | |
248 | } else {\r | |
249 | return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || me.currencySign, v);\r | |
250 | }\r | |
251 | },\r | |
252 | \r | |
253 | /**\r | |
254 | * Formats the passed date using the specified format pattern.\r | |
255 | * Note that this uses the native Javascript Date.parse() method and is therefore subject to its idiosyncrasies.\r | |
256 | * Most formats assume the local timezone unless specified. One notable exception is 'YYYY-MM-DD' (note the dashes)\r | |
257 | * which is typically interpreted in UTC and can cause date shifting.\r | |
258 | * \r | |
259 | * @param {String/Date} value The value to format. Strings must conform to the format\r | |
260 | * expected by the JavaScript Date object's\r | |
261 | * [parse() method](http://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/parse).\r | |
262 | * @param {String} [format] Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.\r | |
263 | * @return {String} The formatted date string.\r | |
264 | */\r | |
265 | date: function(v, format) {\r | |
266 | if (!v) {\r | |
267 | return "";\r | |
268 | }\r | |
269 | if (!Ext.isDate(v)) {\r | |
270 | v = new Date(Date.parse(v));\r | |
271 | }\r | |
272 | return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);\r | |
273 | },\r | |
274 | \r | |
275 | /**\r | |
276 | * Returns a date rendering function that can be reused to apply a date format multiple times efficiently.\r | |
277 | * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.\r | |
278 | * @return {Function} The date formatting function\r | |
279 | */\r | |
280 | dateRenderer : function(format) {\r | |
281 | return function(v) {\r | |
282 | return me.date(v, format);\r | |
283 | };\r | |
284 | },\r | |
285 | \r | |
286 | /**\r | |
287 | * Returns the given number as a base 16 string at least `digits` in length. If\r | |
288 | * the number is fewer digits, 0's are prepended as necessary. If `digits` is\r | |
289 | * negative, the absolute value is the *exact* number of digits to return. In this\r | |
290 | * case, if then number has more digits, only the least significant digits are\r | |
291 | * returned.\r | |
292 | *\r | |
293 | * expect(Ext.util.Format.hex(0x12e4, 2)).toBe('12e4');\r | |
294 | * expect(Ext.util.Format.hex(0x12e4, -2)).toBe('e4');\r | |
295 | * expect(Ext.util.Format.hex(0x0e, 2)).toBe('0e');\r | |
296 | *\r | |
297 | * @param {Number} value The number to format in hex.\r | |
298 | * @param {Number} digits\r | |
299 | * @return {string}\r | |
300 | */\r | |
301 | hex: function (value, digits) {\r | |
302 | var s = parseInt(value || 0, 10).toString(16);\r | |
303 | if (digits) {\r | |
304 | if (digits < 0) {\r | |
305 | digits = -digits;\r | |
306 | if (s.length > digits) {\r | |
307 | s = s.substring(s.length - digits);\r | |
308 | }\r | |
309 | }\r | |
310 | while (s.length < digits) {\r | |
311 | s = '0' + s;\r | |
312 | }\r | |
313 | }\r | |
314 | return s;\r | |
315 | },\r | |
316 | \r | |
317 | /**\r | |
318 | * Returns this result:\r | |
319 | *\r | |
320 | * value || orValue\r | |
321 | *\r | |
322 | * The usefulness of this formatter method is in templates. For example:\r | |
323 | *\r | |
324 | * {foo:or("bar")}\r | |
325 | *\r | |
326 | * @param {Boolean} value The "if" value.\r | |
327 | * @param {Mixed} orValue\r | |
328 | */\r | |
329 | or: function (value, orValue) {\r | |
330 | return value || orValue;\r | |
331 | },\r | |
332 | \r | |
333 | /**\r | |
334 | * If `value` is a number, returns the argument from that index. For example\r | |
335 | *\r | |
336 | * var s = Ext.util.Format.pick(2, 'zero', 'one', 'two');\r | |
337 | * // s === 'two'\r | |
338 | *\r | |
339 | * Otherwise, `value` is treated in a truthy/falsey manner like so:\r | |
340 | *\r | |
341 | * var s = Ext.util.Format.pick(null, 'first', 'second');\r | |
342 | * // s === 'first'\r | |
343 | *\r | |
344 | * s = Ext.util.Format.pick({}, 'first', 'second');\r | |
345 | * // s === 'second'\r | |
346 | *\r | |
347 | * The usefulness of this formatter method is in templates. For example:\r | |
348 | *\r | |
349 | * {foo:pick("F","T")}\r | |
350 | *\r | |
351 | * {bar:pick("first","second","third")}\r | |
352 | *\r | |
353 | * @param {Boolean} value The "if" value.\r | |
354 | * @param {Mixed} firstValue\r | |
355 | * @param {Mixed} secondValue\r | |
356 | */\r | |
357 | pick: function (value, firstValue, secondValue) {\r | |
358 | if (Ext.isNumber(value)) {\r | |
359 | var ret = arguments[value + 1];\r | |
360 | if (ret) {\r | |
361 | return ret;\r | |
362 | }\r | |
363 | }\r | |
364 | return value ? secondValue : firstValue;\r | |
365 | },\r | |
366 | \r | |
367 | /**\r | |
368 | * Strips all HTML tags.\r | |
369 | * @param {Object} value The text from which to strip tags\r | |
370 | * @return {String} The stripped text\r | |
371 | */\r | |
372 | stripTags: function(v) {\r | |
373 | return !v ? v : String(v).replace(me.stripTagsRe, "");\r | |
374 | },\r | |
375 | \r | |
376 | /**\r | |
377 | * Strips all script tags.\r | |
378 | * @param {Object} value The text from which to strip script tags\r | |
379 | * @return {String} The stripped text\r | |
380 | */\r | |
381 | stripScripts : function(v) {\r | |
382 | return !v ? v : String(v).replace(me.stripScriptsRe, "");\r | |
383 | },\r | |
384 | \r | |
385 | /**\r | |
386 | * @method\r | |
387 | * Simple format for a file size (xxx bytes, xxx KB, xxx MB).\r | |
388 | * @param {Number/String} size The numeric value to format\r | |
389 | * @return {String} The formatted file size\r | |
390 | */\r | |
391 | fileSize : (function(){\r | |
392 | var byteLimit = 1024,\r | |
393 | kbLimit = 1048576,\r | |
394 | mbLimit = 1073741824;\r | |
395 | \r | |
396 | return function(size) {\r | |
397 | var out;\r | |
398 | if (size < byteLimit) {\r | |
399 | if (size === 1) {\r | |
400 | out = '1 byte'; \r | |
401 | } else {\r | |
402 | out = size + ' bytes';\r | |
403 | }\r | |
404 | } else if (size < kbLimit) {\r | |
405 | out = (Math.round(((size*10) / byteLimit))/10) + ' KB';\r | |
406 | } else if (size < mbLimit) {\r | |
407 | out = (Math.round(((size*10) / kbLimit))/10) + ' MB';\r | |
408 | } else {\r | |
409 | out = (Math.round(((size*10) / mbLimit))/10) + ' GB';\r | |
410 | }\r | |
411 | return out;\r | |
412 | };\r | |
413 | })(),\r | |
414 | \r | |
415 | /**\r | |
416 | * It does simple math for use in a template, for example:\r | |
417 | *\r | |
418 | * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');\r | |
419 | *\r | |
420 | * @return {Function} A function that operates on the passed value.\r | |
421 | * @method\r | |
422 | */\r | |
423 | math : (function(){\r | |
424 | var fns = {};\r | |
425 | \r | |
426 | return function(v, a){\r | |
427 | if (!fns[a]) {\r | |
428 | fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');\r | |
429 | }\r | |
430 | return fns[a](v);\r | |
431 | };\r | |
432 | }()),\r | |
433 | \r | |
434 | /**\r | |
435 | * Rounds the passed number to the required decimal precision.\r | |
436 | * @param {Number/String} value The numeric value to round.\r | |
437 | * @param {Number} [precision] The number of decimal places to which to round the\r | |
438 | * first parameter's value. If `undefined` the `value` is passed to `Math.round`\r | |
439 | * otherwise the value is returned unmodified.\r | |
440 | * @return {Number} The rounded value.\r | |
441 | */\r | |
442 | round : function(value, precision) {\r | |
443 | var result = Number(value);\r | |
444 | if (typeof precision === 'number') {\r | |
445 | precision = Math.pow(10, precision);\r | |
446 | result = Math.round(value * precision) / precision;\r | |
447 | } else if (precision === undefined) {\r | |
448 | result = Math.round(result);\r | |
449 | }\r | |
450 | return result;\r | |
451 | },\r | |
452 | \r | |
453 | /**\r | |
454 | * Formats the passed number according to the passed format string.\r | |
455 | *\r | |
456 | * The number of digits after the decimal separator character specifies the number of\r | |
457 | * decimal places in the resulting string. The *local-specific* decimal character is\r | |
458 | * used in the result.\r | |
459 | *\r | |
460 | * The *presence* of a thousand separator character in the format string specifies that\r | |
461 | * the *locale-specific* thousand separator (if any) is inserted separating thousand groups.\r | |
462 | *\r | |
463 | * By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.\r | |
464 | *\r | |
465 | * Locale-specific characters are always used in the formatted output when inserting\r | |
466 | * thousand and decimal separators. These can be set using the {@link #thousandSeparator} and\r | |
467 | * {@link #decimalSeparator} options.\r | |
468 | *\r | |
469 | * The format string must specify separator characters according to US/UK conventions ("," as the\r | |
470 | * thousand separator, and "." as the decimal separator)\r | |
471 | *\r | |
472 | * To allow specification of format strings according to local conventions for separator characters, add\r | |
473 | * the string `/i` to the end of the format string. This format depends on the {@link #thousandSeparator} and\r | |
474 | * {@link #decimalSeparator} options. For example, if using European style separators, then the format string\r | |
475 | * can be specified as `'0.000,00'`. This would be equivalent to using `'0,000.00'` when using US style formatting.\r | |
476 | *\r | |
477 | * Examples (123456.789):\r | |
478 | * \r | |
479 | * - `0` - (123457) show only digits, no precision\r | |
480 | * - `0.00` - (123456.79) show only digits, 2 precision\r | |
481 | * - `0.0000` - (123456.7890) show only digits, 4 precision\r | |
482 | * - `0,000` - (123,457) show comma and digits, no precision\r | |
483 | * - `0,000.00` - (123,456.79) show comma and digits, 2 precision\r | |
484 | * - `0,0.00` - (123,456.79) shortcut method, show comma and digits, 2 precision\r | |
485 | * - `0.####` - (123,456.789) Allow maximum 4 decimal places, but do not right pad with zeroes\r | |
486 | * - `0.00##` - (123456.789) Show at least 2 decimal places, maximum 4, but do not right pad with zeroes\r | |
487 | *\r | |
488 | * @param {Number} v The number to format.\r | |
489 | * @param {String} formatString The way you would like to format this text.\r | |
490 | * @return {String} The formatted number.\r | |
491 | */\r | |
492 | number : function(v, formatString) {\r | |
493 | if (!formatString) {\r | |
494 | return v;\r | |
495 | }\r | |
496 | if (isNaN(v)) {\r | |
497 | return '';\r | |
498 | }\r | |
499 | \r | |
500 | var formatFn = me.formatFns[formatString];\r | |
501 | \r | |
502 | // Generate formatting function to be cached and reused keyed by the format string.\r | |
503 | // This results in a 100% performance increase over analyzing the format string each invocation.\r | |
504 | if (!formatFn) {\r | |
505 | \r | |
506 | var originalFormatString = formatString,\r | |
507 | comma = me.thousandSeparator,\r | |
508 | decimalSeparator = me.decimalSeparator,\r | |
509 | precision = 0,\r | |
510 | trimPart = '',\r | |
511 | hasComma,\r | |
512 | splitFormat,\r | |
513 | extraChars,\r | |
514 | trimTrailingZeroes,\r | |
515 | code, len;\r | |
516 | \r | |
517 | // The "/i" suffix allows caller to use a locale-specific formatting string.\r | |
518 | // Clean the format string by removing all but numerals and the decimal separator.\r | |
519 | // Then split the format string into pre and post decimal segments according to *what* the\r | |
520 | // decimal separator is. If they are specifying "/i", they are using the local convention in the format string.\r | |
521 | if (formatString.substr(formatString.length - 2) === '/i') {\r | |
522 | // In a vast majority of cases, the separator will never change over the lifetime of the application.\r | |
523 | // So we'll only regenerate this if we really need to\r | |
524 | if (!me.I18NFormatCleanRe || me.lastDecimalSeparator !== decimalSeparator) {\r | |
525 | me.I18NFormatCleanRe = new RegExp('[^\\d\\' + decimalSeparator + '#]','g');\r | |
526 | me.lastDecimalSeparator = decimalSeparator;\r | |
527 | }\r | |
528 | formatString = formatString.substr(0, formatString.length - 2);\r | |
529 | hasComma = formatString.indexOf(comma) !== -1;\r | |
530 | splitFormat = formatString.replace(me.I18NFormatCleanRe, '').split(decimalSeparator);\r | |
531 | } else {\r | |
532 | hasComma = formatString.indexOf(',') !== -1;\r | |
533 | splitFormat = formatString.replace(me.formatCleanRe, '').split('.');\r | |
534 | }\r | |
535 | extraChars = formatString.replace(me.formatPattern, '');\r | |
536 | \r | |
537 | if (splitFormat.length > 2) {\r | |
538 | //<debug>\r | |
539 | Ext.raise({\r | |
540 | sourceClass: "Ext.util.Format",\r | |
541 | sourceMethod: "number",\r | |
542 | value: v,\r | |
543 | formatString: formatString,\r | |
544 | msg: "Invalid number format, should have no more than 1 decimal"\r | |
545 | });\r | |
546 | //</debug>\r | |
547 | } else if (splitFormat.length === 2) {\r | |
548 | precision = splitFormat[1].length;\r | |
549 | \r | |
550 | // Formatting ending in .##### means maximum 5 trailing significant digits\r | |
551 | trimTrailingZeroes = splitFormat[1].match(me.hashRe);\r | |
552 | if (trimTrailingZeroes) {\r | |
553 | len = trimTrailingZeroes[0].length;\r | |
554 | // Need to escape, since this will be '.' by default\r | |
555 | trimPart = 'trailingZeroes=new RegExp(Ext.String.escapeRegex(utilFormat.decimalSeparator) + "*0{0,' + len + '}$")';\r | |
556 | }\r | |
557 | }\r | |
558 | \r | |
559 | // The function we create is called immediately and returns a closure which has access to vars and some fixed values; RegExes and the format string.\r | |
560 | code = [\r | |
561 | 'var utilFormat=Ext.util.Format,extNumber=Ext.Number,neg,absVal,fnum,parts' +\r | |
562 | (hasComma ? ',thousandSeparator,thousands=[],j,n,i' : '') +\r | |
563 | (extraChars ? ',formatString="' + formatString + '",formatPattern=/[\\d,\\.#]+/' : '') +\r | |
564 | ',trailingZeroes;' +\r | |
565 | 'return function(v){' +\r | |
566 | 'if(typeof v!=="number"&&isNaN(v=extNumber.from(v,NaN)))return"";' +\r | |
567 | 'neg=v<0;',\r | |
568 | 'absVal=Math.abs(v);',\r | |
569 | 'fnum=Ext.Number.toFixed(absVal, ' + precision + ');',\r | |
570 | trimPart, ';'\r | |
571 | ];\r | |
572 | \r | |
573 | if (hasComma) {\r | |
574 | // If we have to insert commas...\r | |
575 | \r | |
576 | // split the string up into whole and decimal parts if there are decimals\r | |
577 | if (precision) {\r | |
578 | code[code.length] = 'parts=fnum.split(".");';\r | |
579 | code[code.length] = 'fnum=parts[0];';\r | |
580 | }\r | |
581 | code[code.length] =\r | |
582 | 'if(absVal>=1000) {';\r | |
583 | code[code.length] = 'thousandSeparator=utilFormat.thousandSeparator;' +\r | |
584 | 'thousands.length=0;' +\r | |
585 | 'j=fnum.length;' +\r | |
586 | 'n=fnum.length%3||3;' +\r | |
587 | 'for(i=0;i<j;i+=n){' +\r | |
588 | 'if(i!==0){' +\r | |
589 | 'n=3;' +\r | |
590 | '}' +\r | |
591 | 'thousands[thousands.length]=fnum.substr(i,n);' +\r | |
592 | '}' +\r | |
593 | 'fnum=thousands.join(thousandSeparator);' + \r | |
594 | '}';\r | |
595 | if (precision) {\r | |
596 | code[code.length] = 'fnum += utilFormat.decimalSeparator+parts[1];';\r | |
597 | }\r | |
598 | \r | |
599 | } else if (precision) {\r | |
600 | // If they are using a weird decimal separator, split and concat using it\r | |
601 | code[code.length] = 'if(utilFormat.decimalSeparator!=="."){' +\r | |
602 | 'parts=fnum.split(".");' +\r | |
603 | 'fnum=parts[0]+utilFormat.decimalSeparator+parts[1];' +\r | |
604 | '}';\r | |
605 | }\r | |
606 | \r | |
607 | /*\r | |
608 | * Edge case. If we have a very small negative number it will get rounded to 0,\r | |
609 | * however the initial check at the top will still report as negative. Replace\r | |
610 | * everything but 1-9 and check if the string is empty to determine a 0 value.\r | |
611 | */\r | |
612 | code[code.length] = 'if(neg&&fnum!=="' + (precision ? '0.' + Ext.String.repeat('0', precision) : '0') + '") { fnum="-"+fnum; }';\r | |
613 | \r | |
614 | if (trimTrailingZeroes) {\r | |
615 | code[code.length] = 'fnum=fnum.replace(trailingZeroes,"");';\r | |
616 | }\r | |
617 | \r | |
618 | code[code.length] = 'return ';\r | |
619 | \r | |
620 | // If there were extra characters around the formatting string, replace the format string part with the formatted number.\r | |
621 | if (extraChars) {\r | |
622 | code[code.length] = 'formatString.replace(formatPattern, fnum);';\r | |
623 | } else {\r | |
624 | code[code.length] = 'fnum;';\r | |
625 | }\r | |
626 | code[code.length] = '};';\r | |
627 | \r | |
628 | formatFn = me.formatFns[originalFormatString] = Ext.functionFactory('Ext', code.join(''))(Ext);\r | |
629 | }\r | |
630 | return formatFn(v);\r | |
631 | },\r | |
632 | \r | |
633 | /**\r | |
634 | * Returns a number rendering function that can be reused to apply a number format multiple\r | |
635 | * times efficiently.\r | |
636 | *\r | |
637 | * @param {String} format Any valid number format string for {@link #number}\r | |
638 | * @return {Function} The number formatting function\r | |
639 | */\r | |
640 | numberRenderer : function(format) {\r | |
641 | return function(v) {\r | |
642 | return me.number(v, format);\r | |
643 | };\r | |
644 | },\r | |
645 | \r | |
646 | /**\r | |
647 | * Formats the passed number as a percentage according to the passed format string.\r | |
648 | * The number should be between 0 and 1 to represent 0% to 100%.\r | |
649 | *\r | |
650 | * @param {Number} value The percentage to format.\r | |
651 | * @param {String} [formatString="0"] See {@link #number} for details.\r | |
652 | * @return {String} The formatted percentage.\r | |
653 | */\r | |
654 | percent: function (value, formatString) {\r | |
655 | return me.number(value * 100, formatString || '0') + me.percentSign;\r | |
656 | },\r | |
657 | \r | |
658 | /**\r | |
659 | * Formats an object of name value properties as HTML element attribute values suitable for using when creating textual markup.\r | |
660 | * @param {Object} attributes An object containing the HTML attributes as properties eg: `{height:40, vAlign:'top'}`\r | |
661 | */\r | |
662 | attributes: function(attributes) {\r | |
663 | if (typeof attributes === 'object') {\r | |
664 | var result = [],\r | |
665 | name;\r | |
666 | \r | |
667 | for (name in attributes) {\r | |
668 | if (attributes.hasOwnProperty(name)) {\r | |
669 | result.push(name, '="', name === 'style' ? \r | |
670 | Ext.DomHelper.generateStyles(attributes[name], null, true) :\r | |
671 | Ext.htmlEncode(attributes[name]), '" ');\r | |
672 | }\r | |
673 | }\r | |
674 | attributes = result.join('');\r | |
675 | }\r | |
676 | return attributes || '';\r | |
677 | },\r | |
678 | \r | |
679 | /**\r | |
680 | * Selectively return the plural form of a word based on a numeric value.\r | |
681 | * \r | |
682 | * For example, the following template would result in "1 Comment". If the \r | |
683 | * value of `count` was 0 or greater than 1, the result would be "x Comments".\r | |
684 | * \r | |
685 | * var tpl = new Ext.XTemplate('{count:plural("Comment")}');\r | |
686 | * \r | |
687 | * tpl.apply({\r | |
688 | * count: 1\r | |
689 | * }); // returns "1 Comment"\r | |
690 | * \r | |
691 | * Examples using the static `plural` method call:\r | |
692 | * \r | |
693 | * Ext.util.Format.plural(2, 'Comment');\r | |
694 | * // returns "2 Comments"\r | |
695 | * \r | |
696 | * Ext.util.Format.plural(4, 'person', 'people');\r | |
697 | * // returns "4 people"\r | |
698 | *\r | |
699 | * @param {Number} value The value to compare against\r | |
700 | * @param {String} singular The singular form of the word\r | |
701 | * @param {String} [plural] The plural form of the word (defaults to the \r | |
702 | * singular form with an "s" appended)\r | |
703 | * @return {String} output The pluralized output of the passed singular form\r | |
704 | */\r | |
705 | plural : function(v, s, p) {\r | |
706 | return v +' ' + (v === 1 ? s : (p ? p : s+'s'));\r | |
707 | },\r | |
708 | \r | |
709 | /**\r | |
710 | * Converts newline characters to the HTML tag `<br/>`\r | |
711 | *\r | |
712 | * @param {String} v The string value to format.\r | |
713 | * @return {String} The string with embedded `<br/>` tags in place of newlines.\r | |
714 | */\r | |
715 | nl2br : function(v) {\r | |
716 | return Ext.isEmpty(v) ? '' : v.replace(me.nl2brRe, '<br/>');\r | |
717 | },\r | |
718 | \r | |
719 | /**\r | |
720 | * Alias for {@link Ext.String#capitalize}.\r | |
721 | * @method\r | |
722 | * @inheritdoc Ext.String#capitalize\r | |
723 | */\r | |
724 | capitalize: Ext.String.capitalize,\r | |
725 | \r | |
726 | /**\r | |
727 | * Alias for {@link Ext.String#uncapitalize}.\r | |
728 | * @method\r | |
729 | * @inheritdoc Ext.String#uncapitalize\r | |
730 | */\r | |
731 | uncapitalize: Ext.String.uncapitalize,\r | |
732 | \r | |
733 | /**\r | |
734 | * Alias for {@link Ext.String#ellipsis}.\r | |
735 | * @method\r | |
736 | * @inheritdoc Ext.String#ellipsis\r | |
737 | */\r | |
738 | ellipsis: Ext.String.ellipsis,\r | |
739 | \r | |
740 | /**\r | |
741 | * Alias for {@link Ext.String#escape}.\r | |
742 | * @method\r | |
743 | * @inheritdoc Ext.String#escape\r | |
744 | */\r | |
745 | escape: Ext.String.escape,\r | |
746 | \r | |
747 | /**\r | |
748 | * Alias for {@link Ext.String#escapeRegex}.\r | |
749 | * @method\r | |
750 | * @inheritdoc Ext.String#escapeRegex\r | |
751 | */\r | |
752 | escapeRegex : Ext.String.escapeRegex,\r | |
753 | \r | |
754 | /**\r | |
755 | * Alias for {@link Ext.String#htmlDecode}.\r | |
756 | * @method\r | |
757 | * @inheritdoc Ext.String#htmlDecode\r | |
758 | */\r | |
759 | htmlDecode: Ext.String.htmlDecode,\r | |
760 | \r | |
761 | /**\r | |
762 | * Alias for {@link Ext.String#htmlEncode}.\r | |
763 | * @method\r | |
764 | * @inheritdoc Ext.String#htmlEncode\r | |
765 | */\r | |
766 | htmlEncode: Ext.String.htmlEncode,\r | |
767 | \r | |
768 | /**\r | |
769 | * Alias for {@link Ext.String#leftPad}.\r | |
770 | * @method\r | |
771 | * @inheritdoc Ext.String#leftPad\r | |
772 | */\r | |
773 | leftPad: Ext.String.leftPad,\r | |
774 | \r | |
775 | /**\r | |
776 | * Alias for {@link Ext.String#toggle}.\r | |
777 | * @method\r | |
778 | * @inheritdoc Ext.String#toggle\r | |
779 | */\r | |
780 | toggle: Ext.String.toggle,\r | |
781 | \r | |
782 | /**\r | |
783 | * Alias for {@link Ext.String#trim}.\r | |
784 | * @method\r | |
785 | * @inheritdoc Ext.String#trim\r | |
786 | */\r | |
787 | trim : Ext.String.trim,\r | |
788 | \r | |
789 | /**\r | |
790 | * Parses a number or string representing margin sizes into an object.\r | |
791 | * Supports CSS-style margin declarations (e.g. 10, "10", "10 10", "10 10 10" and\r | |
792 | * "10 10 10 10" are all valid options and would return the same result).\r | |
793 | *\r | |
794 | * @param {Number/String} box The encoded margins\r | |
795 | * @return {Object} An object with margin sizes for top, right, bottom and left\r | |
796 | */\r | |
797 | parseBox : function(box) {\r | |
798 | box = box || 0;\r | |
799 | \r | |
800 | if (typeof box === 'number') {\r | |
801 | return {\r | |
802 | top : box,\r | |
803 | right : box,\r | |
804 | bottom: box,\r | |
805 | left : box\r | |
806 | };\r | |
807 | }\r | |
808 | \r | |
809 | var parts = box.split(' '),\r | |
810 | ln = parts.length;\r | |
811 | \r | |
812 | if (ln === 1) {\r | |
813 | parts[1] = parts[2] = parts[3] = parts[0];\r | |
814 | }\r | |
815 | else if (ln === 2) {\r | |
816 | parts[2] = parts[0];\r | |
817 | parts[3] = parts[1];\r | |
818 | }\r | |
819 | else if (ln === 3) {\r | |
820 | parts[3] = parts[1];\r | |
821 | }\r | |
822 | \r | |
823 | return {\r | |
824 | top :parseInt(parts[0], 10) || 0,\r | |
825 | right :parseInt(parts[1], 10) || 0,\r | |
826 | bottom:parseInt(parts[2], 10) || 0,\r | |
827 | left :parseInt(parts[3], 10) || 0\r | |
828 | };\r | |
829 | }\r | |
830 | };\r | |
831 | });\r |