Diffstat (limited to 'frontend/gamma/js/MochiKit/Text.js') (more/less context) (ignore whitespace changes)
-rw-r--r-- | frontend/gamma/js/MochiKit/Text.js | 359 |
1 files changed, 164 insertions, 195 deletions
diff --git a/frontend/gamma/js/MochiKit/Text.js b/frontend/gamma/js/MochiKit/Text.js index a44f7e4..ff6366d 100644 --- a/frontend/gamma/js/MochiKit/Text.js +++ b/frontend/gamma/js/MochiKit/Text.js | |||
@@ -7,9 +7,9 @@ See <http://mochikit.com/> for documentation, downloads, license, etc. | |||
7 | (c) 2008 Per Cederberg. All rights Reserved. | 7 | (c) 2008 Per Cederberg. All rights Reserved. |
8 | 8 | ||
9 | ***/ | 9 | ***/ |
10 | 10 | ||
11 | MochiKit.Base._module('Text', '1.5', ['Base', 'Format']); | 11 | MochiKit.Base.module(MochiKit, 'Text', '1.5', ['Base', 'Format']); |
12 | 12 | ||
13 | /** | 13 | /** |
14 | * Checks if a text string starts with the specified substring. If | 14 | * Checks if a text string starts with the specified substring. If |
15 | * either of the two strings is null, false will be returned. | 15 | * either of the two strings is null, false will be returned. |
@@ -21,9 +21,9 @@ MochiKit.Base._module('Text', '1.5', ['Base', 'Format']); | |||
21 | * false otherwise | 21 | * false otherwise |
22 | */ | 22 | */ |
23 | MochiKit.Text.startsWith = function (substr, str) { | 23 | MochiKit.Text.startsWith = function (substr, str) { |
24 | return str != null && substr != null && str.indexOf(substr) == 0; | 24 | return str != null && substr != null && str.indexOf(substr) == 0; |
25 | } | 25 | }; |
26 | 26 | ||
27 | /** | 27 | /** |
28 | * Checks if a text string ends with the specified substring. If | 28 | * Checks if a text string ends with the specified substring. If |
29 | * either of the two strings is null, false will be returned. | 29 | * either of the two strings is null, false will be returned. |
@@ -36,9 +36,9 @@ MochiKit.Text.startsWith = function (substr, str) { | |||
36 | */ | 36 | */ |
37 | MochiKit.Text.endsWith = function (substr, str) { | 37 | MochiKit.Text.endsWith = function (substr, str) { |
38 | return str != null && substr != null && | 38 | return str != null && substr != null && |
39 | str.lastIndexOf(substr) == Math.max(str.length - substr.length, 0); | 39 | str.lastIndexOf(substr) == Math.max(str.length - substr.length, 0); |
40 | } | 40 | }; |
41 | 41 | ||
42 | /** | 42 | /** |
43 | * Checks if a text string contains the specified substring. If | 43 | * Checks if a text string contains the specified substring. If |
44 | * either of the two strings is null, false will be returned. | 44 | * either of the two strings is null, false will be returned. |
@@ -50,9 +50,9 @@ MochiKit.Text.endsWith = function (substr, str) { | |||
50 | * false otherwise | 50 | * false otherwise |
51 | */ | 51 | */ |
52 | MochiKit.Text.contains = function (substr, str) { | 52 | MochiKit.Text.contains = function (substr, str) { |
53 | return str != null && substr != null && str.indexOf(substr) >= 0; | 53 | return str != null && substr != null && str.indexOf(substr) >= 0; |
54 | } | 54 | }; |
55 | 55 | ||
56 | /** | 56 | /** |
57 | * Adds a character to the left-hand side of a string until it | 57 | * Adds a character to the left-hand side of a string until it |
58 | * reaches the specified minimum length. | 58 | * reaches the specified minimum length. |
@@ -70,9 +70,9 @@ MochiKit.Text.padLeft = function (str, minLength, fillChar) { | |||
70 | while (str.length < minLength) { | 70 | while (str.length < minLength) { |
71 | str = fillChar + str; | 71 | str = fillChar + str; |
72 | } | 72 | } |
73 | return str; | 73 | return str; |
74 | } | 74 | }; |
75 | 75 | ||
76 | /** | 76 | /** |
77 | * Adds a character to the right-hand side of a string until it | 77 | * Adds a character to the right-hand side of a string until it |
78 | * reaches the specified minimum length. | 78 | * reaches the specified minimum length. |
@@ -90,9 +90,9 @@ MochiKit.Text.padRight = function (str, minLength, fillChar) { | |||
90 | while (str.length < minLength) { | 90 | while (str.length < minLength) { |
91 | str += fillChar; | 91 | str += fillChar; |
92 | } | 92 | } |
93 | return str; | 93 | return str; |
94 | } | 94 | }; |
95 | 95 | ||
96 | /** | 96 | /** |
97 | * Returns a truncated copy of a string. If the string is shorter | 97 | * Returns a truncated copy of a string. If the string is shorter |
98 | * than the specified maximum length, the object will be returned | 98 | * than the specified maximum length, the object will be returned |
@@ -118,31 +118,57 @@ MochiKit.Text.truncate = function (str, maxLength, tail) { | |||
118 | } | 118 | } |
119 | } else { | 119 | } else { |
120 | return str.slice(0, maxLength); | 120 | return str.slice(0, maxLength); |
121 | } | 121 | } |
122 | } | 122 | }; |
123 | 123 | ||
124 | /** | 124 | /** |
125 | * Splits a text string, applies a function and joins the results | 125 | * Splits a text string using separator as the split point |
126 | * back together again. This is a convenience function for calling | 126 | * If max is given, at most max splits are done, giving at most |
127 | * split(), map() and join() separately. It can be used to easily | 127 | * max + 1 elements in the returned list. |
128 | * trim each line in a text string (using the strip function), or to | ||
129 | * translate a text word-by-word. | ||
130 | * | 128 | * |
131 | * @param {Function} func the function to apply | ||
132 | * @param {String} str the string to split | 129 | * @param {String} str the string to split |
133 | * @param {String} [separator] the separator character to use, | 130 | * @param {String/RegExp} [separator] the separator char or regexp to use, |
134 | * defaults to newline | 131 | * defaults to newline |
132 | * @param {Number} [max] the maximum number of parts to return | ||
133 | * @return {Array} an array of parts of the string | ||
134 | */ | ||
135 | MochiKit.Text.split = function (str, separator, max) { | ||
136 | if (str == null) { | ||
137 | return str; | ||
138 | } | ||
139 | separator = separator || '\n'; | ||
140 | var bits = str.split(separator); | ||
141 | if ((typeof(max) == "undefined") || max >= bits.length - 1) { | ||
142 | return bits; | ||
143 | } | ||
144 | bits.splice(max, bits.length, bits.slice(max, bits.length).join(separator)); | ||
145 | return bits; | ||
146 | }; | ||
147 | |||
148 | /** | ||
149 | * Splits a text string using separator as the split point | ||
150 | * If max is given, at most max splits are done, | ||
151 | * using splits from the right | ||
135 | * | 152 | * |
136 | * @return {String} a string with the joined up results | 153 | * @param {String} str the string to split |
154 | * @param {String/RegExp} [separator] the separator char or regexp to use, | ||
155 | * defaults to newline | ||
156 | * @param {Number} [max] the maximum number of parts to return | ||
157 | * @return {Array} an array of parts of the string | ||
137 | */ | 158 | */ |
138 | MochiKit.Text.splitJoin = function (func, str, separator) { | 159 | MochiKit.Text.rsplit = function (str, separator, max) { |
139 | if (str == null || str.length == 0) { | 160 | if (str == null) { |
140 | return str; | 161 | return str; |
141 | } | 162 | } |
142 | separator = separator || '\n' | 163 | separator = separator || '\n'; |
143 | return MochiKit.Base.map(func, str.split(separator)).join(separator); | 164 | var bits = str.split(separator); |
144 | } | 165 | if ((typeof(max) == "undefined") || max >= bits.length - 1){ |
166 | return bits; | ||
167 | } | ||
168 | bits.splice(0, bits.length-max, bits.slice(0, bits.length-max).join(separator)); | ||
169 | return bits; | ||
170 | }; | ||
145 | 171 | ||
146 | /** | 172 | /** |
147 | * Creates a formatter function for the specified formatter pattern | 173 | * Creates a formatter function for the specified formatter pattern |
148 | * and locale. The returned function takes as many arguments as the | 174 | * and locale. The returned function takes as many arguments as the |
@@ -157,9 +183,9 @@ MochiKit.Text.splitJoin = function (func, str, separator) { | |||
157 | * | 183 | * |
158 | * @throws FormatPatternError if the format pattern was invalid | 184 | * @throws FormatPatternError if the format pattern was invalid |
159 | */ | 185 | */ |
160 | MochiKit.Text.formatter = function (pattern, locale) { | 186 | MochiKit.Text.formatter = function (pattern, locale) { |
161 | if (typeof(locale) == "undefined") { | 187 | if (locale == null) { |
162 | locale = MochiKit.Format.formatLocale(); | 188 | locale = MochiKit.Format.formatLocale(); |
163 | } else if (typeof(locale) == "string") { | 189 | } else if (typeof(locale) == "string") { |
164 | locale = MochiKit.Format.formatLocale(locale); | 190 | locale = MochiKit.Format.formatLocale(locale); |
165 | } | 191 | } |
@@ -174,10 +200,10 @@ MochiKit.Text.formatter = function (pattern, locale) { | |||
174 | res.push(MochiKit.Text.formatValue(parts[i], values, locale)); | 200 | res.push(MochiKit.Text.formatValue(parts[i], values, locale)); |
175 | } | 201 | } |
176 | } | 202 | } |
177 | return res.join(""); | 203 | return res.join(""); |
178 | } | 204 | }; |
179 | } | 205 | }; |
180 | 206 | ||
181 | /** | 207 | /** |
182 | * Formats the specified arguments according to a formatter pattern. | 208 | * Formats the specified arguments according to a formatter pattern. |
183 | * See separate documentation for information about the formatter | 209 | * See separate documentation for information about the formatter |
@@ -192,9 +218,9 @@ MochiKit.Text.formatter = function (pattern, locale) { | |||
192 | */ | 218 | */ |
193 | MochiKit.Text.format = function (pattern/*, ...*/) { | 219 | MochiKit.Text.format = function (pattern/*, ...*/) { |
194 | var func = MochiKit.Text.formatter(pattern); | 220 | var func = MochiKit.Text.formatter(pattern); |
195 | return func.apply(this, MochiKit.Base.extend([], arguments, 1)); | 221 | return func.apply(this, MochiKit.Base.extend([], arguments, 1)); |
196 | } | 222 | }; |
197 | 223 | ||
198 | /** | 224 | /** |
199 | * Format a value with the specified format specifier. | 225 | * Format a value with the specified format specifier. |
200 | * | 226 | * |
@@ -204,35 +230,39 @@ MochiKit.Text.format = function (pattern/*, ...*/) { | |||
204 | * @param {Object} [locale] the locale to use, defaults to | 230 | * @param {Object} [locale] the locale to use, defaults to |
205 | * LOCALE.en_US | 231 | * LOCALE.en_US |
206 | * | 232 | * |
207 | * @return {String} the formatted output string | 233 | * @return {String} the formatted output string |
234 | * | ||
235 | * @throws FormatPatternError if the format specifier was invalid | ||
208 | */ | 236 | */ |
209 | MochiKit.Text.formatValue = function (spec, value, locale) { | 237 | MochiKit.Text.formatValue = function (spec, value, locale) { |
210 | var self = MochiKit.Text; | 238 | var self = MochiKit.Text; |
211 | if (typeof(spec) === "string") { | 239 | if (typeof(spec) === "string") { |
212 | spec = self._parseFormatFlags(spec, 0, spec.length - 1); | 240 | spec = self._parseFormatFlags(spec, 0, spec.length); |
213 | } | 241 | } |
214 | for (var i = 0; spec.path != null && i < spec.path.length; i++) { | 242 | for (var i = 0; spec.path != null && i < spec.path.length; i++) { |
215 | if (value != null) { | 243 | if (value != null) { |
216 | value = value[spec.path[i]]; | 244 | value = value[spec.path[i]]; |
217 | } | 245 | } |
218 | } | 246 | } |
219 | if (typeof(locale) == "undefined") { | 247 | if (locale == null) { |
220 | locale = MochiKit.Format.formatLocale(); | 248 | locale = MochiKit.Format.formatLocale(); |
221 | } else if (typeof(locale) == "string") { | 249 | } else if (typeof(locale) == "string") { |
222 | locale = MochiKit.Format.formatLocale(locale); | 250 | locale = MochiKit.Format.formatLocale(locale); |
223 | } | 251 | } |
224 | var str = ""; | 252 | var str = ""; |
225 | if (spec.numeric) { | 253 | if (spec.type == "number") { |
254 | if (value instanceof Number) { | ||
255 | value = value.valueOf(); | ||
256 | } | ||
226 | if (typeof(value) != "number" || isNaN(value)) { | 257 | if (typeof(value) != "number" || isNaN(value)) { |
227 | str = ""; | 258 | str = ""; |
228 | } else if (value === Number.POSITIVE_INFINITY) { | 259 | } else if (value === Number.POSITIVE_INFINITY) { |
229 | str = "\u221e"; | 260 | str = "\u221e"; |
230 | } else if (value === Number.NEGATIVE_INFINITY) { | 261 | } else if (value === Number.NEGATIVE_INFINITY) { |
231 | str = "-\u221e"; | 262 | str = "-\u221e"; |
232 | } else { | 263 | } else { |
233 | var sign = (spec.sign === "-") ? "" : spec.sign; | 264 | var sign = (value < 0) ? "-" : spec.sign; |
234 | sign = (value < 0) ? "-" : sign; | ||
235 | value = Math.abs(value); | 265 | value = Math.abs(value); |
236 | if (spec.format === "%") { | 266 | if (spec.format === "%") { |
237 | str = self._truncToPercent(value, spec.precision); | 267 | str = self._truncToPercent(value, spec.precision); |
238 | } else if (spec.format === "d") { | 268 | } else if (spec.format === "d") { |
@@ -253,9 +283,9 @@ MochiKit.Text.formatValue = function (spec, value, locale) { | |||
253 | str = self.padLeft(str, spec.width - sign.length - 1, "0"); | 283 | str = self.padLeft(str, spec.width - sign.length - 1, "0"); |
254 | } else if (spec.padding == "0") { | 284 | } else if (spec.padding == "0") { |
255 | str = self.padLeft(str, spec.width - sign.length, "0"); | 285 | str = self.padLeft(str, spec.width - sign.length, "0"); |
256 | } | 286 | } |
257 | str = self._localizeNumber(str, locale, spec.grouping); | 287 | str = self._localizeNumber(str, locale, spec.group); |
258 | str = sign + str; | 288 | str = sign + str; |
259 | } | 289 | } |
260 | if (str !== "" && spec.format === "%") { | 290 | if (str !== "" && spec.format === "%") { |
261 | str = str + locale.percent; | 291 | str = str + locale.percent; |
@@ -263,9 +293,9 @@ MochiKit.Text.formatValue = function (spec, value, locale) { | |||
263 | } else { | 293 | } else { |
264 | if (spec.format == "r") { | 294 | if (spec.format == "r") { |
265 | str = MochiKit.Base.repr(value); | 295 | str = MochiKit.Base.repr(value); |
266 | } else { | 296 | } else { |
267 | str = (value == null) ? "null" : value.toString(); | 297 | str = (value == null) ? "" : value.toString(); |
268 | } | 298 | } |
269 | str = self.truncate(str, spec.precision); | 299 | str = self.truncate(str, spec.precision); |
270 | } | 300 | } |
271 | if (spec.align == "<") { | 301 | if (spec.align == "<") { |
@@ -273,9 +303,9 @@ MochiKit.Text.formatValue = function (spec, value, locale) { | |||
273 | } else { | 303 | } else { |
274 | str = self.padLeft(str, spec.width); | 304 | str = self.padLeft(str, spec.width); |
275 | } | 305 | } |
276 | return str; | 306 | return str; |
277 | } | 307 | }; |
278 | 308 | ||
279 | /** | 309 | /** |
280 | * Adjust an already formatted numeric string for locale-specific | 310 | * Adjust an already formatted numeric string for locale-specific |
281 | * grouping and decimal separators. The grouping is optional and | 311 | * grouping and decimal separators. The grouping is optional and |
@@ -283,34 +313,34 @@ MochiKit.Text.formatValue = function (spec, value, locale) { | |||
283 | * padded zeros (if possible). | 313 | * padded zeros (if possible). |
284 | * | 314 | * |
285 | * @param {String} num the formatted number string | 315 | * @param {String} num the formatted number string |
286 | * @param {Object} locale the formatting locale to use | 316 | * @param {Object} locale the formatting locale to use |
287 | * @param {Boolean} grouping the grouping flag | 317 | * @param {Boolean} group the grouping flag |
288 | * | 318 | * |
289 | * @return {String} the localized number string | 319 | * @return {String} the localized number string |
290 | */ | 320 | */ |
291 | MochiKit.Text._localizeNumber = function (num, locale, grouping) { | 321 | MochiKit.Text._localizeNumber = function (num, locale, group) { |
292 | var parts = num.split(/\./); | 322 | var parts = num.split(/\./); |
293 | var whole = parts[0]; | 323 | var whole = parts[0]; |
294 | var frac = (parts.length == 1) ? "" : parts[1]; | 324 | var frac = (parts.length == 1) ? "" : parts[1]; |
295 | var res = (frac.length > 0) ? locale.decimal : ""; | 325 | var res = (frac.length > 0) ? locale.decimal : ""; |
296 | while (grouping && frac.length > 3) { | 326 | while (group && frac.length > 3) { |
297 | res = res + frac.substring(0, 3) + locale.separator; | 327 | res = res + frac.substring(0, 3) + locale.separator; |
298 | frac = frac.substring(3); | 328 | frac = frac.substring(3); |
299 | if (whole.charAt(0) == "0") { | 329 | if (whole.charAt(0) == "0") { |
300 | whole = whole.substring(1); | 330 | whole = whole.substring(1); |
301 | } | 331 | } |
302 | } | 332 | } |
303 | if (frac.length > 0) { | 333 | if (frac.length > 0) { |
304 | res += frac; | 334 | res = res + frac; |
305 | } | 335 | } |
306 | while (grouping && whole.length > 3) { | 336 | while (group && whole.length > 3) { |
307 | var pos = whole.length - 3; | 337 | var pos = whole.length - 3; |
308 | res = locale.separator + whole.substring(pos) + res; | 338 | res = locale.separator + whole.substring(pos) + res; |
309 | whole = whole.substring((whole.charAt(0) == "0") ? 1 : 0, pos); | 339 | whole = whole.substring((whole.charAt(0) == "0") ? 1 : 0, pos); |
310 | } | 340 | } |
311 | return whole + res; | 341 | return whole + res; |
312 | } | 342 | }; |
313 | 343 | ||
314 | /** | 344 | /** |
315 | * Parses a format pattern and returns an array of constant strings | 345 | * Parses a format pattern and returns an array of constant strings |
316 | * and format info objects. | 346 | * and format info objects. |
@@ -323,46 +353,34 @@ MochiKit.Text._localizeNumber = function (num, locale, grouping) { | |||
323 | */ | 353 | */ |
324 | MochiKit.Text._parsePattern = function (pattern) { | 354 | MochiKit.Text._parsePattern = function (pattern) { |
325 | var self = MochiKit.Text; | 355 | var self = MochiKit.Text; |
326 | var parts = []; | 356 | var parts = []; |
327 | var start = 0; | 357 | var re = /{[^{}]*}|{{?|}}?/g; |
328 | var pos = 0; | 358 | var lastPos = re.lastIndex = 0; |
329 | for (pos = 0; pos < pattern.length; pos++) { | 359 | var m; |
330 | if (pattern.charAt(pos) == "{") { | 360 | while ((m = re.exec(pattern)) != null) { |
331 | if (pos + 1 >= pattern.length) { | 361 | if (lastPos < m.index) { |
332 | var msg = "unescaped { char, should be escaped as {{"; | 362 | parts.push(pattern.substring(lastPos, m.index)) |
333 | throw new self.FormatPatternError(pattern, pos, msg); | 363 | } |
334 | } else if (pattern.charAt(pos + 1) == "{") { | 364 | var str = m[0]; |
335 | parts.push(pattern.substring(start, pos + 1)); | 365 | lastPos = m.index + str.length; |
336 | start = pos + 2; | 366 | if (self.startsWith("{", str) && self.endsWith("}", str)) { |
337 | pos++; | 367 | parts.push(self._parseFormat(pattern, m.index + 1, lastPos - 1)); |
338 | } else { | 368 | } else if (self.startsWith("{{", str) || self.startsWith("}}", str)) { |
339 | if (start < pos) { | 369 | parts.push(str.substring(1)); |
340 | parts.push(pattern.substring(start, pos)); | 370 | } else if (self.startsWith("{", str)) { |
341 | } | 371 | var msg = "unescaped { char, should be escaped as {{"; |
342 | start = pattern.indexOf("}", pos) + 1; | 372 | throw new self.FormatPatternError(pattern, m.index, msg); |
343 | if (start <= 0) { | 373 | } else if (self.startsWith("}", str)) { |
344 | var msg = "unmatched { char, not followed by a } char"; | 374 | var msg = "unescaped } char, should be escaped as }}"; |
345 | throw new self.FormatPatternError(pattern, pos, msg); | 375 | throw new self.FormatPatternError(pattern, m.index, msg); |
346 | } | ||
347 | parts.push(self._parseFormat(pattern, pos + 1, start - 1)); | ||
348 | pos = start - 1; | ||
349 | } | ||
350 | } else if (pattern.charAt(pos) == "}") { | ||
351 | if (pos + 1 >= pattern.length || pattern.charAt(pos + 1) != "}") { | ||
352 | var msg = "unescaped } char, should be escaped as }}"; | ||
353 | throw new self.FormatPatternError(pattern, pos, msg); | ||
354 | } | ||
355 | parts.push(pattern.substring(start, pos + 1)); | ||
356 | start = pos + 2; | ||
357 | pos++; | ||
358 | } | 376 | } |
359 | } | 377 | } |
360 | if (start < pos) { | 378 | if (lastPos < pattern.length) { |
361 | parts.push(pattern.substring(start, pos)); | 379 | parts.push(pattern.substring(lastPos)); |
362 | } | 380 | } |
363 | return parts; | 381 | return parts; |
364 | } | 382 | }; |
365 | 383 | ||
366 | /** | 384 | /** |
367 | * Parses a format instruction and returns a format info object. | 385 | * Parses a format instruction and returns a format info object. |
368 | * | 386 | * |
@@ -376,42 +394,32 @@ MochiKit.Text._parsePattern = function (pattern) { | |||
376 | */ | 394 | */ |
377 | MochiKit.Text._parseFormat = function (pattern, startPos, endPos) { | 395 | MochiKit.Text._parseFormat = function (pattern, startPos, endPos) { |
378 | var self = MochiKit.Text; | 396 | var self = MochiKit.Text; |
379 | var text = pattern.substring(startPos, endPos); | 397 | var text = pattern.substring(startPos, endPos); |
380 | var info; | 398 | var parts = self.split(text, ":", 1); |
381 | var pos = text.indexOf(":"); | 399 | var path = parts[0]; |
382 | if (pos == 0) { | 400 | var flagsPos = startPos + path.length + ((parts.length == 1) ? 0 : 1); |
383 | info = self._parseFormatFlags(pattern, startPos + 1, endPos); | 401 | var info = self._parseFormatFlags(pattern, flagsPos, endPos); |
384 | info.path = [0]; | 402 | info.path = (path == "") ? [] : path.split("."); |
385 | } else if (pos > 0) { | ||
386 | info = self._parseFormatFlags(pattern, startPos + pos + 1, endPos); | ||
387 | info.path = text.substring(0, pos).split("."); | ||
388 | } else { | ||
389 | info = self._parseFormatFlags(pattern, endPos, endPos); | ||
390 | info.path = text.split("."); | ||
391 | } | ||
392 | var DIGITS = /^\d+$/; | ||
393 | for (var i = 0; i < info.path.length; i++) { | 403 | for (var i = 0; i < info.path.length; i++) { |
394 | var e = info.path[i]; | 404 | var v = info.path[i]; |
395 | if (typeof(e) == "string") { | 405 | // TODO: replace with MochiKit.Format.strip? |
396 | // TODO: replace with MochiKit.Format.strip? | 406 | v = v.replace(/^\s+/, "").replace(/\s+$/, ""); |
397 | e = e.replace(/^\s+/, "").replace(/\s+$/, ""); | 407 | if (v == "" && info.path.length == 1) { |
398 | if (e == "" && info.path.length == 1) { | 408 | v = 0; |
399 | e = 0; | 409 | } else if (v == "") { |
400 | } else if (e == "") { | 410 | var msg = "format value path contains blanks"; |
401 | var msg = "format value path contains blanks"; | 411 | throw new self.FormatPatternError(pattern, startPos, msg); |
402 | throw new self.FormatPatternError(pattern, startPos, msg); | 412 | } else if (/^\d+$/.test(v)) { |
403 | } else if (DIGITS.test(e)) { | 413 | v = parseInt(v, 10); |
404 | e = parseInt(e); | ||
405 | } | ||
406 | } | 414 | } |
407 | info.path[i] = e; | 415 | info.path[i] = v; |
408 | } | 416 | } |
409 | if (info.path.length < 0 || typeof(info.path[0]) != "number") { | 417 | if (info.path.length <= 0 || typeof(info.path[0]) != "number") { |
410 | info.path.unshift(0); | 418 | info.path.unshift(0); |
411 | } | 419 | } |
412 | return info; | 420 | return info; |
413 | } | 421 | }; |
414 | 422 | ||
415 | /** | 423 | /** |
416 | * Parses a string with format flags and returns a format info object. | 424 | * Parses a string with format flags and returns a format info object. |
417 | * | 425 | * |
@@ -423,83 +431,54 @@ MochiKit.Text._parseFormat = function (pattern, startPos, endPos) { | |||
423 | * | 431 | * |
424 | * @throws FormatPatternError if the format pattern was invalid | 432 | * @throws FormatPatternError if the format pattern was invalid |
425 | */ | 433 | */ |
426 | MochiKit.Text._parseFormatFlags = function (pattern, startPos, endPos) { | 434 | MochiKit.Text._parseFormatFlags = function (pattern, startPos, endPos) { |
427 | var self = MochiKit.Text; | 435 | var update = MochiKit.Base.update; |
428 | var info = { numeric: false, format: "s", width: 0, precision: -1, | 436 | var info = { type: "string", format: "s", width: 0, precision: -1, |
429 | align: ">", sign: "-", padding: " ", grouping: false }; | 437 | align: ">", sign: "", padding: " ", group: false }; |
430 | // TODO: replace with MochiKit.Format.rstrip? | 438 | // TODO: replace with MochiKit.Format.rstrip? |
431 | var flags = pattern.substring(startPos, endPos).replace(/\s+$/, ""); | 439 | var text = pattern.substring(startPos, endPos).replace(/\s+$/, ""); |
432 | while (flags.length > 0) { | 440 | var m = /^([<>+ 0,-]+)?(\d+)?(\.\d*)?([srbdoxXf%])?(.*)$/.exec(text); |
433 | switch (flags.charAt(0)) { | 441 | var flags = m[1]; |
434 | case ">": | 442 | var width = m[2]; |
435 | case "<": | 443 | var precision = m[3]; |
436 | info.align = flags.charAt(0); | 444 | var type = m[4]; |
437 | flags = flags.substring(1); | 445 | var unmatched = m[5]; |
438 | break; | 446 | for (var i = 0; flags && i < flags.length; i++) { |
439 | case "+": | 447 | var chr = flags.charAt(i); |
440 | case "-": | 448 | if (chr == "<" || chr == ">") { |
441 | case " ": | 449 | info.align = chr; |
442 | info.sign = flags.charAt(0); | 450 | } else if (chr == "+" || chr == "-" || chr == " ") { |
443 | flags = flags.substring(1); | 451 | info.sign = (chr == "-") ? "" : chr; |
444 | break; | 452 | } else if (chr == "0") { |
445 | case ",": | 453 | info.padding = chr; |
446 | info.grouping = true; | 454 | } else if (chr == ",") { |
447 | flags = flags.substring(1); | 455 | info.group = true; |
448 | break; | ||
449 | case ".": | ||
450 | var chars = /^\d*/.exec(flags.substring(1))[0]; | ||
451 | info.precision = parseInt(chars); | ||
452 | flags = flags.substring(1 + chars.length); | ||
453 | break; | ||
454 | case "0": | ||
455 | info.padding = flags.charAt(0); | ||
456 | flags = flags.substring(1); | ||
457 | break; | ||
458 | case "1": | ||
459 | case "2": | ||
460 | case "3": | ||
461 | case "4": | ||
462 | case "5": | ||
463 | case "6": | ||
464 | case "7": | ||
465 | case "8": | ||
466 | case "9": | ||
467 | var chars = /^\d*/.exec(flags)[0]; | ||
468 | info.width = parseInt(chars); | ||
469 | flags = flags.substring(chars.length); | ||
470 | break; | ||
471 | case "s": | ||
472 | case "r": | ||
473 | info.format = flags.charAt(0); | ||
474 | flags = flags.substring(1); | ||
475 | break; | ||
476 | case "b": | ||
477 | case "d": | ||
478 | case "o": | ||
479 | case "x": | ||
480 | case "X": | ||
481 | case "f": | ||
482 | case "%": | ||
483 | info.numeric = true; | ||
484 | info.format = flags.charAt(0); | ||
485 | info.radix = 10; | ||
486 | if (info.format === "b") { | ||
487 | info.radix = 2; | ||
488 | } else if (info.format === "o") { | ||
489 | info.radix = 8; | ||
490 | } else if (info.format === "x" || info.format === "X") { | ||
491 | info.radix = 16; | ||
492 | } | ||
493 | flags = flags.substring(1); | ||
494 | break; | ||
495 | default: | ||
496 | var msg = "unsupported format flag: " + flags.charAt(0); | ||
497 | throw new self.FormatPatternError(pattern, startPos, msg); | ||
498 | } | 456 | } |
499 | } | 457 | } |
458 | if (width) { | ||
459 | info.width = parseInt(width, 10); | ||
460 | } | ||
461 | if (precision && precision.length > 1) { | ||
462 | info.precision = parseInt(precision.substring(1), 10); | ||
463 | } | ||
464 | if (type == "s" || type == "r") { | ||
465 | info.format = type; | ||
466 | } else if (type == "b") { | ||
467 | update(info, { type: "number", format: type, radix: 2 }); | ||
468 | } else if (type == "o") { | ||
469 | update(info, { type: "number", format: type, radix: 8 }); | ||
470 | } else if (type == "x" || type == "X") { | ||
471 | update(info, { type: "number", format: type, radix: 16 }); | ||
472 | } else if (type == "d" || type == "f" || type == "%") { | ||
473 | update(info, { type: "number", format: type, radix: 10 }); | ||
474 | } | ||
475 | if (unmatched) { | ||
476 | var msg = "unsupported format flag: " + unmatched.charAt(0); | ||
477 | throw new MochiKit.Text.FormatPatternError(pattern, startPos, msg); | ||
478 | } | ||
500 | return info; | 479 | return info; |
501 | } | 480 | }; |
502 | 481 | ||
503 | /** | 482 | /** |
504 | * Formats a value as a percentage. This method avoids multiplication | 483 | * Formats a value as a percentage. This method avoids multiplication |
505 | * by 100 since it leads to weird numeric rounding errors. Instead it | 484 | * by 100 since it leads to weird numeric rounding errors. Instead it |
@@ -509,35 +488,25 @@ MochiKit.Text._parseFormatFlags = function (pattern, startPos, endPos) { | |||
509 | * @param {Number} value the value to format | 488 | * @param {Number} value the value to format |
510 | * @param {Number} precision the number of precision digits | 489 | * @param {Number} precision the number of precision digits |
511 | */ | 490 | */ |
512 | MochiKit.Text._truncToPercent = function (value, precision) { | 491 | MochiKit.Text._truncToPercent = function (value, precision) { |
513 | // TODO: This can be simplified by using the same helper function | 492 | // TODO: This can be simplified by using MochiKit.Format._shiftNumber |
514 | // as roundToFixed now does. | 493 | // as roundToFixed does. |
515 | var str; | 494 | var str; |
516 | if (precision >= 0) { | 495 | if (precision >= 0) { |
517 | str = MochiKit.Format.roundToFixed(value, precision + 2); | 496 | str = MochiKit.Format.roundToFixed(value, precision + 2); |
518 | } else { | 497 | } else { |
519 | str = (value == null) ? "0" : value.toString(); | 498 | str = (value == null) ? "0" : value.toString(); |
520 | } | 499 | } |
521 | var fracPos = str.indexOf("."); | 500 | var arr = MochiKit.Text.split(str, ".", 2); |
522 | if (fracPos < 0) { | 501 | var frac = MochiKit.Text.padRight(arr[1], 2, "0"); |
523 | str = str + "00"; | 502 | var whole = arr[0] + frac.substring(0, 2); |
524 | } else if (fracPos + 3 >= str.length) { | 503 | frac = frac.substring(2); |
525 | var fraction = str.substring(fracPos + 1); | 504 | while (/^0[0-9]/.test(whole)) { |
526 | while (fraction.length < 2) { | 505 | whole = whole.substring(1); |
527 | fraction = fraction + "0"; | ||
528 | } | ||
529 | str = str.substring(0, fracPos) + fraction; | ||
530 | } else { | ||
531 | var fraction = str.substring(fracPos + 1); | ||
532 | str = str.substring(0, fracPos) + fraction.substring(0, 2) + | ||
533 | "." + fraction.substring(2); | ||
534 | } | ||
535 | while (str.length > 1 && str.charAt(0) == "0" && str.charAt(1) != ".") { | ||
536 | str = str.substring(1); | ||
537 | } | 506 | } |
538 | return str; | 507 | return (frac.length <= 0) ? whole : whole + "." + frac; |
539 | } | 508 | }; |
540 | 509 | ||
541 | /** | 510 | /** |
542 | * Creates a new format pattern error. | 511 | * Creates a new format pattern error. |
543 | * | 512 | * |
@@ -557,15 +526,15 @@ MochiKit.Text._truncToPercent = function (value, precision) { | |||
557 | MochiKit.Text.FormatPatternError = function (pattern, pos, message) { | 526 | MochiKit.Text.FormatPatternError = function (pattern, pos, message) { |
558 | this.pattern = pattern; | 527 | this.pattern = pattern; |
559 | this.pos = pos; | 528 | this.pos = pos; |
560 | this.message = message; | 529 | this.message = message; |
561 | } | 530 | }; |
562 | MochiKit.Text.FormatPatternError.prototype = | ||
563 | new MochiKit.Base.NamedError("MochiKit.Text.FormatPatternError"); | ||
564 | 531 | ||
532 | MochiKit.Text.FormatPatternError.prototype = new MochiKit.Base.NamedError("MochiKit.Text.FormatPatternError"); | ||
533 | MochiKit.Text.FormatPatternError.constructor = MochiKit.Text.FormatPatternError; | ||
565 | 534 | ||
566 | // | 535 | // |
567 | //XXX: Internet Explorer exception handling blows | 536 | //XXX: Internet Explorer export fix |
568 | // | 537 | // |
569 | if (MochiKit.__export__) { | 538 | if (MochiKit.__export__) { |
570 | formatter = MochiKit.Text.formatter; | 539 | formatter = MochiKit.Text.formatter; |
571 | format = MochiKit.Text.format; | 540 | format = MochiKit.Text.format; |