From ef68436ac04da078ffdcacd7e1f785473a303d45 Mon Sep 17 00:00:00 2001 From: Giulio Cesare Solaroli Date: Sun, 02 Oct 2011 23:56:18 +0000 Subject: First version of the newly restructured repository --- (limited to 'frontend/gamma/js/MochiKit/Text.js') diff --git a/frontend/gamma/js/MochiKit/Text.js b/frontend/gamma/js/MochiKit/Text.js new file mode 100644 index 0000000..a44f7e4 --- a/dev/null +++ b/frontend/gamma/js/MochiKit/Text.js @@ -0,0 +1,577 @@ +/*** + +MochiKit.Text 1.5 + +See for documentation, downloads, license, etc. + +(c) 2008 Per Cederberg. All rights Reserved. + +***/ + +MochiKit.Base._module('Text', '1.5', ['Base', 'Format']); + +/** + * Checks if a text string starts with the specified substring. If + * either of the two strings is null, false will be returned. + * + * @param {String} substr the substring to search for + * @param {String} str the string to search in + * + * @return {Boolean} true if the string starts with the substring, or + * false otherwise + */ +MochiKit.Text.startsWith = function (substr, str) { + return str != null && substr != null && str.indexOf(substr) == 0; +} + +/** + * Checks if a text string ends with the specified substring. If + * either of the two strings is null, false will be returned. + * + * @param {String} substr the substring to search for + * @param {String} str the string to search in + * + * @return {Boolean} true if the string ends with the substring, or + * false otherwise + */ +MochiKit.Text.endsWith = function (substr, str) { + return str != null && substr != null && + str.lastIndexOf(substr) == Math.max(str.length - substr.length, 0); +} + +/** + * Checks if a text string contains the specified substring. If + * either of the two strings is null, false will be returned. + * + * @param {String} substr the substring to search for + * @param {String} str the string to search in + * + * @return {Boolean} true if the string contains the substring, or + * false otherwise + */ +MochiKit.Text.contains = function (substr, str) { + return str != null && substr != null && str.indexOf(substr) >= 0; +} + +/** + * Adds a character to the left-hand side of a string until it + * reaches the specified minimum length. + * + * @param {String} str the string to process + * @param {Number} minLength the requested minimum length + * @param {String} fillChar the padding character to add, defaults + * to a space + * + * @return {String} the padded string + */ +MochiKit.Text.padLeft = function (str, minLength, fillChar) { + str = str || ""; + fillChar = fillChar || " "; + while (str.length < minLength) { + str = fillChar + str; + } + return str; +} + +/** + * Adds a character to the right-hand side of a string until it + * reaches the specified minimum length. + * + * @param {String} str the string to process + * @param {Number} minLength the requested minimum length + * @param {String} fillChar the padding character to add, defaults + * to a space + * + * @return {String} the padded string + */ +MochiKit.Text.padRight = function (str, minLength, fillChar) { + str = str || ""; + fillChar = fillChar || " "; + while (str.length < minLength) { + str += fillChar; + } + return str; +} + +/** + * Returns a truncated copy of a string. If the string is shorter + * than the specified maximum length, the object will be returned + * unmodified. If an optional tail string is specified, additional + * elements will be removed in order to accomodate the tail (that + * will be appended). This function also works on arrays. + * + * @param {String} str the string to truncate + * @param {Number} maxLength the maximum length + * @param {String} [tail] the tail to append on truncation + * + * @return {String} the truncated string + */ +MochiKit.Text.truncate = function (str, maxLength, tail) { + if (str == null || str.length <= maxLength || maxLength < 0) { + return str; + } else if (tail != null) { + str = str.slice(0, Math.max(0, maxLength - tail.length)); + if (typeof(str) == "string") { + return str + tail; + } else { + return MochiKit.Base.extend(str, tail); + } + } else { + return str.slice(0, maxLength); + } +} + +/** + * Splits a text string, applies a function and joins the results + * back together again. This is a convenience function for calling + * split(), map() and join() separately. It can be used to easily + * trim each line in a text string (using the strip function), or to + * translate a text word-by-word. + * + * @param {Function} func the function to apply + * @param {String} str the string to split + * @param {String} [separator] the separator character to use, + * defaults to newline + * + * @return {String} a string with the joined up results + */ +MochiKit.Text.splitJoin = function (func, str, separator) { + if (str == null || str.length == 0) { + return str; + } + separator = separator || '\n' + return MochiKit.Base.map(func, str.split(separator)).join(separator); +} + +/** + * Creates a formatter function for the specified formatter pattern + * and locale. The returned function takes as many arguments as the + * formatter pattern requires. See separate documentation for + * information about the formatter pattern syntax. + * + * @param {String} pattern the formatter pattern string + * @param {Object} [locale] the locale to use, defaults to + * LOCALE.en_US + * + * @return {Function} the formatter function created + * + * @throws FormatPatternError if the format pattern was invalid + */ +MochiKit.Text.formatter = function (pattern, locale) { + if (typeof(locale) == "undefined") { + locale = MochiKit.Format.formatLocale(); + } else if (typeof(locale) == "string") { + locale = MochiKit.Format.formatLocale(locale); + } + var parts = MochiKit.Text._parsePattern(pattern); + return function() { + var values = MochiKit.Base.extend([], arguments); + var res = []; + for (var i = 0; i < parts.length; i++) { + if (typeof(parts[i]) == "string") { + res.push(parts[i]); + } else { + res.push(MochiKit.Text.formatValue(parts[i], values, locale)); + } + } + return res.join(""); + } +} + +/** + * Formats the specified arguments according to a formatter pattern. + * See separate documentation for information about the formatter + * pattern syntax. + * + * @param {String} pattern the formatter pattern string + * @param {Object} [...] the optional values to format + * + * @return {String} the formatted output string + * + * @throws FormatPatternError if the format pattern was invalid + */ +MochiKit.Text.format = function (pattern/*, ...*/) { + var func = MochiKit.Text.formatter(pattern); + return func.apply(this, MochiKit.Base.extend([], arguments, 1)); +} + +/** + * Format a value with the specified format specifier. + * + * @param {String/Object} spec the format specifier string or parsed + * format specifier object + * @param {Object} value the value to format + * @param {Object} [locale] the locale to use, defaults to + * LOCALE.en_US + * + * @return {String} the formatted output string + */ +MochiKit.Text.formatValue = function (spec, value, locale) { + var self = MochiKit.Text; + if (typeof(spec) === "string") { + spec = self._parseFormatFlags(spec, 0, spec.length - 1); + } + for (var i = 0; spec.path != null && i < spec.path.length; i++) { + if (value != null) { + value = value[spec.path[i]]; + } + } + if (typeof(locale) == "undefined") { + locale = MochiKit.Format.formatLocale(); + } else if (typeof(locale) == "string") { + locale = MochiKit.Format.formatLocale(locale); + } + var str = ""; + if (spec.numeric) { + if (typeof(value) != "number" || isNaN(value)) { + str = ""; + } else if (value === Number.POSITIVE_INFINITY) { + str = "\u221e"; + } else if (value === Number.NEGATIVE_INFINITY) { + str = "-\u221e"; + } else { + var sign = (spec.sign === "-") ? "" : spec.sign; + sign = (value < 0) ? "-" : sign; + value = Math.abs(value); + if (spec.format === "%") { + str = self._truncToPercent(value, spec.precision); + } else if (spec.format === "d") { + str = MochiKit.Format.roundToFixed(value, 0); + } else if (spec.radix != 10) { + str = Math.floor(value).toString(spec.radix); + if (spec.format === "x") { + str = str.toLowerCase(); + } else if (spec.format === "X") { + str = str.toUpperCase(); + } + } else if (spec.precision >= 0) { + str = MochiKit.Format.roundToFixed(value, spec.precision); + } else { + str = value.toString(); + } + if (spec.padding === "0" && spec.format === "%") { + str = self.padLeft(str, spec.width - sign.length - 1, "0"); + } else if (spec.padding == "0") { + str = self.padLeft(str, spec.width - sign.length, "0"); + } + str = self._localizeNumber(str, locale, spec.grouping); + str = sign + str; + } + if (str !== "" && spec.format === "%") { + str = str + locale.percent; + } + } else { + if (spec.format == "r") { + str = MochiKit.Base.repr(value); + } else { + str = (value == null) ? "null" : value.toString(); + } + str = self.truncate(str, spec.precision); + } + if (spec.align == "<") { + str = self.padRight(str, spec.width); + } else { + str = self.padLeft(str, spec.width); + } + return str; +} + +/** + * Adjust an already formatted numeric string for locale-specific + * grouping and decimal separators. The grouping is optional and + * will attempt to keep the number string length intact by removing + * padded zeros (if possible). + * + * @param {String} num the formatted number string + * @param {Object} locale the formatting locale to use + * @param {Boolean} grouping the grouping flag + * + * @return {String} the localized number string + */ +MochiKit.Text._localizeNumber = function (num, locale, grouping) { + var parts = num.split(/\./); + var whole = parts[0]; + var frac = (parts.length == 1) ? "" : parts[1]; + var res = (frac.length > 0) ? locale.decimal : ""; + while (grouping && frac.length > 3) { + res = res + frac.substring(0, 3) + locale.separator; + frac = frac.substring(3); + if (whole.charAt(0) == "0") { + whole = whole.substring(1); + } + } + if (frac.length > 0) { + res += frac; + } + while (grouping && whole.length > 3) { + var pos = whole.length - 3; + res = locale.separator + whole.substring(pos) + res; + whole = whole.substring((whole.charAt(0) == "0") ? 1 : 0, pos); + } + return whole + res; +} + +/** + * Parses a format pattern and returns an array of constant strings + * and format info objects. + * + * @param {String} pattern the format pattern to analyze + * + * @return {Array} an array of strings and format info objects + * + * @throws FormatPatternError if the format pattern was invalid + */ +MochiKit.Text._parsePattern = function (pattern) { + var self = MochiKit.Text; + var parts = []; + var start = 0; + var pos = 0; + for (pos = 0; pos < pattern.length; pos++) { + if (pattern.charAt(pos) == "{") { + if (pos + 1 >= pattern.length) { + var msg = "unescaped { char, should be escaped as {{"; + throw new self.FormatPatternError(pattern, pos, msg); + } else if (pattern.charAt(pos + 1) == "{") { + parts.push(pattern.substring(start, pos + 1)); + start = pos + 2; + pos++; + } else { + if (start < pos) { + parts.push(pattern.substring(start, pos)); + } + start = pattern.indexOf("}", pos) + 1; + if (start <= 0) { + var msg = "unmatched { char, not followed by a } char"; + throw new self.FormatPatternError(pattern, pos, msg); + } + parts.push(self._parseFormat(pattern, pos + 1, start - 1)); + pos = start - 1; + } + } else if (pattern.charAt(pos) == "}") { + if (pos + 1 >= pattern.length || pattern.charAt(pos + 1) != "}") { + var msg = "unescaped } char, should be escaped as }}"; + throw new self.FormatPatternError(pattern, pos, msg); + } + parts.push(pattern.substring(start, pos + 1)); + start = pos + 2; + pos++; + } + } + if (start < pos) { + parts.push(pattern.substring(start, pos)); + } + return parts; +} + +/** + * Parses a format instruction and returns a format info object. + * + * @param {String} pattern the format pattern string + * @param {Number} startPos the first index of the format instruction + * @param {Number} endPos the last index of the format instruction + * + * @return {Object} the format info object + * + * @throws FormatPatternError if the format pattern was invalid + */ +MochiKit.Text._parseFormat = function (pattern, startPos, endPos) { + var self = MochiKit.Text; + var text = pattern.substring(startPos, endPos); + var info; + var pos = text.indexOf(":"); + if (pos == 0) { + info = self._parseFormatFlags(pattern, startPos + 1, endPos); + info.path = [0]; + } else if (pos > 0) { + info = self._parseFormatFlags(pattern, startPos + pos + 1, endPos); + info.path = text.substring(0, pos).split("."); + } else { + info = self._parseFormatFlags(pattern, endPos, endPos); + info.path = text.split("."); + } + var DIGITS = /^\d+$/; + for (var i = 0; i < info.path.length; i++) { + var e = info.path[i]; + if (typeof(e) == "string") { + // TODO: replace with MochiKit.Format.strip? + e = e.replace(/^\s+/, "").replace(/\s+$/, ""); + if (e == "" && info.path.length == 1) { + e = 0; + } else if (e == "") { + var msg = "format value path contains blanks"; + throw new self.FormatPatternError(pattern, startPos, msg); + } else if (DIGITS.test(e)) { + e = parseInt(e); + } + } + info.path[i] = e; + } + if (info.path.length < 0 || typeof(info.path[0]) != "number") { + info.path.unshift(0); + } + return info; +} + +/** + * Parses a string with format flags and returns a format info object. + * + * @param {String} pattern the format pattern string + * @param {Number} startPos the first index of the format instruction + * @param {Number} endPos the last index of the format instruction + * + * @return {Object} the format info object + * + * @throws FormatPatternError if the format pattern was invalid + */ +MochiKit.Text._parseFormatFlags = function (pattern, startPos, endPos) { + var self = MochiKit.Text; + var info = { numeric: false, format: "s", width: 0, precision: -1, + align: ">", sign: "-", padding: " ", grouping: false }; + // TODO: replace with MochiKit.Format.rstrip? + var flags = pattern.substring(startPos, endPos).replace(/\s+$/, ""); + while (flags.length > 0) { + switch (flags.charAt(0)) { + case ">": + case "<": + info.align = flags.charAt(0); + flags = flags.substring(1); + break; + case "+": + case "-": + case " ": + info.sign = flags.charAt(0); + flags = flags.substring(1); + break; + case ",": + info.grouping = true; + flags = flags.substring(1); + break; + case ".": + var chars = /^\d*/.exec(flags.substring(1))[0]; + info.precision = parseInt(chars); + flags = flags.substring(1 + chars.length); + break; + case "0": + info.padding = flags.charAt(0); + flags = flags.substring(1); + break; + case "1": + case "2": + case "3": + case "4": + case "5": + case "6": + case "7": + case "8": + case "9": + var chars = /^\d*/.exec(flags)[0]; + info.width = parseInt(chars); + flags = flags.substring(chars.length); + break; + case "s": + case "r": + info.format = flags.charAt(0); + flags = flags.substring(1); + break; + case "b": + case "d": + case "o": + case "x": + case "X": + case "f": + case "%": + info.numeric = true; + info.format = flags.charAt(0); + info.radix = 10; + if (info.format === "b") { + info.radix = 2; + } else if (info.format === "o") { + info.radix = 8; + } else if (info.format === "x" || info.format === "X") { + info.radix = 16; + } + flags = flags.substring(1); + break; + default: + var msg = "unsupported format flag: " + flags.charAt(0); + throw new self.FormatPatternError(pattern, startPos, msg); + } + } + return info; +} + +/** + * Formats a value as a percentage. This method avoids multiplication + * by 100 since it leads to weird numeric rounding errors. Instead it + * just move the decimal separator in the text string. It is ugly, + * but works... + * + * @param {Number} value the value to format + * @param {Number} precision the number of precision digits + */ +MochiKit.Text._truncToPercent = function (value, precision) { + // TODO: This can be simplified by using the same helper function + // as roundToFixed now does. + var str; + if (precision >= 0) { + str = MochiKit.Format.roundToFixed(value, precision + 2); + } else { + str = (value == null) ? "0" : value.toString(); + } + var fracPos = str.indexOf("."); + if (fracPos < 0) { + str = str + "00"; + } else if (fracPos + 3 >= str.length) { + var fraction = str.substring(fracPos + 1); + while (fraction.length < 2) { + fraction = fraction + "0"; + } + str = str.substring(0, fracPos) + fraction; + } else { + var fraction = str.substring(fracPos + 1); + str = str.substring(0, fracPos) + fraction.substring(0, 2) + + "." + fraction.substring(2); + } + while (str.length > 1 && str.charAt(0) == "0" && str.charAt(1) != ".") { + str = str.substring(1); + } + return str; +} + +/** + * Creates a new format pattern error. + * + * @param {String} pattern the format pattern string + * @param {Number} pos the position of the error + * @param {String} message the error message text + * + * @return {Error} the format pattern error + * + * @class The format pattern error class. This error is thrown when + * a syntax error is encountered inside a format string. + * @property {String} pattern The format pattern string. + * @property {Number} pos The position of the error. + * @property {String} message The error message text. + * @extends MochiKit.Base.NamedError + */ +MochiKit.Text.FormatPatternError = function (pattern, pos, message) { + this.pattern = pattern; + this.pos = pos; + this.message = message; +} +MochiKit.Text.FormatPatternError.prototype = + new MochiKit.Base.NamedError("MochiKit.Text.FormatPatternError"); + + +// +//XXX: Internet Explorer exception handling blows +// +if (MochiKit.__export__) { + formatter = MochiKit.Text.formatter; + format = MochiKit.Text.format; + formatValue = MochiKit.Text.formatValue; +} + + +MochiKit.Base.nameFunctions(MochiKit.Text); +MochiKit.Base._exportSymbols(this, MochiKit.Text); -- cgit v0.9.0.2