diff --git a/static/plugin_dbui/src/grid/plugin/Export.js b/static/plugin_dbui/src/grid/plugin/Export.js index 89e1f00e73f54a75486c7a8a3f338be5d5f549ad..992c5d2ac9fb124749360f0f8cd8cf4b91ab28af 100644 --- a/static/plugin_dbui/src/grid/plugin/Export.js +++ b/static/plugin_dbui/src/grid/plugin/Export.js @@ -1,6 +1,6 @@ /** - * The plugin export the content of the grid, as display on the screen, - * into a file. Several formats are available: CSV, LaTex and PDF. + * The plugin export the content of the grid, as rendered on the screen, + * into a file. Several formats are available: CSV, LaTeX and PDF. * * A context menu gives access to the different possibilities. * @@ -24,6 +24,16 @@ Ext.define('App.grid.plugin.Export', { */ newLine: '\n', + /** + * @cfg {Array} + * The LaTeX preamble. + */ + preamble: [ + '\\documentclass[a4paper]{article}', + '\\usepackage[utf8]{inputenc}', + '\\usepackage[T1]{fontenc}' + ], + /** * @cfg {String} * The CSV field separator. @@ -60,17 +70,17 @@ Ext.define('App.grid.plugin.Export', { items : [{ text: this.textToCSV, iconCls: 'xminetype-csv', - handler: this.toCSV, + handler: this.onCsvExport, scope: this }, { text: this.textToLaTeX, iconCls: 'xminetype-tex', - handler: this.toLaTeX, + handler: this.onLatexExport, scope: this }, { text: this.textToPDF, iconCls: 'xminetype-pdf', - handler: this.toPDF, + handler: this.onPdfExport, scope: this }] }); @@ -84,9 +94,14 @@ Ext.define('App.grid.plugin.Export', { }, - // private - // add delimiter to value for CSV export - addDelimiter: function (value) { + /** + * Surround the value with the CSV delimiter. + * Delimiter are only applied to non-numeric value. + * + * @param {String/Number} + * + */ + delimit: function (value) { "use strict"; @@ -97,152 +112,315 @@ Ext.define('App.grid.plugin.Export', { }, /** - * Show the context menu when right clicking in an empty grid. + * Download the data using URI embedded into an HTML link. + * The URI is build from the first three arguments. + * In addition, the data are HTML encoded to preserve special + * character like /, ?, ... * - * @param {App.grid.Panel} grid - * @param {Ext.EventObject} event - * @param {Object} eOpts + * @param {String} + * The mine type of the return file, e.g "text/csv". + * + * @param {String} + * The data encoding, e.g. "utf-8". + * + * @param {String[]} + * The content of the file, one string per line. + * + * @param {String} + * The file name with its extension. * */ - onContainerContextMenu: function (grid, event, eOpts) { + download: function (minetype, encoding, data, filename) { "use strict"; - event.stopEvent(); - this.menu.showAt(event.getXY()); + var uri = "data:" + minetype + ";charset=" + encoding +",", + link; + + // special character like quote, / , ? are HTML encoded + uri += encodeURIComponent(data.join(this.newLine)); + + // download the file by simulating a click on a link + link = Ext.getBody().createChild({ + tag: "a", + href: uri, + download: filename + }); + + link.dom.click(); + link.destroy(); }, /** - * Inhibit the context menu when right clicking in the grid header. + * Get the content of the cells as rendered by the grid. + * The method handle the grouping cell as well. * - * @param {Ext.grid.header.Container} gridheader - * @param {Ext.grid.column.Column} column - * @param {Ext.EventObject} event - * @param {HTMLElement} html - * @param {Object} eOpts + * @param {Ext.dom.Element} + * The DOM element corresponding to the x-grid-row. + * + * @return {Object} + * @return {String} return.groupValue + * @return {String[]} return.values * */ - onHeaderContextMenu: function (gridheader, colum, event, html, eOpts) { + getCells: function (row) { "use strict"; - event.stopEvent(); + + 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}; }, /** - * Show the context menu when right clicking in the non-empty grid. + * Get the label and the alignment of the visible columns. * - * @param {Ext.view.View} view - * @param {Ext.data.Model} record - * @param {HTMLElement} item - * @param {Ext.EventObject} event - * @param {Object} eOpts + * @return {Object} + * @return {String[]} return.alignments + * @return {String[]} return.labels * */ - onItemContextMenu: function (view, record, item, index, event, eOpts) { + getColumnHeader: function () { "use strict"; - event.stopEvent(); - this.menu.showAt(event.getXY()); + var alignments = [], + columns, + container, + labels = [], + i; + + container = this.getCmp().getDockedItems('headercontainer'); + + if (container.length) { + columns = container[0].getVisibleGridColumns(); + + for (i = 0; i < columns.length; i += 1) { + labels.push(columns[i].text); + + if (columns[i].align) { + alignments.push(columns[i].align); + } else { + alignments.push('left'); + } + } + } + + return {alignments: alignments, labels: labels}; }, /** - * Export the content of the grid as a CSV file. - * The method scan the HTML to find the rendered headers, rows - * and cells content. + * Build the file content under CSV format. + * + * @return {String[]} One string per line. * - * Hidden columns and rows are ignored. */ - toCSV: function () { + getCSV: function () { "use strict"; - var el = this.getCmp().getView().getEl(), - headerContainer = this.getCmp().getDockedItems('headercontainer'), - csv = [], - li = [], - cell, - columns, + var cells, + data = [], + header = this.getColumnHeader(), i, - link, - row, - s, - value; - - // scan the grid header - if (headerContainer.length) { - columns = headerContainer[0].getVisibleGridColumns(); + row; - for (i = 0; i < columns.length; i += 1) { - value = this.addDelimiter(columns[i].text); - li.push(value); + // the grid header + if (header.labels.length) { + for (i = 0; i < header.labels.length; i += 1) { + header.labels[i] = this.delimit(header.labels[i]); } - - csv.push(li.join(this.separator)); + data.push(header.labels.join(this.separator)); } - // scan each row of the HTML table - row = el.down("tr"); + // scan the row of the HTML table + row = this.getCmp().getView().getEl().down("tr"); do { - // scan each cell of the HTML row - cell = row.down("td"); - - // NOTE: - // A standard cell is a x-grid-cell - // however it is a x-group-hd-container when the 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 in that they can be null is the group is closed. - if (cell.dom.className === "x-group-hd-container") { - value = this.addDelimiter(cell.dom.childNodes[0].textContent); - csv.push(value); - cell = cell.down("td"); + // 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)); } - // add CSV delimiter to the cell value and separator - if (cell) { - li = []; - do { - value = this.addDelimiter(cell.dom.textContent); - li.push(value); - cell = cell.next(); - } while (cell); + // move to the next HTML row + row = row.next(); + + } while (row); + + return data; + }, + + /** + * Build the file content under CSV format. + * + * @return {String[]} One string per line. + * + */ + getLaTeX: function () { + + "use strict"; + + var cells, + cmd, + data = [], + header = this.getColumnHeader(), + nColumns = header.alignments.length, + i, + row; + + //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]; + } + + cmd = '\\begin{tabular}{' + header.alignments.join('') + '}'; + data.push(cmd); + + // table content + row = this.getCmp().getView().getEl().down("tr"); + do { + // get the cells of the HTML row + cells = this.getCells(row); - csv.push(li.join(this.separator)); + // group cell + if (cells.groupValue) { + cmd = '\\multicolumn{' + nColumns + '}{l}{' + cells.groupValue + '} \\\\'; + data.push(cmd); } + // standard cells + data.push(cells.values.join(' & ') + ' \\\\'); + // move to the next HTML row row = row.next(); + } while (row); - // special character like quote, / , ? are HTML encoded - s = encodeURIComponent(csv.join(this.newLine)); + //end table and document + data.push('\\end{tabular}'); + data.push('\\end{document}'); - // download the file by simulating a click on a link - link = Ext.getBody().createChild({ - tag: "a", - href: "data:text/csv;charset=utf-8," + s, - download: "my_data.csv" - }); - link.dom.click(); - link.destroy(); + return data; + }, + + /** + * 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()); + }, + + /** + * Export the content of the grid as a CSV file. + * + */ + onCsvExport: function () { + + "use strict"; + + var data = this.getCSV(); + this.download("text/csv", "utf-8", data, "my_data.csv"); + }, + + /** + * Inhibit the context menu when right clicking in the grid header. + * + * @param {Ext.grid.header.Container} gridheader + * @param {Ext.grid.column.Column} column + * @param {Ext.EventObject} event + * @param {HTMLElement} html + * @param {Object} eOpts + * + */ + onHeaderContextMenu: function (gridheader, colum, event, html, eOpts) { + + "use strict"; + event.stopEvent(); + }, + + /** + * Show the context menu when right clicking in the non-empty grid. + * + * @param {Ext.view.View} view + * @param {Ext.data.Model} record + * @param {HTMLElement} item + * @param {Ext.EventObject} event + * @param {Object} eOpts + * + */ + onItemContextMenu: function (view, record, item, index, event, eOpts) { + + "use strict"; + + event.stopEvent(); + this.menu.showAt(event.getXY()); }, /** * Export the content of the grid as a LaTex file. * */ - toLaTeX: function () { + onLatexExport: function () { - "use strict"; + "use strict"; + var data = this.getLaTeX(); + this.download("application/x-latex", "utf-8", data, "my_data.tex"); }, /** * Export the content of the grid as a PDF file. * */ - toPDF: function () { + onPdfExport: function () { "use strict"; diff --git a/views/reports/report_4.html b/views/reports/report_4.html index 95887ef091cb3f88cc102e182c472abb960625f3..d48af783926b4faa11a076f68cfcc1a1b518488b 100644 --- a/views/reports/report_4.html +++ b/views/reports/report_4.html @@ -35,9 +35,16 @@ {text: "Controller", dataIndex: 'controller', flex: 0.8}, {text: "Host", dataIndex: 'host', flex: 1}, {text: "Collections", dataIndex: 'collections', flex: 1.2}, - {text: "Ratio", dataIndex: 'ratio', flex: 0.5} + { + text: "Ratio", + dataIndex: 'ratio', + flex: 0.5, + align: 'right', + summaryType: 'sum' + } ], features: [{ftype:'grouping'}], + //features: [{ftype:'groupingsummary'}, {ftype:'summary'}], forceFit: true, //hideHeaders: true, padding: "10 40 20 60",