odoo.define('web.PivotRenderer', function (require) { "use strict"; var AbstractRenderer = require('web.AbstractRenderer'); var core = require('web.core'); var field_utils = require('web.field_utils'); var QWeb = core.qweb; var PivotRenderer = AbstractRenderer.extend({ tagName: 'table', className: 'table-hover table-condensed table-bordered', events: _.extend({}, AbstractRenderer.prototype.events, { 'hover td': '_onTdHover', }), /** * @override * @param {Object} [params.widgets] mapping (fieldName -> widget) used to * format the cells */ init: function (parent, state, params) { this._super.apply(this, arguments); this.fieldWidgets = params.widgets || {}; }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- /** * Used to determine whether or not to display the no content helper. * * @private * @returns {boolean} */ _hasContent: function () { return this.state.has_data && this.state.measures.length; }, /** * @override * @private * @returns {Deferred} */ _render: function () { if (!this._hasContent()) { // display the nocontent helper this.replaceElement(QWeb.render('PivotView.nodata')); return this._super.apply(this, arguments); } if (!this.$el.is('table')) { // coming from the no content helper, so the root element has to be // re-rendered before rendering and appending its content this.renderElement(); } var $fragment = $(document.createDocumentFragment()); var $table = $('').appendTo($fragment); var $thead = $('').appendTo($table); var $tbody = $('').appendTo($table); var nbr_measures = this.state.measures.length; var nbrCols = (this.state.mainColWidth === 1) ? nbr_measures : (this.state.mainColWidth + 1) * nbr_measures; for (var i=0; i < nbrCols + 1; i++) { $table.prepend($('')); } this._renderHeaders($thead, this.state.headers); this._renderRows($tbody, this.state.rows); // todo: make sure the next line does something $table.find('.o_pivot_header_cell_opened,.o_pivot_header_cell_closed').tooltip(); this.$el.html($table.contents()); return this._super.apply(this, arguments); }, /** * @private * @param {jQueryElement} $thead * @param {jQueryElement} headers */ _renderHeaders: function ($thead, headers) { var self = this; var i, j, cell, $row, $cell; var groupbyLabels = _.map(this.state.colGroupBys, function (gb) { return self.state.fields[gb.split(':')[0]].string; }); for (i = 0; i < headers.length; i++) { $row = $(''); for (j = 0; j < headers[i].length; j++) { cell = headers[i][j]; $cell = $(''); $header = $('
') .text(cell.title) .attr('rowspan', cell.height) .attr('colspan', cell.width); if (i > 0) { $cell.attr('title', groupbyLabels[i-1]); } if (cell.expanded !== undefined) { $cell.addClass(cell.expanded ? 'o_pivot_header_cell_opened' : 'o_pivot_header_cell_closed'); $cell.data('id', cell.id); } if (cell.measure) { $cell.addClass('o_pivot_measure_row text-muted') .text(this.state.fields[cell.measure].string); $cell.data('id', cell.id).data('measure', cell.measure); if (cell.id === this.state.sortedColumn.id && cell.measure === this.state.sortedColumn.measure) { $cell.addClass('o_pivot_measure_row_sorted_' + this.state.sortedColumn.order); } } $row.append($cell); $cell.toggleClass('hidden-xs', (cell.expanded !== undefined) || (cell.measure !== undefined && j < headers[i].length - this.state.measures.length)); if (cell.height > 1) { $cell.css('padding', 0); } } $thead.append($row); } }, /** * @private * @param {jQueryElement} $tbody * @param {jQueryElement} rows */ _renderRows: function ($tbody, rows) { var self = this; var i, j, value, measure, name, $row, $cell, $header; var nbrMeasures = this.state.measures.length; var length = rows[0].values.length; var shouldDisplayTotal = this.state.mainColWidth > 1; var groupbyLabels = _.map(this.state.rowGroupBys, function (gb) { return self.state.fields[gb.split(':')[0]].string; }); var measureTypes = this.state.measures.map(function (name) { return self.state.fields[name].type; }); for (i = 0; i < rows.length; i++) { $row = $('
') .text(rows[i].title) .data('id', rows[i].id) .css('padding-left', (5 + rows[i].indent * 30) + 'px') .addClass(rows[i].expanded ? 'o_pivot_header_cell_opened' : 'o_pivot_header_cell_closed'); if (rows[i].indent > 0) $header.attr('title', groupbyLabels[rows[i].indent - 1]); $header.appendTo($row); for (j = 0; j < length; j++) { value = rows[i].values[j]; if (value !== undefined) { name = this.state.measures[j % nbrMeasures]; measure = this.state.fields[name]; var formatter = this.fieldWidgets[name] || measureTypes[j % nbrMeasures]; value = field_utils.format[formatter](value, measure); } $cell = $('') .data('id', rows[i].id) .data('col_id', rows[i].col_ids[Math.floor(j / nbrMeasures)]) .toggleClass('o_empty', !value) .text(value) .addClass('o_pivot_cell_value text-right'); if (((j >= length - this.state.measures.length) && shouldDisplayTotal) || i === 0){ $cell.css('font-weight', 'bold'); } $row.append($cell); $cell.toggleClass('hidden-xs', j < length - nbrMeasures); } $tbody.append($row); } }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- /** * @private * @param {MouseEvent} event */ _onTdHover: function (event) { var $td = $(event.target); $td.closest('table').find('col:eq(' + $td.index()+')').toggleClass('hover'); } }); return PivotRenderer; });