summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/views')
-rw-r--r--addons/web/static/src/js/views/form_common.js75
-rw-r--r--addons/web/static/src/js/views/form_relational_widgets.js426
-rw-r--r--addons/web/static/src/js/views/form_upgrade_widgets.js101
-rw-r--r--addons/web/static/src/js/views/form_view.js439
-rw-r--r--addons/web/static/src/js/views/form_widgets.js848
-rw-r--r--addons/web/static/src/js/views/graph_view.js26
-rw-r--r--addons/web/static/src/js/views/graph_widget.js1
-rw-r--r--addons/web/static/src/js/views/list_view.js543
-rw-r--r--addons/web/static/src/js/views/list_view_editable.js41
-rw-r--r--addons/web/static/src/js/views/pivot_view.js100
-rw-r--r--addons/web/static/src/js/views/search_filters.js33
-rw-r--r--addons/web/static/src/js/views/search_inputs.js3
-rw-r--r--addons/web/static/src/js/views/search_menus.js68
-rw-r--r--addons/web/static/src/js/views/search_view.js480
-rw-r--r--addons/web/static/src/js/views/tree_view.js22
15 files changed, 1339 insertions, 1867 deletions
diff --git a/addons/web/static/src/js/views/form_common.js b/addons/web/static/src/js/views/form_common.js
index 1c86a86c1ec..b3d078dbc1a 100644
--- a/addons/web/static/src/js/views/form_common.js
+++ b/addons/web/static/src/js/views/form_common.js
@@ -215,7 +215,7 @@ var CompletionFieldMixin = {
self._search_create_popup("search", data);
});
},
- classname: 'oe_m2o_dropdown_option'
+ classname: 'o_m2o_dropdown_option'
});
}
// quick create
@@ -228,7 +228,7 @@ var CompletionFieldMixin = {
action: function() {
self._quick_create(search_val);
},
- classname: 'oe_m2o_dropdown_option'
+ classname: 'o_m2o_dropdown_option'
});
}
// create...
@@ -238,14 +238,14 @@ var CompletionFieldMixin = {
action: function() {
self._search_create_popup("form", undefined, self._create_context(search_val));
},
- classname: 'oe_m2o_dropdown_option'
+ classname: 'o_m2o_dropdown_option'
});
}
else if (values.length === 0) {
values.push({
label: _t("No results to show..."),
action: function() {},
- classname: 'oe_m2o_dropdown_option'
+ classname: 'o_m2o_dropdown_option'
});
}
@@ -275,7 +275,7 @@ var CompletionFieldMixin = {
// all search/create popup handling
_search_create_popup: function(view, ids, context) {
var self = this;
- new SelectCreateDialog(this, {
+ new SelectCreateDialog(this, _.extend({}, (this.options || {}), {
res_model: self.field.relation,
domain: self.build_domain(),
context: new data.CompoundContext(self.build_context(), context || {}),
@@ -287,7 +287,7 @@ var CompletionFieldMixin = {
self.add_id(element_ids[0]);
self.focus();
}
- }).open();
+ })).open();
},
/**
* To implement.
@@ -474,7 +474,7 @@ var FormWidget = Widget.extend(InvisibilityChangerMixin, {
debug: session.debug,
widget: widget
});
- },
+ }
}, options || {});
//only show tooltip if we are in debug or if we have a help to show, otherwise it will display
//as empty
@@ -666,16 +666,16 @@ var AbstractField = FormWidget.extend(FieldInterface, {
this._check_css_flags();
});
- this.$translate = $(); // Enterprise compatibility
+ this.$translate = (_t.database.multi_lang && this.field.translate) ? $('<button/>', {
+ type: 'button',
+ }).addClass('o_field_translate fa fa-globe btn btn-link') : $();
},
+
renderElement: function() {
var self = this;
this._super();
- if (this.field.translate && this.view) {
- this.$el.addClass('oe_form_field_translatable');
- this.$el.find('.oe_field_translate').click(this.on_translate);
- }
- this.$label = this.view ? this.view.$el.find('label[for=' + this.id_for_label + ']') : $();
+ this.$el.addClass('o_form_field');
+ this.$label = this.view ? this.view.$('label[for=' + this.id_for_label + ']') : $();
this.do_attach_tooltip(this, this.$label[0] || this.$el);
if (session.debug) {
this.$label.off('dblclick').on('dblclick', function() {
@@ -707,16 +707,23 @@ var AbstractField = FormWidget.extend(FieldInterface, {
});
this.render_value();
this._toggle_label();
+
+ if (this.view) {
+ this.$translate
+ .insertAfter(this.$el)
+ .on('click', _.bind(this.on_translate, this));
+ }
},
_toggle_label: function() {
var empty = this.get('effective_readonly') && this.is_false();
+ this.$label.toggleClass('o_form_label_empty', empty).toggleClass('o_form_label_false', this.get('effective_readonly') && this.get('value') === false);
this.$el.toggleClass('o_form_field_empty', empty);
},
/**
* Private. Do not use.
*/
_set_required: function() {
- this.$el.toggleClass('oe_form_required', this.get("required"));
+ this.$el.toggleClass('o_form_required', this.get("required"));
},
set_value: function(value_) {
this.set({'value': value_});
@@ -752,10 +759,11 @@ var AbstractField = FormWidget.extend(FieldInterface, {
return this.get('value') === false;
},
_check_css_flags: function() {
- if (this.field.translate) {
- this.$el.find('.oe_field_translate').toggle(this.field_manager.get('actual_mode') !== "create");
- }
- this.$el.toggleClass('oe_form_invalid', !this.disable_utility_classes && !!this.field_manager.get('display_invalid_fields') && !this.is_valid());
+ var show_translate = (!this.get('effective_readonly') && this.field_manager.get('actual_mode') !== "create");
+ this.$translate.toggleClass('o_translate_active', !!show_translate);
+
+ this.$el.add(this.$label)
+ .toggleClass('o_form_invalid', !this.disable_utility_classes && !!this.field_manager.get('display_invalid_fields') && !this.is_valid());
},
focus: function() {
return false;
@@ -770,11 +778,10 @@ var AbstractField = FormWidget.extend(FieldInterface, {
self.do_action(r);
});
},
-
- set_dimensions: function (height, width) {
+ set_dimensions: function(height, width) {
this.$el.css({
width: width,
- minHeight: height
+ height: height,
});
},
commit_value: function() {
@@ -802,7 +809,7 @@ var ViewDialog = Dialog.extend({ // FIXME should use ViewManager
options.dialogClass = options.dialogClass || '';
options.dialogClass += ' o_act_window';
- this._super(parent, _.clone(options));
+ this._super(parent, $.extend(true, {}, options));
this.res_model = options.res_model || null;
this.res_id = options.res_id || null;
@@ -864,7 +871,7 @@ var FormViewDialog = ViewDialog.extend({
];
if(!readonly) {
- options.buttons.splice(0, 0, {text: _t("Save") + ((multi_select)? " " + _t(" & Close") : ""), classes: "btn-primary o_formdialog_save", click: function() { // o_formdialog_save class for web_tests!
+ options.buttons.splice(0, 0, {text: _t("Save") + ((multi_select)? " " + _t(" & Close") : ""), classes: "btn-primary", click: function() {
self.view_form.onchanges_mutex.def.then(function() {
if (!self.view_form.warning_displayed) {
$.when(self.view_form.save()).done(function() {
@@ -923,6 +930,7 @@ var FormViewDialog = ViewDialog.extend({
var fragment = document.createDocumentFragment();
self.view_form.appendTo(fragment).then(function () {
self.view_form.do_show().then(function() {
+ self.view_form.autofocus();
_super().$el.append(fragment);
});
});
@@ -941,7 +949,7 @@ var SelectCreateListView = ListView.extend({
this.popup.close();
},
do_select: function(ids, records) {
- this._super(ids, records);
+ this._super.apply(this, arguments);
this.popup.on_click_element(ids);
}
});
@@ -1022,7 +1030,7 @@ var SelectCreateDialog = ViewDialog.extend({
_.extend({'deletable': false,
'selectable': !self.options.disable_multiple_selection,
'import_enabled': false,
- '$buttons': self.$footer,
+ '$buttons': self.$buttons,
'disable_editable_mode': true,
'pager': true,
}, self.options.list_view_options || {}));
@@ -1030,6 +1038,9 @@ var SelectCreateDialog = ViewDialog.extend({
e.cancel = true;
});
self.view_list.popup = self;
+ self.view_list.on('list_view_loaded', self, function() {
+ this.on_view_list_loaded();
+ });
var buttons = [
{text: _t("Cancel"), classes: "btn-default o_form_button_cancel", close: true}
@@ -1076,6 +1087,7 @@ var SelectCreateDialog = ViewDialog.extend({
this.close();
return new FormViewDialog(this.__parentedParent, this.options).open();
},
+ on_view_list_loaded: function() {},
});
var DomainEditorDialog = SelectCreateDialog.extend({
@@ -1095,11 +1107,11 @@ var DomainEditorDialog = SelectCreateDialog.extend({
},
get_domain: function (selected_ids) {
- var group_domain = [],
- domain;
- if (this.$('input.oe_list_record_selector').prop('checked')) {
+ var group_domain = [];
+ var domain;
+ if (this.$('.o_list_record_selector input').prop('checked')) {
if (this.view_list.grouped) {
- var group_domain = _.chain(_.values(this.view_list.groups.children))
+ group_domain = _.chain(_.values(this.view_list.groups.children))
.filter(function (child) { return child.records.length; })
.map(function (c) { return c.datagroup.domain;})
.value();
@@ -1118,6 +1130,11 @@ var DomainEditorDialog = SelectCreateDialog.extend({
}
return this.dataset.domain.concat(group_domain).concat(domain || []);
},
+
+ on_view_list_loaded: function() {
+ this.$('.o_list_record_selector input').prop('checked', true);
+ this.$footer.find(".o_selectcreatepopup_search_select").prop('disabled', false);
+ },
});
return {
diff --git a/addons/web/static/src/js/views/form_relational_widgets.js b/addons/web/static/src/js/views/form_relational_widgets.js
index fc852108035..8e64599f470 100644
--- a/addons/web/static/src/js/views/form_relational_widgets.js
+++ b/addons/web/static/src/js/views/form_relational_widgets.js
@@ -6,7 +6,6 @@ var core = require('web.core');
var data = require('web.data');
var Dialog = require('web.Dialog');
var common = require('web.form_common');
-var FormView = require('web.FormView');
var ListView = require('web.ListView');
var Model = require('web.DataModel');
var session = require('web.session');
@@ -56,16 +55,15 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
events: {
'keydown input': function (e) {
switch (e.which) {
- case $.ui.keyCode.UP:
- case $.ui.keyCode.DOWN:
- e.stopPropagation();
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.DOWN:
+ e.stopPropagation();
}
},
},
init: function(field_manager, node) {
this._super(field_manager, node);
common.CompletionFieldMixin.init.call(this);
- this.set({'value': false});
this.display_value = {};
this.display_value_backup = {};
this.last_search = [];
@@ -91,8 +89,9 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
common.ReinitializeFieldMixin.initialize_field.call(this);
},
initialize_content: function() {
- if (!this.get("effective_readonly"))
+ if (!this.get("effective_readonly")) {
this.render_editable();
+ }
},
destroy_content: function () {
if (this.$dropdown) {
@@ -125,16 +124,15 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
},
render_editable: function() {
var self = this;
- this.$input = this.$el.find("input");
+ this.$input = this.$("input");
this.init_error_displayer();
-
self.$input.on('focus', function() {
self.hide_error_displayer();
});
- this.$dropdown = this.$el.find(".oe_m2o_drop_down_button");
- this.$follow_button = $(".oe_m2o_cm_button", this.$el);
+ this.$dropdown = this.$(".o_dropdown_button");
+ this.$follow_button = this.$(".o_external_button");
this.$follow_button.click(function(ev) {
ev.preventDefault();
@@ -177,8 +175,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
};
this.$input.keydown(input_changed);
this.$input.change(input_changed);
- this.$dropdown.click(function() {
- self.$input.focus();
+ this.$input.on('click', function() {
if (self.$input.autocomplete("widget").is(":visible")) {
self.$input.autocomplete("close");
} else {
@@ -298,7 +295,6 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
});
// set position for list of suggestions box
this.$input.autocomplete( "option", "position", { my : "left top", at: "left bottom" } );
- this.$input.autocomplete("widget").openerpClass();
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
this.$input.keyup(function(e) {
if (e.which === 13) { // ENTER
@@ -311,7 +307,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
},
render_value: function(no_recurse) {
var self = this;
- if (! this.get("value")) {
+ if (!this.get("value")) {
this.display_string("");
return;
}
@@ -320,7 +316,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
this.display_string(display);
return;
}
- if (! no_recurse) {
+ if (!no_recurse) {
var dataset = new data.DataSetStatic(this, this.field.relation, self.build_context());
var def = this.alive(dataset.name_get([self.get("value")])).done(function(data) {
if (!data[0]) {
@@ -341,38 +337,33 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
}
},
display_string: function(str) {
- var self = this;
if (!this.get("effective_readonly")) {
this.$input.val(str.split("\n")[0]);
this.current_display = this.$input.val();
- if (this.is_false()) {
- this.$('.oe_m2o_cm_button').css({'display':'none'});
- } else {
- this.$('.oe_m2o_cm_button').css({'display':'inline'});
- }
+ this.$follow_button.toggle(!this.is_false());
+ this.$el.toggleClass('o_with_button', !!this.$follow_button && this.$follow_button.length > 0 && !this.is_false());
} else {
- var lines = _.escape(str).split("\n");
- var link = "";
- var follow = "";
- link = lines[0];
- follow = _.rest(lines).join("<br />");
- if (follow)
- link += "<br />";
- var $link = this.$el.find('.oe_form_uri')
- .unbind('click')
- .html(link);
- if (! this.options.no_open)
- $link.click(function () {
- var context = self.build_context().eval();
- var model_obj = new Model(self.field.relation);
- model_obj.call('get_formview_action', [self.get("value"), context]).then(function(action){
- self.do_action(action);
- });
- return false;
- });
- $(".oe_form_m2o_follow", this.$el).html(follow);
+ this.$el.html(_.escape(str).split("\n").join("<br/>"));
+ // Define callback to perform when clicking on the field
+ if (!this.options.no_open) {
+ // Remove potential previously added event handler
+ this.$el.off('click');
+ // Ensure that the callback is performed only once even with multiple clicks
+ var execute_formview_action_once = _.once(this.execute_formview_action.bind(this));
+ this.$el.click(function (ev) {
+ ev.preventDefault();
+ execute_formview_action_once();
+ });
+ }
}
},
+ execute_formview_action: function() {
+ var self = this;
+ var context = self.build_context().eval();
+ (new Model(self.field.relation)).call('get_formview_action', [self.get("value"), context]).then(function(action) {
+ self.do_action(action);
+ });
+ },
set_value: function(value_) {
if (value_ instanceof Array) {
this.display_value = {};
@@ -418,11 +409,6 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
this.no_ed = false;
return res;
},
- set_dimensions: function (height, width) {
- this._super(height, width);
- if (!this.get("effective_readonly") && this.$input)
- this.$input.css('height', height);
- }
});
/**
@@ -586,40 +572,39 @@ var AbstractManyField = common.AbstractField.extend({
_.each(command_list, function(command) {
self.mutex.exec(function() {
var id = command[1];
- if (!id || _.isString(id) || _.isNumber(id)) {
- switch (command[0]) {
- case COMMANDS.CREATE:
- var data = _.clone(command[2]);
- delete data.id;
- return dataset.create(data, internal_options).then(function (id) {
+ switch (command[0]) {
+ case COMMANDS.CREATE:
+ var data = _.clone(command[2]);
+ delete data.id;
+ return dataset.create(data, internal_options).then(function (id) {
+ dataset.ids.push(id);
+ res = id;
+ });
+ case COMMANDS.UPDATE:
+ return dataset.write(id, command[2], internal_options).then(function () {
+ if (dataset.ids.indexOf(id) === -1) {
dataset.ids.push(id);
res = id;
- });
- case COMMANDS.UPDATE:
- return dataset.write(id, command[2], internal_options).then(function () {
- if (dataset.ids.indexOf(id) === -1) {
- dataset.ids.push(id);
- res = id;
- }
- });
- case COMMANDS.FORGET:
- return dataset.unlink([id]);
- case COMMANDS.DELETE:
- return dataset.unlink([id]);
- case COMMANDS.LINK_TO:
- if (dataset.ids.indexOf(id) === -1) {
- return dataset.add_ids([id], internal_options);
}
- return;
- case COMMANDS.DELETE_ALL:
- return dataset.reset_ids([], {keep_read_data: true});
- case COMMANDS.REPLACE_WITH:
- dataset.ids = [];
- return dataset.alter_ids(command[2], internal_options);
- }
+ });
+ case COMMANDS.FORGET:
+ return dataset.unlink([id]);
+ case COMMANDS.DELETE:
+ return dataset.unlink([id]);
+ case COMMANDS.LINK_TO:
+ if (dataset.ids.indexOf(id) === -1) {
+ return dataset.add_ids([id], internal_options);
+ }
+ return;
+ case COMMANDS.DELETE_ALL:
+ return dataset.reset_ids([], {keep_read_data: true});
+ case COMMANDS.REPLACE_WITH:
+ dataset.ids = [];
+ return dataset.alter_ids(command[2], internal_options);
+ default:
+ throw new Error("send_commands to '"+self.name+"' receive a non command value." +
+ "\n" + JSON.stringify(command_list));
}
- throw new Error("send_commands to '"+self.name+"' receive a non command value." +
- "\n" + JSON.stringify(command_list));
});
});
@@ -660,10 +645,6 @@ var AbstractManyField = common.AbstractField.extend({
if (record.to_create) {
command_list.push(COMMANDS.create(values));
} else {
- if (index === -1) {
- // because the UPDATE below does not imply LINK_TO
- command_list.push(COMMANDS.link_to(id));
- }
command_list.push(COMMANDS.update(record.id, values));
}
return;
@@ -724,13 +705,12 @@ var FieldX2Many = AbstractManyField.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$el.addClass('oe_form_field');
-
var self = this;
this.load_views();
var destroy = function() {
self.is_loaded = self.is_loaded.then(function() {
+ self.renderElement();
self.viewmanager.destroy();
return $.when(self.load_views()).done(function() {
self.reload_current_view();
@@ -828,7 +808,8 @@ var FieldX2Many = AbstractManyField.extend({
});
});
$.async_when().done(function () {
- self.alive( self.viewmanager.appendTo(self.$el));
+ self.$el.addClass('o_view_manager_content');
+ self.alive(self.viewmanager.attachTo(self.$el));
});
return def;
},
@@ -881,6 +862,9 @@ var FieldX2Many = AbstractManyField.extend({
}
return true;
},
+ is_false: function() {
+ return false;
+ },
});
var X2ManyDataSet = data.BufferedDataSet.extend({
@@ -891,10 +875,15 @@ var X2ManyDataSet = data.BufferedDataSet.extend({
self.context.add(context);
});
return this.context;
- }
+ },
});
var X2ManyViewManager = ViewManager.extend({
+ custom_events: {
+ // Catch event scrollTo to prevent scrolling to the top when using the
+ // pager of List and Kanban views in One2Many fields
+ 'scrollTo': function() {},
+ },
init: function(parent, dataset, views, flags, x2many_views) {
// By default, render buttons and pager in X2M fields, but no sidebar
flags = _.extend({}, flags, {
@@ -984,6 +973,15 @@ var X2ManyListView = ListView.extend({
});
return valid;
},
+ render_pager: function($node, options) {
+ options = _.extend(options || {}, {
+ single_page_hidden: true,
+ });
+ this._super($node, options);
+ },
+ display_nocontent_helper: function () {
+ return false;
+ },
});
/**
@@ -1008,7 +1006,7 @@ var X2ManyList = ListView.List.extend({
var $cell = $('<td>', {
colspan: columns,
- 'class': 'oe_form_field_x2many_list_row_add'
+ 'class': 'o_form_field_x2many_list_row_add'
}).append(
$('<a>', {href: '#'}).text(_t("Add an item"))
.click(function (e) {
@@ -1037,7 +1035,6 @@ var X2ManyList = ListView.List.extend({
});
var One2ManyListView = X2ManyListView.extend({
- _template: 'One2Many.listview',
init: function () {
this._super.apply(this, arguments);
this.options = _.extend(this.options, {
@@ -1177,7 +1174,7 @@ var One2ManyListView = X2ManyListView.extend({
var click_outside = ($target.closest('.ui-autocomplete,.btn,.modal-backdrop').length === 0);
// Check if click inside the current list editable
- var $o2m = $target.closest(".oe_list_editable");
+ var $o2m = $target.closest(".o_list_editable");
if($o2m.length && $o2m[0] === this.el) {
click_outside = false;
}
@@ -1251,39 +1248,19 @@ var One2ManyGroups = ListView.Groups.extend({
}
});
-var One2ManyFormView = FormView.extend({
- form_template: 'One2Many.formview',
- load_form: function(data) {
- this._super(data);
- var self = this;
- this.$buttons.find('button.oe_form_button_create').click(function() {
- self.save().done(self.on_button_new);
- });
- },
- do_notify_change: function() {
- if (this.dataset.parent_view) {
- this.dataset.parent_view.do_notify_change();
- } else {
- this._super.apply(this, arguments);
- }
- }
-});
-
var FieldOne2Many = FieldX2Many.extend({
init: function() {
this._super.apply(this, arguments);
this.x2many_views = {
- form: One2ManyFormView,
kanban: core.view_registry.get('one2many_kanban'),
list: One2ManyListView,
};
},
start: function() {
- this.$el.addClass('oe_form_field_one2many');
+ this.$el.addClass('o_form_field_one2many');
return this._super.apply(this, arguments);
},
commit_value: function() {
- var self = this;
var view = this.viewmanager.active_view;
if(view.type === "list" && view.controller.editable()) {
return this.mutex.def.then(function () {
@@ -1357,7 +1334,7 @@ var FieldMany2Many = FieldX2Many.extend({
};
},
start: function() {
- this.$el.addClass('oe_form_field_many2many');
+ this.$el.addClass('o_form_field_many2many');
return this._super.apply(this, arguments);
}
});
@@ -1373,7 +1350,7 @@ var FieldMany2ManyKanban = FieldMany2Many.extend({
});
var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, common.ReinitializeFieldMixin, {
- className: "oe_form_field o_form_field_many2manytags",
+ className: "o_form_field_many2manytags",
tag_template: "FieldMany2ManyTag",
events: {
@@ -1435,7 +1412,6 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c
}
},
get_render_data: function(ids){
- var self = this;
this.dataset.cancel_read();
var fields = this.fields.color ? ['display_name', 'name', 'color'] : ['display_name', 'name']; // TODO master: remove useless 'name'
return this.dataset.read_ids(ids, fields);
@@ -1475,26 +1451,30 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c
}
return false;
},
- set_dimensions: function (height, width) {
- this._super(height, width);
- var $input = this.$('input');
- if (!this.get("effective_readonly") && $input) {
- $input.css('height', height);
+ set_dimensions: function(height, width) {
+ if(this.many2one) {
+ this.many2one.$el.css('height', 'auto');
+ }
+ this.$el.css({
+ width: width,
+ minHeight: height,
+ });
+ if(this.many2one) {
+ this.many2one.$el.css('height', this.$el.height());
}
},
open_color_picker: function(ev){
- this.mutex.exec(function(){
- if (this.fields.color) {
- this.$color_picker = $(QWeb.render('FieldMany2ManyTag.colorpicker', {
- 'widget': this,
- 'tag_id': $(ev.currentTarget).data('id'),
- }));
- $(ev.currentTarget).append(this.$color_picker);
- this.$color_picker.dropdown('toggle');
- this.$color_picker.attr("tabindex", 1).focus();
- }
- }.bind(this));
+ if (this.fields.color) {
+ this.$color_picker = $(QWeb.render('FieldMany2ManyTag.colorpicker', {
+ 'widget': this,
+ 'tag_id': $(ev.currentTarget).data('id'),
+ }));
+
+ $(ev.currentTarget).append(this.$color_picker);
+ this.$color_picker.dropdown('toggle');
+ this.$color_picker.attr("tabindex", 1).focus();
+ }
},
close_color_picker: function(){
this.$color_picker.remove();
@@ -1513,24 +1493,76 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c
var old_color = tag.data('color');
tag.removeClass('o_tag_color_' + old_color);
tag.data('color', color);
- tag.addClass('o_tag_color_' + color);
+ tag.addClass('o_tag_color_' + color);
});
},
-
});
/**
* Widget for (many2many field) to upload one or more file in same time and display in list.
* The user can delete his files.
- * Options on attribute ; "blockui" {Boolean} block the UI or not
- * during the file is uploading
+ * Options on attribute ; "blockui" {Boolean} block the UI or not during the file is uploading
*/
var FieldMany2ManyBinaryMultiFiles = AbstractManyField.extend(common.ReinitializeFieldMixin, {
template: "FieldBinaryFileUploader",
+ events: {
+ 'click .o_attach': function(e) {
+ this.$('.o_form_input_file').click();
+ },
+ 'change .o_form_input_file': function(e) {
+ e.stopPropagation();
+
+ var $target = $(e.target);
+ var value = $target.val();
+
+ if(value !== '') {
+ if(this.data[0] && this.data[0].upload) { // don't upload more of one file in same time
+ return false;
+ }
+
+ var filename = value.replace(/.*[\\\/]/, '');
+ for(var id in this.get('value')) {
+ // if the files exits, delete the file before upload (if it's a new file)
+ if(this.data[id] && (this.data[id].filename || this.data[id].name) == filename && !this.data[id].no_unlink) {
+ this.ds_file.unlink([id]);
+ }
+ }
+
+ if(this.node.attrs.blockui > 0) { // block UI or not
+ framework.blockUI();
+ }
+
+ // TODO : unactivate send on wizard and form
+
+ // submit file
+ this.$('form.o_form_binary_form').submit();
+ this.$(".oe_fileupload").hide();
+ // add file on data result
+ this.data[0] = {
+ id: 0,
+ name: filename,
+ filename: filename,
+ url: '',
+ upload: true,
+ };
+ }
+ },
+ 'click .oe_delete': function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ var file_id = $(e.currentTarget).data("id");
+ if(file_id) {
+ var files = _.without(this.get('value'), file_id);
+ if(!this.data[file_id].no_unlink) {
+ this.ds_file.unlink([file_id]);
+ }
+ this.set({'value': files});
+ }
+ },
+ },
init: function(field_manager, node) {
- this._super(field_manager, node);
- this.field_manager = field_manager;
- this.node = node;
+ this._super.apply(this, arguments);
this.session = session;
if(this.field.type != "many2many" || this.field.relation != 'ir.attachment') {
throw _.str.sprintf(_t("The type of the field '%s' must be a many2many field with a relation to 'ir.attachment' model."), this.field.string);
@@ -1541,40 +1573,39 @@ var FieldMany2ManyBinaryMultiFiles = AbstractManyField.extend(common.Reinitializ
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
$(window).on(this.fileupload_id, _.bind(this.on_file_loaded, this));
},
- initialize_content: function() {
- this.$el.on('change', 'input.o_form_input_file', this.on_file_change );
- },
- get_file_url: function (attachment) {
+ get_file_url: function(attachment) {
return '/web/content/' + attachment.id + '?download=true';
},
- read_name_values : function () {
+ read_name_values : function() {
var self = this;
// don't reset know values
var ids = this.get('value');
- var _value = _.filter(ids, function (id) { return typeof self.data[id] == 'undefined'; } );
+ var _value = _.filter(ids, function(id) { return self.data[id] === undefined; });
// send request for get_name
- if (_value.length) {
- return this.ds_file.call('read', [_value, ['id', 'name', 'datas_fname', 'mimetype']]).then(function (datas) {
- _.each(datas, function (data) {
- data.no_unlink = true;
- data.url = self.get_file_url(data);
- self.data[data.id] = data;
- });
- return ids;
- });
+ if(_value.length) {
+ return this.ds_file.call('read', [_value, ['id', 'name', 'datas_fname', 'mimetype']])
+ .then(process_data);
} else {
return $.when(ids);
}
+
+ function process_data(datas) {
+ _.each(datas, function(data) {
+ data.no_unlink = true;
+ data.url = self.get_file_url(data);
+ self.data[data.id] = data;
+ });
+ return ids;
+ }
},
- render_value: function () {
+ render_value: function() {
var self = this;
this.read_name_values().then(function (ids) {
- var render = $(QWeb.render('FieldBinaryFileUploader.files', {'widget': self, 'values': ids}));
- render.on('click', '.oe_delete', _.bind(self.on_file_delete, self));
- self.$('.oe_placeholder_files, .oe_attachments').replaceWith( render );
+ self.$('.oe_placeholder_files, .oe_attachments')
+ .replaceWith($(QWeb.render('FieldBinaryFileUploader.files', {'widget': self, 'values': ids})));
// reinit input type file
- var $input = self.$('input.o_form_input_file');
+ var $input = self.$('.o_form_input_file');
$input.after($input.clone(true)).remove();
self.$(".oe_fileupload").show();
@@ -1587,77 +1618,26 @@ var FieldMany2ManyBinaryMultiFiles = AbstractManyField.extend(common.Reinitializ
});
});
},
- on_file_change: function (event) {
- event.stopPropagation();
- var self = this;
- var $target = $(event.target);
- if ($target.val() !== '') {
- var filename = $target.val().replace(/.*[\\\/]/,'');
- // don't uplode more of one file in same time
- if (self.data[0] && self.data[0].upload ) {
- return false;
- }
- for (var id in this.get('value')) {
- // if the files exits, delete the file before upload (if it's a new file)
- if (self.data[id] && (self.data[id].filename || self.data[id].name) == filename && !self.data[id].no_unlink ) {
- self.ds_file.unlink([id]);
- }
- }
-
- // block UI or not
- if(this.node.attrs.blockui>0) {
- framework.blockUI();
- }
-
- // TODO : unactivate send on wizard and form
-
- // submit file
- this.$('form.o_form_binary_form').submit();
- this.$(".oe_fileupload").hide();
- // add file on data result
- this.data[0] = {
- 'id': 0,
- 'name': filename,
- 'filename': filename,
- 'url': '',
- 'upload': true
- };
- }
- },
- on_file_loaded: function (event, result) {
- var files = this.get('value');
-
- // unblock UI
- if(this.node.attrs.blockui>0) {
+ on_file_loaded: function(e, result) {
+ if(this.node.attrs.blockui > 0) { // unblock UI
framework.unblockUI();
}
- if (result.error || !result.id ) {
- this.do_warn( _t('Uploading Error'), result.error);
+ if(result.error || !result.id) {
+ this.do_warn(_t('Uploading Error'), result.error);
delete this.data[0];
} else {
- if (this.data[0] && this.data[0].filename == result.filename && this.data[0].upload) {
+ if(this.data[0] && this.data[0].filename === result.filename && this.data[0].upload) {
delete this.data[0];
}
result.url = this.get_file_url(result);
this.data[result.id] = result;
var values = _.clone(this.get('value'));
values.push(result.id);
- this.set({'value': values});
+ this.set({value: values});
}
this.render_value();
},
- on_file_delete: function (event) {
- event.stopPropagation();
- var file_id=$(event.target).closest('.oe_delete').data("id");
- if (file_id) {
- var files = _.filter(this.get('value'), function (id) {return id != file_id;});
- if(!this.data[file_id].no_unlink) {
- this.ds_file.unlink([file_id]);
- }
- this.set({'value': files});
- }
- },
});
/*
@@ -1672,7 +1652,7 @@ var FieldMany2ManyCheckBoxes = AbstractManyField.extend(common.ReinitializeField
this.set("records", []);
this.field_manager.on("view_content_has_changed", this, function() {
var domain = new data.CompoundDomain(this.build_domain()).eval();
- if (! _.isEqual(domain, this.get("domain"))) {
+ if (!_.isEqual(domain, this.get("domain"))) {
this.set("domain", domain);
}
});
@@ -1694,18 +1674,22 @@ var FieldMany2ManyCheckBoxes = AbstractManyField.extend(common.ReinitializeField
});
},
render_value: function() {
- this.$().html(QWeb.render("FieldMany2ManyCheckBoxes", {widget: this}));
- var inputs = this.$("input");
- inputs.change(_.bind(this.from_dom, this));
- if (this.get("effective_readonly"))
- inputs.attr("disabled", "true");
+ this.$el.html(QWeb.render("FieldMany2ManyCheckBoxes", {widget: this}));
+ var $inputs = this.$("input");
+ $inputs.change(_.bind(this.from_dom, this));
+ if (this.get("effective_readonly")) {
+ $inputs.attr("disabled", "true");
+ }
},
from_dom: function() {
var new_value = this.$("input:checked").map(function() { return +$(this).data("record-id"); }).get();
- if (! _.isEqual(new_value, this.get("value"))) {
+ if (!_.isEqual(new_value, this.get("value"))) {
this.internal_set_value(new_value);
}
- }
+ },
+ is_false: function() {
+ return false;
+ },
});
core.form_widget_registry
@@ -1718,9 +1702,13 @@ core.form_widget_registry
.add('many2many_binary', FieldMany2ManyBinaryMultiFiles)
.add('many2many_checkboxes', FieldMany2ManyCheckBoxes);
+core.one2many_view_registry
+ .add('list', One2ManyListView);
+
return {
FieldMany2ManyTags: FieldMany2ManyTags,
AbstractManyField: AbstractManyField,
+ FieldMany2One: FieldMany2One,
};
});
diff --git a/addons/web/static/src/js/views/form_upgrade_widgets.js b/addons/web/static/src/js/views/form_upgrade_widgets.js
new file mode 100644
index 00000000000..ce32ef0664b
--- /dev/null
+++ b/addons/web/static/src/js/views/form_upgrade_widgets.js
@@ -0,0 +1,101 @@
+odoo.define('web.form_upgrade_widgets', function (require) {
+"use strict";
+
+var core = require('web.core');
+var Dialog = require('web.Dialog');
+var form_widgets = require('web.form_widgets');
+var framework = require('web.framework');
+var Model = require('web.DataModel');
+
+var _t = core._t;
+var QWeb = core.qweb;
+
+/**
+ * This widget is intended to be used in config settings.
+ * When checked, an upgrade popup is showed to the user.
+ */
+var AbstractFieldUpgrade = {
+ events: {
+ 'click input': 'on_click_input',
+ },
+
+ start: function() {
+ this._super.apply(this, arguments);
+
+ this.get_enterprise_label().after($("<span>", {
+ text: "Enterprise",
+ 'class': "label label-primary oe_inline"
+ }));
+ },
+
+ open_dialog: function() {
+ var message = $(QWeb.render('EnterpriseUpgrade'));
+
+ var buttons = [
+ {
+ text: _t("Upgrade now"),
+ classes: 'btn-primary',
+ close: true,
+ click: this.confirm_upgrade,
+ },
+ {
+ text: _t("Cancel"),
+ close: true,
+ },
+ ];
+
+ return new Dialog(this, {
+ size: 'medium',
+ buttons: buttons,
+ $content: $('<div>', {
+ html: message,
+ }),
+ title: _t("Odoo Enterprise"),
+ }).open();
+ },
+
+ confirm_upgrade: function() {
+ new Model("res.users").call("search_count", [[["share", "=", false]]]).then(function(data) {
+ framework.redirect("https://www.odoo.com/odoo-enterprise/upgrade?num_users=" + data);
+ });
+ },
+
+ get_enterprise_label: function() {},
+ on_click_input: function() {},
+};
+
+var UpgradeBoolean = form_widgets.FieldBoolean.extend(AbstractFieldUpgrade, {
+ template: "FieldUpgradeBoolean",
+
+ get_enterprise_label: function() {
+ return this.$label;
+ },
+
+ on_click_input: function() {
+ if(this.$checkbox.prop("checked")) {
+ this.open_dialog().on('closed', this, function() {
+ this.$checkbox.prop("checked", false);
+ });
+ }
+ },
+});
+
+var UpgradeRadio = form_widgets.FieldRadio.extend(AbstractFieldUpgrade, {
+ get_enterprise_label: function() {
+ return this.$('label').last();
+ },
+
+ on_click_input: function(event) {
+ if($(event.target).val() === 1) {
+ this.open_dialog().on('closed', this, function() {
+ this.$('input').first().prop("checked", true);
+ });
+ }
+ },
+});
+
+core.form_widget_registry
+ .add('upgrade_boolean', UpgradeBoolean)
+ .add('upgrade_radio', UpgradeRadio);
+
+});
diff --git a/addons/web/static/src/js/views/form_view.js b/addons/web/static/src/js/views/form_view.js
index 7a2cf428400..d2c7a21eb04 100644
--- a/addons/web/static/src/js/views/form_view.js
+++ b/addons/web/static/src/js/views/form_view.js
@@ -1,12 +1,14 @@
odoo.define('web.FormView', function (require) {
"use strict";
+var common = require('web.form_common');
var core = require('web.core');
var crash_manager = require('web.crash_manager');
var data = require('web.data');
var Dialog = require('web.Dialog');
-var common = require('web.form_common');
+var FormRenderingEngine = require('web.FormRenderingEngine');
var Model = require('web.DataModel');
+var Pager = require('web.Pager');
var Sidebar = require('web.Sidebar');
var utils = require('web.utils');
var View = require('web.View');
@@ -14,7 +16,6 @@ var View = require('web.View');
var _t = core._t;
var _lt = core._lt;
var QWeb = core.qweb;
-var Class = core.Class;
/**
* Properties:
@@ -22,7 +23,7 @@ var Class = core.Class;
* the mode used by the view.
*/
var FormView = View.extend(common.FieldManagerMixin, {
- accesskey: "F",
+ className: "o_form_view",
defaults: _.extend({}, View.prototype.defaults, {
not_interactible_on_create: false,
initial_mode: "view",
@@ -34,8 +35,20 @@ var FormView = View.extend(common.FieldManagerMixin, {
multi_record: false,
// Indicates that this view is not searchable, and thus that no search view should be displayed.
searchable: false,
- template: "FormView",
+ /**
+ * Called each time the form view is attached into the DOM
+ */
+ on_attach_callback: function() {
+ this.trigger('attached');
+ this.autofocus();
+ },
+ /**
+ * Called each time the form view is detached from the DOM
+ */
+ on_detach_callback: function() {
+ this.trigger('detached');
+ },
init: function() {
var self = this;
this._super.apply(this, arguments);
@@ -60,14 +73,11 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.__clicked_inside = false;
this.__blur_timeout = null;
this.rendering_engine = new FormRenderingEngine(this);
- self.set({actual_mode: self.options.initial_mode});
+ this.set({actual_mode: this.options.initial_mode});
this.has_been_loaded.done(function() {
self._build_onchange_specs();
- self.check_actual_mode();
- self.on("change:actual_mode", self, self.check_actual_mode);
self.on("change:actual_mode", self, self.toggle_buttons);
self.on("change:actual_mode", self, self.toggle_sidebar);
- self.on("change:actual_mode", self, self.do_update_pager);
});
self.on("load_record", self, self.load_record);
core.bus.on('clear_uncommitted_changes', this, function(chain_callbacks) {
@@ -90,8 +100,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.rendering_engine.set_tags_registry(this.tags_registry);
this.rendering_engine.set_widgets_registry(this.widgets_registry);
this.rendering_engine.set_fields_view(this.fields_view);
- var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
- this.rendering_engine.render_to($dest);
+ this.rendering_engine.render_to(this.$el);
this.$el.on('mousedown.formBlur', function () {
self.__clicked_inside = true;
@@ -100,22 +109,14 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.has_been_loaded.resolve();
// Add bounce effect on button 'Edit' when click on readonly page view.
- this.$el.find(".oe_form_group_row,.oe_form_field,label,h1,.oe_title,.oe_notebook_page, .oe_list_content").on('click', function (e) {
+ this.$(".oe_title,.o_group").on('mouseup', function (e) { // 'mouseup' event because some widget need bootstrap click event to go up to body
if(self.get("actual_mode") === "view" && self.$buttons && !$(e.target).is('[data-toggle]')) {
- var $button = self.$buttons.find(".oe_form_button_edit");
+ var $button = self.$buttons.find(".o_form_button_edit");
$button.openerpBounce();
e.stopPropagation();
core.bus.trigger('click', e);
}
});
- //bounce effect on red button when click on statusbar.
- this.$el.find(".oe_form_field_status:not(.oe_form_status_clickable)").on('click', function (e) {
- if((self.get("actual_mode") === "view")) {
- var $button = self.$el.find(".oe_highlight:not(.o_form_invisible)").css({'float':'left','clear':'none'});
- $button.openerpBounce();
- e.stopPropagation();
- }
- });
return this._super();
},
destroy: function() {
@@ -133,10 +134,10 @@ var FormView = View.extend(common.FieldManagerMixin, {
* Set this.$buttons with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the FormView inserts them into this.options.$buttons
- * or into a div of its template
+ * if it exists
*/
render_buttons: function($node) {
- this.$buttons = $('<div/>', {'class': 'oe_form_buttons'});
+ this.$buttons = $('<div/>');
var $footer = this.$('footer');
if (this.options.action_buttons !== false || this.options.footer_to_buttons && $footer.children().length === 0) {
@@ -148,23 +149,17 @@ var FormView = View.extend(common.FieldManagerMixin, {
// Show or hide the buttons according to the view mode
this.toggle_buttons();
- this.$buttons.on('click', '.oe_form_button_create', this.on_button_create);
- this.$buttons.on('click', '.oe_form_button_edit', this.on_button_edit);
- this.$buttons.on('click', '.oe_form_button_save', this.on_button_save);
- this.$buttons.on('click', '.oe_form_button_cancel', this.on_button_cancel);
+ this.$buttons.on('click', '.o_form_button_create', this.on_button_create);
+ this.$buttons.on('click', '.o_form_button_edit', this.on_button_edit);
+ this.$buttons.on('click', '.o_form_button_save', this.on_button_save);
+ this.$buttons.on('click', '.o_form_button_cancel', this.on_button_cancel);
- if ($node) {
- this.$buttons.appendTo($node);
- } else {
- this.$('.oe_form_buttons').replaceWith(this.$buttons);
- }
+ this.$buttons.appendTo($node);
},
/**
* Instantiate and render the sidebar if a sidebar is requested
* Sets this.sidebar
* @param {jQuery} [$node] a jQuery node where the sidebar should be inserted
- * $node may be undefined, in which case the FormView inserts the sidebar in a
- * div of its template
**/
render_sidebar: function($node) {
if (!this.sidebar && this.options.sidebar) {
@@ -177,7 +172,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.is_action_enabled('create') && { label: _t('Duplicate'), callback: this.on_button_duplicate }
]));
- $node = $node || this.$('.oe_form_sidebar');
this.sidebar.appendTo($node);
// Show or hide the sidebar according to the view mode
@@ -185,41 +179,33 @@ var FormView = View.extend(common.FieldManagerMixin, {
}
},
/**
- * Render the pager according to the FormView.pager template and add listeners on it.
- * Set this.$pager with the produced jQuery element
- * @param {jQuery} [$node] a jQuery node where the rendered pager should be inserted
+ * Instantiate and render the pager and add listeners on it.
+ * Set this.pager
+ * @param {jQuery} [$node] a jQuery node where the pager should be inserted
* $node may be undefined, in which case the FormView inserts the pager into this.options.$pager
- * or into a div of its template
*/
render_pager: function($node) {
if (this.options.pager) {
var self = this;
- if (this.$pager) {
- this.$pager.remove();
- }
- if (this.get("actual_mode") === "create") {
- return;
- }
- this.$pager = $(QWeb.render("FormView.pager", {'widget': self}));
- this.$pager.on('click','a[data-pager-action]',function() {
- var $el = $(this);
- if ($el.attr("disabled")) {
- return;
- }
- var action = $el.data('pager-action');
- var def = $.when(self.execute_pager_action(action));
- $el.attr("disabled");
- def.always(function() {
- $el.removeAttr("disabled");
+ var options = {
+ validate: _.bind(this.can_be_discarded, this),
+ };
+
+ this.pager = new Pager(this, this.dataset.ids.length, this.dataset.index + 1, 1, options);
+ this.pager.on('pager_changed', this, function (new_state) {
+ this.pager.disable();
+ this.dataset.index = new_state.current_min - 1;
+ this.trigger('pager_action_executed');
+ $.when(this.reload()).then(function () {
+ self.pager.enable();
});
});
- this.do_update_pager();
- $node = $node || this.options.$pager;
- if ($node) {
- this.$pager.appendTo($node);
- } else {
- this.$('.oe_form_pager').replaceWith(this.$pager);
+ this.pager.appendTo($node = $node || this.options.$pager);
+
+ // Hide the pager in create mode
+ if (this.get("actual_mode") === "create") {
+ this.pager.do_hide();
}
}
},
@@ -241,6 +227,16 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.sidebar.do_toggle(this.get("actual_mode") === "view");
}
},
+ update_pager: function() {
+ if (this.pager) {
+ // Hide the pager in create mode
+ if (this.get("actual_mode") === "create") {
+ this.pager.do_hide();
+ } else {
+ this.pager.update_state({size: this.dataset.ids.length, current_min: this.dataset.index + 1});
+ }
+ }
+ },
widgetFocused: function() {
// Clear click flag if used to focus a widget
this.__clicked_inside = false;
@@ -281,11 +277,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
do_show: function (options) {
var self = this;
options = options || {};
- this._super();
- this.$el.css({
- opacity: '0',
- filter: 'alpha(opacity = 0)'
- });
this.$el.removeClass('oe_form_dirty');
var shown = this.has_been_loaded;
@@ -307,10 +298,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
}
return $.when(shown, this._super()).then(function() {
self._actualize_mode(options.mode || self.options.initial_mode);
- self.$el.css({
- opacity: '1',
- filter: 'alpha(opacity = 100)'
- });
core.bus.trigger('form_view_shown', self);
});
},
@@ -324,6 +311,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.datarecord = record;
this._actualize_mode();
this.set({ 'title' : record.id ? record.display_name : _t("New") });
+ this.update_pager();
this.record_loaded = $.Deferred();
_(this.fields).each(function (field, f) {
@@ -342,19 +330,17 @@ var FormView = View.extend(common.FieldManagerMixin, {
self.rendering_engine.init_fields().then(function() {
self.is_initialized.resolve();
self.record_loaded.resolve();
- self.do_update_pager(record.id === null || record.id === undefined);
if (self.sidebar) {
- self.sidebar.do_attachement_update(self.dataset, self.datarecord.id);
+ self.sidebar.do_attachement_update(self.dataset, self.datarecord.id);
}
if (record.id) {
self.do_push_state({id:record.id});
} else {
self.do_push_state({});
}
- self.$el.removeClass('oe_form_dirty');
- self.autofocus();
+ self.$el.removeClass('oe_form_dirty');
});
- });
+ });
},
/**
* Loads and sets up the default values for the model as the current
@@ -370,7 +356,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
self.trigger('load_record', _.clone(r));
});
}
- return self.trigger('load_record', {});
+ return $.when().then(this.trigger.bind(this, 'load_record', {}));
},
on_form_changed: function() {
this.trigger("view_content_has_changed");
@@ -378,40 +364,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
do_notify_change: function() {
this.$el.addClass('oe_form_dirty');
},
- execute_pager_action: function(action) {
- var self = this;
- return this.can_be_discarded().then(function() {
- switch (action) {
- case 'first':
- self.dataset.index = 0;
- break;
- case 'previous':
- self.dataset.previous();
- break;
- case 'next':
- self.dataset.next();
- break;
- case 'last':
- self.dataset.index = self.dataset.ids.length - 1;
- break;
- }
- self.trigger('pager_action_executed');
- return self.reload();
- });
- },
- do_update_pager: function(hide_index) {
- if (this.$pager) {
- // Hide the pager in create mode or when there is only one record
- var pager_visible = (this.get("actual_mode") !== "create") && (this.dataset.ids.length > 1);
- this.$pager.toggle(pager_visible);
- if (hide_index === true) {
- this.$pager.find(".oe_form_pager_state").html("");
- } else {
- this.$pager.find(".oe_form_pager_state").html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
- }
- }
- },
-
_build_onchange_specs: function() {
var self = this;
var find = function(field_name, root) {
@@ -664,15 +616,15 @@ var FormView = View.extend(common.FieldManagerMixin, {
} else if (mode === "create") {
mode = "edit";
}
+
+ var viewMode = (mode === "view");
+ this.$el.toggleClass('o_form_readonly', viewMode).toggleClass('o_form_editable', !viewMode);
+
this.render_value_defs = [];
this.set({actual_mode: mode});
- },
- check_actual_mode: function(source, options) {
- if(this.get("actual_mode") === "view") {
- this.$el.removeClass('oe_form_editable').addClass('oe_form_readonly');
- } else {
- this.$el.removeClass('oe_form_readonly').addClass('oe_form_editable');
- _.defer(_.bind(this.autofocus, this));
+
+ if(!viewMode) {
+ this.autofocus();
}
},
autofocus: function() {
@@ -699,7 +651,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.$('.oe_form_buttons').add(this.$buttons).find('button.o_disabled').removeClass('o_disabled').prop('disabled', false);
this.is_disabled = false;
},
- on_button_save: function(e) {
+ on_button_save: function() {
var self = this;
if (this.is_disabled) {
return;
@@ -716,7 +668,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
self.enable_button();
});
},
- on_button_cancel: function(event) {
+ on_button_cancel: function() {
var self = this;
this.can_be_discarded().then(function() {
if (self.get('actual_mode') === 'create') {
@@ -733,10 +685,11 @@ var FormView = View.extend(common.FieldManagerMixin, {
},
on_button_new: function() {
var self = this;
- this.to_edit_mode();
return $.when(this.has_been_loaded).then(function() {
return self.can_be_discarded().then(function() {
- return self.load_defaults();
+ return self.load_defaults().then(function() {
+ self.to_edit_mode();
+ });
});
});
},
@@ -763,7 +716,8 @@ var FormView = View.extend(common.FieldManagerMixin, {
if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) {
self.dataset.unlink([self.datarecord.id]).done(function() {
if (self.dataset.size()) {
- self.execute_pager_action('next');
+ self.reload();
+ self.update_pager();
} else {
self.do_action('history_back');
}
@@ -790,7 +744,9 @@ var FormView = View.extend(common.FieldManagerMixin, {
title: _t("Warning"),
confirm_callback: function() {
self.$el.removeClass('oe_form_dirty');
- def.resolve();
+ this.on('closed', null, function() { // 'this' is the dialog widget
+ def.resolve();
+ });
},
cancel_callback: function() {
def.reject();
@@ -951,7 +907,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids));
this.dataset.index = 0;
}
- this.do_update_pager();
+ this.update_pager();
if (this.sidebar) {
this.sidebar.do_attachement_update(this.dataset, this.datarecord.id);
}
@@ -1090,7 +1046,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
$box.remove();
});
// add content inside notification box on top of the sheet/form
- var $target = this.$('.oe_form_sheet').length ? this.$('.oe_form_sheet') : this.$el;
+ var $target = this.$('.o_form_sheet_bg').length ? this.$('.o_form_sheet_bg') : this.$el;
$target.prepend($box);
},
open_defaults_dialog: function () {
@@ -1153,7 +1109,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
var $defaults = d.$el.find('#formview_default_fields');
var field_to_set = $defaults.val();
if (!field_to_set) {
- $defaults.parent().addClass('oe_form_invalid');
+ $defaults.parent().addClass('o_form_invalid');
return;
}
var condition = d.$el.find('#formview_default_conditions').val(),
@@ -1234,21 +1190,29 @@ var FormView = View.extend(common.FieldManagerMixin, {
},
});
-/**
- * Interface to be implemented by rendering engines for the form view.
- */
-var FormRenderingEngineInterface = Class.extend({
- set_fields_view: function(fields_view) {},
- set_fields_registry: function(fields_registry) {},
- render_to: function($el) {},
+core.view_registry.add('form', FormView);
+
+return FormView;
+
});
+
+odoo.define('web.FormRenderingEngine', function (require) {
+"use strict";
+
+var common = require('web.form_common');
+var core = require('web.core');
+var utils = require('web.utils');
+
+var _t = core._t;
+var QWeb = core.qweb;
+
/**
* Default rendering engine for the form view.
*
* It is necessary to set the view using set_view() before usage.
*/
-var FormRenderingEngine = FormRenderingEngineInterface.extend({
+return core.Class.extend({
init: function(view) {
this.view = view;
},
@@ -1289,7 +1253,7 @@ var FormRenderingEngine = FormRenderingEngineInterface.extend({
$('board', doc).each(function() {
$(this).attr('layout', $(this).attr('style'));
});
- return $('<div class="oe_form"/>').append(utils.xml_to_str(doc));
+ return $(utils.xml_to_str(doc));
},
render_to: function($target) {
var self = this;
@@ -1305,7 +1269,8 @@ var FormRenderingEngine = FormRenderingEngineInterface.extend({
this.labels = {};
this.process(this.$form);
- this.$form.appendTo(this.$target);
+ this.$form.contents().appendTo(this.$target);
+ this.handle_common_properties(this.$target, this.$form);
this.to_replace = [];
@@ -1382,9 +1347,6 @@ var FormRenderingEngine = FormRenderingEngineInterface.extend({
args[0] = $tag;
return fn.apply(self, args);
} else {
- if( tagname === 'header') {
- $tag.addClass('o_statusbar_buttons');
- }
// generic tag handling, just process children
$tag.children().each(function() {
self.process($(this));
@@ -1408,14 +1370,14 @@ var FormRenderingEngine = FormRenderingEngineInterface.extend({
process_sheet: function($sheet) {
var $new_sheet = this.render_element('FormRenderingSheet', $sheet.getAttributes());
this.handle_common_properties($new_sheet, $sheet);
- var $dst = $new_sheet.find('.oe_form_sheet');
+ var $dst = $new_sheet.find('.o_form_sheet');
$sheet.contents().appendTo($dst);
$sheet.before($new_sheet).remove();
this.process($new_sheet);
},
process_form: function($form) {
if ($form.find('> sheet').length === 0) {
- $form.addClass('oe_form_nosheet');
+ $form.addClass('o_form_nosheet');
}
var $new_form = this.render_element('FormRenderingForm', $form.getAttributes());
this.handle_common_properties($new_form, $form);
@@ -1479,119 +1441,99 @@ var FormRenderingEngine = FormRenderingEngineInterface.extend({
},
process_group: function($group) {
var self = this;
- $group.children('field').each(function() {
- self.preprocess_field($(this));
- });
- var $new_group = this.render_element('FormRenderingGroup', $group.getAttributes());
- var $table;
- if ($new_group.first().is('table.oe_form_group')) {
- $table = $new_group;
- } else if ($new_group.filter('table.oe_form_group').length) {
- $table = $new_group.filter('table.oe_form_group').first();
- } else {
- $table = $new_group.find('table.oe_form_group').first();
- }
- var $tr, $td,
- cols = parseInt($group.attr('col') || 2, 10),
- row_cols = cols;
+ // Processes a group containing nested groups
+ // Renders a div for each nested group, into a div that is the outer group
+ // The nested groups's width is a percentage according to col and colspan attributes
+ var _process_outer_group = function(col) {
+ var $new_group = self.render_element('FormRenderingOuterGroup', $group.getAttributes());
+ $group.children().each(function() {
+ var $child = $(this);
+ var tagName = $child[0].tagName.toLowerCase();
+ var colspan = parseInt($child.attr('colspan') || 1, 10);
- var children = [];
- $group.children().each(function() {
- var $child = $(this);
- var colspan = parseInt($child.attr('colspan') || 1, 10);
- var tagName = $child[0].tagName.toLowerCase();
- var $td = $('<td/>').addClass('oe_form_group_cell').attr('colspan', colspan);
- var newline = tagName === 'newline';
-
- // Note FME: those classes are used in layout debug mode
- if ($tr && row_cols > 0 && (newline || row_cols < colspan)) {
- $tr.addClass('oe_form_group_row_incomplete');
- if (newline) {
- $tr.addClass('oe_form_group_row_newline');
+ if (tagName === 'newline') {
+ $new_group.append($('<br>')); // Skip to the next line
+ return;
}
- }
- if (newline) {
- $tr = null;
- return;
- }
- if (!$tr || row_cols < colspan) {
- $tr = $('<tr/>').addClass('oe_form_group_row').appendTo($table);
- row_cols = cols;
- } else if (tagName==='group') {
- // When <group> <group/><group/> </group>, we need a spacing between the two groups
- $td.addClass('oe_group_right');
- }
- row_cols -= colspan;
+ // compute child's classname from col and colspan attributes
+ $child.addClass('o_group_col_' + Math.min(Math.floor(colspan/col*12), 12));
+ $new_group.append($child);
- // invisibility transfer
- var field_modifiers = JSON.parse($child.attr('modifiers') || '{}');
- var invisible = field_modifiers.invisible;
- self.handle_common_properties($td, $("<dummy>").attr("modifiers", JSON.stringify({invisible: invisible})));
+ children.push($child[0]);
+ });
+ return $new_group;
+ };
+ // Processes a group containing no nested group
+ // Renders an HTML table
+ var _process_inner_group = function(col) {
+ var $new_group = self.render_element('FormRenderingInnerGroup', $group.getAttributes());
+ $new_group.find('td').attr('colspan', col); // Group's title spans all column
+ var row_col = col;
+ var $row = null;
- $tr.append($td.append($child));
- children.push($child[0]);
- });
- if (row_cols && $td) {
- $td.attr('colspan', parseInt($td.attr('colspan'), 10) + row_cols);
- }
- $group.before($new_group).remove();
+ $group.children().each(function() {
+ var $child = $(this);
+ var tagName = $child[0].tagName.toLowerCase();
+ var colspan = parseInt($child.attr('colspan') || 1, 10);
- $table.find('> tbody > tr').each(function() {
- var to_compute = [],
- row_cols = cols,
- total = 100;
- $(this).children().each(function() {
- var $td = $(this),
- $child = $td.children(':first');
- if ($child.attr('cell-class')) {
- $td.addClass($child.attr('cell-class'));
+ if (tagName === 'newline') {
+ $row = null; // Start with a new row
+ return;
}
- switch ($child[0].tagName.toLowerCase()) {
- case 'separator':
- break;
- case 'label':
- if ($child.attr('for')) {
- $td.attr('width', '1%').addClass('oe_form_group_cell_label');
- row_cols-= $td.attr('colspan') || 1;
- total--;
- }
- break;
- default:
- var width = _.str.trim($child.attr('width') || ''),
- iwidth = parseInt(width, 10);
- if (iwidth) {
- if (width.substr(-1) === '%') {
- total -= iwidth;
- width = iwidth + '%';
- } else {
- // Absolute width
- $td.css('min-width', width + 'px');
- }
- $td.attr('width', width);
- $child.removeAttr('width');
- row_cols-= $td.attr('colspan') || 1;
- } else {
- to_compute.push($td);
- }
-
+ row_col -= colspan;
+ if (!$row || row_col < 0) { // Append a new row to the group
+ $row = $('<tr>').appendTo($new_group);
+ row_col = col-colspan;
}
+ $('<td>').attr('colspan', colspan).append($child).appendTo($row);
+
+ children.push($child[0]);
});
- if (row_cols) {
- var unit = Math.floor(total / row_cols);
- if (!$(this).is('.oe_form_group_row_incomplete')) {
- _.each(to_compute, function($td) {
- var width = parseInt($td.attr('colspan'), 10) * unit;
- $td.attr('width', width + '%');
- total -= width;
- });
- }
- }
+
+ // Some kind of an hack
+ // Add width on table's cell to avoid automatic layout of table which
+ // makes first col to take full width available (typically, first col contains label)
+ $new_group.find('tr').each(function() {
+ var $tr = $(this);
+ var nonlabel_width = 100/(col - $tr.find('td > label:first-child').length);
+ $tr.find('td').each(function() {
+ var $td = $(this);
+ var $child = $td.children(':first');
+ if ($child.is("label")) {
+ $td.addClass("o_td_label");
+ } else {
+ $td.css("width", (nonlabel_width*parseInt($td.attr('colspan')))+"%");
+ }
+ });
+
+ });
+ return $new_group;
+ };
+
+ // Preprocess field children
+ $group.children('field').each(function() {
+ self.preprocess_field($(this));
});
+
+ // Process group
+ var nested_groups = $group.children('group').length; // Detect possible nested groups
+ var children = [];
+ var col = parseInt($group.attr('col') || 2, 10);
+ var $new_group;
+ if (nested_groups) {
+ $new_group = _process_outer_group(col);
+ } else {
+ $new_group = _process_inner_group(col);
+ }
+ this.handle_common_properties($new_group, $group);
+
+ // Process group's children
+ $group.before($new_group).remove();
_.each(children, function(el) {
self.process($(el));
});
- this.handle_common_properties($new_group, $group);
+
return $new_group;
},
process_notebook: function($notebook) {
@@ -1658,17 +1600,8 @@ var FormRenderingEngine = FormRenderingEngineInterface.extend({
var dict = {
string: $label.attr('string') || (field_orm || {}).string || '',
help: $label.attr('help') || (field_orm || {}).help || '',
- _for: name ? _.uniqueId('oe-field-input-') : undefined,
+ _for: name ? _.uniqueId('o_field_input_') : undefined,
};
- var align = parseFloat(dict.align);
- if (isNaN(align) || align === 1) {
- align = 'right';
- } else if (align === 0) {
- align = 'left';
- } else {
- align = 'center';
- }
- dict.align = align;
var $new_label = this.render_element('FormRenderingLabel', dict);
$label.before($new_label).remove();
this.handle_common_properties($new_label, $label);
@@ -1698,8 +1631,4 @@ var FormRenderingEngine = FormRenderingEngineInterface.extend({
}
});
-core.view_registry.add('form', FormView);
-
-return FormView;
-
});
diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js
index 2a2a36cdaba..2aeaaa17111 100644
--- a/addons/web/static/src/js/views/form_widgets.js
+++ b/addons/web/static/src/js/views/form_widgets.js
@@ -5,17 +5,17 @@ var core = require('web.core');
var crash_manager = require('web.crash_manager');
var data = require('web.data');
var datepicker = require('web.datepicker');
+var dom_utils = require('web.dom_utils');
+var Priority = require('web.Priority');
var ProgressBar = require('web.ProgressBar');
var Dialog = require('web.Dialog');
var common = require('web.form_common');
var formats = require('web.formats');
var framework = require('web.framework');
var Model = require('web.DataModel');
-var Priority = require('web.Priority');
var pyeval = require('web.pyeval');
var session = require('web.session');
var utils = require('web.utils');
-var dom_utils = require('web.dom_utils');
var _t = core._t;
var QWeb = core.qweb;
@@ -119,24 +119,25 @@ var WidgetButton = common.FormWidget.extend({
var FieldChar = common.AbstractField.extend(common.ReinitializeFieldMixin, {
template: 'FieldChar',
- widget_class: 'oe_form_field_char',
events: {
- 'change input': 'store_dom_value',
+ 'change': 'store_dom_value',
},
init: function (field_manager, node) {
this._super(field_manager, node);
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
},
initialize_content: function() {
- this.setupFocus(this.$('input'));
+ if(!this.get('effective_readonly') && !this.$input) {
+ this.$input = this.$el;
+ }
+ this.setupFocus(this.$el);
+ },
+ destroy_content: function() {
+ this.$input = undefined;
},
store_dom_value: function () {
- if (!this.get('effective_readonly')
- && this.$('input').length
- && this.is_syntax_valid()) {
- this.internal_set_value(
- this.parse_value(
- this.$('input').val()));
+ if (this.$input && this.is_syntax_valid()) {
+ this.internal_set_value(this.parse_value(this.$input.val()));
}
},
commit_value: function () {
@@ -145,20 +146,19 @@ var FieldChar = common.AbstractField.extend(common.ReinitializeFieldMixin, {
},
render_value: function() {
var show_value = this.format_value(this.get('value'), '');
- if (!this.get("effective_readonly")) {
- this.$el.find('input').val(show_value);
+ if (this.$input) {
+ this.$input.val(show_value);
} else {
if (this.password) {
show_value = new Array(show_value.length + 1).join('*');
}
- this.$(".oe_form_char_content").text(show_value);
+ this.$el.text(show_value);
}
},
is_syntax_valid: function() {
- if (!this.get("effective_readonly") && this.$("input").size() > 0) {
+ if (this.$input) {
try {
- this.parse_value(this.$('input').val(), '');
- return true;
+ this.parse_value(this.$input.val(), '');
} catch(e) {
return false;
}
@@ -175,31 +175,30 @@ var FieldChar = common.AbstractField.extend(common.ReinitializeFieldMixin, {
return this.get('value') === '' || this._super();
},
focus: function() {
- var input = this.$('input:first')[0];
- return input ? input.focus() : false;
- },
- set_dimensions: function (height, width) {
- this._super(height, width);
- this.$('input').css({
- height: height,
- width: width
- });
- }
+ if (this.$input) {
+ return this.$input.focus();
+ }
+ return false;
+ },
});
-var KanbanSelection = FieldChar.extend({
+var KanbanSelection = common.AbstractField.extend({
template: "FormSelection",
- init: function (field_manager, node) {
- this._super(field_manager, node);
+ events: {
+ 'click a': function(e) {
+ e.preventDefault();
+ },
+ 'mouseup a': function(e) {
+ e.stopPropagation();
+ },
+ 'click li': 'set_kanban_selection'
},
start: function () {
- var self = this;
- this.states = [];
- this._super.apply(this, arguments);
// hook on form view content changed: recompute the states, because it may be related to the current stage
- this.getParent().on('view_content_has_changed', self, function () {
- self.render_value();
+ this.view.on('view_content_has_changed', this, function () {
+ this.render_value();
});
+ return this._super();
},
prepare_dropdown_selection: function() {
var self = this;
@@ -233,6 +232,7 @@ var KanbanSelection = FieldChar.extend({
this._super();
this.states = this.prepare_dropdown_selection();
var self = this;
+
// Adapt "FormSelection"
var current_state = _.find(this.states, function(state) {
return state.name === self.get('value');
@@ -248,7 +248,6 @@ var KanbanSelection = FieldChar.extend({
var $dropdown = this.$el.find('.dropdown-menu');
$dropdown.children().remove(); // remove old items
$items.appendTo($dropdown);
- this.$el.find('a').on('click', this.set_kanban_selection.bind(this));
},
/* setting the value: in view mode, perform an asynchronous call and reload
the form view; in edit mode, use set_value to save the new value that will
@@ -259,7 +258,7 @@ var KanbanSelection = FieldChar.extend({
var li = $(ev.target).closest('li');
if (li.length) {
var value = String(li.data('value'));
- if (this.view.get('actual_mode') == 'view') {
+ if (this.view.get('actual_mode') === 'view') {
var write_values = {};
write_values[self.name] = value;
return this.view.dataset._model.call(
@@ -339,66 +338,35 @@ var FieldID = FieldChar.extend({
var FieldEmail = FieldChar.extend({
template: 'FieldEmail',
- initialize_content: function() {
- this._super();
- var $button = this.$el.find('button');
- $button.click(this.on_button_clicked);
- this.setupFocus($button);
+ prefix: 'mailto',
+ init: function() {
+ this._super.apply(this, arguments);
+ this.clickable = true;
},
render_value: function() {
- if (!this.get("effective_readonly")) {
- this._super();
- } else {
- this.$el.find('a')
- .attr('href', 'mailto:' + this.get('value'))
- .text(this.get('value') || '');
- }
- },
- on_button_clicked: function() {
- if (!this.get('value') || !this.is_syntax_valid()) {
- this.do_warn(_t("E-mail Error"), _t("Can't send email to invalid e-mail address"));
- } else {
- location.href = 'mailto:' + this.get('value');
+ this._super();
+ if (this.get("effective_readonly") && this.clickable) {
+ this.$el.attr('href', this.prefix + ':' + this.get('value'));
}
}
});
-var FieldUrl = FieldChar.extend({
- template: 'FieldUrl',
- initialize_content: function() {
- this._super();
- var $button = this.$el.find('button');
- $button.click(this.on_button_clicked);
- this.setupFocus($button);
- },
+var FieldUrl = FieldEmail.extend({
render_value: function() {
- if (!this.get("effective_readonly")) {
- this._super();
- } else {
+ this._super();
+ if(this.get("effective_readonly")) {
var tmp = this.get('value');
var s = /(\w+):(.+)|^\.{0,2}\//.exec(tmp);
if (!s) {
tmp = "http://" + this.get('value');
}
var text = this.get('value') ? this.node.attrs.text || tmp : '';
- this.$el.find('a').attr('href', tmp).text(text);
- }
- },
- on_button_clicked: function() {
- if (!this.get('value')) {
- this.do_warn(_t("Resource Error"), _t("This resource is empty"));
- } else {
- var url = $.trim(this.get('value'));
- if(/^www\./i.test(url))
- url = 'http://'+url;
- window.open(url);
+ this.$el.attr('href', tmp).text(text);
}
}
});
var FieldFloat = FieldChar.extend({
- is_field_number: true,
- widget_class: 'oe_form_field_float',
init: function (field_manager, node) {
this._super(field_manager, node);
this.internal_set_value(0);
@@ -408,19 +376,18 @@ var FieldFloat = FieldChar.extend({
this.digits = this.field.digits;
}
},
+ initialize_content: function() {
+ this._super();
+ this.$el.addClass('o_form_field_number');
+ },
set_value: function(value_) {
if (value_ === false || value_ === undefined) {
- // As in GTK client, floats default to 0
- value_ = 0;
+ value_ = 0; // As in GTK client, floats default to 0
}
if (this.digits !== undefined && this.digits.length === 2) {
value_ = utils.round_decimals(value_, this.digits[1]);
}
- this._super.apply(this, [value_]);
- },
- focus: function () {
- var $input = this.$('input:first');
- return $input.length ? $input.select() : false;
+ this._super(value_);
}
});
@@ -474,7 +441,7 @@ var FieldCharDomain = common.AbstractField.extend(common.ReinitializeFieldMixin,
} else {
this.$('.o_count').text(_t('No selected record'));
var $arrow = this.$('button span').detach();
- this.$('button').text(_('Select records ')).append($("<span/>").addClass('fa fa-arrow-right'));
+ this.$('button').text(_t('Select records ')).append($("<span/>").addClass('fa fa-arrow-right'));
}
},
on_click: function(event) {
@@ -497,60 +464,58 @@ var FieldCharDomain = common.AbstractField.extend(common.ReinitializeFieldMixin,
},
});
-var FieldDatetime = common.AbstractField.extend(common.ReinitializeFieldMixin, {
- template: "FieldDatetime",
+var FieldDate = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ tagName: "span",
+ className: "o_form_field_date",
build_widget: function() {
- return new datepicker.DateTimeWidget(this);
+ return new datepicker.DateWidget(this);
},
- destroy_content: function() {
+ initialize_content: function() {
if (this.datewidget) {
this.datewidget.destroy();
this.datewidget = undefined;
}
- },
- initialize_content: function() {
+
if (!this.get("effective_readonly")) {
this.datewidget = this.build_widget();
- this.datewidget.on('datetime_changed', this, _.bind(function() {
+ this.datewidget.on('datetime_changed', this, function() {
this.internal_set_value(this.datewidget.get_value());
- }, this));
- this.datewidget.appendTo(this.$el);
- this.setupFocus(this.datewidget.$input);
+ });
+
+ var self = this;
+ this.datewidget.appendTo('<div>').done(function() {
+ self.datewidget.$el.addClass(self.$el.attr('class'));
+ self.replaceElement(self.datewidget.$el);
+ self.datewidget.$input.addClass('o_form_input');
+ self.setupFocus(self.datewidget.$input);
+ });
}
},
render_value: function() {
- if (!this.get("effective_readonly")) {
- this.datewidget.set_value(this.get('value'));
- } else {
+ if (this.get("effective_readonly")) {
this.$el.text(formats.format_value(this.get('value'), this, ''));
+ } else {
+ this.datewidget.set_value(this.get('value'));
}
},
is_syntax_valid: function() {
- if (!this.get("effective_readonly") && this.datewidget) {
- return this.datewidget.is_valid();
- }
- return true;
+ return this.get("effective_readonly") || !this.datewidget || this.datewidget.is_valid();
},
is_false: function() {
return this.get('value') === '' || this._super();
},
focus: function() {
- var input = this.datewidget && this.datewidget.$input[0];
- return input ? input.focus() : false;
- },
- set_dimensions: function (height, width) {
- this._super(height, width);
if (!this.get("effective_readonly")) {
- this.datewidget.$input.css('height', height);
+ return this.datewidget.$input.focus();
}
- }
+ return false;
+ },
});
-var FieldDate = FieldDatetime.extend({
- template: "FieldDate",
+var FieldDatetime = FieldDate.extend({
build_widget: function() {
- return new datepicker.DateWidget(this);
- }
+ return new datepicker.DateTimeWidget(this);
+ },
});
var FieldText = common.AbstractField.extend(common.ReinitializeFieldMixin, {
@@ -566,46 +531,37 @@ var FieldText = common.AbstractField.extend(common.ReinitializeFieldMixin, {
e.stopPropagation();
}
},
- 'change textarea': 'store_dom_value',
+ 'change': 'store_dom_value',
},
initialize_content: function() {
- if (! this.get("effective_readonly")) {
- this.$textarea = this.$el.find('textarea');
+ if (!this.get("effective_readonly")) {
this.auto_sized = false;
- this.default_height = this.$textarea.css('height');
- if (this.default_height === '0px') this.default_height = '90px';
- if (this.get("effective_readonly")) {
- this.$textarea.attr('disabled', 'disabled');
- }
- this.setupFocus(this.$textarea);
- } else {
- this.$textarea = undefined;
+ this.setupFocus(this.$el);
}
},
commit_value: function () {
- if (! this.get("effective_readonly") && this.$textarea) {
+ if (!this.get("effective_readonly")) {
this.store_dom_value();
}
return this._super();
},
store_dom_value: function () {
- this.internal_set_value(formats.parse_value(this.$textarea.val(), this));
+ this.internal_set_value(formats.parse_value(this.$el.val(), this));
},
render_value: function() {
- if (! this.get("effective_readonly")) {
- var show_value = formats.format_value(this.get('value'), this, '');
- this.$textarea.val(show_value);
- dom_utils.autoresize(this.$textarea, {parent: this, min_height: parseInt(this.default_height)});
- } else {
+ if (this.get("effective_readonly")) {
var txt = this.get("value") || '';
- this.$(".oe_form_text_content").text(txt);
+ this.$el.text(txt);
+ } else {
+ var show_value = formats.format_value(this.get('value'), this, '');
+ this.$el.val(show_value);
+ dom_utils.autoresize(this.$el, {parent: this});
}
},
is_syntax_valid: function() {
- if (!this.get("effective_readonly") && this.$textarea) {
+ if (!this.get("effective_readonly")) {
try {
- formats.parse_value(this.$textarea.val(), this, '');
- return true;
+ formats.parse_value(this.$el.val(), this, '');
} catch(e) {
return false;
}
@@ -616,51 +572,48 @@ var FieldText = common.AbstractField.extend(common.ReinitializeFieldMixin, {
return this.get('value') === '' || this._super();
},
focus: function($el) {
- var input = !this.get("effective_readonly") && this.$textarea && this.$textarea[0];
- return input ? input.focus() : false;
- },
- set_dimensions: function (height, width) {
- this._super(height, width);
- if (!this.get("effective_readonly") && this.$textarea) {
- this.$textarea.css({
- width: width,
- minHeight: height
- });
+ if(!this.get("effective_readonly")) {
+ return this.$el.focus();
}
+ return false;
+ },
+ set_dimensions: function(height, width) {
+ this.$el.css({
+ width: width,
+ minHeight: height,
+ });
},
});
var FieldBoolean = common.AbstractField.extend({
template: 'FieldBoolean',
+ events: {
+ 'click': function() {
+ this.internal_set_value(this.$checkbox.prop('checked'));
+ }
+ },
start: function() {
- var self = this;
- this.$checkbox = $("input", this.$el);
+ this.$checkbox = this.$('input');
+
+ this.$checkbox.prop('disabled', this.get("effective_readonly"));
+ this.on("change:effective_readonly", this, function() {
+ this.$checkbox.prop('disabled', this.get("effective_readonly"));
+ });
+
this.setupFocus(this.$checkbox);
- this.$el.click(_.bind(function() {
- this.internal_set_value(this.$checkbox.is(':checked'));
- }, this));
- var check_readonly = function() {
- self.$checkbox.prop('disabled', self.get("effective_readonly"));
- self.click_disabled_boolean();
- };
- this.on("change:effective_readonly", this, check_readonly);
- check_readonly.call(this);
- this._super.apply(this, arguments);
+
+ return this._super();
},
render_value: function() {
- this.$checkbox[0].checked = this.get('value');
+ this.$checkbox.prop('checked', this.get('value'));
},
focus: function() {
- var input = this.$checkbox && this.$checkbox[0];
- return input ? input.focus() : false;
- },
- click_disabled_boolean: function(){
- var $disabled = this.$el.find('input[type=checkbox]:disabled');
- $disabled.each(function (){
- $(this).next('div').remove();
- $(this).closest("span").append($('<div class="boolean"></div>'));
- });
- }
+ return this.$checkbox.focus();
+ },
+ set_dimensions: function(height, width) {}, // Checkboxes have a fixed height and width (even in list editable)
+ is_false: function() {
+ return false;
+ },
});
/**
@@ -706,9 +659,7 @@ var FieldBooleanButton = common.AbstractField.extend({
},
});
-/**
- The progressbar field expect a float from 0 to 100.
-*/
+// The progressbar field expects a float from 0 to 100.
var FieldProgressBar = common.AbstractField.extend(common.ReinitializeFieldMixin, {
initialize_content: function() {
if(this.progressbar) {
@@ -734,96 +685,41 @@ var FieldProgressBar = common.AbstractField.extend(common.ReinitializeFieldMixin
render_value: function() {
this.progressbar.set_value(this.get('value'));
},
+ is_false: function() {
+ return false;
+ },
});
-/**
- The PercentPie field expect a float from 0 to 100.
-*/
+// The PercentPie field expects a float from 0 to 100.
var FieldPercentPie = common.AbstractField.extend({
template: 'FieldPercentPie',
-
+ start: function() {
+ this.$left_mask = this.$('.o_mask').first();
+ this.$right_mask = this.$('.o_mask').last();
+ this.$pie_value = this.$('.o_pie_value');
+ return this._super();
+ },
render_value: function() {
- var value = this.get('value'),
- formatted_value = Math.round(value || 0) + '%',
- svg = this.$('svg')[0];
-
- svg.innerHTML = "";
- nv.addGraph(function() {
- var width = 42, height = 42;
- var chart = nv.models.pieChart()
- .width(width)
- .height(height)
- .margin({top: 0, right: 0, bottom: 0, left: 0})
- .donut(true)
- .showLegend(false)
- .showLabels(false)
- .color(['#7C7BAD','#DDD'])
- .donutRatio(0.62);
-
- chart.tooltip.enabled(false);
-
- d3.select(svg)
- .datum([{'x': 'value', 'y': value}, {'x': 'complement', 'y': 100 - value}])
- .transition()
- .call(chart)
- .attr('style', 'width: ' + width + 'px; height:' + height + 'px;');
-
- d3.select(svg)
- .append("text")
- .attr({x: width/2, y: height/2 + 3, 'text-anchor': 'middle'})
- .style({"font-size": "10px", "font-weight": "bold"})
- .text(formatted_value);
-
- return chart;
- });
-
- }
-});
+ var value = this.get('value') || 0, degValue = 360*value/100;
-/**
- The FieldBarChart expectsa list of values (indeed)
-*/
-var FieldBarChart = common.AbstractField.extend({
- template: 'FieldBarChart',
+ this.$right_mask.toggleClass('o_full', degValue >= 180);
- render_value: function() {
- var value = JSON.parse(this.get('value'));
- var svg = this.$('svg')[0];
- svg.innerHTML = "";
- nv.addGraph(function() {
- var width = 34, height = 34;
- var chart = nv.models.discreteBarChart()
- .x(function (d) { return d.tooltip; })
- .y(function (d) { return d.value; })
- .width(width)
- .height(height)
- .margin({top: 0, right: 0, bottom: 0, left: 0})
- .showValues(false)
- .transition(350)
- .showXAxis(false)
- .showYAxis(false);
-
- chart.tooltip.enabled(false);
-
- d3.select(svg)
- .datum([{key: 'values', values: value}])
- .transition()
- .call(chart)
- .attr('style', 'width: ' + (width + 4) + 'px; height: ' + (height + 8) + 'px;');
-
- nv.utils.windowResize(chart.update);
-
- return chart;
- });
-
+ var leftDeg = 'rotate(' + ((degValue < 180)? 180 : degValue) + 'deg)';
+ var rightDeg = 'rotate(' + ((degValue < 180)? degValue : 0) + 'deg)';
+ this.$left_mask.css({transform: leftDeg, msTransform: leftDeg, mozTransform: leftDeg, webkitTransform: leftDeg});
+ this.$right_mask.css({transform: rightDeg, msTransform: rightDeg, mozTransform: rightDeg, webkitTransform: rightDeg});
+
+ this.$pie_value.html(Math.round(value) + '%');
+ },
+ is_false: function() {
+ return false;
}
});
-
var FieldSelection = common.AbstractField.extend(common.ReinitializeFieldMixin, {
template: 'FieldSelection',
events: {
- 'change select': 'store_dom_value',
+ 'change': 'store_dom_value',
},
init: function(field_manager, node) {
this._super(field_manager, node);
@@ -832,7 +728,7 @@ var FieldSelection = common.AbstractField.extend(common.ReinitializeFieldMixin,
this.records_orderer = new utils.DropMisordered();
this.field_manager.on("view_content_has_changed", this, function() {
var domain = new data.CompoundDomain(this.build_domain()).eval();
- if (! _.isEqual(domain, this.get("domain"))) {
+ if (!_.isEqual(domain, this.get("domain"))) {
this.set("domain", domain);
}
});
@@ -869,27 +765,27 @@ var FieldSelection = common.AbstractField.extend(common.ReinitializeFieldMixin,
// current row or switching to the next one)
// * If the user presses [RETURN] with a select closed (side-effect:
// also if the user opened the select and pressed [RETURN] without
- // changing the selected value), takes the action as validating the
- // row
- var ischanging = false;
- var $select = this.$el.find('select')
- .change(function () { ischanging = true; })
- .click(function () { ischanging = false; })
- .keyup(function (e) {
- if (e.which !== 13 || !ischanging) { return; }
- e.stopPropagation();
- ischanging = false;
- });
- this.setupFocus($select);
+ // changing the selected value), takes the action as validating the row
+ if(!this.get('effective_readonly')) {
+ var ischanging = false;
+ this.$el
+ .change(function () { ischanging = true; })
+ .click(function () { ischanging = false; })
+ .keyup(function (e) {
+ if (e.which !== 13 || !ischanging) { return; }
+ e.stopPropagation();
+ ischanging = false;
+ });
+ this.setupFocus(this.$el);
+ }
},
commit_value: function () {
this.store_dom_value();
return this._super();
},
store_dom_value: function () {
- if (!this.get('effective_readonly') && this.$('select').length) {
- var val = JSON.parse(this.$('select').val());
- this.internal_set_value(val);
+ if (!this.get('effective_readonly')) {
+ this.internal_set_value(JSON.parse(this.$el.val()));
}
},
set_value: function(value_) {
@@ -901,28 +797,29 @@ var FieldSelection = common.AbstractField.extend(common.ReinitializeFieldMixin,
var values = this.get("values");
values = [[false, this.node.attrs.placeholder || '']].concat(values);
var found = _.find(values, function(el) { return el[0] === this.get("value"); }, this);
- if (! found) {
+ if (!found) {
found = [this.get("value"), _t('Unknown')];
values = [found].concat(values);
}
- if (! this.get("effective_readonly")) {
- this.$().html(QWeb.render("FieldSelectionSelect", {widget: this, values: values}));
- this.$("select").val(JSON.stringify(found[0]));
+ if (!this.get("effective_readonly")) {
+ this.$el.empty();
+ for(var i = 0 ; i < values.length ; i++) {
+ this.$el.append($('<option/>', {
+ value: JSON.stringify(values[i][0]),
+ html: values[i][1]
+ }))
+ }
+ this.$el.val(JSON.stringify(found[0]));
} else {
this.$el.text(found[1]);
}
},
focus: function() {
- var input = this.$('select:first')[0];
- return input ? input.focus() : false;
- },
- set_dimensions: function (height, width) {
- this._super(height, width);
- this.$('select').css({
- height: height,
- width: width
- });
- }
+ if (!this.get("effective_readonly")) {
+ return this.$el.focus();
+ }
+ return false;
+ },
});
/**
@@ -932,20 +829,20 @@ var FieldSelection = common.AbstractField.extend(common.ReinitializeFieldMixin,
the time difference between UTC time and local time, in minutes.
*/
var TimezoneMismatch = FieldSelection.extend({
- initialize_content: function(){
+ initialize_content: function() {
this._super.apply(this, arguments);
this.tz_offset_field = (this.options && this.options.tz_offset_field) || this.tz_offset_field || 'tz_offset';
this.set({"tz_offset": this.field_manager.get_field_value(this.tz_offset_field)});
this.on("change:tz_offset", this, this.render_value);
},
- start: function(){
+ start: function() {
this._super.apply(this, arguments);
// trigger a render_value when tz_offset field change
this.field_manager.on("field_changed:" + this.tz_offset_field, this, function() {
this.set({"tz_offset": this.field_manager.get_field_value(this.tz_offset_field)});
});
},
- check_timezone: function(){
+ check_timezone: function() {
var user_offset = this.get('tz_offset');
if (user_offset) {
var offset = -(new Date().getTimezoneOffset());
@@ -956,17 +853,15 @@ var TimezoneMismatch = FieldSelection.extend({
}
return false;
},
- render_value: function(){
+ render_value: function() {
this._super.apply(this, arguments);
+ this.$label.next('.o_tz_warning').remove();
if(this.check_timezone()){
- this.$label.find('.oe_tz_warning').remove();
var options = _.extend({
delay: { show: 501, hide: 0 },
title: _t("Timezone Mismatch : The timezone of your browser doesn't match the selected one. The time in Odoo is displayed according to your field timezone."),
});
- this.$label.css('white-space', 'normal');
- $(QWeb.render('WebClient.timezone_warning')).appendTo(this.$label);
- this.$label.find('.oe_tz_warning').tooltip(options);
+ $('<span/>').addClass('fa fa-exclamation-triangle o_tz_warning').insertAfter(this.$label).tooltip(options);
}
}
});
@@ -979,8 +874,8 @@ var LabelSelection = FieldSelection.extend({
render_value: function() {
this._super.apply(this, arguments);
if (this.get("effective_readonly")) {
- var btn_class = this.classes[this.get('value')] || 'default';
- this.$el.wrapInner($('<span/>').addClass('label label-' + btn_class));
+ var bt_class = this.classes[this.get('value')] || 'default';
+ this.$el.html($('<span/>', {html: this.$el.html()}).addClass('label label-' + bt_class));
}
},
});
@@ -1001,7 +896,6 @@ var FieldRadio = common.AbstractField.extend(common.ReinitializeFieldMixin, {
this.uniqueId = _.uniqueId("radio");
},
initialize_content: function () {
- this.on("change:effective_readonly", this, this.render_value);
this.field_manager.on("view_content_has_changed", this, this.get_selection);
this.get_selection();
},
@@ -1043,7 +937,7 @@ var FieldRadio = common.AbstractField.extend(common.ReinitializeFieldMixin, {
def.resolve();
}
return def.then(function () {
- if (! _.isEqual(selection, self.selection)) {
+ if (!_.isEqual(selection, self.selection)) {
self.selection = _.clone(selection);
self.renderElement();
self.render_value();
@@ -1066,14 +960,16 @@ var FieldRadio = common.AbstractField.extend(common.ReinitializeFieldMixin, {
},
render_value: function () {
var self = this;
- this.$el.toggleClass("oe_readonly", this.get('effective_readonly'));
- this.$("input").filter(function () {return this.value == self.get_value();}).prop("checked", true);
- this.$(".oe_radio_readonly").text(this.get('value') ? this.get('value')[1] : "");
+ if(this.get('effective_readonly')) {
+ this.$el.html(this.get('value')? this.get('value')[1] : "");
+ } else {
+ this.$("input").filter(function() {return this.value == self.get_value();}).prop("checked", true);
+ }
}
});
var FieldReference = common.AbstractField.extend(common.ReinitializeFieldMixin, {
- template: 'FieldReference',
+ className: 'o_row',
init: function(field_manager, node) {
this._super(field_manager, node);
this.reference_ready = true;
@@ -1086,9 +982,8 @@ var FieldReference = common.AbstractField.extend(common.ReinitializeFieldMixin,
},
initialize_content: function() {
var self = this;
- var fm = new common.DefaultFieldManager(this);
- this.fm = fm;
- fm.extend_field_desc({
+ this.fm = new common.DefaultFieldManager(this);
+ this.fm.extend_field_desc({
"selection": {
selection: this.field_manager.get_field_desc(this.name).selection,
type: "selection",
@@ -1098,23 +993,23 @@ var FieldReference = common.AbstractField.extend(common.ReinitializeFieldMixin,
type: "many2one",
},
});
- this.selection = new FieldSelection(fm, { attrs: {
+ this.selection = new FieldSelection(this.fm, { attrs: {
name: 'selection',
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.selection.on("change:value", this, this.on_selection_changed);
- this.selection.appendTo(this.$(".oe_form_view_reference_selection"));
+ this.selection.appendTo(this.$el);
this.selection
.on('focused', null, function () {self.trigger('focused');})
.on('blurred', null, function () {self.trigger('blurred');});
var FieldMany2One = core.form_widget_registry.get('many2one');
- this.m2o = new FieldMany2One(fm, { attrs: {
+ this.m2o = new FieldMany2One(this.fm, { attrs: {
name: 'Referenced Document',
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.m2o.on("change:value", this, this.data_changed);
- this.m2o.appendTo(this.$(".oe_form_view_reference_m2o"));
+ this.m2o.appendTo(this.$el);
this.m2o
.on('focused', null, function () {self.trigger('focused');})
.on('blurred', null, function () {self.trigger('blurred');});
@@ -1151,6 +1046,9 @@ var FieldReference = common.AbstractField.extend(common.ReinitializeFieldMixin,
this.m2o.do_toggle(!!this.get('value')[0]);
this.reference_ready = true;
},
+ is_false: function() {
+ return !this.get_value();
+ },
});
var FieldBinary = common.AbstractField.extend(common.ReinitializeFieldMixin, {
@@ -1161,7 +1059,7 @@ var FieldBinary = common.AbstractField.extend(common.ReinitializeFieldMixin, {
this.useFileAPI = !!window.FileReader;
this.max_upload_size = 25 * 1024 * 1024; // 25Mo
if (!this.useFileAPI) {
- this.fileupload_id = _.uniqueId('oe_fileupload');
+ this.fileupload_id = _.uniqueId('o_fileupload');
$(window).on(this.fileupload_id, function() {
var args = [].slice.call(arguments).slice(1);
self.on_file_uploaded.apply(self, args);
@@ -1175,13 +1073,14 @@ var FieldBinary = common.AbstractField.extend(common.ReinitializeFieldMixin, {
this._super.apply(this, arguments);
},
initialize_content: function() {
- var self= this;
- this.$('input.o_form_input_file').change(this.on_file_change);
- this.$('button.oe_form_binary_file_save').click(this.on_save_as);
- this.$('.oe_form_binary_file_clear').click(this.on_clear);
- this.$('.oe_form_binary_file_edit').click(function() {
- self.$('input.o_form_input_file').click();
+ this.$inputFile = this.$('.o_form_input_file');
+ this.$inputFile.change(this.on_file_change);
+ var self = this;
+ this.$('.o_select_file_button').click(function() {
+ self.$inputFile.click();
});
+ this.$('.o_save_file_button').click(this.on_save_as);
+ this.$('.o_clear_file_button').click(this.on_clear);
},
on_file_change: function(e) {
var self = this;
@@ -1202,26 +1101,28 @@ var FieldBinary = common.AbstractField.extend(common.ReinitializeFieldMixin, {
self.on_file_uploaded(file.size, file.name, file.type, data);
};
} else {
- this.$el.find('form.o_form_binary_form input[name=session_id]').val(this.session.session_id);
- this.$el.find('form.o_form_binary_form').submit();
+ this.$('form.o_form_binary_form input[name=session_id]').val(this.session.session_id);
+ this.$('form.o_form_binary_form').submit();
}
- this.$el.find('.oe_form_binary_progress').show();
- this.$el.find('.oe_form_binary').hide();
+ this.$('.o_form_binary_progress').show();
+ this.$('button').hide();
}
},
- on_file_uploaded: function(size, name, content_type, file_base64) {
+ on_file_uploaded: function(size, name) {
if (size === false) {
this.do_warn(_t("File Upload"), _t("There was a problem while uploading your file"));
// TODO: use openerp web crashmanager
console.warn("Error while uploading file : ", name);
} else {
- this.filename = name;
this.on_file_uploaded_and_valid.apply(this, arguments);
}
- this.$el.find('.oe_form_binary_progress').hide();
- this.$el.find('.oe_form_binary').show();
+ this.$('.o_form_binary_progress').hide();
+ this.$('button').show();
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
+ this.binary_value = true;
+ this.set_filename(name);
+ this.set_value(file_base64);
},
on_save_as: function(ev) {
var value = this.get('value');
@@ -1248,7 +1149,6 @@ var FieldBinary = common.AbstractField.extend(common.ReinitializeFieldMixin, {
'error': c.rpc_error.bind(c)
});
ev.stopPropagation();
- return false;
}
},
set_filename: function(value) {
@@ -1262,129 +1162,95 @@ var FieldBinary = common.AbstractField.extend(common.ReinitializeFieldMixin, {
}
},
on_clear: function() {
- if (this.get('value') !== false) {
- this.binary_value = false;
- this.internal_set_value(false);
- }
- return false;
+ this.binary_value = false;
+ this.set_filename('');
+ this.set_value(false); // FIXME do not really remove the value
}
});
var FieldBinaryFile = FieldBinary.extend({
template: 'FieldBinaryFile',
initialize_content: function() {
+ var self = this;
this._super();
if (this.get("effective_readonly")) {
- var self = this;
- this.$el.find('a').click(function(ev) {
+ this.$el.click(function(ev) {
if (self.get('value')) {
self.on_save_as(ev);
}
return false;
});
+ } else {
+ this.$input = this.$('.o_form_input').eq(0);
+ this.$input.on('click', function() {
+ self.$inputFile.click();
+ });
}
},
render_value: function() {
- var show_value;
- if (!this.get("effective_readonly")) {
- if (this.node.attrs.filename) {
- show_value = this.view.datarecord[this.node.attrs.filename] || '';
- } else {
- show_value = (this.get('value') !== null && this.get('value') !== undefined && this.get('value') !== false) ? this.get('value') : '';
+ var filename = this.view.datarecord[this.node.attrs.filename];
+ if (this.get("effective_readonly")) {
+ this.do_toggle(!!this.get('value'));
+ if (this.get('value')) {
+ this.$el.empty().append($("<span/>").addClass('fa fa-download'));
+ if (filename) {
+ this.$el.append(" " + filename);
+ }
}
- this.$el.find('input').eq(0).val(show_value);
} else {
- this.$el.find('a').toggle(!!this.get('value'));
- if (this.get('value')) {
- show_value = _t("Download");
- if (this.view)
- show_value += " " + (this.view.datarecord[this.node.attrs.filename] || '');
- this.$el.find('a').text(show_value);
+ if(this.get('value')) {
+ this.$el.children().removeClass('o_hidden');
+ this.$('.o_select_file_button').first().addClass('o_hidden');
+ this.$input.val(filename || this.get('value'));
+ } else {
+ this.$el.children().addClass('o_hidden');
+ this.$('.o_select_file_button').first().removeClass('o_hidden');
}
}
- },
- on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
- this.binary_value = true;
- this.set_filename(name);
- this.internal_set_value(file_base64);
- var show_value = name + " (" + utils.human_size(size) + ")";
- this.$el.find('input').eq(0).val(show_value);
- },
- on_clear: function() {
- this._super.apply(this, arguments);
- this.$el.find('input').eq(0).val('');
- this.set_filename('');
- },
- set_value: function(value_){
- var changed = value_ !== this.get_value();
- this._super.apply(this, arguments);
- // Trigger value change if size is the same
- if (!changed){
- this.trigger("change:value", this, {
- oldValue: value_,
- newValue: value_
- });
- }
- }
+ }
});
var FieldBinaryImage = FieldBinary.extend({
template: 'FieldBinaryImage',
placeholder: "/web/static/src/img/placeholder.png",
render_value: function() {
- var self = this;
- var url;
- this.session = session;
- if (this.get('value') && !utils.is_bin_size(this.get('value'))) {
- url = 'data:image/png;base64,' + this.get('value');
- } else if (this.get('value')) {
- var id = JSON.stringify(this.view.datarecord.id || null);
- var field = this.name;
- if (this.options.preview_image)
- field = this.options.preview_image;
- url = session.url('/web/image', {
- model: this.view.dataset.model,
- id: id,
- field: field,
- unique: (this.view.datarecord.__last_update || '').replace(/[^0-9]/g, ''),
- });
- } else {
- url = this.placeholder;
+ var url = this.placeholder;
+ if(this.get('value')) {
+ if(!utils.is_bin_size(this.get('value'))) {
+ url = 'data:image/png;base64,' + this.get('value');
+ } else {
+ url = session.url('/web/image', {
+ model: this.view.dataset.model,
+ id: JSON.stringify(this.view.datarecord.id || null),
+ field: (this.options.preview_image)? this.options.preview_image : this.name,
+ unique: (this.view.datarecord.__last_update || '').replace(/[^0-9]/g, ''),
+ });
+ }
}
- var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url }));
- $($img).click(function(e) {
+
+ var $img = $(QWeb.render("FieldBinaryImage-img", {widget: this, url: url}));
+
+ var self = this;
+ $img.click(function(e) {
if(self.view.get("actual_mode") == "view") {
- var $button = $(".oe_form_button_edit");
+ var $button = $(".o_form_button_edit");
$button.openerpBounce();
e.stopPropagation();
}
});
- this.$el.find('> img').remove();
+ this.$('> img').remove();
+ if (self.options.size) {
+ $img.css("width", "" + self.options.size[0] + "px");
+ $img.css("height", "" + self.options.size[1] + "px");
+ }
this.$el.prepend($img);
- $img.load(function() {
- if (! self.options.size)
- return;
- $img.css("max-width", "" + self.options.size[0] + "px");
- $img.css("max-height", "" + self.options.size[1] + "px");
- });
$img.on('error', function() {
self.on_clear();
$img.attr('src', self.placeholder);
self.do_warn(_t("Image"), _t("Could not display the selected image."));
});
},
- on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
- this.internal_set_value(file_base64);
- this.binary_value = true;
- this.render_value();
- this.set_filename(name);
- },
- on_clear: function() {
- this._super.apply(this, arguments);
- this.render_value();
- this.set_filename('');
- },
- set_value: function(value_){
+ set_value: function(value_) {
var changed = value_ !== this.get_value();
this._super.apply(this, arguments);
// By default, on binary images read, the server returns the binary size
@@ -1397,7 +1263,16 @@ var FieldBinaryImage = FieldBinary.extend({
newValue: value_
});
}
- }
+ },
+ is_false: function() {
+ return false;
+ },
+ set_dimensions: function(height, width) {
+ this.$el.css({
+ maxWidth: width,
+ minHeight: height,
+ });
+ },
});
var FieldStatus = common.AbstractField.extend({
@@ -1423,13 +1298,14 @@ var FieldStatus = common.AbstractField.extend({
});
this.get_selection();
if (this.options.clickable) {
- this.$el.on('click','li[data-id]',this.on_click_stage);
- }
- if (this.$el.parent().is('header')) {
- this.$el.after('<div class="oe_clear"/>');
+ this.bind_stage_click();
}
this._super();
},
+ bind_stage_click: function () {
+ // This function is overriden in the enterprise webclient
+ this.$el.on('click','li[data-id]',this.on_click_stage);
+ },
set_value: function(value_) {
if (value_ instanceof Array) {
value_ = value_[0];
@@ -1468,15 +1344,15 @@ var FieldStatus = common.AbstractField.extend({
var fold_field = this.options.fold_field;
var calculation = _.bind(function() {
- if (this.field.type == "many2one") {
- return self.get_distant_fields().then(function (fields) {
+ if (this.field.type === "many2one") {
+ return self.get_distant_fields().then(function () {
return new data.DataSetSearch(self, self.field.relation, self.build_context(), self.get("evaluated_selection_domain"))
.read_slice(_.union(_.keys(self.distant_fields), ['id']), {}).then(function (records) {
var ids = _.pluck(records, 'id');
return self.dataset.name_get(ids).then(function (records_name) {
_.each(records, function (record) {
- var name = _.find(records_name, function (val) {return val[0] == record.id;})[1];
- if (fold_field && record[fold_field] && record.id != self.get('value')) {
+ var name = _.find(records_name, function (val) {return val[0] === record.id;})[1];
+ if (fold_field && record[fold_field] && record.id !== self.get('value')) {
selection_folded.push([record.id, name]);
} else {
selection_unfolded.push([record.id, name]);
@@ -1492,7 +1368,7 @@ var FieldStatus = common.AbstractField.extend({
var select = this.field.selection;
for(var i=0; i < select.length; i++) {
var key = select[i][0];
- if(key == this.get('value') || !this.options.visible || this.options.visible.indexOf(key) != -1) {
+ if(key === this.get('value') || !this.options.visible || this.options.visible.indexOf(key) !== -1) {
selection_unfolded.push(select[i]);
}
}
@@ -1529,13 +1405,12 @@ var FieldStatus = common.AbstractField.extend({
return;
}
var val;
- if (this.field.type == "many2one") {
+ if (this.field.type === "many2one") {
val = parseInt($li.data("id"), 10);
- }
- else {
+ } else {
val = $li.data("id");
}
- if (val != self.get('value')) {
+ if (val !== self.get('value')) {
this.view.recursive_save().done(function() {
var change = {};
change[self.name] = val;
@@ -1548,8 +1423,7 @@ var FieldStatus = common.AbstractField.extend({
});
var FieldMonetary = FieldFloat.extend({
- template: "FieldMonetary",
- widget_class: 'oe_form_field_float oe_form_field_monetary',
+ template: 'FieldMonetary',
init: function() {
this._super.apply(this, arguments);
this.set({"currency": false});
@@ -1567,6 +1441,23 @@ var FieldMonetary = FieldFloat.extend({
this.on("change:currency_info", this, this.update);
return tmp;
},
+ initialize_content: function() {
+ if(!this.get('effective_readonly')) {
+ this.$input = this.$('input');
+ this.add_symbol();
+ } else {
+ this.$input = undefined;
+ }
+
+ this._super();
+ },
+ add_symbol: function() {
+ var currency = this.get('currency_info');
+ if(currency) {
+ var before = (currency.position === 'before');
+ this.$el[(before)? 'prepend' : 'append']($('<span/>', {html: currency.symbol}));
+ }
+ },
get_currency_info: function() {
var self = this;
if (this.get("currency") === false) {
@@ -1576,15 +1467,21 @@ var FieldMonetary = FieldFloat.extend({
return self.set({"currency_info": session.get_currency(self.get("currency"))});
},
update: function() {
- if (this.view.options.is_list_editable){
+ if (this.view.options.is_list_editable) {
return;
} else {
return this.reinitialize();
}
},
+ render_value: function() {
+ this._super();
+ if(this.get('effective_readonly')) {
+ this.add_symbol();
+ }
+ },
get_digits_precision: function() {
return this.node.attrs.digits || this.field.digits || (this.get('currency_info') && this.get('currency_info').digits);
- },
+ },
parse_value: function(val, def) {
return formats.parse_value(val, {type: "float", digits: this.get_digits_precision()}, def);
},
@@ -1625,7 +1522,6 @@ var StatInfo = common.AbstractField.extend({
this.$el.html(QWeb.render("StatInfo", options));
this.$el.addClass('o_stat_info');
},
-
});
/**
@@ -1633,14 +1529,18 @@ var StatInfo = common.AbstractField.extend({
switching between a green bullet / gray bullet.
*/
var FieldToggleBoolean = common.AbstractField.extend({
+ template: "toggle_button",
+ events: {
+ 'click': 'set_toggle_button'
+ },
render_value: function () {
- this.icon = this.get_value() ? 'gtk-yes.png' : 'gtk-normal.png';
- this.$el.html(QWeb.render("FieldToggleBoolean", {'widget': this}));
- this.$('.oe_toggle_button').on('click', this.set_toggle_button.bind(this));
+ var $img = this.$('img');
+ var src = $img.attr('src');
+ $img.attr('src', src.substr(0, src.lastIndexOf('/')+1) + (this.get_value() ? 'gtk-yes.png' : 'gtk-normal.png'));
},
set_toggle_button: function () {
var self = this;
- var toggle_value = this.get_value() === false ? true: false;
+ var toggle_value = !this.get_value();
if (this.view.get('actual_mode') == 'view') {
var rec_values = {};
rec_values[self.node.attrs.name] = toggle_value;
@@ -1658,91 +1558,8 @@ var FieldToggleBoolean = common.AbstractField.extend({
reload_record: function () {
this.view.reload();
},
-});
-
-/**
- This widget is intended to be used in config settings.
- When checked, an upgrade popup is showed to the user.
-*/
-
-var AbstractFieldUpgrade = {
- events: {
- 'click input': 'on_click_input',
- },
-
- start: function() {
- this._super.apply(this, arguments);
-
- this.get_enterprise_label().after($("<span>", {
- text: "Enterprise",
- 'class': "label label-primary oe_inline"
- }));
- },
-
- open_dialog: function() {
- var message = $(QWeb.render('EnterpriseUpgrade'));
-
- var buttons = [
- {
- text: _t("Upgrade now"),
- classes: 'btn-primary',
- close: true,
- click: this.confirm_upgrade,
- },
- {
- text: _t("Cancel"),
- close: true,
- },
- ];
-
- return new Dialog(this, {
- size: 'medium',
- buttons: buttons,
- $content: $('<div>', {
- html: message,
- }),
- title: _t("Odoo Enterprise"),
- }).open();
- },
-
- confirm_upgrade: function() {
- new Model("res.users").call("search_count", [[["share", "=", false]]]).then(function(data) {
- framework.redirect("https://www.odoo.com/odoo-enterprise/upgrade?num_users=" + data);
- });
- },
-
- get_enterprise_label: function() {},
- on_click_input: function() {},
-};
-
-var UpgradeBoolean = FieldBoolean.extend(AbstractFieldUpgrade, {
- template: "FieldUpgradeBoolean",
-
- get_enterprise_label: function() {
- return this.$label;
- },
-
- on_click_input: function(event) {
- if(this.$checkbox.prop("checked")) {
- this.open_dialog().on('closed', this, function() {
- this.$checkbox.prop("checked", false);
- });
- }
- },
-});
-
-var UpgradeRadio = FieldRadio.extend(AbstractFieldUpgrade, {
-
- get_enterprise_label: function() {
- return this.$('label').last();
- },
-
- on_click_input: function(event) {
- if($(event.target).val() == 1) {
- this.open_dialog().on('closed', this, function() {
- this.$('input').first().prop("checked", true);
- });
- }
+ is_false: function() {
+ return false;
},
});
@@ -1770,7 +1587,6 @@ core.form_widget_registry
.add('toggle_button', FieldToggleBoolean)
.add('float', FieldFloat)
.add('percentpie', FieldPercentPie)
- .add('barchart', FieldBarChart)
.add('integer', FieldFloat)
.add('float_time', FieldFloat)
.add('progressbar', FieldProgressBar)
@@ -1782,9 +1598,7 @@ core.form_widget_registry
.add('kanban_state_selection', KanbanSelection)
.add('statinfo', StatInfo)
.add('timezone_mismatch', TimezoneMismatch)
- .add('label_selection', LabelSelection)
- .add('upgrade_boolean', UpgradeBoolean)
- .add('upgrade_radio', UpgradeRadio);
+ .add('label_selection', LabelSelection);
/**
@@ -1796,8 +1610,12 @@ core.form_widget_registry
core.form_tag_registry.add('button', WidgetButton);
return {
+ FieldBoolean: FieldBoolean,
FieldChar: FieldChar,
+ FieldEmail: FieldEmail,
FieldFloat: FieldFloat,
+ FieldRadio: FieldRadio,
+ FieldStatus: FieldStatus,
FieldMonetary: FieldMonetary,
WidgetButton: WidgetButton
};
diff --git a/addons/web/static/src/js/views/graph_view.js b/addons/web/static/src/js/views/graph_view.js
index 62ee1e62c23..c8ff16cd0f1 100644
--- a/addons/web/static/src/js/views/graph_view.js
+++ b/addons/web/static/src/js/views/graph_view.js
@@ -14,7 +14,7 @@ var _t = core._t;
var QWeb = core.qweb;
var GraphView = View.extend({
- className: 'oe_graph',
+ className: 'o_graph',
display_name: _lt('Graph'),
icon: 'fa-bar-chart',
require_fields: true,
@@ -54,12 +54,14 @@ var GraphView = View.extend({
if ($node) {
var context = {measures: _.pairs(_.omit(this.measures, '__count__'))};
this.$buttons = $(QWeb.render('GraphView.buttons', context));
- this.$measure_list = this.$buttons.find('.oe-measure-list');
+ this.$measure_list = this.$buttons.find('.o_graph_measures_list');
this.update_measure();
this.$buttons.find('button').tooltip();
this.$buttons.click(this.on_button_click.bind(this));
- this.$buttons.appendTo($node);
+ this.$buttons.find('.o_graph_button[data-mode="' + this.widget.mode + '"]').addClass('active');
+
+ this.$buttons.appendTo($node);
}
},
update_measure: function () {
@@ -86,7 +88,7 @@ var GraphView = View.extend({
},
do_search: function (domain, context, group_by) {
if (!this.widget) {
- this.initial_groupbys = context.graph_groupbys || this.initial_groupbys;
+ this.initial_groupbys = context.graph_groupbys || (group_by.length ? group_by : this.initial_groupbys);
this.widget = new GraphWidget(this, this.model, {
measure: context.graph_measure || this.active_measure,
mode: context.graph_mode || this.active_mode,
@@ -112,14 +114,16 @@ var GraphView = View.extend({
},
on_button_click: function (event) {
var $target = $(event.target);
- if ($target.hasClass('oe-bar-mode')) {this.widget.set_mode('bar');}
- if ($target.hasClass('oe-line-mode')) {this.widget.set_mode('line');}
- if ($target.hasClass('oe-pie-mode')) {this.widget.set_mode('pie');}
- if ($target.parents('.oe-measure-list').length) {
- var parent = $target.parent(),
- field = parent.data('field');
+ if ($target.hasClass('o_graph_button')) {
+ this.widget.set_mode($target.data('mode'));
+ this.$buttons.find('.o_graph_button.active').removeClass('active');
+ $target.addClass('active');
+ }
+ else if ($target.parents('.o_graph_measures_list').length) {
+ var parent = $target.parent();
+ var field = parent.data('field');
this.active_measure = field;
- parent.toggleClass('selected');
+ event.preventDefault();
event.stopPropagation();
this.update_measure();
this.widget.set_measure(this.active_measure);
diff --git a/addons/web/static/src/js/views/graph_widget.js b/addons/web/static/src/js/views/graph_widget.js
index a86ea35e449..e3786408125 100644
--- a/addons/web/static/src/js/views/graph_widget.js
+++ b/addons/web/static/src/js/views/graph_widget.js
@@ -14,6 +14,7 @@ var QWeb = core.qweb;
var MAX_LEGEND_LENGTH = 25 * (1 + config.device.size_class);
return Widget.extend({
+ className: "o_graph_svg_container",
init: function (parent, model, options) {
this._super(parent);
this.context = options.context;
diff --git a/addons/web/static/src/js/views/list_view.js b/addons/web/static/src/js/views/list_view.js
index d42d34a98d2..9214eb27152 100644
--- a/addons/web/static/src/js/views/list_view.js
+++ b/addons/web/static/src/js/views/list_view.js
@@ -8,6 +8,7 @@ var DataExport = require('web.DataExport');
var formats = require('web.formats');
var common = require('web.list_common');
var Model = require('web.DataModel');
+var Pager = require('web.Pager');
var pyeval = require('web.pyeval');
var session = require('web.session');
var Sidebar = require('web.Sidebar');
@@ -34,8 +35,7 @@ var row_decoration = [
var ListView = View.extend({
_template: 'ListView',
- accesskey: 'L',
- display_name: _lt('List'),
+ accesskey: "L",
defaults: _.extend({}, View.prototype.defaults, {
// records can be selected one by one
selectable: true,
@@ -54,8 +54,14 @@ var ListView = View.extend({
//whether the editable property of the view has to be disabled
disable_editable_mode: false,
}),
+ display_name: _lt('List'),
events: {
- 'click thead th.oe_sortable[data-id]': 'sort_by_column'
+ 'click thead th.o_column_sortable[data-id]': 'sort_by_column',
+ 'click .oe_view_nocontent': function() {
+ if (this.$buttons) {
+ this.$buttons.width(this.$buttons.width() + 1).openerpBounce();
+ }
+ },
},
icon: 'fa-list-ul',
@@ -92,8 +98,7 @@ var ListView = View.extend({
});
this.previous_colspan = null;
- this.colors = null;
- this.fonts = null;
+ this.decoration = null;
this.columns = [];
@@ -111,7 +116,6 @@ var ListView = View.extend({
this.groups.datagroup.sort = this.dataset._sort;
}
- this.page = 0;
this.records.bind('change', function (event, record, key) {
if (!_(self.aggregate_columns).chain()
.pluck('name').contains(key).value()) {
@@ -127,6 +131,7 @@ var ListView = View.extend({
this.options.$pager = false;
}
+ this.options.deletable = this.options.deletable && this.is_action_enabled('delete');
this.name = "" + this.fields_view.arch.attrs.string;
// the view's number of records per page (|| section)
@@ -135,14 +140,26 @@ var ListView = View.extend({
(this.getParent().action || {}).limit ||
parseInt(this.fields_view.arch.attrs.limit, 10) ||
80);
+ // the index of the first displayed record (starting from 1)
+ this.current_min = 1;
- //Sort
- var default_order = this.fields_view.arch.attrs.default_order,
- unsorted = !this.dataset._sort.length;
+ // Sort
+ var default_order = this.fields_view.arch.attrs.default_order;
+ var unsorted = !this.dataset._sort.length;
if (unsorted && default_order && !this.grouped) {
this.dataset.set_sort(default_order.split(','));
}
},
+ willStart: function() {
+ // Retrieve the decoration defined on the model's list view
+ this.decoration = _.pick(this.fields_view.arch.attrs, function(value, key) {
+ return row_decoration.indexOf(key) >= 0;
+ });
+ this.decoration = _.mapObject(this.decoration, function(value) {
+ return py.parse(py.tokenize(value));
+ });
+ return this._super();
+ },
/**
* Set a custom Group construct as the root of the List View.
*
@@ -171,43 +188,6 @@ var ListView = View.extend({
}
});
},
- willStart: function() {
- // Retrieve the decoration defined on the model's list view
- this.decoration = _.pick(this.fields_view.arch.attrs, function(value, key) {
- return row_decoration.indexOf(key) >= 0;
- });
- this.decoration = _.mapObject(this.decoration, function(value) {
- return py.parse(py.tokenize(value));
- });
-
- this.setup_columns(this.fields_view.fields, this.grouped);
-
- return this._super();
- },
- start: function() {
- var self = this;
- this.$el.html(QWeb.render(this._template, this));
- this.$el.addClass(this.fields_view.arch.attrs['class']);
- this.$el.addClass('oe_list o_list_view');
-
- // Selecting records
- this.$('.oe_list_record_selector').click(function() {
- var checked = self.$('.oe_list_record_selector').prop('checked');
- self.$('.oe_list_record_selector input').prop('checked', checked || false);
- var selection = self.groups.get_selection();
- $(self.groups).trigger('selected', [selection.ids, selection.records]);
- });
-
- if(this.dataset._sort.length){
- if(this.dataset._sort[0].indexOf('-') === -1){
- this.$el.find('th[data-id=' + this.dataset._sort[0] + ']').addClass("sortdown");
- }else {
- this.$el.find('th[data-id=' + this.dataset._sort[0].split('-')[1] + ']').addClass("sortup");
- }
- }
- this.trigger('list_view_loaded', data, this.grouped);
- return this._super();
- },
/**
* Computes and returns the classnames for the provided record (from the
* ``@decoration`` attribute)
@@ -231,24 +211,58 @@ var ListView = View.extend({
return classnames;
},
/**
+ * Renders the table itself and inserts its content
+ * Computes and displays the aggregates
+ * Hooks up the selection of records event
+ * Adds adequate classname on the sorted column
+ */
+ load_list: function() {
+ var self = this;
+
+ // Render the table and append its content
+ this.$el.html(QWeb.render(this._template, this));
+ this.$el.addClass(this.fields_view.arch.attrs['class']);
+ if (this.grouped) {
+ this.$('.o_list_view').addClass('o_list_view_grouped');
+ }
+ this.$('.o_list_view').append(this.groups.elements);
+
+ // Compute the aggregates and display them in the list's footer
+ this.compute_aggregates();
+
+ // Head hook
+ // Selecting records
+ this.$('thead .o_list_record_selector input').click(function() {
+ self.$('tbody .o_list_record_selector input').prop('checked', $(this).prop('checked') || false);
+ var selection = self.groups.get_selection();
+ $(self.groups).trigger('selected', [selection.ids, selection.records]);
+ });
+
+ // Sort
+ if (this.dataset._sort.length) {
+ if (this.dataset._sort[0].indexOf('-') === -1) {
+ this.$('th[data-id=' + this.dataset._sort[0] + ']').addClass("o-sort-down");
+ } else {
+ this.$('th[data-id=' + this.dataset._sort[0].split('-')[1] + ']').addClass("o-sort-up");
+ }
+ }
+
+ this.trigger('list_view_loaded', data, this.grouped);
+ return $.when();
+ },
+ /**
* Render the buttons according to the ListView.buttons template and
* add listeners on it.
* Set this.$buttons with the produced jQuery element
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the ListView inserts them into this.options.$buttons
- * or into a div of its template
+ * if it exists
*/
render_buttons: function($node) {
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget': this}));
-
this.$buttons.find('.o_list_button_add').click(this.proxy('do_add_record'));
-
- if ($node) {
- this.$buttons.appendTo($node);
- } else {
- this.$('.oe_list_buttons').replaceWith(this.$buttons);
- }
+ this.$buttons.appendTo($node);
}
},
/**
@@ -271,7 +285,7 @@ var ListView = View.extend({
this.is_action_enabled('delete') && { label: _t('Delete'), callback: this.do_delete_selected }
]));
- $node = $node || this.options.$sidebar || this.$('.oe_list_sidebar');
+ $node = $node || this.options.$sidebar;
this.sidebar.appendTo($node);
// Hide the sidebar by default (it will be shown as soon as a record is selected)
@@ -279,72 +293,52 @@ var ListView = View.extend({
}
},
/**
- * Render the pager according to the ListView.pager template and add listeners on it.
- * Set this.$pager with the produced jQuery element
- * @param {jQuery} [$node] a jQuery node where the rendered pager should be inserted
+ * Instantiate and render the pager and add listeners on it.
+ * Set this.pager
+ * @param {jQuery} [$node] a jQuery node where the pager should be inserted
* $node may be undefined, in which case the ListView inserts the pager into this.options.$pager
- * or into a div of its template
*/
- render_pager: function($node) {
- if (!this.$pager && this.options.pager) {
- var self = this;
-
- this.$pager = $(QWeb.render("ListView.pager", {'widget': self}));
- this.$pager
- .on('click', 'a[data-pager-action]', function () {
- var $this = $(this);
- var max_page_index = Math.ceil(self.dataset.size() / self._limit) - 1;
- switch ($this.data('pager-action')) {
- case 'first':
- self.page = 0;
- break;
- case 'last':
- self.page = max_page_index;
- break;
- case 'next':
- self.page += 1;
- break;
- case 'previous':
- self.page -= 1;
- break;
+ render_pager: function($node, options) {
+ if (!this.pager && this.options.pager) {
+ this.pager = new Pager(this, this.dataset.size(), 1, this._limit, options);
+ this.pager.appendTo($node || this.options.$pager);
+
+ this.pager.on('pager_changed', this, function (new_state) {
+ var self = this;
+ var limit_changed = (this._limit !== new_state.limit);
+
+ this._limit = new_state.limit;
+ this.current_min = new_state.current_min;
+ this.reload_content().then(function() {
+ // Reset the scroll position to the top on page changed only
+ if (!limit_changed) {
+ self.set_scrollTop(0);
+ self.trigger_up('scrollTo', {offset: 0});
}
- if (self.page < 0) {
- self.page = max_page_index;
- } else if (self.page > max_page_index) {
- self.page = 0;
- }
- self.reload_content();
- }).find('.oe_list_pager_state')
- .click(function (e) {
- e.stopPropagation();
- var $this = $(this);
-
- var $select = $('<select>')
- .appendTo($this.empty())
- .click(function (e) {e.stopPropagation();})
- .append('<option value="80">80</option>' +
- '<option value="200">200</option>' +
- '<option value="500">500</option>' +
- '<option value="2000">2000</option>' +
- '<option value="NaN">' + _t("Unlimited") + '</option>')
- .change(function () {
- var val = parseInt($select.val(), 10);
- self._limit = (isNaN(val) ? null : val);
- self.page = 0;
- self.reload_content();
- }).blur(function() {
- $(this).trigger('change');
- })
- .val(self._limit || 'NaN');
- });
- this.configure_pager(this.dataset);
-
- $node = $node || this.options.$pager;
- if ($node) {
- this.$pager.appendTo($node);
- } else {
- this.$('.oe_list_pager').replaceWith(this.$pager);
+ });
+ });
+ }
+ },
+ /**
+ * Updates the pager based on the provided dataset's information
+ *
+ * Horrifying side-effect: sets the dataset's data on this.dataset?
+ *
+ * @param {instance.web.DataSet} [dataset]
+ * @param {int} [current_min] the min pager value
+ */
+ update_pager: function (dataset, current_min) {
+ this.dataset.ids = dataset.ids;
+ // Not exactly clean
+ if (dataset._length !== undefined) {
+ this.dataset._length = dataset._length;
+ }
+ if (this.pager && !this.grouped) {
+ var new_state = { size: this.dataset.size(), limit: this._limit };
+ if (current_min) {
+ new_state.current_min = current_min;
}
+ this.pager.update_state(new_state);
}
},
sort_by_column: function (e) {
@@ -357,54 +351,14 @@ var ListView = View.extend({
return false;
}
this.dataset.sort(col_name);
- if($column.hasClass("sortdown") || $column.hasClass("sortup")) {
- $column.toggleClass("sortup sortdown");
+ if($column.hasClass("o-sort-down") || $column.hasClass("o-sort-up")) {
+ $column.toggleClass("o-sort-up o-sort-down");
} else {
- $column.addClass("sortdown");
- }
- $column.siblings('.oe_sortable').removeClass("sortup sortdown");
-
- this.reload_content();
- },
- /**
- * Configures the ListView pager based on the provided dataset's information
- *
- * Horrifying side-effect: sets the dataset's data on this.dataset?
- *
- * @param {instance.web.DataSet} dataset
- */
- configure_pager: function (dataset) {
- this.dataset.ids = dataset.ids;
- // Not exactly clean
- if (dataset._length) {
- this.dataset._length = dataset._length;
+ $column.addClass("o-sort-down");
}
- if (this.$pager) {
- if (this.grouped) {
- // page count is irrelevant on grouped page, replace by limit
- this.$pager.find('.oe-pager-buttons').hide();
- this.$pager.find('.oe_list_pager_state').text(this._limit || '∞');
- } else {
- var total = dataset.size();
- var limit = this._limit || total;
- this.$pager.find('.oe-pager-buttons').toggle(total > limit);
- this.$pager.find('.oe_pager_value').toggle(total !== 0);
- var spager = '-';
- if (total) {
- var range_start = this.page * limit + 1;
- var range_stop = range_start - 1 + limit;
- if (this.records.length) {
- range_stop = range_start - 1 + this.records.length;
- }
- if (range_stop > total) {
- range_stop = total;
- }
- spager = _.str.sprintf(_t("%d-%d of %d"), range_start, range_stop, total);
- }
+ $column.siblings('.o_column_sortable').removeClass("o-sort-up o-sort-down");
- this.$pager.find('.oe_list_pager_state').text(spager);
- }
- }
+ return this.reload_content();
},
/**
* Sets up the listview's columns: merges view and fields data, move
@@ -422,8 +376,7 @@ var ListView = View.extend({
return for_(id, fields[id], field);
}));
if (grouped) {
- this.columns.unshift(
- new ListView.MetaColumn('_group', _t("Group")));
+ this.columns.unshift(new ListView.MetaColumn('_group'));
}
this.visible_columns = _.filter(this.columns, function (column) {
@@ -468,27 +421,27 @@ var ListView = View.extend({
*/
reload_content: synchronized(function () {
var self = this;
- self.$el.find('.oe_list_record_selector').prop('checked', false);
+ this.setup_columns(this.fields_view.fields, this.grouped);
+ this.$('tbody .o_list_record_selector input').prop('checked', false);
this.records.reset();
var reloaded = $.Deferred();
- reloaded.then(function () {
- self.configure_pager(self.dataset);
- });
- this.$el.find('.oe_list_content').append(
- this.groups.render(function () {
- if (self.dataset.index === null) {
- if (self.records.length) {
- self.dataset.index = 0;
- }
- } else if (self.dataset.index >= self.records.length) {
- self.dataset.index = self.records.length ? 0 : null;
+ this.groups.render(function () {
+ if (self.dataset.index === null) {
+ if (self.records.length) {
+ self.dataset.index = 0;
+ }
+ } else if (self.dataset.index >= self.records.length) {
+ self.dataset.index = self.records.length ? 0 : null;
+ }
+ self.load_list().then(function () {
+ if (!self.grouped && self.display_nocontent_helper()) {
+ self.no_result();
}
-
- self.compute_aggregates();
reloaded.resolve();
- }));
+ });
+ });
this.do_push_state({
- page: this.page,
+ min: this.current_min,
limit: this._limit
});
return reloaded.promise();
@@ -531,8 +484,8 @@ var ListView = View.extend({
do_load_state: function(state, warm) {
var reload = false;
- if (state.page && this.page !== state.page) {
- this.page = state.page;
+ if (state.min && this.current_min !== state.min) {
+ this.current_min = state.min;
reload = true;
}
if (state.limit) {
@@ -558,7 +511,7 @@ var ListView = View.extend({
* @param {Object} results results of evaluating domain and process for a search
*/
do_search: function (domain, context, group_by) {
- this.page = 0;
+ this.current_min = 1;
this.groups.datagroup = new DataGroup(
this, this.model, domain, context, group_by);
this.groups.datagroup.sort = this.dataset._sort;
@@ -569,6 +522,10 @@ var ListView = View.extend({
this.no_leaf = !!context['group_by_no_leaf'];
this.grouped = !!group_by;
+ // Hide the pager in grouped mode
+ if (this.pager && this.grouped) {
+ this.pager.do_hide();
+ }
return this.reload_content();
},
/**
@@ -581,26 +538,25 @@ var ListView = View.extend({
return;
}
var self = this;
+
return $.when(this.dataset.unlink(ids)).done(function () {
_(ids).each(function (id) {
self.records.remove(self.records.get(id));
});
// Hide the table if there is no more record in the dataset
- if (self.dataset.size() === 0) {
+ if (self.display_nocontent_helper()) {
self.no_result();
} else {
+ // Load previous page if the current one is empty
if (self.records.length === 0 && self.dataset.size() > 0) {
- //Trigger previous manually to navigate to previous page,
- //If all records are deleted on current page.
- self.$pager.find('ul li:first a').trigger('click');
- } else if (self.dataset.size() === self._limit) {
- //Reload listview to update current page with next page records
- //because pager going to be hidden if dataset.size == limit
+ self.pager.previous();
+ }
+ // Reload the list view if we are not on the last page
+ if (self.current_min + self._limit - 1 < self.dataset.size()) {
self.reload();
- } else {
- self.configure_pager(self.dataset);
}
}
+ self.update_pager(self.dataset);
self.compute_aggregates();
});
},
@@ -613,7 +569,7 @@ var ListView = View.extend({
do_select: function (ids, records, deselected) {
// uncheck header hook if at least one row has been deselected
if (deselected) {
- this.$('.oe_list_record_selector').prop('checked', false);
+ this.$('thead .o_list_record_selector input').prop('checked', false);
}
if (!ids.length) {
@@ -810,13 +766,14 @@ var ListView = View.extend({
this.display_aggregates(aggregates);
},
display_aggregates: function (aggregation) {
- var $footer_cells = this.$el.find('.oe_list_footer');
+ var $footer_cells = this.$('tfoot td');
_(this.aggregate_columns).each(function (column) {
if (!column['function']) {
return;
}
$footer_cells.filter(_.str.sprintf('[data-field=%s]', column.id))
+ .addClass('o_list_number')
.html(column.format(aggregation, { process_modifiers: false }));
});
},
@@ -831,7 +788,7 @@ var ListView = View.extend({
*/
get_active_domain: function () {
var self = this;
- if (this.$('.oe_list_record_selector').prop('checked')) {
+ if (this.$('thead .o_list_record_selector input').prop('checked')) {
var search_view = this.getParent().searchview;
var search_data = search_view.build_search_data();
return pyeval.eval_domains_and_contexts({
@@ -859,7 +816,7 @@ var ListView = View.extend({
pad_columns: function (count, options) {
options = options || {};
// padding for action/pager header
- var $first_header = this.$el.find('thead tr:first th');
+ var $first_header = this.$('thead tr:first th');
var colspan = $first_header.attr('colspan');
if (colspan) {
if (!this.previous_colspan) {
@@ -882,7 +839,7 @@ var ListView = View.extend({
* Removes all padding columns of the table
*/
unpad_columns: function () {
- this.$el.find('.oe_list_padding').remove();
+ this.$('.oe_list_padding').remove();
if (this.previous_colspan) {
this.$el
.find('thead tr:first th')
@@ -890,23 +847,20 @@ var ListView = View.extend({
this.previous_colspan = null;
}
},
+ display_nocontent_helper: function () {
+ return (this.dataset.size() === 0);
+ },
no_result: function () {
- this.$el.find('.oe_view_nocontent').remove();
- if (this.groups.group_by
- || !this.options.action
- || !this.options.action.help) {
+ this.$('.oe_view_nocontent').remove();
+ if (this.groups.group_by ||
+ !this.options.action ||
+ !this.options.action.help) {
return;
}
- this.$el.find('table:first').hide();
+ this.$('table:first').hide();
this.$el.prepend(
$('<div class="oe_view_nocontent">').html(this.options.action.help)
);
- if (this.$buttons) {
- var $buttons = this.$buttons;
- this.$el.find('.oe_view_nocontent').click(function() {
- $buttons.width($buttons.width() + 1).openerpBounce();
- });
- }
}
});
core.view_registry.add('list', ListView);
@@ -975,9 +929,8 @@ ListView.List = Class.extend({
$row = self.$current.children(
'[data-id=' + record.get('id') + ']');
}
-
var $newRow = $(self.render_record(record));
- $newRow.find('.oe_list_record_selector input').prop('checked', !!$row.find('.oe_list_record_selector input').prop('checked'));
+ $newRow.find('.o_list_record_selector input').prop('checked', !!$row.find('.o_list_record_selector input').prop('checked'));
$row.replaceWith($newRow);
},
'add': function (ev, records, record, index) {
@@ -1012,14 +965,14 @@ ListView.List = Class.extend({
*/
e.preventDefault();
})
- .delegate('th.oe_list_record_selector', 'click', function (e) {
+ .delegate('td.o_list_record_selector', 'click', function (e) {
e.stopPropagation();
var selection = self.get_selection();
var checked = $(e.currentTarget).find('input').prop('checked');
$(self).trigger(
'selected', [selection.ids, selection.records, ! checked]);
})
- .delegate('td.oe_list_record_delete', 'click', function (e) {
+ .delegate('td.o_list_record_delete', 'click', function (e) {
e.stopPropagation();
var $row = $(e.target).closest('tr');
$(self).trigger('deleted', [[self.row_id($row)]]);
@@ -1028,7 +981,7 @@ ListView.List = Class.extend({
$('<input />').appendTo('body').focus().remove();
}
})
- .delegate('td.oe_list_field_cell button', 'click', function (e) {
+ .delegate('td button', 'click', function (e) {
e.stopPropagation();
var $target = $(e.currentTarget),
field = $target.closest('td').data('field'),
@@ -1158,7 +1111,7 @@ ListView.List = Class.extend({
}
var cells = [];
if (this.options.selectable) {
- cells.push('<th class="oe_list_record_selector"></td>');
+ cells.push('<td class="o_list_record_selector"></td>');
}
_(this.columns).each(function(column) {
if (column.invisible === '1') {
@@ -1167,7 +1120,7 @@ ListView.List = Class.extend({
cells.push('<td title="' + column.string + '">&nbsp;</td>');
});
if (this.options.deletable) {
- cells.push('<td class="oe_list_record_delete"></td>');
+ cells.push('<td class="o_list_record_delete"></td>');
}
cells.unshift('<tr>');
cells.push('</tr>');
@@ -1187,7 +1140,7 @@ ListView.List = Class.extend({
return result;
}
var records = this.records;
- this.$current.find('th.oe_list_record_selector input:checked')
+ this.$current.find('td.o_list_record_selector input:checked')
.closest('tr').each(function () {
var record = records.get($(this).data('id'));
result.ids.push(record.get('id'));
@@ -1206,14 +1159,12 @@ ListView.List = Class.extend({
return $(row).data('id');
},
/**
- * Death signal, cleans up list display
+ * Death signal, cleans up records's callbacks
*/
on_records_reset: function () {
_(this.record_callbacks).each(function (callback, event) {
this.records.unbind(event, callback);
}, this);
- if (!this.$current) { return; }
- this.$current.remove();
},
get_records: function () {
return this.records.map(function (record) {
@@ -1279,7 +1230,7 @@ ListView.Groups = Class.extend({
this.$row = null;
this.children = {};
- this.page = 0;
+ this.pager = null; // group pager
var self = this;
this.records.bind('reset', function () {
@@ -1315,51 +1266,46 @@ ListView.Groups = Class.extend({
}
return red_letter_tboday;
},
- make_paginator: function () {
+ /**
+ * Renders the group pager and append it to the group's header
+ */
+ render_group_pager: function () {
var self = this;
- var $prev = $('<button type="button" data-pager-action="previous">&lt;</button>')
- .click(function (e) {
- e.stopPropagation();
- if (self.page > 0) {
- self.page -= 1;
- } else {
- self.page = self.max_page_index;
- }
-
- self.$row.closest('tbody').next()
- .replaceWith(self.render());
+ if (this.datagroup.length > this.view._limit) {
+ this.pager = new Pager(this, this.datagroup.length, 1, this.view._limit);
+ this.pager.on('pager_changed', this, function (state) {
+ self.view._limit = state.limit;
+ self.current_min = state.current_min;
+ self.render().then(function() {
+ self.$row.closest('tbody').next().replaceWith($(self.elements));
+ });
});
- var $next = $('<button type="button" data-pager-action="next">&gt;</button>')
- .click(function (e) {
- e.stopPropagation();
- if (self.page < self.max_page_index) {
- self.page += 1;
- } else {
- self.page = 0;
- }
- self.$row.closest('tbody').next()
- .replaceWith(self.render());
+ var $last_cell = this.$row.children().last();
+ $last_cell.addClass('o_group_pager');
+ // Prevent group to fold when clicking on the pager
+ $last_cell.click(function(e) {
+ e.stopPropagation();
});
- this.$row.children().last()
- .addClass('oe_list_group_pagination')
- .append($prev)
- .append('<span class="oe_list_pager_state"></span>')
- .append($next);
+ this.pager.appendTo($last_cell);
+ }
},
open: function (point_insertion) {
- this.render().insertAfter(point_insertion);
+ this.render();
+ $(this.elements).insertAfter(point_insertion);
var no_subgroups = _(this.datagroup.group_by).isEmpty(),
records_terminated = !this.datagroup.context['group_by_no_leaf'];
if (no_subgroups && records_terminated) {
- this.make_paginator();
+ this.render_group_pager();
}
},
close: function () {
- this.$row.children().last().find('button').remove();
- this.$row.children().last().find('span').remove();
+ if (this.pager) {
+ this.pager.destroy();
+ }
this.records.reset();
+ this.$to_be_removed.remove();
},
/**
* Prefixes ``$node`` with floated spaces in order to indent it relative
@@ -1391,20 +1337,20 @@ ListView.Groups = Class.extend({
self.bind_child_events(child);
child.datagroup = group;
- var $row = child.$row = $('<tr class="oe_group_header">');
+ var $row = child.$row = $('<tr class="o_group_header">');
if (group.openable && group.length) {
$row.click(function (e) {
if (!$row.data('open')) {
$row.data('open', true)
- .find('span.ui-icon')
- .removeClass('ui-icon-triangle-1-e')
- .addClass('ui-icon-triangle-1-s');
+ .find('span.fa')
+ .removeClass('fa-caret-right')
+ .addClass('fa-caret-down');
child.open(self.point_insertion(e.currentTarget));
} else {
$row.removeData('open')
- .find('span.ui-icon')
- .removeClass('ui-icon-triangle-1-s')
- .addClass('ui-icon-triangle-1-e');
+ .find('span.fa')
+ .removeClass('fa-caret-down')
+ .addClass('fa-caret-right');
child.close();
// force recompute the selection as closing group reset properties
var selection = self.get_selection();
@@ -1414,7 +1360,7 @@ ListView.Groups = Class.extend({
}
placeholder.appendChild($row[0]);
- var $group_column = $('<th class="oe_list_group_name">').appendTo($row);
+ var $group_column = $('<th class="o_group_name">').appendTo($row);
// Don't fill this if group_by_no_leaf but no group_by
if (group.grouped_on) {
var row_data = {};
@@ -1449,12 +1395,9 @@ ListView.Groups = Class.extend({
if (group.length && group.openable) {
// Make openable if not terminal group & group_by_no_leaf
- $group_column.prepend('<span class="ui-icon ui-icon-triangle-1-e" style="float: left;">');
+ $group_column.prepend('<span class="fa fa-caret-right" style="padding-right: 5px;">');
} else {
- // Kinda-ugly hack: jquery-ui has no "empty" icon, so set
- // wonky background position to ensure nothing is displayed
- // there but the rest of the behavior is ui-icon's
- $group_column.prepend('<span class="ui-icon" style="float: left; background-position: 150px 150px">');
+ $group_column.prepend('<span class="fa">');
}
}
self.indent($group_column, group.level);
@@ -1511,13 +1454,11 @@ ListView.Groups = Class.extend({
});
this.bind_child_events(list);
- var view = this.view,
- limit = view._limit,
- page = this.datagroup.openable ? this.page : view.page;
+ var view = this.view;
+ var current_min = this.datagroup.openable ? this.current_min : view.current_min;
var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name');
- var options = { offset: page * limit, limit: limit, context: {bin_size: true} };
- //TODO xmo: investigate why we need to put the setTimeout
+ var options = { offset: current_min - 1, limit: view._limit, context: {bin_size: true} };
return $.async_when().then(function() {
return dataset.read_slice(fields, options).then(function (records) {
// FIXME: ignominious hacks, parents (aka form view) should not send two ListView#reload_content concurrently
@@ -1525,34 +1466,12 @@ ListView.Groups = Class.extend({
self.records.reset(null, {silent: true});
}
if (!self.datagroup.openable) {
- view.configure_pager(dataset);
- } else {
- if (dataset.size() == records.length) {
- // only one page
- self.$row.find('td.oe_list_group_pagination').find('button').remove();
- self.$row.find('td.oe_list_group_pagination').find('span').remove();
- } else {
- self.max_page_index = Math.ceil(dataset.size() / limit) - 1;
- self.$row
- .find('.oe_list_pager_state')
- .text(_.str.sprintf(_t("%(page)d/%(page_count)d"), {
- page: page + 1,
- page_count: self.max_page_index + 1
- }))
- .end()
- .find('button[data-pager-action=previous]')
- .toggleClass('disabled', page === 0)
- .end()
- .find('button[data-pager-action=next]')
- .toggleClass('disabled', page === self.max_page_index);
- }
+ // Update the main list view pager
+ view.update_pager(dataset, current_min);
}
self.records.add(records, {silent: true});
list.render();
- if (_.isEmpty(records)) {
- view.no_result();
- }
return list;
});
});
@@ -1580,7 +1499,7 @@ ListView.Groups = Class.extend({
helper: 'clone'
});
if (sequence_field) {
- list.$current.sortable('option', 'handle', '.oe_list_field_handle');
+ list.$current.sortable('option', 'handle', '.o_row_handle');
}
list.$current.sortable('option', {
start: function (e, ui) {
@@ -1631,7 +1550,7 @@ ListView.Groups = Class.extend({
var $el = $('<tbody>');
this.elements = [$el[0]];
- this.datagroup.list(
+ return this.datagroup.list(
_(this.view.visible_columns).chain()
.filter(function (column) { return column.tag === 'field';})
.pluck('name').value(),
@@ -1640,7 +1559,7 @@ ListView.Groups = Class.extend({
self.render_groups(groups));
if (post_render) { post_render(); }
}, function (dataset) {
- self.render_dataset(dataset).then(function (list) {
+ return self.render_dataset(dataset).then(function (list) {
self.children[null] = list;
self.elements =
[list.$current.replaceAll($el)[0]];
@@ -1650,7 +1569,6 @@ ListView.Groups = Class.extend({
self.view.trigger('view_list_rendered');
});
});
- return $el;
},
/**
* Returns the ids of all selected records for this group, and the records
@@ -1669,8 +1587,11 @@ ListView.Groups = Class.extend({
return {ids: ids, records: records};
},
on_records_reset: function () {
+ this.$to_be_removed = $(this.elements);
+ _.each(this.children, function(child){
+ this.$to_be_removed = this.$to_be_removed.add(child.$to_be_removed);
+ }.bind(this));
this.children = {};
- $(this.elements).remove();
},
get_records: function () {
if (_(this.children).isEmpty()) {
@@ -1725,13 +1646,12 @@ var DataGroup = Class.extend({
fields = _.unique((fields || []).concat(this.group_by));
}
var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
- $.when(query).done(function (querygroups) {
+ return $.when(query).then(function (querygroups) {
// leaf node
if (!querygroups) {
var ds = new data.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain());
ds._sort = self.sort;
- ifRecords(ds);
- return;
+ return ifRecords(ds);
}
// internal node
var child_datagroups = _(querygroups).map(function (group) {
@@ -1763,7 +1683,7 @@ var StaticDataGroup = DataGroup.extend({
this.dataset = dataset;
},
list: function (fields, ifGroups, ifRecords) {
- ifRecords(this.dataset);
+ return ifRecords(this.dataset);
}
});
@@ -1811,6 +1731,10 @@ var Column = Class.extend({
C.prototype = this;
return new C(aggregation_func, this[aggregation_func]);
},
+ heading: function () {
+ return _.escape(this.string);
+ },
+ width: function () {},
/**
*
* @param row_data record whose values should be displayed in the cell
@@ -1834,7 +1758,8 @@ var Column = Class.extend({
? ''
: options.value_if_empty;
}
- return this._format(row_data, options);
+ var f = this._format(row_data, options);
+ return (f !== '')? f : '&nbsp;';
},
/**
* Method to override in order to provide alternative HTML content for the
@@ -1890,7 +1815,7 @@ var ColumnBoolean = Column.extend({
* @private
*/
_format: function (row_data, options) {
- return _.str.sprintf('<input type="checkbox" %s readonly="readonly"/>',
+ return _.str.sprintf('<div class="o_checkbox"><input type="checkbox" %s disabled="disabled"/><span/></div>',
row_data[this.id].value ? 'checked="checked"' : '');
}
});
@@ -1965,14 +1890,19 @@ var ColumnHandle = Column.extend({
this._super.apply(this, arguments);
// Handle overrides the field to not be form-editable.
this.modifiers.readonly = true;
+ this.string = ""; // Don't display the column header
+ },
+ heading: function () {
+ return '<span class="o_row_handle fa fa-arrows invisible"></span>';
},
+ width: function () { return 1; },
/**
* Return styling hooks for a drag handle
*
* @private
*/
_format: function (row_data, options) {
- return '<i class="oe_list_handle fa fa-circle"/>';
+ return '<span class="o_row_handle fa fa-arrows"/>';
}
});
@@ -2042,8 +1972,9 @@ var ColumnUrl = Column.extend({
var ColumnMonetary = Column.extend({
_format: function (row_data, options) {
+ var options = pyeval.py_eval(this.options || '{}');
//name of currency field is defined either by field attribute, in view options or we assume it is named currency_id
- var currency_field = (this.options && this.options.currency_field) || this.currency_field || 'currency_id';
+ var currency_field = (_.isEmpty(options) === false && options.currency_field) || this.currency_field || 'currency_id';
var currency_id = row_data[currency_field] && row_data[currency_field].value[0];
var currency = session.get_currency(currency_id);
var digits_precision = this.digits || (currency && currency.digits);
diff --git a/addons/web/static/src/js/views/list_view_editable.js b/addons/web/static/src/js/views/list_view_editable.js
index 8937b8bb36f..f4a4c695334 100644
--- a/addons/web/static/src/js/views/list_view_editable.js
+++ b/addons/web/static/src/js/views/list_view_editable.js
@@ -69,9 +69,9 @@ var Editor = Widget.extend({
if (!(arch.attrs && arch.attrs.version === "7.0")) {
throw new Error("Editor delegate's #edition_view must be a version 7 view");
}
- if (!/\boe_form_container\b/.test(arch.attrs['class'])) {
+ if (!/\bo_list_editable_form\b/.test(arch.attrs['class'])) {
throw new Error("Editor delegate's #edition_view must have the class " +
- "'boe_form_container' on its root element");
+ "'o_list_editable_form' on its root element");
}
return edition_view;
},
@@ -183,12 +183,12 @@ ListView.include(/** @lends instance.web.ListView# */{
}
});
this.on('edit:after', this, function () {
- self.$el.add(self.$buttons).addClass('oe_editing');
+ self.$el.add(self.$buttons).addClass('o-editing');
self.$('.ui-sortable').sortable('disable');
});
this.on('save:after cancel:after', this, function () {
self.$('.ui-sortable').sortable('enable');
- self.$el.add(self.$buttons).removeClass('oe_editing');
+ self.$el.add(self.$buttons).removeClass('o-editing');
});
},
destroy: function () {
@@ -260,7 +260,7 @@ ListView.include(/** @lends instance.web.ListView# */{
this._super.apply(this, arguments);
}
},
- start: function () {
+ load_list: function (data, grouped) {
// tree/@editable takes priority on everything else if present.
var result = this._super.apply(this, arguments);
@@ -271,10 +271,10 @@ ListView.include(/** @lends instance.web.ListView# */{
this.editor = new Editor(this); // Editor is not restartable due to formview not being restartable
if(this.editable()) {
- this.$el.addClass('oe_list_editable');
+ this.$el.addClass('o_list_editable');
return $.when(result, this.editor.prependTo(this.$el).done(this.proxy('setup_events')));
} else {
- this.$el.removeClass('oe_list_editable');
+ this.$el.removeClass('o_list_editable');
}
return result;
},
@@ -366,11 +366,11 @@ ListView.include(/** @lends instance.web.ListView# */{
fields[field_name] = field;
self.fields_for_resize.push({field: field, cell: cell});
}, options).then(function () {
- $recordRow.addClass('oe_edition');
+ $recordRow.addClass('o_row_edition');
self.resize_fields();
// Local function that returns true if field is visible and editable
var is_focusable = function(field) {
- return field && field.$el.is(':visible:not(.oe_readonly)');
+ return field && field.$el.is(':visible:not(.o_readonly)');
};
var focus_field = options && options.focus_field ? options.focus_field : undefined;
if (!is_focusable(fields[focus_field])) {
@@ -423,16 +423,21 @@ ListView.include(/** @lends instance.web.ListView# */{
resize_field: function (field, cell) {
var $cell = $(cell);
field.set_dimensions($cell.outerHeight(), $cell.outerWidth());
- field.$el.addClass('o_temp_visible').css({top: 0, left: 0}).position({
- my: 'left top',
- at: 'left top',
- of: $cell,
- }).removeClass('o_temp_visible');
+ position_element(field.$el, 'left top');
+ position_element(field.$translate, 'right top');
if(field.get('effective_readonly')) {
- field.$el.addClass('oe_readonly');
+ field.$el.addClass('o_readonly');
}
if(field.widget == "handle") {
- field.$el.addClass('oe_list_field_handle');
+ field.$el.addClass('o_row_handle');
+ }
+
+ function position_element($el, pos) {
+ $el.addClass('o_temp_visible').css({top: 0, left: 0}).position({
+ my: pos,
+ at: pos,
+ of: $cell,
+ }).removeClass('o_temp_visible');
}
},
/**
@@ -541,7 +546,7 @@ ListView.include(/** @lends instance.web.ListView# */{
var view = $.extend(true, {}, this.fields_view);
view.arch.tag = 'form';
_.extend(view.arch.attrs, {
- 'class': 'oe_form_container',
+ 'class': 'o_list_editable_form',
version: '7.0'
});
_(view.arch.children).chain()
@@ -817,7 +822,7 @@ ListView.List.include(/** @lends instance.web.ListView.List# */{
var record_id = $(event.currentTarget).data('id');
return this.view.start_edition(
((record_id)? this.records.get(record_id) : null), {
- focus_field: $(event.target).not(".oe_readonly").data('field'),
+ focus_field: $(event.target).not(".o_readonly").data('field'),
}).fail(function() {
return _super.apply(self, args); // The record can't be edited so open it in a modal (use-case: readonly mode)
});
diff --git a/addons/web/static/src/js/views/pivot_view.js b/addons/web/static/src/js/views/pivot_view.js
index 9dc2c4c2cdd..c7710b86605 100644
--- a/addons/web/static/src/js/views/pivot_view.js
+++ b/addons/web/static/src/js/views/pivot_view.js
@@ -21,11 +21,11 @@ var QWeb = core.qweb;
var PivotView = View.extend({
events: {
- 'click .oe-opened': 'on_open_header_click',
- 'click .oe-closed': 'on_closed_header_click',
- 'click .o-field-menu': 'on_field_menu_selection',
+ 'click .o_pivot_header_cell_opened': 'on_open_header_click',
+ 'click .o_pivot_header_cell_closed': 'on_closed_header_click',
+ 'click .o_pivot_field_menu': 'on_field_menu_selection',
'click td': 'on_cell_click',
- 'click .measure-row': 'on_measure_row_click',
+ 'click .o_pivot_measure_row': 'on_measure_row_click',
},
display_name: _lt('Pivot'),
icon: 'fa-table',
@@ -100,6 +100,7 @@ var PivotView = View.extend({
if ((!this.active_measures.length) || this.fields_view.arch.attrs.display_quantity) {
this.active_measures.push('__count__');
}
+
return $.when(fields_def, xlwt_def, this._super()).then(function (fields) {
self.prepare_fields(fields);
// add active measures to the measure list. This is very rarely necessary, but it
@@ -116,14 +117,13 @@ var PivotView = View.extend({
});
},
start: function () {
- this.$table_container = this.$('.o-pivot-table');
- this.$el.toggleClass('oe_enable_linking', this.enable_linking);
+ this.$el.toggleClass('o_enable_linking', this.enable_linking);
var context = {
fields: _.chain(this.groupable_fields).pairs().sortBy(function(f) {
return f[1].string;
}).value(),
};
- this.$field_selection = this.$('.o-field-selection');
+ this.$field_selection = this.$('.o_field_selection');
this.$field_selection.html(QWeb.render('PivotView.FieldSelection', context));
core.bus.on('click', this, function () {
this.$field_selection.find('ul').first().hide();
@@ -161,11 +161,6 @@ var PivotView = View.extend({
render_sidebar: function($node) {
if (this.xlwt_installed && $node && this.options.sidebar) {
this.sidebar = new Sidebar(this, {editable: this.is_action_enabled('edit')});
- this.sidebar.add_items('other', [{
- label: _t("Download xls"),
- callback: this.download_table.bind(this),
- }]);
-
this.sidebar.appendTo($node);
}
},
@@ -207,11 +202,12 @@ var PivotView = View.extend({
},
do_show: function () {
var self = this;
+ var _super = this._super.bind(this);
this.do_push_state({});
- this.data_loaded.done(function () {
+ return this.data_loaded.done(function () {
self.display_table();
+ _super();
});
- return this._super();
},
get_context: function () {
return !this.ready ? {} : {
@@ -222,16 +218,17 @@ var PivotView = View.extend({
},
on_button_click: function (event) {
var $target = $(event.target);
- if ($target.hasClass('oe-pivot-flip')) { return this.flip();}
- if ($target.hasClass('oe-pivot-expand-all')) {return this.expand_all();}
- if ($target.parents('.oe-measure-list').length) {
- var parent = $target.parent(),
- field = parent.data('field');
+ if ($target.hasClass('o_pivot_flip_button')) { return this.flip(); }
+ if ($target.hasClass('o_pivot_expand_button')) { return this.expand_all(); }
+ if ($target.parents('.o_pivot_measures_list').length) {
+ var parent = $target.parent();
+ var field = parent.data('field');
parent.toggleClass('selected');
+ event.preventDefault();
event.stopPropagation();
return this.toggle_measure(field);
}
- if ($target.hasClass('oe-pivot-download')) {
+ if ($target.hasClass('o_pivot_download')) {
return this.download_table();
}
},
@@ -252,21 +249,23 @@ var PivotView = View.extend({
this.expand_header(header, groupbys[header.path.length - 1])
.then(this.proxy('display_table'));
} else {
- var $test = $(event.target);
- var pos = $test.position();
this.last_header_selected = id;
+ var $test = $(event.target);
var $menu = this.$field_selection.find('ul').first();
- $menu.css('top', pos.top + $test.parent().height() - 2);
- $menu.css('left', pos.left + event.offsetX);
+ var position = $test.position();
+ $menu.css({
+ top: position.top + $test.height(),
+ left: position.left + event.offsetX,
+ });
$menu.show();
event.stopPropagation();
}
},
on_cell_click: function (event) {
var $target = $(event.target);
- if ($target.hasClass('oe-closed')
- || $target.hasClass('oe-opened')
- || $target.hasClass('oe-empty')
+ if ($target.hasClass('o_pivot_header_cell_closed')
+ || $target.hasClass('o_pivot_header_cell_opened')
+ || $target.hasClass('o_empty')
|| !this.enable_linking) {
return;
}
@@ -297,11 +296,10 @@ var PivotView = View.extend({
col_id = $target.data('id'),
measure = $target.data('measure');
- this.sort_rows(col_id, measure, $target.hasClass('o-sorted-asc'));
+ this.sort_rows(col_id, measure, $target.hasClass('o_pivot_measure_row_sorted_asc'));
this.display_table();
},
sort_rows: function (col_id, measure, descending) {
- console.log('order', descending);
var self = this;
traverse_tree(this.main_row.root, function (header) {
header.children.sort(compare);
@@ -321,6 +319,7 @@ var PivotView = View.extend({
}
},
on_field_menu_selection: function (event) {
+ event.preventDefault();
var field = $(event.target).parent().data('field'),
interval = $(event.target).data('interval'),
header = this.headers[this.last_header_selected];
@@ -550,13 +549,22 @@ var PivotView = View.extend({
}
return find_path_in_tree(root, path);
},
+ _clean_table: function () {
+ return this.$el
+ .find('table')
+ .remove()
+ .end()
+ .find('.oe_view_nocontent')
+ .remove()
+ .end();
+ },
display_table: function () {
if (!this.active_measures.length || !this.has_data) {
- return this.$table_container.empty().append(QWeb.render('PivotView.nodata'));
+ return this._clean_table().append(QWeb.render('PivotView.nodata'));
}
var $fragment = $(document.createDocumentFragment()),
$table = $('<table>')
- .addClass('table table-hover table-condensed')
+ .addClass('table table-hover table-condensed table-bordered')
.appendTo($fragment),
$thead = $('<thead>').appendTo($table),
$tbody = $('<tbody>').appendTo($table),
@@ -572,8 +580,8 @@ var PivotView = View.extend({
$table.on('hover', 'td', function () {
$table.find('col:eq(' + $(this).index()+')').toggleClass('hover');
});
- this.$table_container.empty().append($fragment);
- this.$table_container.find('.oe-opened,.oe-closed').tooltip();
+ this._clean_table().append($fragment);
+ this.$('.o_pivot_header_cell_opened,.o_pivot_header_cell_closed').tooltip();
},
draw_headers: function ($thead, headers) {
var self = this,
@@ -591,26 +599,27 @@ var PivotView = View.extend({
.text(cell.title)
.attr('rowspan', cell.height)
.attr('colspan', cell.width);
- if (cell.total) {
- $cell.addClass('oe-total');
- }
if (i > 0) {
$cell.attr('title', groupby_labels[i-1]);
}
if (cell.expanded !== undefined) {
- $cell.addClass(cell.expanded ? 'oe-opened' : 'oe-closed');
+ $cell.addClass(cell.expanded ? 'o_pivot_header_cell_opened' : 'o_pivot_header_cell_closed');
$cell.data('id', cell.id);
}
if (cell.measure) {
- $cell.addClass('measure-row text-muted')
- .text(this.measures[cell.measure].string)
- .toggleClass('oe-total', cell.is_bold);
+ $cell.addClass('o_pivot_measure_row text-muted')
+ .text(this.measures[cell.measure].string);
$cell.data('id', cell.id).data('measure', cell.measure);
if (cell.id === this.sorted_column.id && cell.measure === this.sorted_column.measure) {
- $cell.addClass('o-sorted o-sorted-' + this.sorted_column.order);
+ $cell.addClass('o_pivot_measure_row_sorted_' + this.sorted_column.order);
}
}
$row.append($cell);
+
+ $cell.toggleClass('hidden-xs', (cell.expanded !== undefined) || (cell.measure !== undefined && j < headers[i].length - this.active_measures.length));
+ if (cell.height > 1) {
+ $cell.css('padding', 0);
+ }
}
$thead.append($row);
}
@@ -635,7 +644,7 @@ var PivotView = View.extend({
.text(rows[i].title)
.data('id', rows[i].id)
.css('padding-left', (5 + rows[i].indent * 30) + 'px')
- .addClass(rows[i].expanded ? 'oe-opened' : 'oe-closed');
+ .addClass(rows[i].expanded ? 'o_pivot_header_cell_opened' : 'o_pivot_header_cell_closed');
if (rows[i].indent > 0) $header.attr('title', groupby_labels[rows[i].indent - 1]);
$header.appendTo($row);
for (j = 0; j < length; j++) {
@@ -643,12 +652,15 @@ var PivotView = View.extend({
$cell = $('<td>')
.data('id', rows[i].id)
.data('col_id', rows[i].col_ids[Math.floor(j / nbr_measures)])
- .toggleClass('oe-empty', !value)
- .text(value);
+ .toggleClass('o_empty', !value)
+ .text(value)
+ .addClass('o_pivot_cell_value text-right');
if (((j >= length - this.active_measures.length) && display_total) || i === 0){
$cell.css('font-weight', 'bold');
}
$row.append($cell);
+
+ $cell.toggleClass('hidden-xs', j < length - this.active_measures.length);
}
$tbody.append($row);
}
diff --git a/addons/web/static/src/js/views/search_filters.js b/addons/web/static/src/js/views/search_filters.js
index c6193488578..041cda35580 100644
--- a/addons/web/static/src/js/views/search_filters.js
+++ b/addons/web/static/src/js/views/search_filters.js
@@ -12,9 +12,9 @@ var _lt = core._lt;
var ExtendedSearchProposition = Widget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{
template: 'SearchView.extended_search.proposition',
events: {
- 'change .searchview_extended_prop_field': 'changed',
- 'change .searchview_extended_prop_op': 'operator_changed',
- 'click .searchview_extended_delete_prop': function (e) {
+ 'change .o_searchview_extended_prop_field': 'changed',
+ 'change .o_searchview_extended_prop_op': 'operator_changed',
+ 'click .o_searchview_extended_delete_prop': function (e) {
e.stopPropagation();
this.getParent().remove_proposition(this);
},
@@ -40,13 +40,13 @@ var ExtendedSearchProposition = Widget.extend(/** @lends instance.web.search.Ext
return this._super().done(this.proxy('changed'));
},
changed: function() {
- var nval = this.$(".searchview_extended_prop_field").val();
+ var nval = this.$(".o_searchview_extended_prop_field").val();
if(this.attrs.selected === null || this.attrs.selected === undefined || nval != this.attrs.selected.name) {
this.select_field(_.detect(this.fields, function(x) {return x.name == nval;}));
}
},
operator_changed: function (e) {
- var $value = this.$('.searchview_extended_prop_value');
+ var $value = this.$('.o_searchview_extended_prop_value');
switch ($(e.target).val()) {
case '∃':
case '∄':
@@ -66,7 +66,7 @@ var ExtendedSearchProposition = Widget.extend(/** @lends instance.web.search.Ext
if(this.attrs.selected !== null && this.attrs.selected !== undefined) {
this.value.destroy();
this.value = null;
- this.$('.searchview_extended_prop_op').html('');
+ this.$('.o_searchview_extended_prop_op').html('');
}
this.attrs.selected = field;
if(field === null || field === undefined) {
@@ -80,9 +80,9 @@ var ExtendedSearchProposition = Widget.extend(/** @lends instance.web.search.Ext
_.each(this.value.operators, function(operator) {
$('<option>', {value: operator.value})
.text(String(operator.text))
- .appendTo(self.$('.searchview_extended_prop_op'));
+ .appendTo(self.$('.o_searchview_extended_prop_op'));
});
- var $value_loc = this.$('.searchview_extended_prop_value').show().empty();
+ var $value_loc = this.$('.o_searchview_extended_prop_value').show().empty();
this.value.appendTo($value_loc);
},
@@ -90,7 +90,7 @@ var ExtendedSearchProposition = Widget.extend(/** @lends instance.web.search.Ext
if (this.attrs.selected === null || this.attrs.selected === undefined)
return null;
var field = this.attrs.selected,
- op_select = this.$('.searchview_extended_prop_op')[0],
+ op_select = this.$('.o_searchview_extended_prop_op')[0],
operator = op_select.options[op_select.selectedIndex];
return {
@@ -155,7 +155,10 @@ ExtendedSearchProposition.Field = Widget.extend({
});
ExtendedSearchProposition.Char = ExtendedSearchProposition.Field.extend({
- template: 'SearchView.extended_search.proposition.char',
+ tagName: 'input',
+ attributes: {
+ type: 'text'
+ },
operators: [
{value: "ilike", text: _lt("contains")},
{value: "not ilike", text: _lt("doesn't contain")},
@@ -170,7 +173,7 @@ ExtendedSearchProposition.Char = ExtendedSearchProposition.Field.extend({
});
ExtendedSearchProposition.DateTime = ExtendedSearchProposition.Field.extend({
- template: 'SearchView.extended_search.proposition.empty',
+ tagName: 'span',
operators: [
{value: "=", text: _lt("is equal to")},
{value: "!=", text: _lt("is not equal to")},
@@ -204,7 +207,11 @@ ExtendedSearchProposition.Date = ExtendedSearchProposition.DateTime.extend({
});
ExtendedSearchProposition.Integer = ExtendedSearchProposition.Field.extend({
- template: 'SearchView.extended_search.proposition.integer',
+ tagName: 'input',
+ attributes: {
+ type: 'number',
+ value: '0',
+ },
operators: [
{value: "=", text: _lt("is equal to")},
{value: "!=", text: _lt("is not equal to")},
@@ -280,7 +287,7 @@ ExtendedSearchProposition.Selection = ExtendedSearchProposition.Field.extend({
});
ExtendedSearchProposition.Boolean = ExtendedSearchProposition.Field.extend({
- template: 'SearchView.extended_search.proposition.empty',
+ tagName: 'span',
operators: [
{value: "=", text: _lt("is true")},
{value: "!=", text: _lt("is false")}
diff --git a/addons/web/static/src/js/views/search_inputs.js b/addons/web/static/src/js/views/search_inputs.js
index 44b79999259..0263f270172 100644
--- a/addons/web/static/src/js/views/search_inputs.js
+++ b/addons/web/static/src/js/views/search_inputs.js
@@ -541,6 +541,7 @@ var FilterGroup = Input.extend(/** @lends instance.web.search.FilterGroup# */{
return {
category: _t("Filter"),
icon: this.icon,
+ separator: _t(" or "),
values: values,
field: this
};
@@ -617,6 +618,7 @@ var FilterGroup = Input.extend(/** @lends instance.web.search.FilterGroup# */{
});
},
toggle_filter: function (e) {
+ e.preventDefault();
e.stopPropagation();
this.toggle(this.filters[Number($(e.target).parent().data('index'))]);
},
@@ -686,6 +688,7 @@ var GroupbyGroup = FilterGroup.extend({
return {
category: _t("Group By"),
icon: this.icon,
+ separator: " > ",
values: values,
field: this.searchview._s_groupby
};
diff --git a/addons/web/static/src/js/views/search_menus.js b/addons/web/static/src/js/views/search_menus.js
index 9edb817c567..10ab44ef30c 100644
--- a/addons/web/static/src/js/views/search_menus.js
+++ b/addons/web/static/src/js/views/search_menus.js
@@ -15,12 +15,16 @@ return Widget.extend({
'click li': function (event) {
event.stopImmediatePropagation();
},
- 'click .oe-save-search a': function () {
+ 'click li a': function (event) {
+ event.preventDefault();
+ },
+ 'click .o_save_search a': function (event) {
+ event.preventDefault();
this.toggle_save_menu();
},
- 'click .oe-save-name button': 'save_favorite',
+ 'click .o_save_name button': 'save_favorite',
'hidden.bs.dropdown': 'close_menus',
- 'keyup .oe-save-name input': function (ev) {
+ 'keyup .o_save_name input': function (ev) {
if (ev.which === $.ui.keyCode.ENTER) {
this.save_favorite();
}
@@ -38,8 +42,8 @@ return Widget.extend({
start: function () {
var self = this;
this.$filters = {};
- this.$save_search = this.$('.oe-save-search');
- this.$save_name = this.$('.oe-save-name');
+ this.$save_search = this.$('.o_save_search');
+ this.$save_name = this.$('.o_save_name');
this.$inputs = this.$save_name.find('input');
this.$divider = this.$('.divider');
this.$inputs.eq(0).val(this.searchview.get_title());
@@ -116,7 +120,6 @@ return Widget.extend({
model_id: this.target_model,
context: results.context,
domain: results.domain,
- sort: JSON.stringify(this.searchview.dataset._sort),
is_default: default_filter,
action_id: this.action_id,
};
@@ -204,12 +207,11 @@ return Widget.extend({
this.$divider.show();
if (!(key in this.$filters)) {
var $filter = $('<li>')
- .addClass(filter.user_id ? 'oe_searchview_custom_private'
- : 'oe_searchview_custom_public')
- .toggleClass('oe_searchview_custom_default', filter.is_default)
- .append($('<a>').text(filter.name))
+ .addClass(filter.user_id ? 'o-searchview-custom-private'
+ : 'o-searchview-custom-public')
+ .append($('<a>', {'href': '#'}).text(filter.name))
.append($('<span>', {
- class: 'fa fa-trash-o remove-filter',
+ class: 'fa fa-trash-o o-remove-filter',
on: {
click: function (event) {
event.stopImmediatePropagation();
@@ -235,13 +237,6 @@ return Widget.extend({
}
this.query.reset([this.facet_for(filter)], {
preventSearch: preventSearch || false});
-
- // Load sort settings on view
- if (!_.isUndefined(filter.sort)){
- var sort_items = JSON.parse(filter.sort);
- this.searchview.dataset.set_sort(sort_items);
- }
-
this.$filters[this.key_for(filter)].addClass('selected');
},
remove_filter: function (filter, $filter, key) {
@@ -277,16 +272,20 @@ var Widget = require('web.Widget');
return Widget.extend({
template: 'SearchView.FilterMenu',
events: {
- 'click .oe-add-filter': function () {
+ 'click .o_add_filter': function (event) {
+ event.preventDefault();
this.toggle_custom_filter_menu();
},
- 'click li': function (event) {event.stopImmediatePropagation();},
+ 'click li': function (event) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ },
'hidden.bs.dropdown': function () {
this.toggle_custom_filter_menu(false);
},
- 'click .oe-add-condition': 'append_proposition',
- 'click .oe-apply-filter': 'commit_search',
- 'keyup .searchview_extended_prop_value': function (ev) {
+ 'click .o_add_condition': 'append_proposition',
+ 'click .o_apply_filter': 'commit_search',
+ 'keyup .o_searchview_extended_prop_value': function (ev) {
if (ev.which === $.ui.keyCode.ENTER) {
this.commit_search();
}
@@ -301,10 +300,10 @@ return Widget.extend({
},
start: function () {
var self = this;
- this.$menu = this.$('.filters-menu');
- this.$add_filter = this.$('.oe-add-filter');
- this.$apply_filter = this.$('.oe-apply-filter');
- this.$add_filter_menu = this.$('.oe-add-filter-menu');
+ this.$menu = this.$('.o_filters_menu');
+ this.$add_filter = this.$('.o_add_filter');
+ this.$apply_filter = this.$('.o_apply_filter');
+ this.$add_filter_menu = this.$('.o_add_filter_menu');
_.each(this.filters, function (group) {
if (group.is_visible()) {
group.insertBefore(self.$add_filter);
@@ -340,7 +339,7 @@ return Widget.extend({
.toggleClass('o_closed_menu', !self.custom_filters_open)
.toggleClass('o_open_menu', self.custom_filters_open);
self.$add_filter_menu.toggle(self.custom_filters_open);
- self.$('.oe-filter-condition').toggle(self.custom_filters_open);
+ self.$('.o_filter_condition').toggle(self.custom_filters_open);
});
},
append_proposition: function () {
@@ -402,7 +401,8 @@ return Widget.extend({
'hidden.bs.dropdown': function () {
this.toggle_add_menu(false);
},
- 'click .add-custom-group a': function () {
+ 'click .o_add_custom_group a': function (event) {
+ event.preventDefault();
this.toggle_add_menu();
},
},
@@ -413,13 +413,13 @@ return Widget.extend({
this.searchview = parent;
},
start: function () {
- this.$menu = this.$('.group-by-menu');
+ this.$menu = this.$('.o_group_by_menu');
var divider = this.$menu.find('.divider');
_.invoke(this.groups, 'insertBefore', divider);
if (this.groups.length) {
divider.show();
}
- this.$add_group = this.$menu.find('.add-custom-group');
+ this.$add_group = this.$menu.find('.o_add_custom_group');
},
get_fields: function () {
var self = this;
@@ -435,9 +435,9 @@ return Widget.extend({
self.groupable_fields = _.sortBy(filter_group_field, 'string');
self.$menu.append(QWeb.render('GroupByMenuSelector', self));
- self.$add_group_menu = self.$('.oe-add-group');
- self.$group_selector = self.$('.oe-group-selector');
- self.$('.oe-select-group').click(function () {
+ self.$add_group_menu = self.$('.o_add_group');
+ self.$group_selector = self.$('.o_group_selector');
+ self.$('.o_select_group').click(function () {
self.toggle_add_menu(false);
var field = self.$group_selector.find(':selected').data('name');
self.add_groupby_to_menu(field);
diff --git a/addons/web/static/src/js/views/search_view.js b/addons/web/static/src/js/views/search_view.js
index 125b7c9c152..fa1794cdc7c 100644
--- a/addons/web/static/src/js/views/search_view.js
+++ b/addons/web/static/src/js/views/search_view.js
@@ -2,6 +2,7 @@ odoo.define('web.SearchView', function (require) {
"use strict";
var AutoComplete = require('web.AutoComplete');
+var config = require('web.config');
var core = require('web.core');
var data_manager = require('web.data_manager');
var FavoriteMenu = require('web.FavoriteMenu');
@@ -9,9 +10,9 @@ var FilterMenu = require('web.FilterMenu');
var GroupByMenu = require('web.GroupByMenu');
var pyeval = require('web.pyeval');
var search_inputs = require('web.search_inputs');
-var utils = require('web.utils');
var View = require('web.View');
var Widget = require('web.Widget');
+var _t = core._t;
var Backbone = window.Backbone;
@@ -134,125 +135,32 @@ var InputView = Widget.extend({
template: 'SearchView.InputView',
events: {
focus: function () { this.trigger('focused', this); },
- blur: function () { this.$el.text(''); this.trigger('blurred', this); },
+ blur: function () { this.$el.val(''); this.trigger('blurred', this); },
keydown: 'onKeydown',
- paste: 'onPaste',
- },
- getSelection: function () {
- this.el.normalize();
- // get Text node
- var root = this.el.childNodes[0];
- if (!root || !root.textContent) {
- // if input does not have a child node, or the child node is an
- // empty string, then the selection can only be (0, 0)
- return {start: 0, end: 0};
- }
- var range = window.getSelection().getRangeAt(0);
- // In Firefox, depending on the way text is selected (drag, double- or
- // triple-click) the range may start or end on the parent of the
- // selected text node‽ Check for this condition and fixup the range
- // note: apparently with C-a this can go even higher?
- if (range.startContainer === this.el && range.startOffset === 0) {
- range.setStart(root, 0);
- }
- if (range.endContainer === this.el && range.endOffset === 1) {
- range.setEnd(root, root.length);
- }
- utils.assert(range.startContainer === root,
- "selection should be in the input view");
- utils.assert(range.endContainer === root,
- "selection should be in the input view");
- return {
- start: range.startOffset,
- end: range.endOffset
- };
},
onKeydown: function (e) {
- this.el.normalize();
- var sel;
switch (e.which) {
- // Do not insert newline, but let it bubble so searchview can use it
- case $.ui.keyCode.ENTER:
- e.preventDefault();
- break;
+ case $.ui.keyCode.BACKSPACE:
+ if(this.$el.val() === '') {
+ var preceding = this.getParent().siblingSubview(this, -1);
+ if (preceding && (preceding instanceof FacetView)) {
+ preceding.model.destroy();
+ }
+ }
+ break;
- // FIXME: may forget content if non-empty but caret at index 0, ok?
- case $.ui.keyCode.BACKSPACE:
- sel = this.getSelection();
- if (sel.start === 0 && sel.start === sel.end) {
- e.preventDefault();
- var preceding = this.getParent().siblingSubview(this, -1);
- if (preceding && (preceding instanceof FacetView)) {
- preceding.model.destroy();
+ case $.ui.keyCode.LEFT: // Stop propagation to parent if not at beginning of input value
+ if(this.el.selectionStart > 0) {
+ e.stopPropagation();
}
- }
- break;
+ break;
- // let left/right events propagate to view if caret is at input border
- // and not a selection
- case $.ui.keyCode.LEFT:
- sel = this.getSelection();
- if (sel.start !== 0 || sel.start !== sel.end) {
- e.stopPropagation();
- }
- break;
- case $.ui.keyCode.RIGHT:
- sel = this.getSelection();
- var len = this.$el.text().length;
- if (sel.start !== len || sel.start !== sel.end) {
- e.stopPropagation();
- }
- break;
+ case $.ui.keyCode.RIGHT: // Stop propagation to parent if not at end of input value
+ if(this.el.selectionStart < this.$el.val().length) {
+ e.stopPropagation();
+ }
+ break;
}
- },
- setCursorAtEnd: function () {
- this.el.normalize();
- var sel = window.getSelection();
- sel.removeAllRanges();
- var range = document.createRange();
- // in theory, range.selectNodeContents should work here. In practice,
- // MSIE9 has issues from time to time, instead of selecting the inner
- // text node it would select the reference node instead (e.g. in demo
- // data, company news, copy across the "Company News" link + the title,
- // from about half the link to half the text, paste in search box then
- // hit the left arrow key, getSelection would blow up).
- //
- // Explicitly selecting only the inner text node (only child node
- // since we've normalized the parent) avoids the issue
- range.selectNode(this.el.childNodes[0]);
- range.collapse(false);
- sel.addRange(range);
- },
- onPaste: function () {
- this.el.normalize();
- // In MSIE and Webkit, it is possible to get various representations of
- // the clipboard data at this point e.g.
- // window.clipboardData.getData('Text') and
- // event.clipboardData.getData('text/plain') to ensure we have a plain
- // text representation of the object (and probably ensure the object is
- // pastable as well, so nobody puts an image in the search view)
- // (nb: since it's not possible to alter the content of the clipboard
- // — at least in Webkit — to ensure only textual content is available,
- // using this would require 1. getting the text data; 2. manually
- // inserting the text data into the content; and 3. cancelling the
- // paste event)
- //
- // But Firefox doesn't support the clipboard API (as of FF18)
- // although it correctly triggers the paste event (Opera does not even
- // do that) => implement lowest-denominator system where onPaste
- // triggers a followup "cleanup" pass after the data has been pasted
- setTimeout(function () {
- // Read text content (ignore pasted HTML)
- var data = this.$el.text();
- if (!data)
- return;
- // paste raw text back in
- this.$el.empty().text(data);
- this.el.normalize();
- // Set the cursor at the end of the text, so the cursor is not lost
- // in some kind of error-spawning limbo.
- this.setCursorAtEnd();
- }.bind(this), 0);
}
});
@@ -262,7 +170,7 @@ var FacetView = Widget.extend({
'focus': function () { this.trigger('focused', this); },
'blur': function () { this.trigger('blurred', this); },
'click': function (e) {
- if ($(e.target).is('.oe_facet_remove')) {
+ if ($(e.target).hasClass('o_facet_remove')) {
this.model.destroy();
return false;
}
@@ -272,10 +180,10 @@ var FacetView = Widget.extend({
'keydown': function (e) {
var keys = $.ui.keyCode;
switch (e.which) {
- case keys.BACKSPACE:
- case keys.DELETE:
- this.model.destroy();
- return false;
+ case keys.BACKSPACE:
+ case keys.DELETE:
+ this.model.destroy();
+ return false;
}
}
},
@@ -290,9 +198,12 @@ var FacetView = Widget.extend({
},
start: function () {
var self = this;
- var $e = this.$('> span:last-child');
+ var $e = this.$('.o_facet_values').last();
return $.when(this._super()).then(function () {
- return $.when.apply(null, self.model.values.map(function (value) {
+ return $.when.apply(null, self.model.values.map(function (value, index) {
+ if (index > 0) {
+ $('<span/>', {html: self.model.get('separator') || _t(" or ")}).addClass('o_facet_values_sep').appendTo($e);
+ }
return new FacetValueView(self, value).appendTo($e);
}));
});
@@ -319,49 +230,34 @@ var FacetValueView = Widget.extend({
});
var SearchView = View.extend({
- defaults: _.extend({}, View.prototype.defaults, {
- hidden: false,
- disable_filters: false,
- disable_groupby: false,
- disable_favorites: false,
- disable_custom_filters: false,
- }),
events: {
- // focus last input if view itself is clicked
- 'click': function (e) {
- if (e.target === this.$('.oe_searchview_facets')[0]) {
- this.$('.oe_searchview_input:last').focus();
- }
- },
- // search button
- 'click div.oe_searchview_search': function (e) {
- e.stopImmediatePropagation();
- this.do_search();
- },
- 'click .oe_searchview_unfold_drawer': function (e) {
- e.stopImmediatePropagation();
- $(e.target).toggleClass('fa-caret-down fa-caret-up');
+ 'click .o_searchview_more': function (e) {
+ $(e.target).toggleClass('fa-search-plus fa-search-minus');
localStorage.visible_search_menu = (localStorage.visible_search_menu !== 'true');
this.toggle_buttons();
},
- 'keydown .oe_searchview_input, .oe_searchview_facet': function (e) {
+ 'keydown .o_searchview_input, .o_searchview_facet': function (e) {
switch(e.which) {
- case $.ui.keyCode.LEFT:
- this.focusPreceding(e.target);
- e.preventDefault();
- break;
- case $.ui.keyCode.RIGHT:
- if (!this.autocomplete.is_expandable()) {
- this.focusFollowing(e.target);
- }
- e.preventDefault();
- break;
+ case $.ui.keyCode.LEFT:
+ this.focusPreceding(e.target);
+ e.preventDefault();
+ break;
+ case $.ui.keyCode.RIGHT:
+ if(!this.autocomplete.is_expandable()) {
+ this.focusFollowing(e.target);
+ }
+ e.preventDefault();
+ break;
}
},
- 'autocompleteopen': function () {
- this.$el.autocomplete('widget').css('z-index', 9999);
- },
},
+ defaults: _.extend({}, View.prototype.defaults, {
+ hidden: false,
+ disable_filters: false,
+ disable_groupby: false,
+ disable_favorites: false,
+ disable_custom_filters: false,
+ }),
template: "SearchView",
/**
@@ -377,9 +273,9 @@ var SearchView = View.extend({
*/
init: function() {
this._super.apply(this, arguments);
- this.title = this.options.action && this.options.action.name;
- this.action_id = this.options.action && this.options.action.id;
this.query = undefined;
+ this.title = this.options.action && this.options.action.name;
+ this.action_id = this.options.action && this.options.action.id;
this.search_fields = [];
this.filters = [];
this.groupbys = [];
@@ -408,15 +304,14 @@ var SearchView = View.extend({
this.do_hide();
}
this.toggle_visibility(false);
- this.$facets_container = this.$('div.oe_searchview_facets');
this.setup_global_completion();
this.query = new SearchQuery()
.on('add change reset remove', this.proxy('do_search'))
.on('change', this.proxy('renderChangedFacets'))
.on('add reset remove', this.proxy('renderFacets'));
- this.$('.oe_searchview_unfold_drawer')
- .toggleClass('fa-caret-down', !this.visible_filters)
- .toggleClass('fa-caret-up', this.visible_filters);
+ this.$('.o_searchview_more')
+ .toggleClass('fa-search-minus', this.visible_filters)
+ .toggleClass('fa-search-plus', !this.visible_filters);
var menu_defs = [];
this.prepare_search_inputs();
if (this.$buttons) {
@@ -516,8 +411,8 @@ var SearchView = View.extend({
if (this.$buttons) {
this.$buttons.toggle(!this.headless && is_visible && this.visible_filters);
}
- if (!this.headless && is_visible) {
- this.$('div.oe_searchview_input').last().focus();
+ if (!config.device.touch && config.device.size_class >= config.device.SIZES.SM) {
+ this.$('input').focus();
}
},
toggle_buttons: function (is_visible) {
@@ -535,7 +430,7 @@ var SearchView = View.extend({
source: this.proxy('complete_global_search'),
select: this.proxy('select_completion'),
get_search_string: function () {
- return self.$('div.oe_searchview_input').text();
+ return self.$('.o_searchview_input').val().trim();
},
});
this.autocomplete.appendTo(this.$el);
@@ -569,10 +464,11 @@ var SearchView = View.extend({
*/
select_completion: function (e, ui) {
e.preventDefault();
- var input_index = _(this.input_subviews).indexOf(
- this.subviewForRoot(
- this.$('div.oe_searchview_input:focus')[0]));
- this.query.add(ui.item.facet, {at: input_index / 2});
+ if(ui.item.facet.values && ui.item.facet.values.length && String(ui.item.facet.values[0].value).trim() !== "") {
+ this.query.add(ui.item.facet);
+ } else {
+ this.query.trigger('add');
+ }
},
subviewForRoot: function (subview_root) {
return _(this.input_subviews).detect(function (subview) {
@@ -609,34 +505,21 @@ var SearchView = View.extend({
_.invoke(this.input_subviews, 'destroy');
this.input_subviews = [];
- var i = new InputView(this);
- started.push(i.appendTo(this.$facets_container));
- this.input_subviews.push(i);
this.query.each(function (facet) {
var f = new FacetView(this, facet);
- started.push(f.appendTo(self.$facets_container));
+ started.push(f.appendTo(self.$el));
self.input_subviews.push(f);
-
- var i = new InputView(this);
- started.push(i.appendTo(self.$facets_container));
- self.input_subviews.push(i);
}, this);
+ var i = new InputView(this);
+ started.push(i.appendTo(self.$el));
+ self.input_subviews.push(i);
_.each(this.input_subviews, function (childView) {
childView.on('focused', self, self.proxy('childFocused'));
childView.on('blurred', self, self.proxy('childBlurred'));
});
$.when.apply(null, started).then(function () {
- if (options && options.focus_input === false) return;
- var input_to_focus;
- // options.at: facet inserted at given index, focus next input
- // otherwise just focus last input
- if (!options || typeof options.at !== 'number') {
- input_to_focus = _.last(self.input_subviews);
- } else {
- input_to_focus = self.input_subviews[(options.at + 1) * 2];
- }
- input_to_focus.$el.focus();
+ _.last(self.input_subviews).$el.focus();
});
},
childFocused: function () {
@@ -726,230 +609,3 @@ _.extend(SearchView, {
return SearchView;
});
-
-odoo.define('web.AutoComplete', function (require) {
-"use strict";
-
-var Widget = require('web.Widget');
-
-return Widget.extend({
- template: "SearchView.autocomplete",
-
- // Parameters for autocomplete constructor:
- //
- // parent: this is used to detect keyboard events
- //
- // options.source: function ({term:query}, callback). This function will be called to
- // obtain the search results corresponding to the query string. It is assumed that
- // options.source will call callback with the results.
- // options.select: function (ev, {item: {facet:facet}}). Autocomplete widget will call
- // that function when a selection is made by the user
- // options.get_search_string: function (). This function will be called by autocomplete
- // to obtain the current search string.
- init: function (parent, options) {
- this._super(parent);
- this.$input = parent.$el;
- this.source = options.source;
- this.select = options.select;
- this.get_search_string = options.get_search_string;
-
- this.current_result = null;
-
- this.searching = true;
- this.search_string = '';
- this.current_search = null;
- },
- start: function () {
- var self = this;
- this.$input.on('keyup', function (ev) {
- if (ev.which === $.ui.keyCode.RIGHT) {
- self.searching = true;
- ev.preventDefault();
- return;
- }
- if (ev.which === $.ui.keyCode.ENTER) {
- if (self.search_string.length) {
- self.select_item(ev);
- }
- return;
- }
- var search_string = self.get_search_string();
- if (self.search_string !== search_string) {
- if (search_string.length) {
- self.search_string = search_string;
- self.initiate_search(search_string);
- } else {
- self.close();
- }
- }
- });
- this.$input.on('keypress', function (ev) {
- self.search_string = self.search_string + String.fromCharCode(ev.which);
- if (self.search_string.length) {
- self.searching = true;
- var search_string = self.search_string;
- self.initiate_search(search_string);
- } else {
- self.close();
- }
- });
- this.$input.on('keydown', function (ev) {
- switch (ev.which) {
- case $.ui.keyCode.ENTER:
-
- // TAB and direction keys are handled at KeyDown because KeyUp
- // is not guaranteed to fire.
- // See e.g. https://github.com/aef-/jquery.masterblaster/issues/13
- case $.ui.keyCode.TAB:
- if (self.search_string.length) {
- self.select_item(ev);
- }
- break;
- case $.ui.keyCode.DOWN:
- self.move('down');
- self.searching = false;
- ev.preventDefault();
- break;
- case $.ui.keyCode.UP:
- self.move('up');
- self.searching = false;
- ev.preventDefault();
- break;
- case $.ui.keyCode.RIGHT:
- self.searching = false;
- var current = self.current_result;
- if (current && current.expand && !current.expanded) {
- self.expand();
- self.searching = true;
- }
- ev.preventDefault();
- break;
- case $.ui.keyCode.ESCAPE:
- self.close();
- self.searching = false;
- break;
- }
- });
- },
- initiate_search: function (query) {
- if (query === this.search_string && query !== this.current_search) {
- this.search(query);
- }
- },
- search: function (query) {
- var self = this;
- this.current_search = query;
- this.source({term:query}, function (results) {
- if (results.length) {
- self.render_search_results(results);
- self.focus_element(self.$('li:first-child'));
- } else {
- self.close();
- }
- });
- },
- render_search_results: function (results) {
- var self = this;
- var $list = this.$('ul');
- $list.empty();
- var render_separator = false;
- results.forEach(function (result) {
- if (result.is_separator) {
- if (render_separator)
- $list.append($('<li>').addClass('oe-separator'));
- render_separator = false;
- } else {
- var $item = self.make_list_item(result).appendTo($list);
- result.$el = $item;
- render_separator = true;
- }
- });
- this.show();
- },
- make_list_item: function (result) {
- var self = this;
- var $li = $('<li>')
- .hover(function () {self.focus_element($li);})
- .mousedown(function (ev) {
- if (ev.button === 0) { // left button
- self.select(ev, {item: {facet: result.facet}});
- self.close();
- } else {
- ev.preventDefault();
- }
- })
- .data('result', result);
- if (result.expand) {
- var $expand = $('<span class="oe-expand">').text('▶').appendTo($li);
- $expand.mousedown(function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
- if (result.expanded)
- self.fold();
- else
- self.expand();
- });
- result.expanded = false;
- }
- if (result.indent) $li.addClass('oe-indent');
- $li.append($('<span>').html(result.label));
- return $li;
- },
- expand: function () {
- var self = this;
- var current_result = this.current_result;
- current_result.expand(this.get_search_string()).then(function (results) {
- (results || [{label: '(no result)'}]).reverse().forEach(function (result) {
- result.indent = true;
- var $li = self.make_list_item(result);
- current_result.$el.after($li);
- });
- current_result.expanded = true;
- current_result.$el.find('span.oe-expand').html('▼');
- });
- },
- fold: function () {
- var $next = this.current_result.$el.next();
- while ($next.hasClass('oe-indent')) {
- $next.remove();
- $next = this.current_result.$el.next();
- }
- this.current_result.expanded = false;
- this.current_result.$el.find('span.oe-expand').html('▶');
- },
- focus_element: function ($li) {
- this.$('li').removeClass('oe-selection-focus');
- $li.addClass('oe-selection-focus');
- this.current_result = $li.data('result');
- },
- select_item: function (ev) {
- if (this.current_result.facet) {
- this.select(ev, {item: {facet: this.current_result.facet}});
- this.close();
- }
- },
- show: function () {
- this.$el.show();
- },
- close: function () {
- this.current_search = null;
- this.search_string = '';
- this.searching = true;
- this.$el.hide();
- },
- move: function (direction) {
- var $next;
- if (direction === 'down') {
- $next = this.$('li.oe-selection-focus').nextAll(':not(.oe-separator)').first();
- if (!$next.length) $next = this.$('li:first-child');
- } else {
- $next = this.$('li.oe-selection-focus').prevAll(':not(.oe-separator)').first();
- if (!$next.length) $next = this.$('li:not(.oe-separator)').last();
- }
- this.focus_element($next);
- },
- is_expandable: function () {
- return !!this.$('.oe-selection-focus .oe-expand').length;
- },
-});
-});
diff --git a/addons/web/static/src/js/views/tree_view.js b/addons/web/static/src/js/views/tree_view.js
index 6246a1494f5..aeb206257fe 100644
--- a/addons/web/static/src/js/views/tree_view.js
+++ b/addons/web/static/src/js/views/tree_view.js
@@ -87,7 +87,7 @@ var TreeView = View.extend({
return;
}
- var $select = self.$el.find('select')
+ var $select = self.$('select')
.change(function () {
var $option = $(this).find(':selected');
self.getdata($option.val(), $option.data('children'));
@@ -155,8 +155,8 @@ var TreeView = View.extend({
children_ids = record[self.children_field];
_(children_ids).each(function(childid) {
- if (self.$el.find('[id=treerow_' + childid + '][data-row-parent-id='+ record_id +']').length ) {
- if (self.$el.find('[id=treerow_' + childid + '][data-row-parent-id='+ record_id +']').is(':hidden')) {
+ if (self.$('[id=treerow_' + childid + '][data-row-parent-id='+ record_id +']').length ) {
+ if (self.$('[id=treerow_' + childid + '][data-row-parent-id='+ record_id +']').is(':hidden')) {
is_loaded = -1;
} else {
is_loaded++;
@@ -164,7 +164,7 @@ var TreeView = View.extend({
}
});
if (is_loaded === 0) {
- if (!$this.parent().hasClass('oe_open')) {
+ if (!$this.parent().hasClass('o_open')) {
self.getdata(record_id, children_ids);
}
} else {
@@ -180,7 +180,7 @@ var TreeView = View.extend({
_(records).each(function (record) {
self.records[record.id] = record;
});
- var $curr_node = self.$el.find('#treerow_' + id);
+ var $curr_node = self.$('#treerow_' + id);
var children_rows = QWeb.render('TreeView.rows', {
'records': records,
'children_field': self.children_field,
@@ -192,10 +192,10 @@ var TreeView = View.extend({
'row_parent_id': id
});
if ($curr_node.length) {
- $curr_node.addClass('oe_open');
+ $curr_node.addClass('o_open');
$curr_node.after(children_rows);
} else {
- self.$el.find('tbody').html(children_rows);
+ self.$('tbody').html(children_rows);
}
});
},
@@ -229,11 +229,11 @@ var TreeView = View.extend({
// show & hide the contents
showcontent: function (curnode,record_id, show) {
- curnode.parent('tr').toggleClass('oe_open', show);
+ curnode.parent('tr').toggleClass('o_open', show);
_(this.records[record_id][this.children_field]).each(function (child_id) {
- var $child_row = this.$el.find('[id=treerow_' + child_id + '][data-row-parent-id='+ curnode.data('id') +']');
- if ($child_row.hasClass('oe_open')) {
- $child_row.toggleClass('oe_open',show);
+ var $child_row = this.$('[id=treerow_' + child_id + '][data-row-parent-id='+ curnode.data('id') +']');
+ if ($child_row.hasClass('o_open')) {
+ $child_row.toggleClass('o_open',show);
this.showcontent($child_row, child_id, false);
}
$child_row.toggle(show);