Antony Lesuisse 2014-05-01 19:25:08 +02:00
commit 5e21a4235d
11 changed files with 121 additions and 77 deletions

View File

@ -581,9 +581,11 @@ class Home(http.Controller):
redirect = '/web?' + request.httprequest.query_string redirect = '/web?' + request.httprequest.query_string
values['redirect'] = redirect values['redirect'] = redirect
if request.httprequest.method == 'POST': if request.httprequest.method == 'POST':
old_uid = request.uid
uid = request.session.authenticate(request.session.db, request.params['login'], request.params['password']) uid = request.session.authenticate(request.session.db, request.params['login'], request.params['password'])
if uid is not False: if uid is not False:
return http.redirect_with_hash(redirect) return http.redirect_with_hash(redirect)
request.uid = old_uid
values['error'] = "Wrong login/password" values['error'] = "Wrong login/password"
return render_bootstrap_template('web.login', values) return render_bootstrap_template('web.login', values)

View File

@ -1,4 +1,4 @@
@charset "utf-8"; @charset "UTF-8";
@font-face { @font-face {
font-family: "mnmliconsRegular"; font-family: "mnmliconsRegular";
src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot"); src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot");
@ -720,7 +720,7 @@
border-bottom-left-radius: 8px; border-bottom-left-radius: 8px;
} }
.openerp .oe_notification { .openerp .oe_notification {
z-index: 1050; z-index: 1500;
} }
.openerp .oe_webclient_timezone_notification a { .openerp .oe_webclient_timezone_notification a {
color: white; color: white;
@ -2742,13 +2742,15 @@
padding: 3px 6px; padding: 3px 6px;
white-space: pre-line; white-space: pre-line;
} }
.openerp .oe_list_content > tbody > tr > td > button.btn_img, .openerp .oe_list_content > tbody > tr > th > button.btn_img { .openerp .oe_list_content > tbody > tr > td > button, .openerp .oe_list_content > tbody > tr > th > button {
border: none; border: none;
background: transparent; background: transparent;
padding: 0; padding: 0;
-moz-box-shadow: none; }
-webkit-box-shadow: none; .openerp .oe_list_content > tbody > tr > td > button.btn_txt, .openerp .oe_list_content > tbody > tr > th > button.btn_txt {
box-shadow: none; border: 1px solid rgba(0, 0, 0, 0.4);
background: #e3e3e3;
padding: 3px 12px;
} }
.openerp .oe_list_content > tbody > tr > td.oe_list_checkbox:first-child, .openerp .oe_list_content > tbody > tr th.oe_list_checkbox:first-child { .openerp .oe_list_content > tbody > tr > td.oe_list_checkbox:first-child, .openerp .oe_list_content > tbody > tr th.oe_list_checkbox:first-child {
width: 17px; width: 17px;
@ -3293,11 +3295,6 @@ body.oe_single_form .oe_single_form_container {
overflow: hidden !important; overflow: hidden !important;
} }
} }
.ui-icon {
width: 18px;
height: 18px;
}
.tooltip { .tooltip {
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -3307,8 +3304,6 @@ body.oe_single_form .oe_single_form_container {
background: white; background: white;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5); text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
background-color: transparent; background-color: transparent;
/*We need a greater z-index in order for tooltip to go over bootstrap modal z-index*/
z-index: 1500;
} }
.tooltip .tooltip-inner { .tooltip .tooltip-inner {
text-align: left !important; text-align: left !important;
@ -3345,6 +3340,12 @@ body.oe_single_form .oe_single_form_container {
.tooltip .tooltip-inner .oe_tooltip_message { .tooltip .tooltip-inner .oe_tooltip_message {
max-width: 310px; max-width: 310px;
} }
.ui-icon {
width: 18px;
height: 18px;
}
.modal .modal-header button.close { .modal .modal-header button.close {
border: none; border: none;
background: none; background: none;
@ -3355,12 +3356,17 @@ body.oe_single_form .oe_single_form_container {
.modal .modal-footer { .modal .modal-footer {
text-align: left; text-align: left;
} }
.modal .oe_act_window.modal-body{ .modal .oe_button {
padding: 0;
}
.modal .oe_button{
margin: 0 4px 0 0; margin: 0 4px 0 0;
} }
.modal .oe_act_window.modal-body {
padding: 0;
}
.ui-datepicker {
z-index: 1500 !important;
}
input[type="radio"], input[type="checkbox"] { input[type="radio"], input[type="checkbox"] {
margin-right: 4px; margin-right: 4px;
margin-left: 4px; margin-left: 4px;

View File

@ -639,7 +639,8 @@ $sheet-padding: 16px
// }}} // }}}
// Notifications {{{ // Notifications {{{
.oe_notification .oe_notification
z-index: 1050 z-index: 1500
.oe_webclient_timezone_notification .oe_webclient_timezone_notification
a a
color: white color: white
@ -2220,11 +2221,14 @@ $sheet-padding: 16px
padding: 3px 6px padding: 3px 6px
white-space: pre-line white-space: pre-line
> td, > th > td, > th
> button.btn_img > button
border: none border: none
background: transparent background: transparent
padding: 0 padding: 0
@include box-shadow(none) > button.btn_txt
border: 1px solid rgba(0,0,0,0.4)
background: #e3e3e3
padding: 3px 12px
> td.oe_list_checkbox:first-child, th.oe_list_checkbox:first-child > td.oe_list_checkbox:first-child, th.oe_list_checkbox:first-child
width: 17px width: 17px
&:after &:after
@ -2736,6 +2740,9 @@ body.oe_single_form
.oe_act_window.modal-body .oe_act_window.modal-body
padding: 0 padding: 0
.ui-datepicker
z-index: 1500 !important
input[type="radio"], input[type="checkbox"] input[type="radio"], input[type="checkbox"]
margin-right: 4px margin-right: 4px
margin-left: 4px margin-left: 4px

View File

@ -32,7 +32,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
var self = this; var self = this;
var options = { var options = {
buttons: [ buttons: [
{text: _t("Close"), click: function () { self.close(); }}, {text: _t("Close"), click: function () { self.$el.parents('.modal').modal('hide'); }},
{text: _t("Export To File"), click: function () { self.on_click_export_data(); }} {text: _t("Export To File"), click: function () { self.on_click_export_data(); }}
], ],
close: function () { self.close();} close: function () { self.close();}

View File

@ -29,8 +29,8 @@ my.Facet = B.Model.extend({
B.Model.prototype.initialize.apply(this, arguments); B.Model.prototype.initialize.apply(this, arguments);
this.values = new my.FacetValues(values || []); this.values = new my.FacetValues(values || []);
this.values.on('add remove change reset', function () { this.values.on('add remove change reset', function (_, options) {
this.trigger('change', this); this.trigger('change', this, options);
}, this); }, this);
}, },
get: function (key) { get: function (key) {
@ -399,7 +399,8 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
this.setup_global_completion(); this.setup_global_completion();
this.query = new my.SearchQuery() this.query = new my.SearchQuery()
.on('add change reset remove', this.proxy('do_search')) .on('add change reset remove', this.proxy('do_search'))
.on('add change reset remove', this.proxy('renderFacets')); .on('change', this.proxy('renderChangedFacets'))
.on('add reset remove', this.proxy('renderFacets'));
if (this.options.hidden) { if (this.options.hidden) {
this.$el.hide(); this.$el.hide();
@ -578,14 +579,20 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
.trigger('blur'); .trigger('blur');
}, },
/** /**
* * Call the renderFacets method with the correct arguments.
* @param {openerp.web.search.SearchQuery | openerp.web.search.Facet} _1 * This is due to the fact that change events are called with two arguments
* @param {openerp.web.search.Facet} [_2] * (model, options) while add, reset and remove events are called with
* (collection, model, options) as arguments
*/
renderChangedFacets: function (model, options) {
this.renderFacets(undefined, model, options);
},
/**
* @param {openerp.web.search.SearchQuery | undefined} Undefined if event is change
* @param {openerp.web.search.Facet}
* @param {Object} [options] * @param {Object} [options]
*/ */
renderFacets: function (_1, _2, options) { renderFacets: function (collection, model, options) {
// _1: model if event=change, otherwise collection
// _2: undefined if event=change, otherwise model
var self = this; var self = this;
var started = []; var started = [];
var $e = this.$('div.oe_searchview_facets'); var $e = this.$('div.oe_searchview_facets');
@ -610,6 +617,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
}); });
$.when.apply(null, started).then(function () { $.when.apply(null, started).then(function () {
if (options && options.focus_input === false) return;
var input_to_focus; var input_to_focus;
// options.at: facet inserted at given index, focus next input // options.at: facet inserted at given index, focus next input
// otherwise just focus last input // otherwise just focus last input
@ -618,7 +626,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
} else { } else {
input_to_focus = self.input_subviews[(options.at + 1) * 2]; input_to_focus = self.input_subviews[(options.at + 1) * 2];
} }
input_to_focus.$el.focus(); input_to_focus.$el.focus();
}); });
}, },
@ -1602,8 +1609,11 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
return facetValue.get('label'); return facetValue.get('label');
}, },
make_domain: function (name, operator, facetValue) { make_domain: function (name, operator, facetValue) {
if (operator === this.default_operator) { switch(operator){
case this.default_operator:
return [[name, '=', facetValue.get('value')]]; return [[name, '=', facetValue.get('value')]];
case 'child_of':
return [[name, 'child_of', facetValue.get('value')]];
} }
return this._super(name, operator, facetValue); return this._super(name, operator, facetValue);
}, },

View File

@ -3489,7 +3489,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
self.display_value_backup = {}; self.display_value_backup = {};
self.render_value(); self.render_value();
self.focus(); self.focus();
self.view.do_onchange(self); self.trigger('changed_value');
}); });
}); });
}); });

View File

@ -40,7 +40,7 @@
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 class="modal-title"><t t-raw="title"/></h3> <h3 class="modal-title"><t t-raw="title"/></h3>
</div> </div>
<div class="modal-body"> <div class="modal-body" style="overflow-y: auto;">
</div> </div>
</div> </div>
</div> </div>
@ -800,12 +800,11 @@
</t> </t>
<button t-name="ListView.row.text_button" type="button" <button t-name="ListView.row.text_button" type="button"
t-att-title="widget.string" t-att-disabled="disabled || undefined" t-att-title="widget.string" t-att-disabled="disabled || undefined"
t-att-class="disabled ? 'oe_list_button_disabled btn' : 'btn'"> t-att-class="disabled ? 'oe_list_button_disabled btn_txt oe_link' : 'btn_txt oe_link'"
<t t-esc="widget.string"/> ><t t-esc="widget.string"/></button>
</button>
<button t-name="ListView.row.button" type="button" <button t-name="ListView.row.button" type="button"
t-att-title="widget.string" t-att-disabled="disabled || undefined" t-att-title="widget.string" t-att-disabled="disabled || undefined"
t-att-class="disabled ? 'oe_list_button_disabled btn_img' : 'btn_img'" t-att-class="disabled ? 'oe_list_button_disabled' : ''"
><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png" ><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png"
t-att-alt="widget.string"/></button> t-att-alt="widget.string"/></button>
<t t-extend="ListView.row"> <t t-extend="ListView.row">

View File

@ -196,6 +196,13 @@ openerp.web_calendar = function(instance) {
} else { } else {
this.avatar_title = attrs.avatar_title; this.avatar_title = attrs.avatar_title;
} }
if (isNullOrUndef(attrs.avatar_filter)) {
this.avatar_filter = this.avatar_model;
} else {
this.avatar_filter = attrs.avatar_filter;
}
this.color_field = attrs.color; this.color_field = attrs.color;
if (this.color_field && this.selected_filters.length === 0) { if (this.color_field && this.selected_filters.length === 0) {
@ -577,7 +584,7 @@ openerp.web_calendar = function(instance) {
if (!self.colorIsAttendee || the_attendee_people != temp_ret[self.color_field]) { if (!self.colorIsAttendee || the_attendee_people != temp_ret[self.color_field]) {
tempColor = (self.all_filters[the_attendee_people] !== undefined) tempColor = (self.all_filters[the_attendee_people] !== undefined)
? self.all_filters[the_attendee_people].color ? self.all_filters[the_attendee_people].color
: self.all_filters[-1].color; : (self.all_filters[-1] ? self.all_filters[-1].color : 1);
the_title_avatar += '<i class="fa fa-user attendee_head color_'+tempColor+'" title="' + self.all_attendees[the_attendee_people] + '" ></i>'; the_title_avatar += '<i class="fa fa-user attendee_head color_'+tempColor+'" title="' + self.all_attendees[the_attendee_people] + '" ></i>';
}//else don't add myself }//else don't add myself
} }
@ -700,7 +707,6 @@ openerp.web_calendar = function(instance) {
} }
if (!self.useContacts) { // If we use all peoples displayed in the current month as filter in sidebars if (!self.useContacts) { // If we use all peoples displayed in the current month as filter in sidebars
var filter_value; var filter_value;
var filter_item; var filter_item;
@ -713,7 +719,7 @@ openerp.web_calendar = function(instance) {
value: filter_value, value: filter_value,
label: e[self.color_field][1], label: e[self.color_field][1],
color: self.get_color(filter_value), color: self.get_color(filter_value),
avatar_model: self.avatar_model, avatar_model: (_.str.toBoolElse(self.avatar_filter, true) ? self.avatar_filter : false ),
is_checked: true is_checked: true
}; };
self.all_filters[e[self.color_field][0]] = filter_item; self.all_filters[e[self.color_field][0]] = filter_item;
@ -734,7 +740,7 @@ openerp.web_calendar = function(instance) {
return null; return null;
}); });
} }
return self.perform_necessary_name_gets(events).then(callback);
} }
else { //WE USE CONTACT else { //WE USE CONTACT
if (self.attendee_people !== undefined) { if (self.attendee_people !== undefined) {
@ -751,6 +757,9 @@ openerp.web_calendar = function(instance) {
} }
} }
}
var all_attendees = $.map(events, function (e) { return e[self.attendee_people]; }); var all_attendees = $.map(events, function (e) { return e[self.attendee_people]; });
all_attendees = _.chain(all_attendees).flatten().uniq().value(); all_attendees = _.chain(all_attendees).flatten().uniq().value();
@ -770,7 +779,6 @@ openerp.web_calendar = function(instance) {
}); });
return self.perform_necessary_name_gets(events).then(callback); return self.perform_necessary_name_gets(events).then(callback);
} }
}
}); });
}, },
eventDataTransform: function (event) { eventDataTransform: function (event) {

View File

@ -167,7 +167,7 @@ instance.web_graph.GraphView = instance.web.View.extend({
row_search_facet = query.findWhere({category:'GroupBy'}); row_search_facet = query.findWhere({category:'GroupBy'});
if (row_search_facet) { if (row_search_facet) {
row_search_facet.values.reset(row_facet.values); row_search_facet.values.reset(row_facet.values, {focus_input:false});
} else { } else {
if (row_groupby.length) { if (row_groupby.length) {
query.add(row_facet); query.add(row_facet);
@ -181,7 +181,7 @@ instance.web_graph.GraphView = instance.web.View.extend({
col_search_facet = query.findWhere({category:'ColGroupBy'}); col_search_facet = query.findWhere({category:'ColGroupBy'});
if (col_search_facet) { if (col_search_facet) {
col_search_facet.values.reset(col_facet.values); col_search_facet.values.reset(col_facet.values, {focus_input:false});
} else { } else {
if (col_groupby.length) { if (col_groupby.length) {
query.add(col_facet); query.add(col_facet);

View File

@ -51,7 +51,7 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
this.search_view = parent.searchview; this.search_view = parent.searchview;
openerp.session.rpc('/web_graph/check_xlwt').then(function (result) { openerp.session.rpc('/web_graph/check_xlwt').then(function (result) {
self.$('.graph_options_selection label').toggle(result); self.$('.graph_options_selection label').last().toggle(result);
}); });
return this.model.call('fields_get', []).then(function (f) { return this.model.call('fields_get', []).then(function (f) {
@ -499,10 +499,12 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
// Main display method // Main display method
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
display_data: function () { display_data: function () {
var scroll = $(window).scrollTop();
this.$('.graph_main_content svg').remove(); this.$('.graph_main_content svg').remove();
this.$('.graph_main_content div').remove(); this.$('.graph_main_content div').remove();
this.table.empty(); this.table.empty();
this.table.toggleClass('heatmap', this.heatmap_mode !== 'none'); this.table.toggleClass('heatmap', this.heatmap_mode !== 'none');
this.$('.graph_options_selection label').last().toggleClass('disabled', this.pivot.no_data);
this.width = this.$el.width(); this.width = this.$el.width();
this.height = Math.min(Math.max(document.documentElement.clientHeight - 116 - 60, 250), Math.round(0.8*this.$el.width())); this.height = Math.min(Math.max(document.documentElement.clientHeight - 116 - 60, 250), Math.round(0.8*this.$el.width()));
@ -512,6 +514,7 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
} else { } else {
if (this.mode === 'pivot') { if (this.mode === 'pivot') {
this.draw_table(); this.draw_table();
$(window).scrollTop(scroll);
} else { } else {
this.$('.graph_main_content').append($('<div><svg>')); this.$('.graph_main_content').append($('<div><svg>'));
this.svg = this.$('.graph_main_content svg')[0]; this.svg = this.$('.graph_main_content svg')[0];
@ -626,7 +629,7 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
if ((dim_x === 0) && (dim_y === 0)) { if ((dim_x === 0) && (dim_y === 0)) {
data = [{key: _t('Total'), values:[{ data = [{key: _t('Total'), values:[{
x: _t('Total'), x: _t('Total'),
y: this.pivot.get_total(), y: this.pivot.get_total()[0],
}]}]; }]}];
// Only column groupbys // Only column groupbys
} else if ((dim_x === 0) && (dim_y >= 1)){ } else if ((dim_x === 0) && (dim_y >= 1)){
@ -639,7 +642,7 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
// Just 1 row groupby // Just 1 row groupby
} else if ((dim_x === 1) && (dim_y === 0)) { } else if ((dim_x === 1) && (dim_y === 0)) {
data = _.map(this.pivot.main_row().children, function (pt) { data = _.map(this.pivot.main_row().children, function (pt) {
var value = self.pivot.get_total(pt), var value = self.pivot.get_total(pt)[0],
title = (pt.title !== undefined) ? pt.title : _t('Undefined'); title = (pt.title !== undefined) ? pt.title : _t('Undefined');
return {x: title, y: value}; return {x: title, y: value};
}); });
@ -676,8 +679,6 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
nv.addGraph(function () { nv.addGraph(function () {
var chart = nv.models.multiBarChart() var chart = nv.models.multiBarChart()
.width(self.width)
.height(self.height)
.reduceXTicks(false) .reduceXTicks(false)
.stacked(self.bar_ui === 'stack') .stacked(self.bar_ui === 'stack')
.showControls(show_controls); .showControls(show_controls);
@ -705,9 +706,12 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
dim_x = this.pivot.rows.groupby.length, dim_x = this.pivot.rows.groupby.length,
dim_y = this.pivot.cols.groupby.length; dim_y = this.pivot.cols.groupby.length;
var rows = this.pivot.get_rows_with_depth(dim_x),
labels = _.pluck(rows, 'title');
var data = _.map(this.pivot.get_cols_leaves(), function (col) { var data = _.map(this.pivot.get_cols_leaves(), function (col) {
var values = _.map(self.pivot.get_rows_with_depth(dim_x), function (row) { var values = _.map(rows, function (row, index) {
return {x: row.title, y: self.pivot.get_values(row.id,col.id)[0] || 0}; return {x: index, y: self.pivot.get_values(row.id,col.id)[0] || 0};
}); });
var title = _.map(col.path, function (p) { var title = _.map(col.path, function (p) {
return p || _t('Undefined'); return p || _t('Undefined');
@ -720,10 +724,9 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
nv.addGraph(function () { nv.addGraph(function () {
var chart = nv.models.lineChart() var chart = nv.models.lineChart()
.x(function (d,u) { return u; }) .x(function (d,u) { return u; });
.width(self.width)
.height(self.height) chart.xAxis.tickFormat(function (d,u) {return labels[d];});
.margin({top: 30, right: 20, bottom: 20, left: 60});
d3.select(self.svg) d3.select(self.svg)
.attr('width', self.width) .attr('width', self.width)
@ -745,14 +748,14 @@ openerp.web_graph.Graph = openerp.web.Widget.extend({
if (dim_x === 0) { if (dim_x === 0) {
title = self.measure_label; title = self.measure_label;
} }
return {x: title, y: self.pivot.get_total(row)}; return {x: title, y: self.pivot.get_total(row)[0]};
}); });
nv.addGraph(function () { nv.addGraph(function () {
var chart = nv.models.pieChart() var chart = nv.models.pieChart()
.color(d3.scale.category10().range())
.width(self.width) .width(self.width)
.height(self.height); .height(self.height)
.color(d3.scale.category10().range());
d3.select(self.svg) d3.select(self.svg)
.datum(data) .datum(data)

View File

@ -14,6 +14,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({
this.cells = []; this.cells = [];
this.domain = domain; this.domain = domain;
this.no_data = true; this.no_data = true;
this.updating = false;
this.model = model; this.model = model;
this.fields = fields; this.fields = fields;
this.fields.__count = {type: 'integer', string:_t('Quantity')}; this.fields.__count = {type: 'integer', string:_t('Quantity')};
@ -55,6 +56,13 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({
}, },
set: function (domain, row_groupby, col_groupby) { set: function (domain, row_groupby, col_groupby) {
var self = this;
if (this.updating) {
return this.updating.then(function () {
self.updating = false;
return self.set(domain, row_groupby,col_groupby);
});
}
var row_gb_changed = !_.isEqual(row_groupby, this.rows.groupby), var row_gb_changed = !_.isEqual(row_groupby, this.rows.groupby),
col_gb_changed = !_.isEqual(col_groupby, this.cols.groupby); col_gb_changed = !_.isEqual(col_groupby, this.cols.groupby);
@ -251,7 +259,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({
// to null before calling update_data. // to null before calling update_data.
update_data: function () { update_data: function () {
var self = this; var self = this;
return this.perform_requests().then (function () { this.updating = this.perform_requests().then (function () {
var data = Array.prototype.slice.call(arguments); var data = Array.prototype.slice.call(arguments);
self.no_data = !data[0].length; self.no_data = !data[0].length;
if (self.no_data) { if (self.no_data) {
@ -273,6 +281,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({
self.set_headers(row_headers, self.rows); self.set_headers(row_headers, self.rows);
self.set_headers(col_headers, self.cols); self.set_headers(col_headers, self.cols);
}); });
return this.updating;
}, },
make_headers_and_cell: function (data_pts, row_headers, col_headers, index, prefix, expand) { make_headers_and_cell: function (data_pts, row_headers, col_headers, index, prefix, expand) {