2222 lines
86 KiB
JavaScript
2222 lines
86 KiB
JavaScript
/* This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
this repository contains the full copyright notices and license terms. */
|
|
(function() {
|
|
'use strict';
|
|
|
|
Sao.View.TreeXMLViewParser = Sao.class_(Sao.View.XMLViewParser, {
|
|
_parse_tree: function(node, attributes) {
|
|
[].forEach.call(node.childNodes, function(child) {
|
|
this.parse(child);
|
|
}.bind(this));
|
|
},
|
|
_parse_field: function(node, attributes) {
|
|
var name = attributes.name;
|
|
var ColumnFactory = Sao.View.TreeXMLViewParser.WIDGETS[
|
|
attributes.widget];
|
|
var column = new ColumnFactory(this.view.screen.model, attributes);
|
|
if (!this.view.widgets[name]) {
|
|
this.view.widgets[name] = [];
|
|
}
|
|
this.view.widgets[name].push(column);
|
|
|
|
var prefixes = [], suffixes = [];
|
|
if (~['url', 'email', 'callto', 'sip'
|
|
].indexOf(attributes.widget)) {
|
|
column.prefixes.push(
|
|
new Sao.View.Tree.Affix(attributes, attributes.widget));
|
|
}
|
|
if ('icon' in attributes) {
|
|
column.prefixes.push(new Sao.View.Tree.Affix(attributes));
|
|
}
|
|
var affix, affix_attributes;
|
|
var affixes = node.childNodes;
|
|
for (i = 0; i < affixes.length; i++) {
|
|
affix = affixes[i];
|
|
affix_attributes = {};
|
|
for (var i = 0, len = affix.attributes.length; i < len; i++) {
|
|
var attribute = affix.attributes[i];
|
|
affix_attributes[attribute.name] = attribute.value;
|
|
}
|
|
if (!affix_attributes.name) {
|
|
affix_attributes.name = name;
|
|
}
|
|
var list;
|
|
if (affix.tagName == 'prefix') {
|
|
list = column.prefixes;
|
|
} else {
|
|
list = column.suffixes;
|
|
}
|
|
list.push(new Sao.View.Tree.Affix(affix_attributes));
|
|
}
|
|
if (!this.view.attributes.sequence &&
|
|
!this.view.children_field &&
|
|
this.field_attrs[name].sortable !== false){
|
|
column.sortable = true;
|
|
}
|
|
this.view.columns.push(column);
|
|
|
|
if (attributes.sum) {
|
|
var label = attributes.sum + Sao.i18n.gettext(': ');
|
|
var sum = jQuery('<label/>', {
|
|
'text': label,
|
|
});
|
|
var aggregate = jQuery('<span/>', {
|
|
'class': 'value',
|
|
});
|
|
this.view.sum_widgets[name] = [sum, aggregate];
|
|
}
|
|
},
|
|
_parse_button: function(node, attributes) {
|
|
var column = new Sao.View.Tree.ButtonColumn(
|
|
this.view.screen, attributes);
|
|
this.view.columns.push(column);
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree = Sao.class_(Sao.View, {
|
|
view_type: 'tree',
|
|
xml_parser: Sao.View.TreeXMLViewParser,
|
|
init: function(view_id, screen, xml, children_field) {
|
|
this.children_field = children_field;
|
|
this.sum_widgets = {};
|
|
this.columns = [];
|
|
this.selection_mode = (screen.attributes.selection_mode ||
|
|
Sao.common.SELECTION_MULTIPLE);
|
|
this.el = jQuery('<div/>', {
|
|
'class': 'treeview responsive'
|
|
});
|
|
this.expanded = {};
|
|
|
|
Sao.View.Tree._super.init.call(this, view_id, screen, xml);
|
|
|
|
// Table of records
|
|
this.rows = [];
|
|
this.edited_row = null;
|
|
this.table = jQuery('<table/>', {
|
|
'class': 'tree table table-hover table-striped table-condensed'
|
|
});
|
|
if (this.editable) {
|
|
this.table.addClass('table-bordered');
|
|
}
|
|
this.el.append(this.table);
|
|
var colgroup = jQuery('<colgroup/>').appendTo(this.table);
|
|
var col = jQuery('<col/>', {
|
|
'class': 'selection-state',
|
|
}).appendTo(colgroup);
|
|
if (this.selection_mode == Sao.common.SELECTION_NONE) {
|
|
col.css('width', 0);
|
|
}
|
|
this.thead = jQuery('<thead/>').appendTo(this.table);
|
|
var tr = jQuery('<tr/>');
|
|
var th = jQuery('<th/>', {
|
|
'class': 'selection-state'
|
|
});
|
|
this.selection = jQuery('<input/>', {
|
|
'type': 'checkbox',
|
|
});
|
|
this.selection.change(this.selection_changed.bind(this));
|
|
th.append(this.selection);
|
|
tr.append(th);
|
|
this.thead.append(tr);
|
|
|
|
this.tfoot = null;
|
|
var sum_row;
|
|
if (!jQuery.isEmptyObject(this.sum_widgets)) {
|
|
sum_row = jQuery('<tr/>');
|
|
sum_row.append(jQuery('<td/>'));
|
|
this.tfoot = jQuery('<tfoot/>');
|
|
this.tfoot.append(sum_row);
|
|
this.table.append(this.tfoot);
|
|
}
|
|
|
|
this.columns.forEach(function(column) {
|
|
col = jQuery('<col/>', {
|
|
'class': column.attributes.widget,
|
|
}).appendTo(colgroup);
|
|
th = jQuery('<th/>', {
|
|
'class': column.attributes.widget,
|
|
});
|
|
var label = jQuery('<label/>')
|
|
.text(column.attributes.string)
|
|
.attr('title', column.attributes.string);
|
|
if (this.editable) {
|
|
if (column.attributes.required) {
|
|
label.addClass('required');
|
|
}
|
|
if (!column.attributes.readonly) {
|
|
label.addClass('editable');
|
|
}
|
|
}
|
|
if (column.attributes.help) {
|
|
label.attr('title', column.attributes.help);
|
|
}
|
|
if (column.sortable) {
|
|
var arrow = jQuery('<img/>', {
|
|
'class': 'icon',
|
|
});
|
|
label.append(arrow);
|
|
column.arrow = arrow;
|
|
th.click(column, this.sort_model.bind(this));
|
|
label.addClass('sortable');
|
|
}
|
|
tr.append(th.append(label));
|
|
column.header = th;
|
|
column.col = col;
|
|
|
|
column.footers = [];
|
|
if (!jQuery.isEmptyObject(this.sum_widgets)) {
|
|
var field_name = column.attributes.name;
|
|
var total_cell = jQuery('<td/>', {
|
|
'class': column.class_,
|
|
});
|
|
if (field_name in this.sum_widgets) {
|
|
var sum_label = this.sum_widgets[field_name][0];
|
|
var sum_value = this.sum_widgets[field_name][1];
|
|
total_cell.append(sum_label);
|
|
total_cell.append(sum_value);
|
|
total_cell.attr('data-title', sum_label.text());
|
|
}
|
|
sum_row.append(total_cell);
|
|
column.footers.push(total_cell);
|
|
}
|
|
}, this);
|
|
this.tbody = jQuery('<tbody/>');
|
|
this.table.append(this.tbody);
|
|
|
|
this.display_size = Sao.config.display_size;
|
|
},
|
|
get editable() {
|
|
return (Boolean(this.attributes.editable) &&
|
|
!this.screen.attributes.readonly);
|
|
},
|
|
sort_model: function(e){
|
|
var column = e.data;
|
|
var arrow = column.arrow;
|
|
this.columns.forEach(function(col) {
|
|
if (col.arrow){
|
|
if (col != column && col.arrow.attr('src')) {
|
|
col.arrow.attr('src', '');
|
|
}
|
|
}
|
|
});
|
|
this.screen.order = this.screen.default_order;
|
|
if (arrow.data('order') == 'ASC') {
|
|
arrow.data('order', 'DESC');
|
|
Sao.common.ICONFACTORY.get_icon_url('tryton-arrow-up')
|
|
.then(function(url) {
|
|
arrow.attr('src', url);
|
|
});
|
|
this.screen.order = [[column.attributes.name, 'DESC']];
|
|
} else if (arrow.data('order') == 'DESC') {
|
|
arrow.data('order', '');
|
|
arrow.attr('src', '');
|
|
} else {
|
|
arrow.data('order', 'ASC');
|
|
Sao.common.ICONFACTORY.get_icon_url('tryton-arrow-down')
|
|
.then(function(url) {
|
|
arrow.attr('src', url);
|
|
});
|
|
this.screen.order = [[column.attributes.name, 'ASC']];
|
|
}
|
|
var unsaved_records = [];
|
|
this.group.forEach(function(unsaved_record) {
|
|
if (unsaved_record.id < 0) {
|
|
unsaved_records = unsaved_record.group;
|
|
}
|
|
});
|
|
var search_string = this.screen.screen_container.get_text();
|
|
if ((!jQuery.isEmptyObject(unsaved_records)) ||
|
|
(this.screen.search_count == this.group.length) ||
|
|
(this.group.parent)) {
|
|
this.screen.search_filter(search_string, true).then(
|
|
function(ids) {
|
|
this.group.sort(function(a, b) {
|
|
a = ids.indexOf(a.id);
|
|
a = a < 0 ? ids.length : a;
|
|
b = ids.indexOf(b.id);
|
|
b = b < 0 ? ids.length : b;
|
|
if (a < b) {
|
|
return -1;
|
|
} else if (a > b) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
});
|
|
this.screen.display();
|
|
}.bind(this));
|
|
} else {
|
|
this.screen.search_filter(search_string);
|
|
}
|
|
},
|
|
update_arrow: function() {
|
|
var order = this.screen.order,
|
|
name = null,
|
|
direction = null,
|
|
icon = '';
|
|
if (order && (order.length == 1)) {
|
|
name = order[0][0];
|
|
direction = order[0][1];
|
|
icon = {
|
|
'ASC': 'tryton-arrow-down',
|
|
'DESC': 'tryton-arrow-up',
|
|
}[direction];
|
|
}
|
|
this.columns.forEach(function(col) {
|
|
var arrow = col.arrow;
|
|
if (arrow) {
|
|
if (col.attributes.name != name) {
|
|
arrow.data('order', '');
|
|
arrow.attr('src', '');
|
|
} else {
|
|
arrow.data('order', direction);
|
|
Sao.common.ICONFACTORY.get_icon_url(icon)
|
|
.then(function(url) {
|
|
arrow.attr('src', url);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
},
|
|
get_fields: function() {
|
|
return Object.keys(this.widgets);
|
|
},
|
|
get_buttons: function() {
|
|
var buttons = [];
|
|
this.columns.forEach(function(column) {
|
|
if (column instanceof Sao.View.Tree.ButtonColumn) {
|
|
buttons.push(column);
|
|
}
|
|
});
|
|
return buttons;
|
|
},
|
|
display: function(selected, expanded) {
|
|
var current_record = this.record;
|
|
if (jQuery.isEmptyObject(selected)) {
|
|
selected = this.get_selected_paths();
|
|
if (this.selection.prop('checked') &&
|
|
!this.selection.prop('indeterminate')) {
|
|
this.screen.group.slice(
|
|
this.rows.length, this.display_size)
|
|
.forEach(function(record) {
|
|
selected.push([record.id]);
|
|
});
|
|
} else {
|
|
if (current_record) {
|
|
var current_path = current_record.get_path(this.group);
|
|
current_path = current_path.map(function(e) {
|
|
return e[1];
|
|
});
|
|
if (!Sao.common.contains(selected, current_path)) {
|
|
selected = [current_path];
|
|
}
|
|
} else if (!current_record) {
|
|
selected = [];
|
|
}
|
|
}
|
|
}
|
|
expanded = expanded || [];
|
|
|
|
if (this.selection_mode == Sao.common.SELECTION_MULTIPLE) {
|
|
this.selection.show();
|
|
} else {
|
|
this.selection.hide();
|
|
}
|
|
|
|
var row_records = function() {
|
|
return this.rows.map(function(row) {
|
|
return row.record;
|
|
});
|
|
}.bind(this);
|
|
var min_display_size = Math.min(
|
|
this.group.length, this.display_size);
|
|
// XXX find better check to keep focus
|
|
if (this.children_field) {
|
|
this.construct();
|
|
} else if ((min_display_size > this.rows.length) &&
|
|
Sao.common.compare(
|
|
this.group.slice(0, this.rows.length),
|
|
row_records())) {
|
|
this.construct(true);
|
|
} else if ((min_display_size != this.rows.length) ||
|
|
!Sao.common.compare(
|
|
this.group.slice(0, this.rows.length),
|
|
row_records())){
|
|
this.construct();
|
|
}
|
|
|
|
// Set column visibility depending on attributes and domain
|
|
var visible_columns = 1; // start at 1 because of the checkbox
|
|
var domain = [];
|
|
if (!jQuery.isEmptyObject(this.screen.domain)) {
|
|
domain.push(this.screen.domain);
|
|
}
|
|
var tab_domain = this.screen.screen_container.get_tab_domain();
|
|
if (!jQuery.isEmptyObject(tab_domain)) {
|
|
domain.push(tab_domain);
|
|
}
|
|
var inversion = new Sao.common.DomainInversion();
|
|
domain = inversion.simplify(domain);
|
|
var decoder = new Sao.PYSON.Decoder(this.screen.context);
|
|
this.columns.forEach(function(column) {
|
|
visible_columns += 1;
|
|
var name = column.attributes.name;
|
|
if (!name) {
|
|
return;
|
|
}
|
|
var related_cells = column.footers.slice();
|
|
related_cells.push(column.header);
|
|
if ((decoder.decode(column.attributes.tree_invisible || '0')) ||
|
|
(name === this.screen.exclude_field)) {
|
|
visible_columns -= 1;
|
|
related_cells.forEach(function(cell) {
|
|
cell.hide();
|
|
cell.addClass('invisible');
|
|
});
|
|
} else {
|
|
var inv_domain = inversion.domain_inversion(domain, name);
|
|
if (typeof inv_domain != 'boolean') {
|
|
inv_domain = inversion.simplify(inv_domain);
|
|
}
|
|
var unique = inversion.unique_value(inv_domain)[0];
|
|
if (unique && jQuery.isEmptyObject(this.children_field)) {
|
|
visible_columns -= 1;
|
|
related_cells.forEach(function(cell) {
|
|
cell.hide();
|
|
cell.addClass('invisible');
|
|
});
|
|
} else {
|
|
related_cells.forEach(function(cell) {
|
|
cell.show();
|
|
cell.removeClass('invisible');
|
|
});
|
|
}
|
|
}
|
|
|
|
if (column.header.hasClass('invisible')) {
|
|
column.col.css('width', 0);
|
|
column.col.hide();
|
|
} else if (!column.col.hasClass('selection-state') &&
|
|
!column.col.hasClass('favorite')) {
|
|
var width = {
|
|
'integer': 6,
|
|
'biginteger': 6,
|
|
'float': 8,
|
|
'numeric': 8,
|
|
'timedelta': 10,
|
|
'date': 10,
|
|
'datetime': 10,
|
|
'time': 10,
|
|
'selection': 9,
|
|
'char': 10,
|
|
'one2many': 5,
|
|
'many2many': 5,
|
|
'boolean': 2,
|
|
'binary': 20,
|
|
}[column.attributes.widget] || 10;
|
|
width = width * 100 + '%';
|
|
column.col.css('width', width);
|
|
column.col.show();
|
|
}
|
|
}.bind(this));
|
|
this.tbody.find('tr.more-row > td').attr(
|
|
'colspan', visible_columns);
|
|
|
|
if (!this.table.hasClass('no-responsive') &
|
|
(this.columns.filter(function(c) {
|
|
return !c.header.hasClass('invisible');
|
|
}).length > 1)) {
|
|
this.table.addClass('responsive');
|
|
this.table.addClass('responsive-header');
|
|
} else {
|
|
this.table.removeClass('responsive');
|
|
this.table.removeClass('responsive-header');
|
|
}
|
|
|
|
this.update_arrow();
|
|
return this.redraw(selected, expanded).done(
|
|
Sao.common.debounce(this.update_sum.bind(this), 250));
|
|
},
|
|
construct: function(extend) {
|
|
var tbody = this.tbody;
|
|
if (!extend) {
|
|
this.rows = [];
|
|
this.tbody = jQuery('<tbody/>');
|
|
this.edited_row = null;
|
|
} else {
|
|
this.tbody.find('tr.more-row').remove();
|
|
}
|
|
var start = this.rows.length;
|
|
var add_row = function(record, pos, group) {
|
|
var RowBuilder;
|
|
if (this.editable) {
|
|
RowBuilder = Sao.View.Tree.RowEditable;
|
|
} else {
|
|
RowBuilder = Sao.View.Tree.Row;
|
|
}
|
|
var tree_row = new RowBuilder(this, record, this.rows.length);
|
|
this.rows.push(tree_row);
|
|
tree_row.construct();
|
|
};
|
|
this.group.slice(start, this.display_size).forEach(
|
|
add_row.bind(this));
|
|
if (!extend) {
|
|
tbody.replaceWith(this.tbody);
|
|
}
|
|
|
|
if (this.display_size < this.group.length) {
|
|
var more_row = jQuery('<tr/>', {
|
|
'class': 'more-row',
|
|
});
|
|
var more_cell = jQuery('<td/>');
|
|
var more_button = jQuery('<button/>', {
|
|
'class': 'btn btn-default',
|
|
'type': 'button'
|
|
}).append(Sao.i18n.gettext('More')
|
|
).click(function() {
|
|
this.display_size += Sao.config.display_size;
|
|
this.display();
|
|
}.bind(this));
|
|
more_cell.append(more_button);
|
|
more_row.append(more_cell);
|
|
this.tbody.append(more_row);
|
|
}
|
|
},
|
|
redraw: function(selected, expanded) {
|
|
return redraw_async(this.rows, selected, expanded);
|
|
},
|
|
switch_: function(path) {
|
|
this.screen.row_activate();
|
|
},
|
|
select_changed: function(record) {
|
|
if (this.edited_row) {
|
|
record = this.edited_row.record;
|
|
this.edited_row.set_selection(true);
|
|
}
|
|
this.record = record;
|
|
// TODO update_children
|
|
},
|
|
update_sum: function() {
|
|
for (var name in this.sum_widgets) {
|
|
if (!this.sum_widgets.hasOwnProperty(name)) {
|
|
continue;
|
|
}
|
|
|
|
var selected_records = this.selected_records;
|
|
var aggregate = '-';
|
|
var sum_label = this.sum_widgets[name][0];
|
|
var sum_value = this.sum_widgets[name][1];
|
|
var sum_ = null;
|
|
var selected_sum = null;
|
|
var loaded = true;
|
|
var digit = 0;
|
|
var field = this.screen.model.fields[name];
|
|
var i, record;
|
|
var records_ids = selected_records.map(function(record){
|
|
return record.id;
|
|
});
|
|
for (i=0; i < this.group.length; i++) {
|
|
record = this.group[i];
|
|
if (!record.get_loaded([name]) && record.id >=0){
|
|
loaded = false;
|
|
break;
|
|
}
|
|
var value = field.get(record);
|
|
if (value && value.isTimeDelta) {
|
|
value = value.asSeconds();
|
|
}
|
|
if (value !== null){
|
|
if (sum_ === null){
|
|
sum_ = value;
|
|
}else {
|
|
sum_ += value;
|
|
}
|
|
if (~records_ids.indexOf(record.id) ||
|
|
!selected_records){
|
|
if (selected_sum === null){
|
|
selected_sum = value;
|
|
}else {
|
|
selected_sum += value;
|
|
}
|
|
}
|
|
if (field.digits) {
|
|
var fdigits = field.digits(record);
|
|
if (fdigits && digit !== null){
|
|
digit = Math.max(fdigits[1], digit);
|
|
} else {
|
|
digit = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (loaded) {
|
|
if (field.description.type == 'timedelta'){
|
|
var converter = field.converter(this.group);
|
|
selected_sum = Sao.common.timedelta.format(
|
|
Sao.TimeDelta(null, selected_sum), converter);
|
|
sum_ = Sao.common.timedelta.format(
|
|
Sao.TimeDelta(null, sum_), converter);
|
|
} else if (digit !== null){
|
|
var options = {};
|
|
options.minimumFractionDigits = digit;
|
|
options.maximumFractionDigits = digit;
|
|
selected_sum = (selected_sum || 0).toLocaleString(
|
|
Sao.i18n.BC47(Sao.i18n.getlang()), options);
|
|
sum_ = (sum_ || 0).toLocaleString(
|
|
Sao.i18n.BC47(Sao.i18n.getlang()), options);
|
|
}
|
|
aggregate = selected_sum + ' / ' + sum_;
|
|
}
|
|
sum_value.text(aggregate);
|
|
sum_value.parent().attr(
|
|
'title', sum_label.text() + ' ' + sum_value.text());
|
|
}
|
|
},
|
|
get selected_records() {
|
|
if (this.selection_mode == Sao.common.SELECTION_NONE) {
|
|
return [];
|
|
}
|
|
var records = [];
|
|
var add_record = function(row) {
|
|
if (row.is_selected()) {
|
|
records.push(row.record);
|
|
}
|
|
row.rows.forEach(add_record);
|
|
};
|
|
this.rows.forEach(add_record);
|
|
if (this.selection.prop('checked') &&
|
|
!this.selection.prop('indeterminate')) {
|
|
this.group.slice(this.rows.length)
|
|
.forEach(function(record) {
|
|
records.push(record);
|
|
});
|
|
}
|
|
return records;
|
|
},
|
|
select_records: function(from, to) {
|
|
if (!from && to) {
|
|
from = this.rows[0].record;
|
|
}
|
|
if (from && to) {
|
|
var from_idx = from.get_index_path(this.screen.group);
|
|
var to_idx = to.get_index_path(this.screen.group);
|
|
var max_len = Math.min(from_idx.length, to_idx.length);
|
|
var tmp;
|
|
for (var i=0; i < max_len; i++) {
|
|
if (from_idx[i] > to_idx[i]) {
|
|
tmp = from;
|
|
from = to;
|
|
to = tmp;
|
|
break;
|
|
}
|
|
}
|
|
if (!tmp && (from_idx.length > to_idx.length)) {
|
|
tmp = from;
|
|
from = to;
|
|
to = tmp;
|
|
}
|
|
}
|
|
var value = this.rows[0].record === from;
|
|
var select_record = function(row) {
|
|
var record = row.record;
|
|
if (record === from) {
|
|
value = true;
|
|
}
|
|
row.set_selection(value);
|
|
if (record === to) {
|
|
value = false;
|
|
}
|
|
row.rows.forEach(select_record);
|
|
};
|
|
this.rows.forEach(select_record);
|
|
},
|
|
selection_changed: function() {
|
|
var value = this.selection.prop('checked');
|
|
var set_checked = function(row) {
|
|
row.set_selection(value);
|
|
row.rows.forEach(set_checked);
|
|
};
|
|
this.rows.forEach(set_checked);
|
|
if (value && this.rows[0]) {
|
|
this.select_changed(this.rows[0].record);
|
|
} else {
|
|
this.select_changed(null);
|
|
}
|
|
this.update_sum();
|
|
},
|
|
update_selection: function() {
|
|
this.update_sum();
|
|
var selected_records = this.selected_records;
|
|
this.selection.prop('indeterminate', false);
|
|
if (jQuery.isEmptyObject(selected_records)) {
|
|
this.selection.prop('checked', false);
|
|
} else if (selected_records.length ==
|
|
this.tbody.children().length &&
|
|
this.display_size >= this.group.length) {
|
|
this.selection.prop('checked', true);
|
|
} else {
|
|
this.selection.prop('indeterminate', true);
|
|
// Set checked to go first unchecked after first click
|
|
this.selection.prop('checked', true);
|
|
}
|
|
},
|
|
get_selected_paths: function() {
|
|
var selected_paths = [];
|
|
function get_selected(row, path) {
|
|
var i, r, len, r_path;
|
|
for (i = 0, len = row.rows.length; i < len; i++) {
|
|
r = row.rows[i];
|
|
r_path = path.concat([r.record.id]);
|
|
if (r.is_selected()) {
|
|
selected_paths.push(r_path);
|
|
}
|
|
get_selected(r, r_path);
|
|
}
|
|
}
|
|
get_selected(this, []);
|
|
return selected_paths;
|
|
},
|
|
get_expanded_paths: function(starting_path, starting_id_path) {
|
|
var id_path, id_paths, row, children_rows, path;
|
|
if (starting_path === undefined) {
|
|
starting_path = [];
|
|
}
|
|
if (starting_id_path === undefined) {
|
|
starting_id_path = [];
|
|
}
|
|
id_paths = [];
|
|
row = this.find_row(starting_path);
|
|
children_rows = row ? row.rows : this.rows;
|
|
for (var path_idx = 0, len = this.n_children(row) ;
|
|
path_idx < len ; path_idx++) {
|
|
path = starting_path.concat([path_idx]);
|
|
row = children_rows[path_idx];
|
|
if (row && row.is_expanded()) {
|
|
id_path = starting_id_path.concat(row.record.id);
|
|
id_paths.push(id_path);
|
|
id_paths = id_paths.concat(this.get_expanded_paths(path,
|
|
id_path));
|
|
}
|
|
}
|
|
return id_paths;
|
|
},
|
|
find_row: function(path) {
|
|
var index;
|
|
var row = null;
|
|
var group = this.rows;
|
|
for (var i=0, len=path.length; i < len; i++) {
|
|
index = path[i];
|
|
if (!group || index >= group.length) {
|
|
return null;
|
|
}
|
|
row = group[index];
|
|
group = row.rows;
|
|
if (!this.children_field) {
|
|
break;
|
|
}
|
|
}
|
|
return row;
|
|
},
|
|
n_children: function(row) {
|
|
if (!row || !this.children_field) {
|
|
return this.rows.length;
|
|
}
|
|
return row.record._values[this.children_field].length;
|
|
},
|
|
set_cursor: function(new_, reset_view) {
|
|
var i, root_group, path, row_path, row, column;
|
|
var row_idx, rest, td;
|
|
|
|
if (!this.record) {
|
|
return;
|
|
}
|
|
path = this.record.get_index_path(this.group);
|
|
if (this.rows.length < path[0]) {
|
|
this.display_size = this.group.length;
|
|
this.display();
|
|
}
|
|
row_idx = path[0];
|
|
rest = path.slice(1);
|
|
if (rest.length > 0) {
|
|
this.rows[row_idx].expand_to_path(rest);
|
|
}
|
|
row = this.find_row(path);
|
|
column = row.next_column(null, new_);
|
|
if (column !== null) {
|
|
td = row._get_column_td(column);
|
|
if (this.editable && new_) {
|
|
td.trigger('click');
|
|
}
|
|
td.find(':input,[tabindex=0]').focus();
|
|
}
|
|
},
|
|
save_row: function() {
|
|
var i, prm, edited_row = this.edited_row;
|
|
if (!this.editable || !this.edited_row) {
|
|
return jQuery.when();
|
|
}
|
|
if (!this.edited_row.record.validate(
|
|
this.get_fields(), false, false, true)) {
|
|
var focused = false;
|
|
var invalid_fields = this.edited_row.record.invalid_fields();
|
|
for (i = 0; i < this.columns.length; i++) {
|
|
var col = this.columns[i];
|
|
if (col.attributes.name in invalid_fields) {
|
|
var td = this.edited_row._get_column_td(i);
|
|
var editable_el = this.edited_row.get_editable_el(td);
|
|
var widget = editable_el.data('widget');
|
|
widget.display(this.edited_row.record, col.field);
|
|
if (!focused) {
|
|
widget.focus();
|
|
focused = true;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (!this.group.parent) {
|
|
prm = this.edited_row.record.save();
|
|
} else if (this.screen.attributes.pre_validate) {
|
|
prm = this.record.pre_validate();
|
|
} else {
|
|
prm = jQuery.when();
|
|
}
|
|
prm.fail(function() {
|
|
if (this.edited_row != edited_row) {
|
|
this.edit_row(null);
|
|
edited_row.set_selection(true);
|
|
edited_row.selection_changed();
|
|
this.edit_row(edited_row);
|
|
}
|
|
}.bind(this));
|
|
return prm;
|
|
},
|
|
edit_row: function(row) {
|
|
if (!this.editable || this.edited_row == row) {
|
|
return;
|
|
}
|
|
if (this.edited_row) {
|
|
this.edited_row.unset_editable();
|
|
}
|
|
if (row) {
|
|
row.set_editable();
|
|
}
|
|
this.edited_row = row;
|
|
}
|
|
});
|
|
|
|
function redraw_async(rows, selected, expanded) {
|
|
var chunk = Sao.config.display_size;
|
|
var dfd = jQuery.Deferred();
|
|
var redraw_rows = function(i) {
|
|
rows.slice(i, i + chunk).forEach(function(row) {
|
|
row.redraw(selected, expanded);
|
|
});
|
|
i += chunk;
|
|
if (i < rows.length) {
|
|
setTimeout(redraw_rows, 0, i);
|
|
} else {
|
|
dfd.resolve();
|
|
}
|
|
};
|
|
setTimeout(redraw_rows, 0, 0);
|
|
return dfd;
|
|
}
|
|
|
|
Sao.View.Tree.Row = Sao.class_(Object, {
|
|
init: function(tree, record, pos, parent) {
|
|
this.tree = tree;
|
|
this.current_column = null;
|
|
this.rows = [];
|
|
this.record = record;
|
|
this.parent_ = parent;
|
|
this.children_field = tree.children_field;
|
|
this.expander = null;
|
|
var path = [];
|
|
if (parent) {
|
|
path = jQuery.extend([], parent.path.split('.'));
|
|
}
|
|
path.push(pos);
|
|
this.path = path.join('.');
|
|
this.el = jQuery('<tr/>');
|
|
this.el.on('click', this.select_row.bind(this));
|
|
},
|
|
is_expanded: function() {
|
|
return (this.path in this.tree.expanded);
|
|
},
|
|
get_last_child: function() {
|
|
if (!this.children_field || !this.is_expanded() ||
|
|
jQuery.isEmptyObject(this.rows)) {
|
|
return this;
|
|
}
|
|
return this.rows[this.rows.length - 1].get_last_child();
|
|
},
|
|
get_id_path: function() {
|
|
if (!this.parent_) {
|
|
return [this.record.id];
|
|
}
|
|
return this.parent_.get_id_path().concat([this.record.id]);
|
|
},
|
|
build_widgets: function() {
|
|
var table = jQuery('<table/>');
|
|
table.css('width', '100%');
|
|
var row = jQuery('<tr/>');
|
|
table.append(row);
|
|
return [table, row];
|
|
},
|
|
construct: function() {
|
|
var el_node = this.el[0];
|
|
while (el_node.firstChild) {
|
|
el_node.removeChild(el_node.firstChild);
|
|
}
|
|
|
|
var td;
|
|
this.tree.el.uniqueId();
|
|
td = jQuery('<td/>', {
|
|
'class': 'selection-state',
|
|
}).click(function(event_) {
|
|
event_.stopPropagation();
|
|
this.selection.click();
|
|
}.bind(this));
|
|
this.el.append(td);
|
|
this.selection = jQuery('<input/>', {
|
|
'type': 'checkbox',
|
|
'name': 'tree-selection-' + this.tree.el.attr('id'),
|
|
});
|
|
this.selection.click(function(event_) {
|
|
event_.stopPropagation();
|
|
});
|
|
this.selection.change(this.selection_changed.bind(this));
|
|
td.append(this.selection);
|
|
|
|
var depth = this.path.split('.').length;
|
|
for (var i = 0; i < this.tree.columns.length; i++) {
|
|
var column = this.tree.columns[i];
|
|
td = jQuery('<td/>', {
|
|
'data-title': column.attributes.string + Sao.i18n.gettext(': ')
|
|
}).append(jQuery('<span/>', { // For responsive min-height
|
|
'aria-hidden': true
|
|
}));
|
|
td.on('click keypress', {'index': i}, function(event_) {
|
|
if (this.expander && !this.is_expanded()) {
|
|
this.toggle_row();
|
|
}
|
|
this.select_column(event_.data.index);
|
|
}.bind(this));
|
|
if (!this.tree.editable) {
|
|
td.dblclick(this.switch_row.bind(this));
|
|
} else {
|
|
if (column.attributes.required) {
|
|
td.addClass('required');
|
|
}
|
|
if (!column.attributes.readonly) {
|
|
td.addClass('editable');
|
|
}
|
|
}
|
|
var widgets = this.build_widgets();
|
|
var table = widgets[0];
|
|
var row = widgets[1];
|
|
td.append(table);
|
|
if ((i === 0) && this.children_field) {
|
|
this.expander = jQuery('<img/>', {
|
|
'tabindex': 0,
|
|
'class': 'icon',
|
|
});
|
|
this.expander.html(' ');
|
|
var margin = 'margin-left';
|
|
if (Sao.i18n.rtl) {
|
|
margin = 'margin-right';
|
|
}
|
|
this.expander.css(margin, (depth - 1) + 'em');
|
|
this.expander.on('click keypress',
|
|
Sao.common.click_press(this.toggle_row.bind(this)));
|
|
row.append(jQuery('<td/>', {
|
|
'class': 'expander'
|
|
}).append(this.expander).css('width', 1));
|
|
}
|
|
var j;
|
|
if (column.prefixes) {
|
|
for (j = 0; j < column.prefixes.length; j++) {
|
|
var prefix = column.prefixes[j];
|
|
row.append(jQuery('<td/>', {
|
|
'class': 'prefix'
|
|
}).css('width', 1));
|
|
}
|
|
}
|
|
row.append(jQuery('<td/>', {
|
|
'class': 'widget'
|
|
}));
|
|
if (column.suffixes) {
|
|
for (j = 0; j < column.suffixes.length; j++) {
|
|
var suffix = column.suffixes[j];
|
|
row.append(jQuery('<td/>', {
|
|
'class': 'suffix'
|
|
}).css('width', 1));
|
|
}
|
|
}
|
|
|
|
this.el.append(td);
|
|
}
|
|
if (this.parent_) {
|
|
var last_child = this.parent_.get_last_child();
|
|
last_child.el.after(this.el);
|
|
} else {
|
|
this.tree.tbody.append(this.el);
|
|
}
|
|
},
|
|
_get_column_td: function(column_index, row) {
|
|
row = row || this.el;
|
|
return jQuery(row.children()[column_index + 1]);
|
|
},
|
|
redraw: function(selected, expanded) {
|
|
selected = selected || [];
|
|
expanded = expanded || [];
|
|
var thead_visible = this.tree.thead.is(':visible');
|
|
|
|
switch(this.tree.selection_mode) {
|
|
case Sao.common.SELECTION_NONE:
|
|
this.selection.hide();
|
|
break;
|
|
case Sao.common.SELECTION_SINGLE:
|
|
this.selection.attr('type', 'radio');
|
|
this.selection.show();
|
|
break;
|
|
case Sao.common.SELECTION_MULTIPLE:
|
|
this.selection.attr('type', 'checkbox');
|
|
this.selection.show();
|
|
break;
|
|
}
|
|
|
|
|
|
for (var i = 0; i < this.tree.columns.length; i++) {
|
|
var column = this.tree.columns[i];
|
|
var td = this._get_column_td(i);
|
|
var tr = td.find('tr');
|
|
var cell;
|
|
if (column.prefixes) {
|
|
for (var j = 0; j < column.prefixes.length; j++) {
|
|
var prefix = column.prefixes[j];
|
|
var prefix_el = jQuery(tr.children('.prefix')[j]);
|
|
cell = prefix_el.children();
|
|
if (cell.length) {
|
|
prefix.render(this.record, cell);
|
|
} else {
|
|
prefix_el.html(prefix.render(this.record));
|
|
}
|
|
}
|
|
}
|
|
var widget = tr.children('.widget');
|
|
cell = widget.children();
|
|
if (cell.length) {
|
|
column.render(this.record, cell);
|
|
} else {
|
|
widget.html(column.render(this.record));
|
|
}
|
|
if (column.suffixes) {
|
|
for (var k = 0; k < column.suffixes.length; k++) {
|
|
var suffix = column.suffixes[k];
|
|
var suffix_el = jQuery(tr.children('.suffix')[k]);
|
|
cell = suffix_el.children();
|
|
if (cell.length) {
|
|
suffix.render(this.record, cell);
|
|
} else {
|
|
suffix_el.html(suffix.render(this.record));
|
|
}
|
|
}
|
|
}
|
|
if ((column.header.is(':hidden') && thead_visible) ||
|
|
column.header.css('display') == 'none') {
|
|
td.hide();
|
|
td.addClass('invisible');
|
|
} else {
|
|
td.show();
|
|
td.removeClass('invisible');
|
|
}
|
|
}
|
|
var row_id_path = this.get_id_path();
|
|
this.set_selection(Sao.common.contains(selected, row_id_path));
|
|
if (this.children_field) {
|
|
this.record.load(this.children_field).done(function() {
|
|
if (this.is_expanded() ||
|
|
Sao.common.contains(expanded, row_id_path)) {
|
|
this.expander.css('visibility', 'visible');
|
|
this.tree.expanded[this.path] = this;
|
|
this.expand_children(selected, expanded);
|
|
this.update_expander(true);
|
|
} else {
|
|
var length = this.record.field_get_client(
|
|
this.children_field).length;
|
|
this.expander.css('visibility',
|
|
length ? 'visible' : 'hidden');
|
|
this.update_expander(false);
|
|
}
|
|
}.bind(this));
|
|
}
|
|
if (this.record.deleted || this.record.removed) {
|
|
this.el.css('text-decoration', 'line-through');
|
|
} else {
|
|
this.el.css('text-decoration', 'inherit');
|
|
}
|
|
},
|
|
toggle_row: function() {
|
|
if (this.is_expanded()) {
|
|
this.update_expander(false);
|
|
delete this.tree.expanded[this.path];
|
|
this.collapse_children();
|
|
} else {
|
|
if (this.tree.n_children(this) > Sao.config.limit) {
|
|
this.tree.record = this.record;
|
|
this.tree.screen.switch_view('form');
|
|
} else {
|
|
this.update_expander(true);
|
|
this.tree.expanded[this.path] = this;
|
|
this.expand_children();
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
update_expander: function(expanded) {
|
|
var icon;
|
|
if (expanded) {
|
|
icon = 'tryton-arrow-down';
|
|
} else {
|
|
icon = 'tryton-arrow-right';
|
|
}
|
|
Sao.common.ICONFACTORY.get_icon_url(icon)
|
|
.then(function(url) {
|
|
this.expander.attr('src', url);
|
|
}.bind(this));
|
|
},
|
|
collapse_children: function() {
|
|
this.rows.forEach(function(row, pos, rows) {
|
|
row.collapse_children();
|
|
var node = row.el[0];
|
|
node.parentNode.removeChild(node);
|
|
});
|
|
this.rows = [];
|
|
},
|
|
expand_children: function(selected, expanded) {
|
|
return this.record.load(this.children_field).done(function() {
|
|
if (this.rows.length === 0) {
|
|
var children = this.record.field_get_client(
|
|
this.children_field);
|
|
children.forEach(function(record, pos, group) {
|
|
var tree_row = new this.Class(
|
|
this.tree, record, pos, this);
|
|
tree_row.construct(selected, expanded);
|
|
this.rows.push(tree_row);
|
|
}.bind(this));
|
|
}
|
|
redraw_async(this.rows, selected, expanded);
|
|
}.bind(this));
|
|
},
|
|
switch_row: function() {
|
|
if (window.getSelection) {
|
|
if (window.getSelection().empty) { // Chrome
|
|
window.getSelection().empty();
|
|
} else if (window.getSelection().removeAllRanges) { // Firefox
|
|
window.getSelection().removeAllRanges();
|
|
}
|
|
} else if (document.selection) { // IE?
|
|
document.selection.empty();
|
|
}
|
|
if (this.tree.selection_mode != Sao.common.SELECTION_NONE) {
|
|
this.set_selection(true);
|
|
this.selection_changed();
|
|
if (!this.is_selected()) {
|
|
return;
|
|
}
|
|
}
|
|
this.tree.switch_(this.path);
|
|
},
|
|
select_column: function(index) {
|
|
},
|
|
select_row: function(event_) {
|
|
if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
|
|
this.tree.select_changed(this.record);
|
|
this.switch_row();
|
|
} else {
|
|
var current_record;
|
|
if (event_.shiftKey &&
|
|
this.tree.selection_mode != Sao.common.SELECTION_SINGLE) {
|
|
current_record = this.tree.screen.current_record;
|
|
this.tree.select_records(current_record, this.record);
|
|
} else {
|
|
if (!event_.ctrlKey ||
|
|
this.tree.selection_mode ==
|
|
Sao.common.SELECTION_SINGLE) {
|
|
this.tree.select_records(null, null);
|
|
}
|
|
this.set_selection(!this.is_selected());
|
|
}
|
|
this.selection_changed();
|
|
if (current_record) {
|
|
// Keep original current record with shift select
|
|
this.tree.screen.current_record = current_record;
|
|
}
|
|
}
|
|
},
|
|
is_selected: function() {
|
|
if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
|
|
return false;
|
|
}
|
|
return this.selection.prop('checked');
|
|
},
|
|
set_selection: function(value) {
|
|
if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
|
|
return;
|
|
}
|
|
this.selection.prop('checked', value);
|
|
if (value) {
|
|
this.el.addClass('selected');
|
|
} else {
|
|
this.el.removeClass('selected');
|
|
}
|
|
if (!value) {
|
|
this.tree.selection.prop('checked', false);
|
|
}
|
|
},
|
|
selection_changed: function() {
|
|
var is_selected = this.is_selected();
|
|
this.set_selection(is_selected);
|
|
if (is_selected) {
|
|
this.tree.select_changed(this.record);
|
|
} else {
|
|
this.tree.select_changed(
|
|
this.tree.selected_records[0] || null);
|
|
}
|
|
this.tree.update_selection();
|
|
},
|
|
expand_to_path: function(path) {
|
|
var row_idx, rest;
|
|
row_idx = path[0];
|
|
rest = path.slice(1);
|
|
if (rest.length > 0) {
|
|
this.rows[row_idx].expand_children().done(function() {
|
|
this.rows[row_idx].expand_to_path(rest);
|
|
}.bind(this));
|
|
}
|
|
},
|
|
next_column: function(path, editable, sign) {
|
|
var i, readonly, invisible;
|
|
var column, column_index, state_attrs;
|
|
|
|
sign = sign || 1;
|
|
if ((path === null) && (sign > 0)) {
|
|
path = -1;
|
|
} else if (path === null) {
|
|
path = 0;
|
|
}
|
|
column_index = 0;
|
|
for (i = 0; i < this.tree.columns.length; i++) {
|
|
column_index = ((path + (sign * (i + 1))) %
|
|
this.tree.columns.length);
|
|
// javascript modulo returns negative number for negative
|
|
// numbers
|
|
if (column_index < 0) {
|
|
column_index += this.tree.columns.length;
|
|
}
|
|
column = this.tree.columns[column_index];
|
|
if (!column.field) {
|
|
continue;
|
|
}
|
|
state_attrs = column.field.get_state_attrs(this.record);
|
|
invisible = state_attrs.invisible;
|
|
if (column.header.is(':hidden')) {
|
|
invisible = true;
|
|
}
|
|
if (editable) {
|
|
readonly = (column.attributes.readonly ||
|
|
state_attrs.readonly);
|
|
} else {
|
|
readonly = false;
|
|
}
|
|
if (!(invisible || readonly)) {
|
|
return column_index;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.RowEditable = Sao.class_(Sao.View.Tree.Row, {
|
|
init: function(tree, record, pos, parent) {
|
|
Sao.View.Tree.RowEditable._super.init.call(this, tree, record, pos,
|
|
parent);
|
|
this.edited_column = null;
|
|
this.el.on('keypress', function(event_) {
|
|
if ((event_.which == Sao.common.RETURN_KEYCODE) &&
|
|
(this.tree.edited_row != this)) {
|
|
this.tree.edit_row(this);
|
|
event_.preventDefault();
|
|
}
|
|
}.bind(this));
|
|
},
|
|
redraw: function(selected, expanded) {
|
|
var i, tr, td, widget;
|
|
var field;
|
|
|
|
Sao.View.Tree.RowEditable._super.redraw.call(this, selected,
|
|
expanded);
|
|
// The autocompletion widget do not call display thus we have to
|
|
// call it when redrawing the row
|
|
for (i = 0; i < this.tree.columns.length; i++) {
|
|
td = this._get_column_td(i);
|
|
tr = td.find('tr');
|
|
widget = jQuery(tr.children('.widget-editable')).data('widget');
|
|
if (widget) {
|
|
field = this.record.model.fields[widget.field_name];
|
|
widget.display(this.record, field);
|
|
}
|
|
}
|
|
},
|
|
select_column: function(index) {
|
|
this.edited_column = index;
|
|
},
|
|
select_row: function(event_) {
|
|
var body, listener;
|
|
event_.stopPropagation();
|
|
if (this.tree.edited_row &&
|
|
(event_.currentTarget == this.tree.edited_row.el[0])) {
|
|
return;
|
|
}
|
|
|
|
body = listener = jQuery(document.body);
|
|
if (body.hasClass('modal-open')) {
|
|
listener = this.tree.el.parents('.modal').last();
|
|
}
|
|
var handler = function(event_) {
|
|
if ((event_.currentTarget == body[0]) &&
|
|
body.hasClass('modal-open')) {
|
|
return;
|
|
}
|
|
|
|
if (!this.tree.save_row()) {
|
|
event_.preventDefault();
|
|
event_.stopPropagation();
|
|
return;
|
|
}
|
|
body.off('click.sao.editabletree');
|
|
this.tree.edit_row(null);
|
|
return true;
|
|
}.bind(this);
|
|
if (!handler(event_)) {
|
|
return;
|
|
}
|
|
listener.on('click.sao.editabletree', handler);
|
|
|
|
Sao.View.Tree.RowEditable._super.select_row.call(this, event_);
|
|
|
|
if (!event_.shiftKey && !event_.ctrlKey) {
|
|
this.tree.edit_row(this);
|
|
}
|
|
},
|
|
unset_editable: function() {
|
|
this.tree.columns.forEach(function(col, idx) {
|
|
var td = this._get_column_td(idx);
|
|
var static_el = this.get_static_el(td);
|
|
static_el.html(col.render(this.record)).show();
|
|
this.get_editable_el(td)
|
|
.empty()
|
|
.data('widget', null)
|
|
.parents('.treeview td').addBack().removeClass('edited');
|
|
}.bind(this));
|
|
},
|
|
set_editable: function() {
|
|
var focus_widget = null;
|
|
for (var i = 0, len=this.tree.columns.length; i < len; i++) {
|
|
var td = this._get_column_td(i);
|
|
var col = this.tree.columns[i];
|
|
if (!col.field) {
|
|
continue;
|
|
}
|
|
var state_attrs = col.field.get_state_attrs(this.record);
|
|
var readonly = col.attributes.readonly || state_attrs.readonly;
|
|
|
|
var EditableBuilder = Sao.View.EditableTree.WIDGETS[
|
|
col.attributes.widget];
|
|
if (!readonly && EditableBuilder) {
|
|
var widget = new EditableBuilder(
|
|
this.tree, col.attributes);
|
|
widget.el.on('keydown', this.key_press.bind(this));
|
|
|
|
var editable_el = this.get_editable_el(td);
|
|
editable_el.append(widget.el);
|
|
editable_el.data('widget', widget);
|
|
widget.display(this.record, col.field);
|
|
|
|
var static_el = this.get_static_el(td);
|
|
static_el.hide();
|
|
editable_el.show();
|
|
editable_el.parents('.treeview td').addBack()
|
|
.addClass('edited');
|
|
|
|
if (this.edited_column == i) {
|
|
focus_widget = widget;
|
|
}
|
|
}
|
|
}
|
|
if (focus_widget) {
|
|
focus_widget.focus();
|
|
}
|
|
},
|
|
get_static_el: function(td) {
|
|
td = td || this.get_active_td();
|
|
return td.find('.widget');
|
|
},
|
|
get_editable_el: function(td) {
|
|
td = td || this.get_active_td();
|
|
var editable = td.find('.widget-editable');
|
|
if (!editable.length) {
|
|
editable = jQuery('<td/>', {
|
|
'class': 'widget-editable'
|
|
}).insertAfter(td.find('.widget'));
|
|
}
|
|
return editable;
|
|
},
|
|
get_active_td: function() {
|
|
return this._get_column_td(this.edited_column);
|
|
},
|
|
key_press: function(event_) {
|
|
var current_td, selector, next_column, next_idx, i, next_row;
|
|
var states;
|
|
|
|
if ((event_.which != Sao.common.TAB_KEYCODE) &&
|
|
(event_.which != Sao.common.UP_KEYCODE) &&
|
|
(event_.which != Sao.common.DOWN_KEYCODE) &&
|
|
(event_.which != Sao.common.ESC_KEYCODE) &&
|
|
(event_.which != Sao.common.RETURN_KEYCODE)) {
|
|
return;
|
|
}
|
|
var td = this._get_column_td(this.edited_column);
|
|
var editable_el = this.get_editable_el(td);
|
|
var widget = editable_el.data('widget');
|
|
widget.focus_out();
|
|
var column = this.tree.columns[this.edited_column];
|
|
if (column.field.validate(this.record)) {
|
|
if (event_.which == Sao.common.TAB_KEYCODE) {
|
|
var sign = 1;
|
|
if (event_.shiftKey) {
|
|
sign = -1;
|
|
}
|
|
event_.preventDefault();
|
|
next_idx = this.next_column(this.edited_column, true, sign);
|
|
if (next_idx !== null) {
|
|
this.edited_column = next_idx;
|
|
td = this._get_column_td(next_idx);
|
|
editable_el = this.get_editable_el(td);
|
|
widget = editable_el.data('widget');
|
|
widget.focus();
|
|
}
|
|
} else if (event_.which == Sao.common.UP_KEYCODE ||
|
|
event_.which == Sao.common.DOWN_KEYCODE) {
|
|
if (event_.which == Sao.common.UP_KEYCODE) {
|
|
next_row = this.el.prev('tr');
|
|
} else {
|
|
next_row = this.el.next('tr');
|
|
}
|
|
next_column = this.edited_column;
|
|
this.record.validate(this.tree.get_fields())
|
|
.then(function(validate) {
|
|
if (!validate) {
|
|
next_row = null;
|
|
var invalid_fields =
|
|
this.record.invalid_fields();
|
|
for (i = 0; i < this.tree.columns.length; i++) {
|
|
var col = this.tree.columns[i];
|
|
if (col.attributes.name in invalid_fields) {
|
|
next_column = i;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
var prm;
|
|
if (!this.tree.screen.group.parent) {
|
|
prm = this.record.save();
|
|
} else if (this.tree.screen.attributes.pre_validate) {
|
|
prm = this.record.pre_validate();
|
|
}
|
|
if (prm) {
|
|
return prm.fail(function() {
|
|
widget.focus();
|
|
});
|
|
}
|
|
}
|
|
}.bind(this)).then(function() {
|
|
window.setTimeout(function() {
|
|
this._get_column_td(next_column, next_row)
|
|
.trigger('click');
|
|
}.bind(this), 0);
|
|
}.bind(this));
|
|
} else if (event_.which == Sao.common.ESC_KEYCODE) {
|
|
this.tree.edit_row(null);
|
|
this.get_static_el().show().find('[tabindex=0]').focus();
|
|
} else if (event_.which == Sao.common.RETURN_KEYCODE) {
|
|
var focus_cell = function(row) {
|
|
this._get_column_td(this.edited_column, row)
|
|
.trigger('click');
|
|
}.bind(this);
|
|
if (this.tree.attributes.editable == 'bottom') {
|
|
next_row = this.el.next('tr');
|
|
} else {
|
|
next_row = this.el.prev('tr');
|
|
}
|
|
if (next_row.length) {
|
|
focus_cell(next_row);
|
|
} else {
|
|
var model = this.tree.screen.group;
|
|
var access = Sao.common.MODELACCESS.get(
|
|
this.tree.screen.model_name);
|
|
var limit = ((this.tree.screen.size_limit !== null) &&
|
|
(model.length >= this.tree.screen.size_limit));
|
|
var prm;
|
|
if (!access.create || limit) {
|
|
prm = jQuery.when();
|
|
} else {
|
|
prm = this.tree.screen.new_();
|
|
}
|
|
prm.done(function() {
|
|
var new_row;
|
|
var rows = this.tree.tbody.children('tr');
|
|
if (this.tree.attributes.editable == 'bottom') {
|
|
new_row = rows.last();
|
|
} else {
|
|
new_row = rows.first();
|
|
}
|
|
focus_cell(new_row);
|
|
}.bind(this));
|
|
}
|
|
}
|
|
event_.preventDefault();
|
|
} else {
|
|
widget.display(this.record, column.field);
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.Affix = Sao.class_(Object, {
|
|
init: function(attributes, protocol) {
|
|
this.attributes = attributes;
|
|
this.protocol = protocol || null;
|
|
this.icon = attributes.icon;
|
|
if (this.protocol && !this.icon) {
|
|
this.icon = 'tryton-public';
|
|
}
|
|
},
|
|
get_cell: function() {
|
|
var cell;
|
|
if (this.protocol) {
|
|
cell = jQuery('<a/>', {
|
|
'target': '_new'
|
|
});
|
|
cell.append(jQuery('<img/>'));
|
|
cell.click({'cell': cell}, this.clicked.bind(this));
|
|
} else if (this.icon) {
|
|
cell = jQuery('<img/>');
|
|
} else {
|
|
cell = jQuery('<span/>');
|
|
cell.attr('tabindex', 0);
|
|
}
|
|
cell.addClass('column-affix');
|
|
return cell;
|
|
},
|
|
render: function(record, cell) {
|
|
if (!cell) {
|
|
cell = this.get_cell();
|
|
}
|
|
record.load(this.attributes.name).done(function() {
|
|
var value;
|
|
var field = record.model.fields[this.attributes.name];
|
|
var invisible = field.get_state_attrs(record).invisible;
|
|
if (invisible) {
|
|
cell.hide();
|
|
} else {
|
|
cell.show();
|
|
}
|
|
if (this.protocol) {
|
|
value = field.get(record);
|
|
if (!jQuery.isEmptyObject(value)) {
|
|
switch (this.protocol) {
|
|
case 'email':
|
|
value = 'mailto:' + value;
|
|
break;
|
|
case 'callto':
|
|
value = 'callto:' + value;
|
|
break;
|
|
case 'sip':
|
|
value = 'sip:' + value;
|
|
break;
|
|
}
|
|
}
|
|
cell.attr('src', value);
|
|
}
|
|
if (this.icon) {
|
|
if (this.icon in record.model.fields) {
|
|
var icon_field = record.model.fields[this.icon];
|
|
value = icon_field.get_client(record);
|
|
}
|
|
else {
|
|
value = this.icon;
|
|
}
|
|
Sao.common.ICONFACTORY.get_icon_url(value)
|
|
.done(function(url) {
|
|
var img_tag;
|
|
if (cell.children('img').length) {
|
|
img_tag = cell.children('img');
|
|
} else {
|
|
img_tag = cell;
|
|
}
|
|
img_tag.attr('src', url || '');
|
|
}.bind(this));
|
|
} else {
|
|
value = this.attributes.string || '';
|
|
if (!value) {
|
|
value = field.get_client(record) || '';
|
|
}
|
|
cell.text(value);
|
|
}
|
|
}.bind(this));
|
|
return cell;
|
|
},
|
|
clicked: function(event) {
|
|
event.preventDefault(); // prevent edition
|
|
window.open(event.data.cell.attr('src'), '_blank');
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.CharColumn = Sao.class_(Object, {
|
|
class_: 'column-char',
|
|
init: function(model, attributes) {
|
|
this.type = 'field';
|
|
this.model = model;
|
|
this.field = model.fields[attributes.name];
|
|
this.attributes = attributes;
|
|
this.prefixes = [];
|
|
this.suffixes = [];
|
|
this.header = null;
|
|
this.footers = [];
|
|
},
|
|
get_cell: function() {
|
|
var cell = jQuery('<div/>', {
|
|
'class': this.class_,
|
|
'tabindex': 0
|
|
});
|
|
return cell;
|
|
},
|
|
update_text: function(cell, record) {
|
|
var text = this.field.get_client(record);
|
|
cell.text(text).attr('title', text);
|
|
},
|
|
render: function(record, cell) {
|
|
if (!cell) {
|
|
cell = this.get_cell();
|
|
}
|
|
record.load(this.attributes.name).done(function() {
|
|
this.update_text(cell, record);
|
|
this.field.set_state(record);
|
|
var state_attrs = this.field.get_state_attrs(record);
|
|
if (state_attrs.invisible) {
|
|
cell.hide();
|
|
} else {
|
|
cell.show();
|
|
}
|
|
}.bind(this));
|
|
return cell;
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.TextColum = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-text'
|
|
});
|
|
|
|
Sao.View.Tree.IntegerColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-integer',
|
|
init: function(model, attributes) {
|
|
Sao.View.Tree.IntegerColumn._super.init.call(this, model, attributes);
|
|
this.factor = Number(attributes.factor || 1);
|
|
},
|
|
get_cell: function() {
|
|
return Sao.View.Tree.IntegerColumn._super.get_cell.call(this);
|
|
},
|
|
update_text: function(cell, record) {
|
|
var value = this.field.get_client(record, this.factor);
|
|
cell.text(value).attr('title', value);
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.FloatColumn = Sao.class_(Sao.View.Tree.IntegerColumn, {
|
|
class_: 'column-float'
|
|
});
|
|
|
|
Sao.View.Tree.BooleanColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-boolean',
|
|
get_cell: function() {
|
|
return jQuery('<input/>', {
|
|
'type': 'checkbox',
|
|
'disabled': true,
|
|
'class': this.class_,
|
|
'tabindex': 0
|
|
});
|
|
},
|
|
update_text: function(cell, record) {
|
|
cell.prop('checked', this.field.get(record));
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.Many2OneColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-many2one',
|
|
get_cell: function() {
|
|
var cell = Sao.View.Tree.Many2OneColumn._super.get_cell.call(this);
|
|
cell.append(jQuery('<a/>', {
|
|
'href': '#',
|
|
}));
|
|
return cell;
|
|
},
|
|
update_text: function(cell, record) {
|
|
cell = cell.children('a');
|
|
cell.unbind('click');
|
|
Sao.View.Tree.Many2OneColumn._super.update_text.call(this, cell, record);
|
|
cell.click(function(event) {
|
|
event.stopPropagation();
|
|
var params = {};
|
|
params.model = this.attributes.relation;
|
|
params.res_id = this.field.get(record);
|
|
params.mode = ['form'];
|
|
params.name = this.attributes.string;
|
|
Sao.Tab.create(params);
|
|
}.bind(this));
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.One2OneColumn = Sao.class_(Sao.View.Tree.Many2OneColumn, {
|
|
class_: 'column-one2one'
|
|
});
|
|
|
|
Sao.View.Tree.SelectionColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-selection',
|
|
init: function(model, attributes) {
|
|
Sao.View.Tree.SelectionColumn._super.init.call(this, model,
|
|
attributes);
|
|
Sao.common.selection_mixin.init.call(this);
|
|
this.init_selection();
|
|
},
|
|
init_selection: function(key) {
|
|
Sao.common.selection_mixin.init_selection.call(this, key);
|
|
},
|
|
update_selection: function(record, callback) {
|
|
Sao.common.selection_mixin.update_selection.call(this, record,
|
|
this.field, callback);
|
|
},
|
|
update_text: function(cell, record) {
|
|
this.update_selection(record, function() {
|
|
var value = this.field.get(record);
|
|
var prm, text, found = false;
|
|
for (var i = 0, len = this.selection.length; i < len; i++) {
|
|
if (this.selection[i][0] === value) {
|
|
found = true;
|
|
text = this.selection[i][1];
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
prm = Sao.common.selection_mixin.get_inactive_selection
|
|
.call(this, value).then(function(inactive) {
|
|
return inactive[1];
|
|
});
|
|
} else {
|
|
prm = jQuery.when(text);
|
|
}
|
|
prm.done(function(text_value) {
|
|
cell.text(text_value).attr('title', text_value);
|
|
}.bind(this));
|
|
}.bind(this));
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.ReferenceColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-reference',
|
|
init: function(model, attributes) {
|
|
Sao.View.Tree.ReferenceColumn._super.init.call(this, model,
|
|
attributes);
|
|
Sao.common.selection_mixin.init.call(this);
|
|
this.init_selection();
|
|
},
|
|
init_selection: function(key) {
|
|
Sao.common.selection_mixin.init_selection.call(this, key);
|
|
},
|
|
update_selection: function(record, callback) {
|
|
Sao.common.selection_mixin.update_selection.call(this, record,
|
|
this.field, callback);
|
|
},
|
|
update_text: function(cell, record) {
|
|
this.update_selection(record, function() {
|
|
var value = this.field.get_client(record);
|
|
var model, name, text;
|
|
if (!value) {
|
|
model = '';
|
|
name = '';
|
|
} else {
|
|
model = value[0];
|
|
name = value[1];
|
|
}
|
|
if (model) {
|
|
for (var i = 0, len = this.selection.length; i < len; i++) {
|
|
if (this.selection[i][0] === model) {
|
|
model = this.selection[i][1];
|
|
break;
|
|
}
|
|
}
|
|
text = model + ',' + name;
|
|
} else {
|
|
text = name;
|
|
}
|
|
cell.text(text).attr('title', text);
|
|
}.bind(this));
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.DateColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-date',
|
|
update_text: function(cell, record) {
|
|
var value = this.field.get_client(record);
|
|
var date_format = this.field.date_format(record);
|
|
var text = Sao.common.format_date(date_format, value);
|
|
cell.text(text).attr('title', text);
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.TimeColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-time',
|
|
update_text: function(cell, record) {
|
|
var value = this.field.get_client(record);
|
|
var text = Sao.common.format_time(
|
|
this.field.time_format(record), value);
|
|
cell.text(text).attr('title', text);
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.TimeDeltaColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-timedelta'
|
|
});
|
|
|
|
Sao.View.Tree.One2ManyColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-one2many',
|
|
update_text: function(cell, record) {
|
|
var text = '( ' + this.field.get_client(record).length + ' )';
|
|
cell.text(text).attr('title', text);
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.Many2ManyColumn = Sao.class_(Sao.View.Tree.One2ManyColumn, {
|
|
class_: 'column-many2many'
|
|
});
|
|
|
|
Sao.View.Tree.BinaryColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-binary',
|
|
init: function(model, attributes) {
|
|
Sao.View.Tree.BinaryColumn._super.init.call(this, model, attributes);
|
|
this.filename = attributes.filename || null;
|
|
},
|
|
get_cell: function() {
|
|
var cell = Sao.View.Tree.BinaryColumn._super.get_cell.call(this);
|
|
jQuery('<span/>').appendTo(cell);
|
|
return cell;
|
|
},
|
|
update_text: function(cell, record) {
|
|
var size;
|
|
if (this.field.get_size) {
|
|
size = this.field.get_size(record);
|
|
} else {
|
|
size = this.field.get(record).length;
|
|
}
|
|
var text = size? Sao.common.humanize(size) : '';
|
|
cell.children('span').text(text).attr('title', text);
|
|
var button = cell.children('button');
|
|
if (!button.length) {
|
|
button = jQuery('<button/>', {
|
|
'class': 'btn btn-default btn-sm',
|
|
'type': 'button',
|
|
}).append(Sao.common.ICONFACTORY.get_icon_img('tryton-save')
|
|
).appendTo(cell)
|
|
.click(record, function(event) {
|
|
// Prevent editable tree to start edition
|
|
event.stopPropagation();
|
|
this.save_as(event.data);
|
|
}.bind(this));
|
|
}
|
|
if (!size) {
|
|
button.hide();
|
|
} else {
|
|
button.show();
|
|
}
|
|
},
|
|
save_as: function(record) {
|
|
var filename;
|
|
var mimetype = 'application/octet-binary';
|
|
var filename_field = record.model.fields[this.filename];
|
|
if (filename_field) {
|
|
filename = filename_field.get_client(record);
|
|
mimetype = Sao.common.guess_mimetype(filename);
|
|
}
|
|
var prm;
|
|
if (this.field.get_data) {
|
|
prm = this.field.get_data(record);
|
|
} else {
|
|
prm = jQuery.when(this.field.get(record));
|
|
}
|
|
prm.done(function(data) {
|
|
Sao.common.download_file(data, filename);
|
|
}.bind(this));
|
|
},
|
|
});
|
|
|
|
Sao.View.Tree.ImageColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-image',
|
|
get_cell: function() {
|
|
var cell = jQuery('<img/>', {
|
|
'class': this.class_,
|
|
'tabindex': 0
|
|
});
|
|
cell.css('width', '100%');
|
|
return cell;
|
|
},
|
|
render: function(record, cell) {
|
|
if (!cell) {
|
|
cell = this.get_cell();
|
|
}
|
|
record.load(this.attributes.name).done(function() {
|
|
var value = this.field.get_client(record);
|
|
if (value) {
|
|
if (value > Sao.common.BIG_IMAGE_SIZE) {
|
|
value = jQuery.when(null);
|
|
} else {
|
|
value = this.field.get_data(record);
|
|
}
|
|
} else {
|
|
value = jQuery.when(null);
|
|
}
|
|
value.done(function(data) {
|
|
var img_url, blob;
|
|
if (!data) {
|
|
img_url = null;
|
|
} else {
|
|
blob = new Blob([data]);
|
|
img_url = window.URL.createObjectURL(blob);
|
|
}
|
|
cell.attr('src', img_url);
|
|
}.bind(this));
|
|
}.bind(this));
|
|
return cell;
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.URLColumn = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-url',
|
|
render: function(record, cell) {
|
|
cell = Sao.View.Tree.URLColumn._super.render.call(
|
|
this, record, cell);
|
|
this.field.set_state(record);
|
|
var state_attrs = this.field.get_state_attrs(record);
|
|
if (state_attrs.readonly) {
|
|
cell.hide();
|
|
} else {
|
|
cell.show();
|
|
}
|
|
return cell;
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.ProgressBar = Sao.class_(Sao.View.Tree.CharColumn, {
|
|
class_: 'column-progressbar',
|
|
get_cell: function() {
|
|
var cell = jQuery('<div/>', {
|
|
'class': this.class_ + ' progress',
|
|
'tabindex': 0
|
|
});
|
|
var progressbar = jQuery('<div/>', {
|
|
'class': 'progress-bar',
|
|
'role': 'progressbar',
|
|
'aria-valuemin': 0,
|
|
'aria-valuemax': 100
|
|
}).appendTo(cell);
|
|
progressbar.css('min-width: 2em');
|
|
return cell;
|
|
},
|
|
update_text: function(cell, record) {
|
|
var text = this.field.get_client(record, 100);
|
|
if (text) {
|
|
text = Sao.i18n.gettext('%1%', text);
|
|
}
|
|
var value = this.field.get(record) || 0;
|
|
var progressbar = cell.find('.progress-bar');
|
|
progressbar.attr('aria-valuenow', value * 100);
|
|
progressbar.css('width', value * 100 + '%');
|
|
progressbar.text(text).attr('title', text);
|
|
}
|
|
});
|
|
|
|
Sao.View.Tree.ButtonColumn = Sao.class_(Object, {
|
|
init: function(screen, attributes) {
|
|
this.screen = screen;
|
|
this.type = 'button';
|
|
this.attributes = attributes;
|
|
},
|
|
render: function(record, el) {
|
|
var button = new Sao.common.Button(this.attributes, el);
|
|
if (!el) {
|
|
button.el.click(
|
|
[record, button], this.button_clicked.bind(this));
|
|
}
|
|
var fields = jQuery.map(this.screen.model.fields,
|
|
function(field, name) {
|
|
if ((field.description.loading || 'eager') ==
|
|
'eager') {
|
|
return name;
|
|
} else {
|
|
return undefined;
|
|
}
|
|
});
|
|
// Wait at least one eager field is loaded before evaluating states
|
|
record.load(fields[0]).done(function() {
|
|
button.set_state(record);
|
|
});
|
|
return button.el;
|
|
},
|
|
button_clicked: function(event) {
|
|
var record = event.data[0];
|
|
var button = event.data[1];
|
|
if (record != this.record) {
|
|
// Need to raise the event to get the record selected
|
|
return true;
|
|
}
|
|
var states = record.expr_eval(this.attributes.states || {});
|
|
if (states.invisible || states.readonly) {
|
|
return;
|
|
}
|
|
button.el.prop('disabled', true);
|
|
this.screen.button(this.attributes).always(function() {
|
|
button.el.prop('disabled', false);
|
|
});
|
|
}
|
|
});
|
|
|
|
Sao.View.TreeXMLViewParser.WIDGETS = {
|
|
'biginteger': Sao.View.Tree.IntegerColumn,
|
|
'binary': Sao.View.Tree.BinaryColum,
|
|
'boolean': Sao.View.Tree.BooleanColumn,
|
|
'callto': Sao.View.Tree.URLColumn,
|
|
'char': Sao.View.Tree.CharColumn,
|
|
'date': Sao.View.Tree.DateColumn,
|
|
'email': Sao.View.Tree.URLColumn,
|
|
'float': Sao.View.Tree.FloatColumn,
|
|
'image': Sao.View.Tree.ImageColumn,
|
|
'integer': Sao.View.Tree.IntegerColumn,
|
|
'many2many': Sao.View.Tree.Many2ManyColumn,
|
|
'many2one': Sao.View.Tree.Many2OneColumn,
|
|
'numeric': Sao.View.Tree.FloatColumn,
|
|
'one2many': Sao.View.Tree.One2ManyColumn,
|
|
'one2one': Sao.View.Tree.One2OneColumn,
|
|
'progressbar': Sao.View.Tree.ProgressBar,
|
|
'reference': Sao.View.Tree.ReferenceColumn,
|
|
'selection': Sao.View.Tree.SelectionColumn,
|
|
'sip': Sao.View.Tree.URLColumn,
|
|
'text': Sao.View.Tree.TextColum,
|
|
'time': Sao.View.Tree.TimeColumn,
|
|
'timedelta': Sao.View.Tree.TimeDeltaColumn,
|
|
'url': Sao.View.Tree.URLColumn,
|
|
};
|
|
|
|
Sao.View.EditableTree = {};
|
|
|
|
Sao.View.EditableTree.editable_mixin = function(widget) {
|
|
var key_press = function(event_) {
|
|
if ((event_.which == Sao.common.TAB_KEYCODE) ||
|
|
(event_.which == Sao.common.UP_KEYCODE) ||
|
|
(event_.which == Sao.common.DOWN_KEYCODE) ||
|
|
(event_.which == Sao.common.ESC_KEYCODE) ||
|
|
(event_.which == Sao.common.RETURN_KEYCODE)) {
|
|
this.focus_out();
|
|
}
|
|
};
|
|
widget.el.on('keydown', key_press.bind(widget));
|
|
};
|
|
|
|
Sao.View.EditableTree.Char = Sao.class_(Sao.View.Form.Char, {
|
|
class_: 'editabletree-char',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Char._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Date = Sao.class_(Sao.View.Form.Date, {
|
|
class_: 'editabletree-date',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Date._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Time = Sao.class_(Sao.View.Form.Time, {
|
|
class_: 'editabletree-time',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Time._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.TimeDelta = Sao.class_(Sao.View.Form.TimeDelta, {
|
|
class_: 'editabletree-timedelta',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.TimeDelta._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Integer = Sao.class_(Sao.View.Form.Integer, {
|
|
class_: 'editabletree-integer',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Integer._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Float = Sao.class_(Sao.View.Form.Float, {
|
|
class_: 'editabletree-float',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Float._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Selection = Sao.class_(Sao.View.Form.Selection, {
|
|
class_: 'editabletree-selection',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Selection._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Boolean = Sao.class_(Sao.View.Form.Boolean, {
|
|
class_: 'editabletree-boolean',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Boolean._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Many2One = Sao.class_(Sao.View.Form.Many2One, {
|
|
class_: 'editabletree-many2one',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Many2One._super.init.call(
|
|
this, view, attributes);
|
|
this.el.on('keydown', this.key_press.bind(this));
|
|
},
|
|
key_press: function(event_) {
|
|
if (event_.which == Sao.common.TAB_KEYCODE) {
|
|
this.focus_out();
|
|
} else {
|
|
Sao.View.EditableTree.Many2One._super.key_press.call(this,
|
|
event_);
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Reference = Sao.class_(Sao.View.Form.Reference, {
|
|
class_: 'editabletree-reference',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Reference._super.init.call(
|
|
this, view, attributes);
|
|
this.el.on('keydown', this.key_press.bind(this));
|
|
},
|
|
key_press: function(event_) {
|
|
if (event_.which == Sao.common.TAB_KEYCODE) {
|
|
this.focus_out();
|
|
} else {
|
|
Sao.View.EditableTree.Reference._super.key_press.call(this,
|
|
event_);
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.One2One = Sao.class_(Sao.View.Form.One2One, {
|
|
class_: 'editabletree-one2one',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.One2One._super.init.call(
|
|
this, view, attributes);
|
|
this.el.on('keydown', this.key_press.bind(this));
|
|
},
|
|
key_press: function(event_) {
|
|
if (event_.which == Sao.common.TAB_KEYCODE) {
|
|
this.focus_out();
|
|
} else {
|
|
Sao.View.EditableTree.One2One._super.key_press.call(this,
|
|
event_);
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.One2Many = Sao.class_(Sao.View.EditableTree.Char, {
|
|
class_: 'editabletree-one2many',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.One2Many._super.init.call(
|
|
this, view, attributes);
|
|
},
|
|
display: function(record, field) {
|
|
if (record) {
|
|
this.el.val('(' + field.get_client(record).length + ')');
|
|
} else {
|
|
this.el.val('');
|
|
}
|
|
},
|
|
key_press: function(event_) {
|
|
if (event_.which == Sao.common.TAB_KEYCODE) {
|
|
this.focus_out();
|
|
}
|
|
},
|
|
set_value: function(record, field) {
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.Binary = Sao.class_(Sao.View.Form.Binary, {
|
|
class_: 'editabletree-binary',
|
|
init: function(view, attributes) {
|
|
Sao.View.EditableTree.Binary._super.init.call(
|
|
this, view, attributes);
|
|
Sao.View.EditableTree.editable_mixin(this);
|
|
}
|
|
});
|
|
|
|
Sao.View.EditableTree.WIDGETS = {
|
|
'biginteger': Sao.View.EditableTree.Integer,
|
|
'binary': Sao.View.EditableTree.Binary,
|
|
'boolean': Sao.View.EditableTree.Boolean,
|
|
'callto': Sao.View.EditableTree.Char,
|
|
'char': Sao.View.EditableTree.Char,
|
|
'date': Sao.View.EditableTree.Date,
|
|
'email': Sao.View.EditableTree.Char,
|
|
'float': Sao.View.EditableTree.Float,
|
|
'integer': Sao.View.EditableTree.Integer,
|
|
'many2many': Sao.View.EditableTree.Many2Many,
|
|
'many2one': Sao.View.EditableTree.Many2One,
|
|
'numeric': Sao.View.EditableTree.Float,
|
|
'one2many': Sao.View.EditableTree.One2Many,
|
|
'one2one': Sao.View.EditableTree.One2One,
|
|
'reference': Sao.View.EditableTree.Reference,
|
|
'selection': Sao.View.EditableTree.Selection,
|
|
'sip': Sao.View.EditableTree.Char,
|
|
'text': Sao.View.EditableTree.Char,
|
|
'time': Sao.View.EditableTree.Time,
|
|
'timedelta': Sao.View.EditableTree.TimeDelta,
|
|
'url': Sao.View.EditableTree.Char,
|
|
};
|
|
|
|
}());
|