From 046c803974c5dc3916066d0301007919fc10c484 Mon Sep 17 00:00:00 2001
From: Renaud Le Gac <legac@cppm.in2p3.fr>
Date: Tue, 23 Sep 2014 21:19:17 +0200
Subject: [PATCH] Better coding of the CSV / LaTeX export. It also covers the
 case with grouping summary.

---
 static/plugin_dbui/src/grid/plugin/Export.js | 298 +++++++++----------
 1 file changed, 144 insertions(+), 154 deletions(-)

diff --git a/static/plugin_dbui/src/grid/plugin/Export.js b/static/plugin_dbui/src/grid/plugin/Export.js
index 965bb855..60aaae81 100644
--- a/static/plugin_dbui/src/grid/plugin/Export.js
+++ b/static/plugin_dbui/src/grid/plugin/Export.js
@@ -11,6 +11,7 @@ Ext.define('App.grid.plugin.Export', {
 
     extend: 'Ext.AbstractPlugin',
     alias: 'plugin.pGridExport',
+    requires: 'Ext.form.action.StandardSubmit',
 
     /**
      * @cfg {String}
@@ -111,57 +112,12 @@ Ext.define('App.grid.plugin.Export', {
         return value;
     },
 
-    /**
-     * Get the content of the cells as rendered by the grid.
-     * The method handle the grouping cell as well.
-     *
-     * @param {Ext.dom.Element}
-     * The DOM element corresponding to the x-grid-row.
-     *
-     * @return {Object}
-     * @return {String} return.groupValue
-     * @return {String[]} return.values
-     *
-     */
-    getCells: function (row) {
-
-        "use strict";
-
-        var cell,
-            groupValue,
-            values = [];
-
-        // get the first cell of the HTML row
-        cell = row.down("td");
-
-        // the first cell can be a container when grouping is used
-        //
-        // a) extract the grouping value in the first children
-        // b) extract the cells of the first row in the second children
-        //    keep in mind that they can be null is the group is closed.
-        //
-        if (cell.dom.className === "x-group-hd-container") {
-            groupValue = cell.dom.childNodes[0].textContent;
-            cell = cell.down("td");
-        }
-
-        // scan the row
-        if (cell) {
-            do {
-                values.push(cell.dom.textContent);
-                cell = cell.next();
-            } while (cell);
-        }
-
-        return {groupValue: groupValue, values: values};
-    },
-
     /**
      * Get the label and the alignment of the visible columns.
      *
      * @return {Object}
      * @return {String[]} return.alignments
-     * @return {String[]} return.labels
+     * @return {String[]} return.labels empty for hidden headers
      *
      */
     getColumnHeader: function () {
@@ -190,144 +146,94 @@ Ext.define('App.grid.plugin.Export', {
             }
         }
 
+        if (container[0].hiddenHeaders) {
+            labels = [];
+        }
+
         return {alignments: alignments, labels: labels};
     },
 
     /**
-     * Build the file content under CSV format.
+     * Show the context menu when right clicking in an empty grid.
      *
-     * @return {String[]} One string per line.
+     * @param {App.grid.Panel} grid
+     * @param {Ext.EventObject} event
+     * @param {Object} eOpts
      *
      */
-    getCSV: function () {
+    onContainerContextMenu: function (grid, event, eOpts) {
 
         "use strict";
 
-        var cells,
-            data = [],
-            header = this.getColumnHeader(),
-            i,
-            row;
-
-        // the grid header
-        if (header.labels.length) {
-            for (i = 0; i < header.labels.length; i += 1) {
-                header.labels[i] = this.delimit(header.labels[i]);
-            }
-            data.push(header.labels.join(this.separator));
-        }
-
-        // scan the row of the HTML table
-        row = this.getCmp().getView().getEl().down("tr");
-        do {
-            // get the cells of the HTML row
-            cells = this.getCells(row);
-
-            // group cell
-            if (cells.groupValue) {
-                data.push(this.delimit(cells.groupValue));
-            }
-
-            // standard cells
-            if (cells.values.length) {
-                for (i = 0; i < cells.values.length; i += 1) {
-                    cells.values[i] = this.delimit(cells.values[i]);
-                }
-                data.push(cells.values.join(this.separator));
-            }
-
-            // move to the next HTML row
-            row = row.next();
-
-        } while (row);
-
-        return data;
+        event.stopEvent();
+        this.menu.showAt(event.getXY());
     },
 
     /**
-     * Build the file content under CSV format.
-     *
-     * @return {String[]} One string per line.
+     * Export the content of the grid as a CSV file.
      *
      */
-    getLaTeX: function () {
+    onCsvExport: function () {
 
         "use strict";
 
-        var cells,
-            cmd,
-            data = [],
+        var div,
+            grid = this.getCmp(),
             header = this.getColumnHeader(),
-            nColumns = header.alignments.length,
             i,
-            row;
+            li,
+            lines = [],
+            total;
 
-        //begin document
-        data = data.concat(this.preamble);
-        data.push('\\begin{document}');
-
-
-        //begin table
-        for (i = 0; i < nColumns; i += 1) {
-            header.alignments[i] = header.alignments[i][0];
+        // convert the header
+        if (header.labels.length) {
+            for (i = 0; i < header.labels.length; i += 1) {
+                header.labels[i] = this.delimit(header.labels[i]);
+            }
+            lines.push(header.labels.join(this.separator));
         }
 
-        cmd = '\\begin{tabular}{' + header.alignments.join('') + '}';
-        data.push(cmd);
+        //
+        // convert the HTML row associated to the Ext.grid.View
+        // The structure of the HTML table is complex because it contains
+        // several types of row depending on the grouping features.
+        //
+        grid.getView().getEl().select("table tr").each(function(trEl, cEl, index){
 
-        // table content
-        row = this.getCmp().getView().getEl().down("tr");
-        do {
-            // get the cells of the HTML row
-            cells = this.getCells(row);
-
-            // group cell
-            if (cells.groupValue) {
-                cmd = '\\multicolumn{' + nColumns + '}{l}{' + cells.groupValue + '} \\\\';
-                data.push(cmd);
-            }
+            if(trEl.hasCls("x-grid-wrap-row")) {
 
-            // standard cells
-            data.push(cells.values.join(' & ') + ' \\\\');
+                div = trEl.down(".x-grid-group-title");
+                if (div) {
+                    lines.push(this.delimit(div.dom.textContent));
+                }
 
-            // move to the next HTML row
-            row = row.next();
+            } else if (trEl.hasCls("x-grid-data-row") || trEl.hasCls("x-grid-row-summary")) {
 
-        } while (row);
+                li = [];
 
-        //end table and document
-        data.push('\\end{tabular}');
-        data.push('\\end{document}');
+                trEl.select("td").each(function(tdEl){
+                    li.push(this.delimit(tdEl.dom.textContent));
+                }, this);
 
-        return data;
-    },
+                li = li.join(this.separator);
 
-    /**
-     * Show the context menu when right clicking in an empty grid.
-     *
-     * @param {App.grid.Panel} grid
-     * @param {Ext.EventObject} event
-     * @param {Object} eOpts
-     *
-     */
-    onContainerContextMenu: function (grid, event, eOpts) {
-
-        "use strict";
-
-        event.stopEvent();
-        this.menu.showAt(event.getXY());
-    },
+                // unfortunately the grand total appear as the first row (Ext JS 4.2)
+                if (index === 0 && trEl.hasCls("x-grid-row-summary")) {
+                    total = li;
+                } else {
+                    lines.push(li);
+                }
+            }
 
-    /**
-     * Export the content of the grid as a CSV file.
-     *
-     */
-    onCsvExport: function () {
+        }, this);
 
-        "use strict";
+        // push the grand total at the end
+        if (total) {
+            lines.push(total);
+        }
 
-        var data = this.getCSV();
-        this.save("text/csv", "utf-8", data);
+        // trigger the transfer using URI technique
+        this.save("text/csv", "utf-8", lines);
     },
 
     /**
@@ -372,8 +278,8 @@ Ext.define('App.grid.plugin.Export', {
 
         "use strict";
 
-        var data = this.getLaTeX();
-        this.save("application/x-latex", "utf-8", data);
+        var lines = this.toLaTeX();
+        this.save("application/x-latex", "utf-8", lines);
     },
 
     /**
@@ -389,7 +295,7 @@ Ext.define('App.grid.plugin.Export', {
         var data,
             form;
 
-        data = this.getLaTeX().join(this.newLine);
+        data = this.toLaTeX().join(this.newLine);
 
         // send the latex data to the server.
         // the method used standardSubmit using a form.
@@ -444,5 +350,89 @@ Ext.define('App.grid.plugin.Export', {
         window.location.href = uri;
     },
 
+    /**
+     * Convert the Ext.grid.View content into a string.
+     * the information are encoded using LaTeX standard.
+     *
+     * @return {String[]} One string per line.
+     *
+     */
+    toLaTeX: function () {
+
+        "use strict";
+
+        var cmd,
+            div,
+            grid = this.getCmp(),
+            header = this.getColumnHeader(),
+            li,
+            lines = [],
+            nColumns = header.alignments.length,
+            total;
+
+        //begin document
+        lines = lines.concat(this.preamble);
+        lines.push('\\begin{document}');
+
+
+        //begin table
+        header.alignments.forEach(function(value, index, array){
+            array[index] = value[0];
+        });
+
+        cmd = '\\begin{tabular}{' + header.alignments.join('') + '}';
+        lines.push(cmd);
+
+        // table header
+        if (header.labels.length) {
+            lines.push(header.labels.join(' & ') + ' \\\\');
+        }
+
+        // table content
+        //
+        // convert the HTML row associated to the Ext.grid.View.
+        // The structure of the HTML table is complex because it contains
+        // several type of row depending on the grouping features.
+        //
+        grid.getView().getEl().select("table tr").each(function(trEl, cEl, index){
+
+            if(trEl.hasCls("x-grid-wrap-row")) {
+
+                div = trEl.down(".x-grid-group-title");
+                if (div) {
+                    cmd = '\\multicolumn{' + nColumns + '}{l}{' + div.dom.textContent + '} \\\\';
+                    lines.push(cmd);
+                }
+
+            } else if (trEl.hasCls("x-grid-data-row") || trEl.hasCls("x-grid-row-summary")) {
+
+                li = [];
+
+                trEl.select("td").each(function(tdEl){
+                    li.push(tdEl.dom.textContent);
+                }, this);
+
+                li = li.join(' & ') + ' \\\\';
+
+                // unfortunately the grand total appear as the first row (Ext JS 4.2)
+                if (index === 0 && trEl.hasCls("x-grid-row-summary")) {
+                    total = li;
+                } else {
+                    lines.push(li);
+                }
+            }
+
+        }, this);
+
+        // push the grand total at the end
+        if (total) {
+            lines.push(total);
+        }
+
+        //end table and document
+        lines.push('\\end{tabular}');
+        lines.push('\\end{document}');
 
+        return lines;
+    }
 });
\ No newline at end of file
-- 
GitLab