/** * Reusable data formatting functions */ Ext.define('Ext.util.Format', { requires: [ 'Ext.DateExtras' ], singleton: true, /** * The global default date format. */ defaultDateFormat: 'm/d/Y', escapeRe: /('|\\)/g, trimRe: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g, formatRe: /\{(\d+)\}/g, escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g, dashesRe: /-/g, iso8601TestRe: /\d\dT\d\d/, iso8601SplitRe: /[- :T\.Z\+]/, /** * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length. * @param {String} value The string to truncate. * @param {Number} length The maximum length to allow before truncating. * @param {Boolean} [word=false] True to try to find a common word break. * @return {String} The converted text. */ ellipsis: function(value, len, word) { if (value && value.length > len) { if (word) { var vs = value.substr(0, len - 2), index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?')); if (index != -1 && index >= (len - 15)) { return vs.substr(0, index) + "..."; } } return value.substr(0, len - 3) + "..."; } return value; }, /** * Escapes the passed string for use in a regular expression. * @param {String} str * @return {String} */ escapeRegex: function(s) { return s.replace(Ext.util.Format.escapeRegexRe, "\\$1"); }, /** * Escapes the passed string for ' and \. * @param {String} string The string to escape. * @return {String} The escaped string. */ escape: function(string) { return string.replace(Ext.util.Format.escapeRe, "\\$1"); }, /** * Utility function that allows you to easily switch a string between two alternating values. The passed value * is compared to the current string, and if they are equal, the other value that was passed in is returned. If * they are already different, the first value passed in is returned. * * __Note:__ This method returns the new value but does not change the current string. * * // alternate sort directions * sort = Ext.util.Format.toggle(sort, 'ASC', 'DESC'); * * // instead of conditional logic: * sort = (sort === 'ASC' ? 'DESC' : 'ASC'); * * @param {String} string The current string * @param {String} value The value to compare to the current string * @param {String} other The new value to use if the string already equals the first value passed in * @return {String} The new value */ toggle: function(string, value, other) { return string == value ? other : value; }, /** * Trims whitespace from either end of a string, leaving spaces within the string intact. Example: * * var s = ' foo bar '; * alert('-' + s + '-'); // alerts "- foo bar -" * alert('-' + Ext.util.Format.trim(s) + '-'); // alerts "-foo bar-" * * @param {String} string The string to escape * @return {String} The trimmed string */ trim: function(string) { return string.replace(Ext.util.Format.trimRe, ""); }, /** * Pads the left side of a string with a specified character. This is especially useful * for normalizing number and date strings. Example usage: * * var s = Ext.util.Format.leftPad('123', 5, '0'); * // s now contains the string: '00123' * * @param {String} string The original string. * @param {Number} size The total length of the output string. * @param {String} [char=' '] (optional) The character with which to pad the original string. * @return {String} The padded string. */ leftPad: function (val, size, ch) { var result = String(val); ch = ch || " "; while (result.length < size) { result = ch + result; } return result; }, /** * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each * token must be unique, and must increment in the format {0}, {1}, etc. Example usage: * * var cls = 'my-class', text = 'Some text'; * var s = Ext.util.Format.format('<div class="{0}">{1}</div>', cls, text); * // s now contains the string: '<div class="my-class">Some text</div>' * * @param {String} string The tokenized string to be formatted. * @param {String...} values The values to replace token {0}, {1}, etc. * @return {String} The formatted string. */ format: function (format) { var args = Ext.toArray(arguments, 1); return format.replace(Ext.util.Format.formatRe, function(m, i) { return args[i]; }); }, /** * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages. * @param {String} value The string to encode. * @return {String} The encoded text. */ htmlEncode: function(value) { return ! value ? value: String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """); }, /** * Convert certain characters (&, <, >, and ') from their HTML character equivalents. * @param {String} value The string to decode. * @return {String} The decoded text. */ htmlDecode: function(value) { return ! value ? value: String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&"); }, /** * Parse a value into a formatted date using the specified format pattern. * Note that this uses the native Javascript Date.parse() method and is therefore subject to its idiosyncrasies. * Most formats assume the local timezone unless specified. One notable exception is 'YYYY-MM-DD' (note the dashes) * which is typically interpreted in UTC and can cause date shifting. * @param {String/Date} value The value to format. Strings must conform to the format expected by the JavaScript * Date object's [parse() method](http://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/parse). * @param {String} [format='m/d/Y'] (optional) Any valid date format string. * @return {String} The formatted date string. */ date: function(value, format) { var date = value; if (!value) { return ""; } if (!Ext.isDate(value)) { date = new Date(Date.parse(value)); if (isNaN(date)) { // Dates with ISO 8601 format are not well supported by mobile devices, this can work around the issue. if (this.iso8601TestRe.test(value)) { // Fix for older android browsers to properly implement ISO 8601 formatted dates with timezone if (Ext.os.is.Android && Ext.os.version.isLessThan("3.0")) { /** * This code is modified from the following source: <https://github.com/csnover/js-iso8601> * © 2011 Colin Snover <http://zetafleet.com> * Released under MIT license. */ var potentialUndefinedKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; var dateParsed, minutesOffset = 0; // Capture Groups // 1 YYYY (optional) // 2 MM // 3 DD // 4 HH // 5 mm (optional) // 6 ss (optional) // 7 msec (optional) // 8 Z (optional) // 9 ± (optional) // 10 tzHH (optional) // 11 tzmm (optional) if ((dateParsed = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(value))) { //Set any undefined values needed for Date to 0 for (var i = 0, k; (k = potentialUndefinedKeys[i]); ++i) { dateParsed[k] = +dateParsed[k] || 0; } // Fix undefined month and decrement dateParsed[2] = (+dateParsed[2] || 1) - 1; //fix undefined days dateParsed[3] = +dateParsed[3] || 1; // Correct for timezone if (dateParsed[8] !== 'Z' && dateParsed[9] !== undefined) { minutesOffset = dateParsed[10] * 60 + dateParsed[11]; if (dateParsed[9] === '+') { minutesOffset = 0 - minutesOffset; } } // Calculate valid date date = new Date(Date.UTC(dateParsed[1], dateParsed[2], dateParsed[3], dateParsed[4], dateParsed[5] + minutesOffset, dateParsed[6], dateParsed[7])); } } else { date = value.split(this.iso8601SplitRe); date = new Date(date[0], date[1] - 1, date[2], date[3], date[4], date[5]); } } } if (isNaN(date)) { // Dates with the format "2012-01-20" fail, but "2012/01/20" work in some browsers. We'll try and // get around that. date = new Date(Date.parse(value.replace(this.dashesRe, "/"))); //<debug> if (isNaN(date)) { Ext.Logger.error("Cannot parse the passed value " + value + " into a valid date"); } //</debug> } value = date; } return Ext.Date.format(value, format || Ext.util.Format.defaultDateFormat); } });