    /** * This base class contains utility methods for dealing with formats such as CSV (Comma * Separated Values) as specified in <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>. * * The base class implements the mechanics and is governed by these config options: * *  * `{@link #delimiter}` *  * `{@link #lineBreak}` *  * `{@link #quote}` * * These options affect the `{@link #method-encode}` and `{@link #method-decode}` methods. * When *decoding*, however, `{@link #lineBreak}` is ignored and instead each line can * be separated by any standard line terminator character or character sequence: * *  * ```\u000a``` *  * ```\u000d``` *  * ```\u000d\u000a``` * * Strings which contain the {@link #delimiter} character are quoted using the * {@link #quote} character, and any internal {@link #quote} characters are doubled. * * *Important* * While the primary use case is to encode strings, other atomic data types can be encoded * as values within a line such as: * *  * Number *  * Boolean *  * Date (encoded as an <a href="http://www.iso.org/iso/home/standards/iso8601.htm">ISO 8601</a> date string.) *  * null (encoded as an empty string.) *  * undefined (encoded as an empty string.) * * Not that when *decoding*, all data is read as strings. This class does not convert * incoming data. To do that, use an {@link Ext.data.reader.Array ArrayReader}. * * See `{@link Ext.util.CSV}` and  `{@link Ext.util.TSV}` for pre-configured instances. * * @since 5.1.0 */Ext.define('Ext.util.DelimitedValue', {    /**     * @cfg {String} dateFormat      * The {@link Ext.Date#format format} to use for dates     */    dateFormat: 'C',     /**     * @cfg {String} delimiter      * The string used to separate the values in a row. Common values for this config     * are comma (",") and tab ("\t"). See `{@link Ext.util.CSV}` and  `{@link Ext.util.TSV}`     * for pre-configured instances of these formats.     */    delimiter: '\t',     /**     * @cfg {String} lineBreak      * The string used by `{@link #encode}` to separate each row. The `{@link #decode}`     * method accepts all forms of line break.     */    lineBreak: '\n',     /**     * @cfg {String} quote      * The character to use as to quote values that contain the special `delimiter`     * or `{@link #lineBreak}` characters.     */    quote: '"',        parseREs: {},    quoteREs: {},     lineBreakRe: /\r?\n/g,    // match line break at end of input     lastLineBreakRe: /(\r?\n|\r)$/,     constructor: function (config) {        if (config) {            Ext.apply(this, config);        }    },     /**     * Decodes a string of encoded values into an array of rows. Each row is an array of     * strings.     *     * Note that this function does not convert the string values in each column into     * other data types. To do that, use an {@link Ext.data.reader.Array ArrayReader}.     *     * For example:     *     *     Ext.util.CSV.decode('"foo ""bar"", bletch",Normal String,2010-01-01T21:45:32.004Z\u000a3.141592653589793,1,false');     *     * produces the following array of string arrays:     *     *     [     *         ['foo "bar", bletch','Normal String', '2010-01-01T21:45:32.004Z'],     *         ['3.141592653589793', '1', 'false']     *     ]     *     * @param {String} input The string to parse.     *     * @param {String} [delimiter] The column delimiter to use if the default value     * of {@link #cfg-delimiter delimiter} is not desired.     *     * @return {String[][]} An array of rows where each row is an array of Strings.     */    decode: function (input, delimiter) {        if (!input) {            return [];        }                var me = this,            // Check to see if the column delimiter is defined. If not,             // then default to comma.             delim = delimiter || me.delimiter,            row = [],            result = [row],            quote = me.quote,            quoteREs = me.quoteREs,            parseREs = me.parseREs,            input = input.replace(me.lastLineBreakRe, ''),             // Create a regular expression to parse the CSV values unless we already have             // one for this delimiter.             parseRE = parseREs[delim] ||                (parseREs[delim] = new RegExp(                    // Delimiters.                     "(\\" + delim + "|\\r?\\n|\\r|^)" +                     // Quoted fields.                     "(?:\\" + quote + "([^\\" + quote + "]*(?:\\" + quote + "\\" + quote +                            "[^\\" + quote + "]*)*)\\" + quote + "|" +                     // Standard fields.                     "([^\"\\" + delim + "\\r\\n]*))",                "gi")),             dblQuoteRE = quoteREs[quote] ||                (quoteREs[quote] = new RegExp('\\' + quote + '\\' + quote, 'g')),             arrMatches, strMatchedDelimiter, strMatchedValue;         // Keep looping over the regular expression matches         // until we can no longer find a match.         while (arrMatches = parseRE.exec(input)) {            strMatchedDelimiter = arrMatches[1];             // Check to see if the given delimiter has a length             // (is not the start of string) and if it matches             // field delimiter. If id does not, then we know             // that this delimiter is a row delimiter.             if (strMatchedDelimiter.length && strMatchedDelimiter !== delim) {                // Since we have reached a new row of data,                 // add an empty row to our data array.                 result.push(row = []);            }             // Now that we have our delimiter out of the way,             // let's check to see which kind of value we             // captured (quoted or unquoted).             if (arrMatches[2]) {                // We found a quoted value. When we capture                 // this value, unescape any double quotes.                 strMatchedValue = arrMatches[2].replace(dblQuoteRE, '"');            } else {                // We found a non-quoted value.                 strMatchedValue = arrMatches[3];            }             row.push(strMatchedValue);        }         return result;    },     /**     * Converts a two-dimensional array into an encoded string.     *     * For example:     *     *     Ext.util.CSV.encode([     *         ['foo "bar", bletch', 'Normal String', new Date()],     *         [Math.PI, 1, false]     *     ]);     *     * The above produces the following string:     *     *     '"foo ""bar"", bletch",Normal String,2010-01-01T21:45:32.004Z\u000a3.141592653589793,1,false'     *     * @param {Mixed[][]} input An array of row data arrays.     *     * @param {String} [delimiter] The column delimiter to use if the default value     * of {@link #cfg-delimiter delimiter} is not desired.     *     * @return {String} A string in which data items are separated by {@link #delimiter}     * characters, and rows are separated by {@link #lineBreak} characters.     */    encode: function (input, delimiter) {        var me = this,            delim = delimiter || me.delimiter,            dateFormat = me.dateFormat,            quote = me.quote,            twoQuotes = quote + quote,            rowIndex = input.length,            lineBreakRe = me.lineBreakRe,            result = [],            outputRow = [],            col, columnIndex, inputRow;         while (rowIndex-- > 0) {            inputRow = input[rowIndex];            outputRow.length = columnIndex = inputRow.length;             while (columnIndex-- > 0) {                col = inputRow[columnIndex];                 if (col == null) { // == null || === undefined                     col = '';                } else if (typeof col === 'string') {                    if (col) {                        // If the value contains quotes, double them up, and wrap with quotes                         if (col.indexOf(quote) > -1) {                            col = quote + col.split(quote).join(twoQuotes) + quote;                        } else if (col.indexOf(delim) > -1 || lineBreakRe.test(col)) {                            col = quote + col + quote;                        }                    }                } else if (Ext.isDate(col)) {                    col = Ext.Date.format(col, dateFormat);                }                //<debug>                 else if (col && (isNaN(col) || Ext.isArray(col))) {                    Ext.raise('Cannot serialize ' + Ext.typeOf(col) + ' into CSV');                }                //</debug>                  outputRow[columnIndex] = col;            }             result[rowIndex] = outputRow.join(delim);        }         return result.join(me.lineBreak);    }});