Files
tryton/src/view/tree.js
T
Cédric Krier 59665e84a9 Improve treeview headers
- Use same alignment as the widget
- Make label bold by inherited header style
- Use bordered on editable

issue6681
review39561002
2017-09-03 11:31:04 +02:00

1916 lines
75 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.tree_column_get = function(type) {
switch (type) {
case 'char':
return Sao.View.Tree.CharColumn;
case 'text':
return Sao.View.Tree.TextColum;
case 'many2one':
return Sao.View.Tree.Many2OneColumn;
case 'one2one':
return Sao.View.Tree.One2OneColumn;
case 'date':
return Sao.View.Tree.DateColumn;
case 'time':
return Sao.View.Tree.TimeColumn;
case 'timedelta':
return Sao.View.Tree.TimeDeltaColumn;
case 'one2many':
return Sao.View.Tree.One2ManyColumn;
case 'many2many':
return Sao.View.Tree.Many2ManyColumn;
case 'selection':
return Sao.View.Tree.SelectionColumn;
case 'reference':
return Sao.View.Tree.ReferenceColumn;
case 'float':
case 'numeric':
return Sao.View.Tree.FloatColumn;
case 'integer':
case 'biginteger':
return Sao.View.Tree.IntegerColumn;
case 'boolean':
return Sao.View.Tree.BooleanColumn;
case 'binary':
return Sao.View.Tree.BinaryColumn;
case 'image':
return Sao.View.Tree.ImageColumn;
case 'url':
case 'email':
case 'callto':
case 'sip':
return Sao.View.Tree.URLColumn;
case 'progressbar':
return Sao.View.Tree.ProgressBar;
}
};
Sao.View.Tree = Sao.class_(Sao.View, {
init: function(screen, xml, children_field) {
Sao.View.Tree._super.init.call(this, screen, xml);
this.view_type = 'tree';
this.selection_mode = (screen.attributes.selection_mode ||
Sao.common.SELECTION_SINGLE);
this.el = jQuery('<div/>', {
'class': 'treeview responsive'
});
this.expanded = {};
this.children_field = children_field;
this.editable = Boolean(this.attributes.editable);
// Columns
this.columns = [];
this.create_columns(screen.model, xml);
// Table of records
this.rows = [];
this.table = jQuery('<table/>', {
'class': 'tree table table-hover table-striped'
});
if (this.editable) {
this.table.addClass('table-bordered');
}
this.el.append(this.table);
this.thead = jQuery('<thead/>').appendTo(this.table);
var tr = jQuery('<tr/>');
if (this.selection_mode != Sao.common.SELECTION_NONE) {
var th = jQuery('<th/>', {
'class': 'selection'
});
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.columns.forEach(function(column) {
th = jQuery('<th/>', {
'class': column.attributes.widget,
});
var label = jQuery('<label/>')
.text(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.data('toggle', 'tooltip');
label.attr('title', column.attributes.help);
label.tooltip();
}
if (column.sortable) {
var arrow = jQuery('<span/>');
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;
}, this);
this.tbody = jQuery('<tbody/>');
this.table.append(this.tbody);
// Footer for more
var footer = jQuery('<div/>', {
'class': 'treefooter'
});
this.more = 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));
footer.append(this.more);
this.more.hide();
this.display_size = Sao.config.display_size;
this.el.append(footer);
},
create_columns: function(model, xml) {
xml.find('tree').children().each(function(pos, child) {
var column, editable_column, attribute;
var attributes = {};
for (var i = 0, len = child.attributes.length; i < len; i++) {
attribute = child.attributes[i];
attributes[attribute.name] = attribute.value;
}
['readonly', 'expand', 'completion'].forEach(
function(name) {
if (attributes[name]) {
attributes[name] = attributes[name] == 1;
}
});
if (child.tagName == 'field') {
var name = attributes.name;
if (!attributes.widget) {
attributes.widget = model.fields[name].description.type;
}
var attribute_names = ['relation', 'domain', 'selection',
'relation_field', 'string', 'views', 'invisible',
'add_remove', 'sort', 'context', 'filename',
'autocomplete', 'translate', 'create', 'delete',
'selection_change_with', 'schema_model', 'required',
'readonly', 'help'];
for (i in attribute_names) {
var attr = attribute_names[i];
if ((attr in model.fields[name].description) &&
(child.getAttribute(attr) === null)) {
attributes[attr] = model.fields[name]
.description[attr];
}
}
var ColumnFactory = Sao.View.tree_column_get(
attributes.widget);
column = new ColumnFactory(model, attributes);
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 = child.childNodes;
for (i = 0; i < affixes.length; i++) {
affix = affixes[i];
affix_attributes = {};
for (i = 0, len = affix.attributes.length; i < len;
i++) {
attribute = affix.attributes[i];
affix_attributes[attribute.name] = attribute.value;
}
if (!affix_attributes.name) {
affix_attributes.name = name;
}
if (affix.tagName == 'prefix') {
column.prefixes.push(new Sao.View.Tree.Affix(
affix_attributes));
} else {
column.suffixes.push(new Sao.View.Tree.Affix(
affix_attributes));
}
}
if (!this.attributes.sequence &&
!this.children_field &&
model.fields[name].description.sortable !== false){
column.sortable = true;
}
this.fields[name] = true;
// TODO sum
} else if (child.tagName == 'button') {
column = new Sao.View.Tree.ButtonColumn(this.screen,
attributes);
}
this.columns.push(column);
}.bind(this));
},
sort_model: function(e){
var column = e.data;
var arrow = column.arrow;
var arrow_top = 'glyphicon glyphicon-triangle-top';
var arrow_bottom = 'glyphicon glyphicon-triangle-bottom';
this.columns.forEach(function(col) {
if (col.arrow){
if (col != column && col.arrow.hasClass('glyphicon')) {
col.arrow.removeClass(arrow_top + ' ' + arrow_bottom);
}
}
});
this.screen.order = this.screen.default_order;
if (arrow.hasClass(arrow_bottom)) {
arrow.removeClass(arrow_bottom);
arrow.addClass(arrow_top);
this.screen.order = [[column.attributes.name, 'DESC']];
} else if (arrow.hasClass(arrow_top)) {
arrow.removeClass(arrow_top);
} else {
arrow.addClass(arrow_bottom);
this.screen.order = [[column.attributes.name, 'ASC']];
}
var unsaved_records = [];
this.screen.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.screen.group.length) ||
(this.screen.parent)) {
this.screen.search_filter(search_string, true).then(
function(ids) {
this.screen.group.sort(ids);
}.bind(this));
} else {
this.screen.search_filter(search_string);
}
},
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.screen.current_record;
if (!selected) {
selected = this.get_selected_paths();
if (current_record) {
var current_path = current_record.get_path(this.screen.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 || [];
var row_records = function() {
return this.rows.map(function(row) {
return row.record;
});
}.bind(this);
var min_display_size = Math.min(
this.screen.group.length, this.display_size);
// XXX find better check to keep focus
if (this.children_field) {
this.construct(selected, expanded);
} else if ((min_display_size > this.rows.length) &&
(Sao.common.compare(
this.screen.group.slice(0, this.rows.length),
row_records()))) {
this.construct(selected, expanded, true);
} else if ((min_display_size != this.rows.length) ||
!Sao.common.compare(this.screen.group, row_records())){
this.construct(selected, expanded);
}
// Set column visibility depending on attributes and domain
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) {
var name = column.attributes.name;
if (!name) {
return;
}
if ((decoder.decode(column.attributes.tree_invisible || '0')) ||
(name === this.screen.exclude_field)) {
column.header.hide();
column.header.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)) {
column.header.hide();
column.header.addClass('invisible');
} else {
column.header.show();
column.header.removeClass('invisible');
}
}
}.bind(this));
if (this.columns.filter(function(c) {
return c.header.is(':visible');
}).length > 1) {
this.table.addClass('responsive');
this.table.addClass('responsive-header');
} else {
this.table.removeClass('responsive');
this.table.removeClass('responsive-header');
}
return this.redraw(selected, expanded);
},
construct: function(selected, expanded, extend) {
var tbody = this.tbody;
if (!extend) {
this.rows = [];
this.tbody = jQuery('<tbody/>');
}
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(selected, expanded);
};
this.screen.group.slice(start, this.display_size).forEach(
add_row.bind(this));
if (this.display_size >= this.screen.group.length) {
this.more.hide();
} else {
this.more.show();
}
if (!extend) {
tbody.replaceWith(this.tbody);
}
},
redraw: function(selected, expanded) {
return redraw_async(this.rows, selected, expanded);
},
switch_: function(path) {
this.screen.row_activate();
},
select_changed: function(record) {
var previous_record = this.screen.current_record;
this.screen.set_current_record(record);
if (this.editable && previous_record) {
var go_previous = function() {
this.screen.set_current_record(previous_record);
this.set_cursor();
}.bind(this);
if (!this.screen.group.parent && previous_record !== record) {
previous_record.validate(this.get_fields())
.then(function(validate) {
if (!validate) {
go_previous();
} else {
previous_record.save().fail(go_previous);
}
});
} else if (previous_record !== record &&
this.screen.attributes.pre_validate) {
previous_record.pre_validate().then(function(validate) {
if (!validate) {
go_previous();
}
});
}
}
// TODO update_children
},
selected_records: function() {
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.screen.group.slice(this.rows.length)
.forEach(function(record) {
records.push(record);
});
}
return records;
},
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);
}
},
update_selection: function() {
if (this.selection.prop('checked')) {
return;
}
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.screen.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.screen.current_record) {
return;
}
path = null;
for (i = 0; i < this.rows.length; i++) {
row_path = this.rows[i].record_to_path(
this.screen.current_record);
if (row_path) {
row_path.unshift(i);
path = row_path;
break;
}
}
row = null;
if (path) {
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);
} else if (this.rows.length > 0) {
row = this.rows[0];
}
if (row) {
column = row.next_column(null, new_);
if (column !== null) {
td = row._get_column_td(column);
if (this.editable && new_) {
td.triggerHandler('click');
}
td.find(':input,[tabindex=0]').focus();
}
}
}
});
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.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/>');
},
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(selected, expanded) {
selected = selected || [];
expanded = expanded || [];
var el_node = this.el[0];
while (el_node.firstChild) {
el_node.removeChild(el_node.firstChild);
}
var td;
if (this.tree.selection_mode != Sao.common.SELECTION_NONE) {
td = jQuery('<td/>', {
'class': 'selection',
});
this.el.append(td);
this.selection = jQuery('<input/>', {
'type': 'checkbox',
});
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/>', {
// TODO RTL
'data-title': column.attributes.string + Sao.i18n.gettext(': ')
}).append(jQuery('<div/>', { // For responsive min-height
'aria-hidden': true
}));
td.css('overflow', 'hidden');
td.on('click keypress', {column: i, td: td},
Sao.common.click_press(this.select_row.bind(this),
true));
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) {
var expanded_icon = 'glyphicon-plus';
if (this.is_expanded() ||
~expanded.indexOf(this.record.id)) {
expanded_icon = 'glyphicon-minus';
}
this.expander = jQuery('<span/>', {
'class': 'glyphicon ' + expanded_icon,
'tabindex': 0
});
this.expander.html('&nbsp;');
this.expander.css('margin-left', (depth - 1) + 'em');
this.expander.css('float', 'left');
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);
}
var row_id_path = this.get_id_path();
if (this.is_expanded() ||
Sao.common.contains(expanded, row_id_path)) {
this.tree.expanded[this.path] = this;
this.expand_children(selected, expanded);
}
},
_get_column_td: function(column_index, row) {
row = row || this.el;
var child_offset = 0;
if (this.tree.selection_mode != Sao.common.SELECTION_NONE) {
child_offset += 1;
}
return jQuery(row.children()[column_index + child_offset]);
},
redraw: function(selected, expanded) {
selected = selected || [];
expanded = expanded || [];
var update_expander = function() {
if (jQuery.isEmptyObject(
this.record.field_get(
this.children_field))) {
this.expander.css('visibility', 'hidden');
}
};
var thead_visible = this.tree.thead.is(':visible');
for (var i = 0; i < this.tree.columns.length; i++) {
if ((i === 0) && this.children_field) {
this.record.load(this.children_field).done(
update_expander.bind(this));
}
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.is_expanded() ||
Sao.common.contains(expanded, row_id_path)) {
this.tree.expanded[this.path] = this;
if (!this.record._values[this.children_field] ||
(this.record._values[this.children_field].length > 0 &&
this.rows.length === 0)) {
this.expand_children(selected, expanded);
} else {
redraw_async(this.rows, selected, expanded);
}
if (this.expander) {
this.update_expander(true);
}
} else {
if (this.expander) {
this.update_expander(false);
}
}
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 {
this.update_expander(true);
this.tree.expanded[this.path] = this;
this.expand_children();
}
return false;
},
update_expander: function(expanded) {
if (expanded) {
this.expander.removeClass('glyphicon-plus');
this.expander.addClass('glyphicon-minus');
} else {
this.expander.removeClass('glyphicon-minus');
this.expander.addClass('glyphicon-plus');
}
},
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) {
var add_children = function() {
if (!jQuery.isEmptyObject(this.rows)) {
return;
}
var new_rows = [];
var add_row = 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);
new_rows.push(tree_row);
};
var children = this.record.field_get_client(
this.children_field);
children.forEach(add_row.bind(this));
redraw_async(new_rows, selected, expanded);
};
return this.record.load(this.children_field).done(
add_children.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_row: function(event_) {
if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
this.tree.select_changed(this.record);
this.switch_row();
} else {
if (!event_.ctrlKey &&
this.tree.selection_mode ==
Sao.common.SELECTION_SINGLE) {
this.tree.rows.forEach(function(row) {
row.set_selection(false);
}.bind(this));
}
this.set_selection(!this.is_selected());
this.selection_changed();
}
// The 'click'-event must be handled next time the row is clicked
var td = event_.data.td;
var column = event_.data.column;
td.on('click keypress', {column: column, td: td},
Sao.common.click_press(this.select_row.bind(this), true));
},
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.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();
},
record_to_path: function(record) {
// recursively get the path to the record
var i, path;
if (record == this.record) {
return [];
} else {
for (i = 0; i < this.rows.length; i++) {
path = this.rows[i].record_to_path(record);
if (path) {
path.unshift(i);
return path;
}
}
}
},
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];
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;
},
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_row: function(event_) {
var previously_selected, previous_td;
var inner_rows, readonly_row, editable_row, current_td;
var field, widget;
function get_previously_selected(rows, selected) {
var i, r;
for (i = 0; i < rows.length; i++) {
r = rows[i];
if (r.is_selected()) {
previously_selected = r;
}
if (r != selected) {
r.set_selection(false);
}
get_previously_selected(r.rows, selected);
}
}
get_previously_selected(this.tree.rows, this);
this.selection_changed();
var save_prm;
if (previously_selected && previously_selected != this &&
!this.tree.screen.group.parent) {
save_prm = previously_selected.record.save();
} else {
save_prm = jQuery.when();
}
save_prm.done(function () {
if (previously_selected &&
previously_selected.edited_column !== null) {
previous_td = previously_selected.get_active_td();
previous_td.on('click keypress', {
td: previous_td,
column: previously_selected.edited_column
}, Sao.common.click_press(
previously_selected.select_row.bind(previously_selected),
true));
var previous_column = this.tree.columns[
previously_selected.edited_column];
previously_selected.get_static_el()
.html(previous_column.render(previously_selected.record))
.show();
previously_selected.empty_editable_el();
}
var selected = this.is_selected();
var column_name = event_.data.column;
var column = this.tree.columns[column_name];
var state_attrs = column.field.get_state_attrs(this.record);
var readonly = (
column.attributes.readonly || state_attrs.readonly);
if (selected && !readonly) {
this.edited_column = column_name;
current_td = this.get_active_td();
var attributes = column.attributes;
var EditableBuilder = Sao.View.editabletree_widget_get(
attributes.widget);
widget = new EditableBuilder(attributes.name,
this.tree.screen.model, attributes);
widget.view = this.tree;
// We have to define an overflow:visible in order for the
// completion widget to be shown
widget.el.on('focusin', function() {
jQuery(this).parents('.treeview td')
.css('overflow', 'visible');
});
widget.el.on('focusout', function() {
jQuery(this).parents('.treeview td')
.css('overflow', 'hidden');
});
var editable_el = this.get_editable_el();
editable_el.append(widget.el);
editable_el.data('widget', widget);
// We use keydown to be able to catch TAB events
widget.el.on('keydown', this.key_press.bind(this));
field = this.record.model.fields[widget.field_name];
widget.display(this.record, field);
this.get_static_el().hide();
this.get_editable_el().show();
widget.focus();
} else if (!selected) {
this.set_selection(true);
this.selection_changed();
var td = event_.data.td;
td.on('click keypress', {column: column_name, td: td},
Sao.common.click_press(this.select_row.bind(this),
true));
}
}.bind(this));
},
get_static_el: function() {
var td = this.get_active_td();
return td.find('.widget');
},
get_editable_el: function() {
var 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;
},
empty_editable_el: function() {
var editable;
editable = this.get_editable_el();
editable.empty();
editable.data('widget', null);
this.edited_column = null;
},
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 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) {
window.setTimeout(function() {
var td = this._get_column_td(next_idx);
td.triggerHandler('click', {
column: next_idx,
td: td
});
}.bind(this), 0);
}
} 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;
}
}
} else {
if (this.tree.screen.attributes.pre_validate) {
return this.record.pre_validate()
.fail(function() {
next_row = null;
});
} else if (!this.tree.screen.model.parent) {
return this.record.save()
.fail(function() {
next_row = null;
});
}
}
}.bind(this)).then(function() {
window.setTimeout(function() {
this._get_column_td(next_column, next_row)
.trigger('click').trigger('click');
}.bind(this), 0);
}.bind(this));
} else if (event_.which == Sao.common.ESC_KEYCODE) {
this.get_static_el().show();
current_td = this.get_active_td();
current_td.on('click keypress',
{column: this.edited_column, td: current_td},
Sao.common.click_press(this.select_row.bind(this),
true));
this.empty_editable_el();
} else if (event_.which == Sao.common.RETURN_KEYCODE) {
var focus_cell = function(row) {
var td = this._get_column_td(this.edited_column, row);
td.triggerHandler('click');
td.triggerHandler('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));
}
}
}
}
});
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-web-browser';
}
},
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, icon_prm;
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;
}
icon_prm = Sao.common.ICONFACTORY.register_icon(value);
icon_prm.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;
},
get_cell: function() {
var cell = jQuery('<div/>', {
'class': this.class_,
'tabindex': 0
});
return cell;
},
update_text: function(cell, record) {
cell.text(this.field.get_client(record));
},
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) {
cell.text(this.field.get_client(record, this.factor));
}
});
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'
});
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);
}.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;
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;
}
}
cell.text(model + ',' + name);
} else {
cell.text(name);
}
}.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);
cell.text(Sao.common.format_date(date_format, value));
}
});
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);
cell.text(Sao.common.format_time(
this.field.time_format(record), value));
}
});
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) {
cell.text('( ' + this.field.get_client(record).length + ' )');
}
});
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',
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;
}
cell.text(Sao.common.humanize(size));
}
});
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);
}
});
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.screen.current_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);
try {
this.screen.button(this.attributes);
} finally {
button.el.prop('disabled', false);
}
}
});
Sao.View.editabletree_widget_get = function(type) {
switch (type) {
case 'char':
case 'text':
case 'url':
case 'email':
case 'callto':
case 'sip':
return Sao.View.EditableTree.Char;
case 'date':
return Sao.View.EditableTree.Date;
case 'time':
return Sao.View.EditableTree.Time;
case 'timedelta':
return Sao.View.EditableTree.TimeDelta;
case 'integer':
case 'biginteger':
return Sao.View.EditableTree.Integer;
case 'float':
case 'numeric':
return Sao.View.EditableTree.Float;
case 'selection':
return Sao.View.EditableTree.Selection;
case 'boolean':
return Sao.View.EditableTree.Boolean;
case 'many2one':
return Sao.View.EditableTree.Many2One;
case 'reference':
return Sao.View.EditableTree.Reference;
case 'one2one':
return Sao.View.EditableTree.One2One;
case 'one2many':
case 'many2many':
return Sao.View.EditableTree.One2Many;
case 'binary':
return Sao.View.EditableTree.Binary;
}
};
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(field_name, model, attributes) {
Sao.View.EditableTree.Char._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Date = Sao.class_(Sao.View.Form.Date, {
class_: 'editabletree-date',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.Date._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Time = Sao.class_(Sao.View.Form.Time, {
class_: 'editabletree-time',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.Time._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.TimeDelta = Sao.class_(Sao.View.Form.TimeDelta, {
class_: 'editabletree-timedelta',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.TimeDelta._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Integer = Sao.class_(Sao.View.Form.Integer, {
class_: 'editabletree-integer',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.Integer._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Float = Sao.class_(Sao.View.Form.Float, {
class_: 'editabletree-float',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.Float._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Selection = Sao.class_(Sao.View.Form.Selection, {
class_: 'editabletree-selection',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.Selection._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Boolean = Sao.class_(Sao.View.Form.Boolean, {
class_: 'editabletree-boolean',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.Boolean._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Many2One = Sao.class_(Sao.View.Form.Many2One, {
class_: 'editabletree-many2one',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.Many2One._super.init.call(this, field_name,
model, 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(field_name, model, attributes) {
Sao.View.EditableTree.Reference._super.init.call(this, field_name,
model, 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(field_name, model, attributes) {
Sao.View.EditableTree.One2One._super.init.call(this, field_name,
model, 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(field_name, model, attributes) {
Sao.View.EditableTree.One2Many._super.init.call(this, field_name,
model, 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(field_name, model, attributes) {
Sao.View.EditableTree.Binary._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
}());