summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/form_widgets.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/views/form_widgets.js')
-rw-r--r--addons/web/static/src/js/views/form_widgets.js1590
1 files changed, 1590 insertions, 0 deletions
diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js
new file mode 100644
index 00000000000..69150e4f199
--- /dev/null
+++ b/addons/web/static/src/js/views/form_widgets.js
@@ -0,0 +1,1590 @@
+odoo.define('web.form_widgets', ['web.core', 'web.crash_manager', 'web.data', 'web.datepicker', 'web.Dialog', 'web.form_common', 'web.formats', 'web.framework', 'web.Model', 'web.pyeval', 'web.session', 'web.utils'], function (require) {
+"use strict";
+
+var core = require('web.core');
+var crash_manager = require('web.crash_manager');
+var data = require('web.data');
+var datepicker = require('web.datepicker');
+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.Model');
+var pyeval = require('web.pyeval');
+var session = require('web.session');
+var utils = require('web.utils');
+
+var _t = core._t;
+var QWeb = core.qweb;
+
+var WidgetButton = common.FormWidget.extend({
+ template: 'WidgetButton',
+ init: function(field_manager, node) {
+ node.attrs.type = node.attrs['data-button-type'];
+ this.is_stat_button = /\boe_stat_button\b/.test(node.attrs['class']);
+ this.icon_class = node.attrs.icon && "stat_button_icon fa " + node.attrs.icon + " fa-fw";
+ this._super(field_manager, node);
+ this.force_disabled = false;
+ this.string = (this.node.attrs.string || '').replace(/_/g, '');
+ if (JSON.parse(this.node.attrs.default_focus || "0")) {
+ // TODO fme: provide enter key binding to widgets
+ this.view.default_focus_button = this;
+ }
+ if (this.node.attrs.icon && (! /\//.test(this.node.attrs.icon))) {
+ this.node.attrs.icon = '/web/static/src/img/icons/' + this.node.attrs.icon + '.png';
+ }
+ },
+ start: function() {
+ this._super.apply(this, arguments);
+ this.view.on('view_content_has_changed', this, this.check_disable);
+ this.check_disable();
+ this.$el.click(this.on_click);
+ if (this.node.attrs.help || session.debug) {
+ this.do_attach_tooltip();
+ }
+ this.setupFocus(this.$el);
+ },
+ on_click: function() {
+ var self = this;
+ this.force_disabled = true;
+ this.check_disable();
+ this.execute_action().always(function() {
+ self.force_disabled = false;
+ self.check_disable();
+ });
+ },
+ execute_action: function() {
+ var self = this;
+ var exec_action = function() {
+ if (self.node.attrs.confirm) {
+ var def = $.Deferred();
+ var dialog = new Dialog(this, {
+ title: _t('Confirm'),
+ buttons: [
+ {text: _t("Cancel"), click: function() {
+ this.parents('.modal').modal('hide');
+ }
+ },
+ {text: _t("Ok"), click: function() {
+ var self2 = this;
+ self.on_confirmed().always(function() {
+ self2.parents('.modal').modal('hide');
+ });
+ }
+ }
+ ],
+ }, $('<div/>').text(self.node.attrs.confirm)).open();
+ dialog.on("closing", null, function() {def.resolve();});
+ return def.promise();
+ } else {
+ return self.on_confirmed();
+ }
+ };
+ if (!this.node.attrs.special) {
+ return this.view.recursive_save().then(exec_action);
+ } else {
+ return exec_action();
+ }
+ },
+ on_confirmed: function() {
+ var self = this;
+
+ var context = this.build_context();
+ return this.view.do_execute_action(
+ _.extend({}, this.node.attrs, {context: context}),
+ this.view.dataset, this.view.datarecord.id, function (reason) {
+ if (!_.isObject(reason)) {
+ self.view.recursive_reload();
+ }
+ });
+ },
+ check_disable: function() {
+ var disabled = (this.force_disabled || !this.view.is_interactible_record());
+ this.$el.prop('disabled', disabled);
+ this.$el.css('color', disabled ? 'grey' : '');
+ }
+});
+
+var FieldChar = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ template: 'FieldChar',
+ widget_class: 'oe_form_field_char',
+ events: {
+ 'change input': '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'));
+ },
+ 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()));
+ }
+ },
+ commit_value: function () {
+ this.store_dom_value();
+ return this._super();
+ },
+ render_value: function() {
+ var show_value = this.format_value(this.get('value'), '');
+ if (!this.get("effective_readonly")) {
+ this.$el.find('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);
+ }
+ },
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly") && this.$("input").size() > 0) {
+ try {
+ this.parse_value(this.$('input').val(), '');
+ return true;
+ } catch(e) {
+ return false;
+ }
+ }
+ return true;
+ },
+ parse_value: function(val, def) {
+ return formats.parse_value(val, this, def);
+ },
+ format_value: function(val, def) {
+ return formats.format_value(val, this, def);
+ },
+ is_false: function() {
+ 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
+ });
+ }
+});
+
+var KanbanSelection = FieldChar.extend({
+ init: function (field_manager, node) {
+ this._super(field_manager, node);
+ },
+ 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();
+ });
+ },
+ prepare_dropdown_selection: function() {
+ var self = this;
+ var _data = [];
+ var fetch_stage;
+ var selection = this.field.selection || [];
+ var stage_id = _.isArray(this.view.datarecord.stage_id) ? this.view.datarecord.stage_id[0] : this.view.datarecord.stage_id;
+ var legend_field = this.options && this.options.states_legend_field || false;
+ var fields_to_read = _.map(
+ this.options && this.options.states_legend || {},
+ function (value, key, list) { return value; });
+ if (legend_field && fields_to_read && stage_id) {
+ fetch_stage = new data.DataSet(
+ this,
+ self.view.fields[legend_field].field.relation).read_ids([stage_id],
+ fields_to_read);
+ }
+ else { fetch_stage = $.Deferred().resolve(false); }
+ return $.when(fetch_stage).then(function (stage_data) {
+ _.map(selection, function(res) {
+ var value = {
+ 'name': res[0],
+ 'tooltip': res[1],
+ 'state_name': res[1],
+ };
+ if (stage_data && stage_data[0][self.options.states_legend[res[0]]]) {
+ value['state_name'] = stage_data[0][self.options.states_legend[res[0]]];
+ value['tooltip'] = stage_data[0][self.options.states_legend[res[0]]];
+ }
+ if (res[0] == 'normal') { value['state_class'] = 'oe_kanban_status'; }
+ else if (res[0] == 'done') { value['state_class'] = 'oe_kanban_status oe_kanban_status_green'; }
+ else { value['state_class'] = 'oe_kanban_status oe_kanban_status_red'; }
+ _data.push(value);
+ });
+ return _data;
+ });
+ },
+ render_value: function() {
+ var self = this;
+ var dd_fetched = this.prepare_dropdown_selection();
+ return $.when(dd_fetched).then(function (states) {
+ self.states = states;
+ self.$el.html(QWeb.render("KanbanSelection", {'widget': self}));
+ self.$el.find('li').on('click', self.set_kanban_selection.bind(self));
+ });
+ },
+ /* 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
+ be written when saving the record. */
+ set_kanban_selection: function (ev) {
+ var self = this;
+ var li = $(ev.target).closest('li');
+ if (li.length) {
+ var value = String(li.data('value'));
+ if (this.view.get('actual_mode') == 'view') {
+ var write_values = {};
+ write_values[self.name] = value;
+ return this.view.dataset._model.call(
+ 'write', [
+ [this.view.datarecord.id],
+ write_values,
+ self.view.dataset.get_context()
+ ]).done(self.reload_record.bind(self));
+ }
+ else {
+ return this.set_value(value);
+ }
+ }
+ },
+ reload_record: function() {
+ this.view.reload();
+ },
+});
+
+var Priority = FieldChar.extend({
+ init: function (field_manager, node) {
+ this._super(field_manager, node);
+ },
+ prepare_priority: function() {
+ var self = this;
+ var selection = this.field.selection || [];
+ var init_value = selection && selection[0][0] || 0;
+ var data = _.map(selection.slice(1), function(element, index) {
+ var value = {
+ 'value': element[0],
+ 'name': element[1],
+ 'click_value': element[0],
+ };
+ if (index === 0 && self.get('value') === element[0]) {
+ value['click_value'] = init_value;
+ }
+ return value;
+ });
+ return data;
+ },
+ render_value: function() {
+ this.priorities = this.prepare_priority();
+ this.$el.html(QWeb.render("Priority", {'widget': this}));
+ if (!this.get('readonly')){
+ this.$el.find('li').on('click', this.set_priority.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
+ be written when saving the record. */
+ set_priority: function (ev) {
+ var self = this;
+ var li = $(ev.target).closest('li');
+ if (li.length) {
+ var value = String(li.data('value'));
+ if (this.view.get('actual_mode') == 'view') {
+ var write_values = {};
+ write_values[self.name] = value;
+ return this.view.dataset._model.call(
+ 'write', [
+ [this.view.datarecord.id],
+ write_values,
+ self.view.dataset.get_context()
+ ]).done(self.reload_record.bind(self));
+ }
+ else {
+ return this.set_value(value);
+ }
+ }
+
+ },
+ reload_record: function() {
+ this.view.reload();
+ },
+});
+
+var FieldID = FieldChar.extend({
+ process_modifiers: function () {
+ this._super();
+ this.set({ readonly: true });
+ },
+});
+
+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);
+ },
+ 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');
+ }
+ }
+});
+
+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);
+ },
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this._super();
+ } else {
+ 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);
+ }
+ }
+});
+
+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);
+ if (this.node.attrs.digits) {
+ this.digits = this.node.attrs.digits;
+ } else {
+ this.digits = this.field.digits;
+ }
+ },
+ set_value: function(value_) {
+ if (value_ === false || value_ === undefined) {
+ // As in GTK client, floats default to 0
+ value_ = 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;
+ }
+});
+
+var FieldCharDomain = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ render_value: function() {
+ var self = this;
+ this.$el.html(QWeb.render("FieldCharDomain", {widget: this}));
+ if (this.get('value')) {
+ var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
+ var domain = pyeval.eval('domain', this.get('value'));
+ var ds = new data.DataSetStatic(self, model, self.build_context());
+ ds.call('search_count', [domain]).then(function (results) {
+ $('.oe_domain_count', self.$el).text(results + ' records selected');
+ if (self.get('effective_readonly')) {
+ $('button span', self.$el).text(' See selection');
+ }
+ else {
+ $('button span', self.$el).text(' Change selection');
+ }
+ });
+ } else {
+ $('.oe_domain_count', this.$el).text('0 record selected');
+ $('button span', this.$el).text(' Select records');
+ }
+ this.$('.select_records').on('click', self.on_click);
+ },
+ on_click: function(event) {
+ event.preventDefault();
+ var self = this;
+ var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
+
+ var options = {
+ title: this.get('effective_readonly') ? 'Selected records' : 'Select records...',
+ readonly: this.get('effective_readonly'),
+ disable_multiple_selection: this.get('effective_readonly'),
+ no_create: this.get('effective_readonly'),
+ };
+ var domain = this.get('value');
+ var popup = new common.DomainEditorPopup(this);
+ popup.select_element(model, options, domain, {});
+ popup.on("elements_selected", self, function(selected_ids) {
+ if (!self.get('effective_readonly')) {
+ self.set_value(popup.get_domain(selected_ids));
+ }
+ });
+ },
+});
+
+var FieldDatetime = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ template: "FieldDatetime",
+ build_widget: function() {
+ return new datepicker.DateTimeWidget(this);
+ },
+ destroy_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.internal_set_value(this.datewidget.get_value());
+ }, this));
+ this.datewidget.appendTo(this.$el);
+ this.setupFocus(this.datewidget.$input);
+ }
+ },
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this.datewidget.set_value(this.get('value'));
+ } else {
+ this.$el.text(formats.format_value(this.get('value'), this, ''));
+ }
+ },
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly") && this.datewidget) {
+ return this.datewidget.is_valid_();
+ }
+ return true;
+ },
+ 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);
+ }
+ }
+});
+
+var FieldDate = FieldDatetime.extend({
+ template: "FieldDate",
+ build_widget: function() {
+ return new datepicker.DateWidget(this);
+ }
+});
+
+var FieldText = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ template: 'FieldText',
+ events: {
+ 'keyup': function (e) {
+ if (e.which === $.ui.keyCode.ENTER) {
+ e.stopPropagation();
+ }
+ },
+ 'keypress': function (e) {
+ if (e.which === $.ui.keyCode.ENTER) {
+ e.stopPropagation();
+ }
+ },
+ 'change textarea': 'store_dom_value',
+ },
+ initialize_content: function() {
+ if (! this.get("effective_readonly")) {
+ this.$textarea = this.$el.find('textarea');
+ 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;
+ }
+ },
+ commit_value: function () {
+ if (! this.get("effective_readonly") && this.$textarea) {
+ this.store_dom_value();
+ }
+ return this._super();
+ },
+ store_dom_value: function () {
+ this.internal_set_value(formats.parse_value(this.$textarea.val(), this));
+ },
+ render_value: function() {
+ if (! this.get("effective_readonly")) {
+ var show_value = formats.format_value(this.get('value'), this, '');
+ if (show_value === '') {
+ this.$textarea.css('height', parseInt(this.default_height, 10)+"px");
+ }
+ this.$textarea.val(show_value);
+ if (! this.auto_sized) {
+ this.auto_sized = true;
+ this.$textarea.autosize();
+ } else {
+ this.$textarea.trigger("autosize");
+ }
+ } else {
+ var txt = this.get("value") || '';
+ this.$(".oe_form_text_content").text(txt);
+ }
+ },
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly") && this.$textarea) {
+ try {
+ formats.parse_value(this.$textarea.val(), this, '');
+ return true;
+ } catch(e) {
+ return false;
+ }
+ }
+ return true;
+ },
+ is_false: function() {
+ 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
+ });
+ }
+ },
+});
+
+/**
+ * FieldTextHtml Widget
+ * Intended for FieldText widgets meant to display HTML content. This
+ * widget will instantiate the CLEditor (see cleditor in static/src/lib)
+ * To find more information about CLEditor configutation: go to
+ * http://premiumsoftware.net/cleditor/docs/GettingStarted.html
+ */
+var FieldTextHtml = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ template: 'FieldTextHtml',
+ init: function() {
+ this._super.apply(this, arguments);
+ },
+ initialize_content: function() {
+ var self = this;
+ if (! this.get("effective_readonly")) {
+ self._updating_editor = false;
+ this.$textarea = this.$el.find('textarea');
+ var width = ((this.node.attrs || {}).editor_width || 'calc(100% - 4px)');
+ var height = ((this.node.attrs || {}).editor_height || 250);
+ this.$textarea.cleditor({
+ width: width, // width not including margins, borders or padding
+ height: height, // height not including margins, borders or padding
+ controls: // controls to add to the toolbar
+ "bold italic underline strikethrough " +
+ "| removeformat | bullets numbering | outdent " +
+ "indent | link unlink | source",
+ bodyStyle: // style to assign to document body contained within the editor
+ "margin:4px; color:#4c4c4c; font-size:13px; font-family:'Lucida Grande',Helvetica,Verdana,Arial,sans-serif; cursor:text"
+ });
+ this.$cleditor = this.$textarea.cleditor()[0];
+ this.$cleditor.change(function() {
+ if (! self._updating_editor) {
+ self.$cleditor.updateTextArea();
+ self.internal_set_value(self.$textarea.val());
+ }
+ });
+ if (this.field.translate) {
+ var $img = $('<img class="oe_field_translate oe_input_icon" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>')
+ .click(this.on_translate);
+ this.$cleditor.$toolbar.append($img);
+ }
+ }
+ },
+ render_value: function() {
+ if (! this.get("effective_readonly")) {
+ this.$textarea.val(this.get('value') || '');
+ this._updating_editor = true;
+ this.$cleditor.updateFrame();
+ this._updating_editor = false;
+ } else {
+ this.$el.html(this.get('value'));
+ }
+ },
+});
+
+var FieldBoolean = common.AbstractField.extend({
+ template: 'FieldBoolean',
+ start: function() {
+ var self = this;
+ this.$checkbox = $("input", this.$el);
+ 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);
+ },
+ render_value: function() {
+ this.$checkbox[0].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>'));
+ });
+ }
+});
+
+/**
+ The progressbar field expect a float from 0 to 100.
+*/
+var FieldProgressBar = common.AbstractField.extend({
+ template: 'FieldProgressBar',
+ render_value: function() {
+ this.$el.progressbar({
+ value: this.get('value') || 0,
+ disabled: this.get("effective_readonly")
+ });
+ var formatted_value = formats.format_value(this.get('value') || 0, { type : 'float' });
+ this.$('span').html(formatted_value + '%');
+ }
+});
+
+/**
+ The PercentPie field expect a float from 0 to 100.
+*/
+var FieldPercentPie = common.AbstractField.extend({
+ template: 'FieldPercentPie',
+
+ 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)
+ .tooltips(false)
+ .color(['#7C7BAD','#DDD'])
+ .donutRatio(0.62);
+
+ 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;
+ });
+
+ }
+});
+
+/**
+ The FieldBarChart expectsa list of values (indeed)
+*/
+var FieldBarChart = common.AbstractField.extend({
+ template: 'FieldBarChart',
+
+ 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})
+ .tooltips(false)
+ .showValues(false)
+ .transitionDuration(350)
+ .showXAxis(false)
+ .showYAxis(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 FieldSelection = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ template: 'FieldSelection',
+ events: {
+ 'change select': 'store_dom_value',
+ },
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.set("value", false);
+ this.set("values", []);
+ 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"))) {
+ this.set("domain", domain);
+ }
+ });
+ },
+ initialize_field: function() {
+ common.ReinitializeFieldMixin.initialize_field.call(this);
+ this.on("change:domain", this, this.query_values);
+ this.set("domain", new data.CompoundDomain(this.build_domain()).eval());
+ this.on("change:values", this, this.render_value);
+ },
+ query_values: function() {
+ var self = this;
+ var def;
+ if (this.field.type === "many2one") {
+ var model = new Model(this.field.relation);
+ def = model.call("name_search", ['', this.get("domain")], {"context": this.build_context()});
+ } else {
+ var values = _.reject(this.field.selection, function (v) { return v[0] === false && v[1] === ''; });
+ def = $.when(values);
+ }
+ this.records_orderer.add(def).then(function(values) {
+ if (! _.isEqual(values, self.get("values"))) {
+ self.set("values", values);
+ }
+ });
+ },
+ initialize_content: function() {
+ // Flag indicating whether we're in an event chain containing a change
+ // event on the select, in order to know what to do on keyup[RETURN]:
+ // * If the user presses [RETURN] as part of changing the value of a
+ // selection, we should just let the value change and not let the
+ // event broadcast further (e.g. to validating the current state of
+ // the form in editable list view, which would lead to saving the
+ // 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);
+ },
+ 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);
+ }
+ },
+ set_value: function(value_) {
+ value_ = value_ === null ? false : value_;
+ value_ = value_ instanceof Array ? value_[0] : value_;
+ this._super(value_);
+ },
+ render_value: function() {
+ 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) {
+ 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]));
+ } 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
+ });
+ }
+});
+
+var FieldRadio = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ template: 'FieldRadio',
+ events: {
+ 'click input': 'click_change_value'
+ },
+ init: function(field_manager, node) {
+ /* Radio button widget: Attributes options:
+ * - "horizontal" to display in column
+ * - "no_radiolabel" don't display text values
+ */
+ this._super(field_manager, node);
+ this.selection = _.clone(this.field.selection) || [];
+ this.domain = false;
+ 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();
+ },
+ click_change_value: function (event) {
+ var val = $(event.target).val();
+ val = this.field.type == "selection" ? val : +val;
+ if (val !== this.get_value()) {
+ this.set_value(val);
+ }
+ },
+ /** Get the selection and render it
+ * selection: [[identifier, value_to_display], ...]
+ * For selection fields: this is directly given by this.field.selection
+ * For many2one fields: perform a search on the relation of the many2one field
+ */
+ get_selection: function() {
+ var self = this;
+ var selection = [];
+ var def = $.Deferred();
+ if (self.field.type == "many2one") {
+ var domain = pyeval.eval('domain', this.build_domain()) || [];
+ if (! _.isEqual(self.domain, domain)) {
+ self.domain = domain;
+ var ds = new data.DataSetStatic(self, self.field.relation, self.build_context());
+ ds.call('search', [self.domain])
+ .then(function (records) {
+ ds.name_get(records).then(function (records) {
+ selection = records;
+ def.resolve();
+ });
+ });
+ } else {
+ selection = self.selection;
+ def.resolve();
+ }
+ }
+ else if (self.field.type == "selection") {
+ selection = self.field.selection || [];
+ def.resolve();
+ }
+ return def.then(function () {
+ if (! _.isEqual(selection, self.selection)) {
+ self.selection = _.clone(selection);
+ self.renderElement();
+ self.render_value();
+ }
+ });
+ },
+ set_value: function (value_) {
+ if (this.field.type == "selection") {
+ value_ = _.find(this.field.selection, function (sel) { return sel[0] == value_;});
+ }
+ else if (!this.selection.length) {
+ this.selection = [value_];
+ }
+
+ this._super(value_);
+ },
+ get_value: function () {
+ var value = this.get('value');
+ return value instanceof Array ? value[0] : value;
+ },
+ 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] : "");
+ }
+});
+
+var FieldReference = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ template: 'FieldReference',
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.reference_ready = true;
+ },
+ destroy_content: function() {
+ if (this.fm) {
+ this.fm.destroy();
+ this.fm = undefined;
+ }
+ },
+ initialize_content: function() {
+ var self = this;
+ var fm = new common.DefaultFieldManager(this);
+ this.fm = fm;
+ fm.extend_field_desc({
+ "selection": {
+ selection: this.field_manager.get_field_desc(this.name).selection,
+ type: "selection",
+ },
+ "m2o": {
+ relation: null,
+ type: "many2one",
+ },
+ });
+ this.selection = new FieldSelection(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
+ .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: {
+ 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
+ .on('focused', null, function () {self.trigger('focused');})
+ .on('blurred', null, function () {self.trigger('blurred');});
+ },
+ on_selection_changed: function() {
+ if (this.reference_ready) {
+ this.internal_set_value([this.selection.get_value(), false]);
+ this.render_value();
+ }
+ },
+ data_changed: function() {
+ if (this.reference_ready) {
+ this.internal_set_value([this.selection.get_value(), this.m2o.get_value()]);
+ }
+ },
+ set_value: function(val) {
+ if (val) {
+ val = val.split(',');
+ val[0] = val[0] || false;
+ val[1] = val[0] ? (val[1] ? parseInt(val[1], 10) : val[1]) : false;
+ }
+ this._super(val || [false, false]);
+ },
+ get_value: function() {
+ return this.get('value')[0] && this.get('value')[1] ? (this.get('value')[0] + ',' + this.get('value')[1]) : false;
+ },
+ render_value: function() {
+ this.reference_ready = false;
+ if (!this.get("effective_readonly")) {
+ this.selection.set_value(this.get('value')[0]);
+ }
+ this.m2o.field.relation = this.get('value')[0];
+ this.m2o.set_value(this.get('value')[1]);
+ this.m2o.$el.toggle(!!this.get('value')[0]);
+ this.reference_ready = true;
+ },
+});
+
+var FieldBinary = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ init: function(field_manager, node) {
+ var self = this;
+ this._super(field_manager, node);
+ this.binary_value = false;
+ this.useFileAPI = !!window.FileReader;
+ this.max_upload_size = 25 * 1024 * 1024; // 25Mo
+ if (!this.useFileAPI) {
+ this.fileupload_id = _.uniqueId('oe_fileupload');
+ $(window).on(this.fileupload_id, function() {
+ var args = [].slice.call(arguments).slice(1);
+ self.on_file_uploaded.apply(self, args);
+ });
+ }
+ },
+ stop: function() {
+ if (!this.useFileAPI) {
+ $(window).off(this.fileupload_id);
+ }
+ this._super.apply(this, arguments);
+ },
+ initialize_content: function() {
+ var self= this;
+ this.$el.find('input.oe_form_binary_file').change(this.on_file_change);
+ this.$el.find('button.oe_form_binary_file_save').click(this.on_save_as);
+ this.$el.find('.oe_form_binary_file_clear').click(this.on_clear);
+ this.$el.find('.oe_form_binary_file_edit').click(function(event){
+ self.$el.find('input.oe_form_binary_file').click();
+ });
+ },
+ on_file_change: function(e) {
+ var self = this;
+ var file_node = e.target;
+ if ((this.useFileAPI && file_node.files.length) || (!this.useFileAPI && $(file_node).val() !== '')) {
+ if (this.useFileAPI) {
+ var file = file_node.files[0];
+ if (file.size > this.max_upload_size) {
+ var msg = _t("The selected file exceed the maximum file size of %s.");
+ core.bus.trigger('display_notification_warning', _t("File upload"), _.str.sprintf(msg, utils.human_size(this.max_upload_size)));
+ return false;
+ }
+ var filereader = new FileReader();
+ filereader.readAsDataURL(file);
+ filereader.onloadend = function(upload) {
+ var data = upload.target.result;
+ data = data.split(',')[1];
+ self.on_file_uploaded(file.size, file.name, file.type, data);
+ };
+ } else {
+ this.$el.find('form.oe_form_binary_form input[name=session_id]').val(this.session.session_id);
+ this.$el.find('form.oe_form_binary_form').submit();
+ }
+ this.$el.find('.oe_form_binary_progress').show();
+ this.$el.find('.oe_form_binary').hide();
+ }
+ },
+ on_file_uploaded: function(size, name, content_type, file_base64) {
+ 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();
+ },
+ on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
+ },
+ on_save_as: function(ev) {
+ var value = this.get('value');
+ if (!value) {
+ this.do_warn(_t("Save As..."), _t("The field is empty, there's nothing to save !"));
+ ev.stopPropagation();
+ } else {
+ framework.blockUI();
+ var c = crash_manager;
+ var filename_fieldname = this.node.attrs.filename;
+ var filename_field = this.view.fields && this.view.fields[filename_fieldname];
+ this.session.get_file({
+ url: '/web/binary/saveas_ajax',
+ data: {data: JSON.stringify({
+ model: this.view.dataset.model,
+ id: (this.view.datarecord.id || ''),
+ field: this.name,
+ filename_field: (filename_fieldname || ''),
+ data: utils.is_bin_size(value) ? null : value,
+ filename: filename_field ? filename_field.get('value') : null,
+ context: this.view.dataset.get_context()
+ })},
+ complete: framework.unblockUI,
+ error: c.rpc_error.bind(c)
+ });
+ ev.stopPropagation();
+ return false;
+ }
+ },
+ set_filename: function(value) {
+ var filename = this.node.attrs.filename;
+ if (filename) {
+ var tmp = {};
+ tmp[filename] = value;
+ this.field_manager.set_values(tmp);
+ }
+ },
+ on_clear: function() {
+ if (this.get('value') !== false) {
+ this.binary_value = false;
+ this.internal_set_value(false);
+ }
+ return false;
+ }
+});
+
+var FieldBinaryFile = FieldBinary.extend({
+ template: 'FieldBinaryFile',
+ initialize_content: function() {
+ this._super();
+ if (this.get("effective_readonly")) {
+ var self = this;
+ this.$el.find('a').click(function(ev) {
+ if (self.get('value')) {
+ self.on_save_as(ev);
+ }
+ return false;
+ });
+ }
+ },
+ 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') : '';
+ }
+ 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);
+ }
+ }
+ },
+ on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
+ this.binary_value = true;
+ this.internal_set_value(file_base64);
+ var show_value = name + " (" + utils.human_size(size) + ")";
+ this.$el.find('input').eq(0).val(show_value);
+ this.set_filename(name);
+ },
+ on_clear: function() {
+ this._super.apply(this, arguments);
+ this.$el.find('input').eq(0).val('');
+ this.set_filename('');
+ }
+});
+
+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/binary/image', {
+ model: this.view.dataset.model,
+ id: id,
+ field: field,
+ t: (new Date().getTime()),
+ });
+ } else {
+ url = this.placeholder;
+ }
+ var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url }));
+ $($img).click(function(e) {
+ if(self.view.get("actual_mode") == "view") {
+ var $button = $(".oe_form_button_edit");
+ $button.openerpBounce();
+ e.stopPropagation();
+ }
+ });
+ this.$el.find('> img').remove();
+ 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() {
+ $img.attr('src', self.placeholder);
+ core.bus.trigger('display_notification_warning', _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_){
+ var changed = value_ !== this.get_value();
+ this._super.apply(this, arguments);
+ // By default, on binary images read, the server returns the binary size
+ // This is possible that two images have the exact same size
+ // Therefore we trigger the change in case the image value hasn't changed
+ // So the image is re-rendered correctly
+ if (!changed){
+ this.trigger("change:value", this, {
+ oldValue: value_,
+ newValue: value_
+ });
+ }
+ }
+});
+
+var FieldStatus = common.AbstractField.extend({
+ template: "FieldStatus",
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.options.clickable = this.options.clickable || (this.node.attrs || {}).clickable || false;
+ this.options.visible = this.options.visible || (this.node.attrs || {}).statusbar_visible || false;
+ this.set({value: false});
+ this.selection = {'unfolded': [], 'folded': []};
+ this.set("selection", {'unfolded': [], 'folded': []});
+ this.selection_dm = new utils.DropMisordered();
+ this.dataset = new data.DataSetStatic(this, this.field.relation, this.build_context());
+ },
+ start: function() {
+ this.field_manager.on("view_content_has_changed", this, this.calc_domain);
+ this.calc_domain();
+ this.on("change:value", this, this.get_selection);
+ this.on("change:evaluated_selection_domain", this, this.get_selection);
+ this.on("change:selection", this, function() {
+ this.selection = this.get("selection");
+ this.render_value();
+ });
+ 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._super();
+ },
+ set_value: function(value_) {
+ if (value_ instanceof Array) {
+ value_ = value_[0];
+ }
+ this._super(value_);
+ },
+ render_value: function() {
+ var self = this;
+ var content = QWeb.render("FieldStatus.content", {
+ 'widget': self,
+ 'value_folded': _.find(self.selection.folded, function(i){return i[0] === self.get('value');})
+ });
+ self.$el.html(content);
+ },
+ calc_domain: function() {
+ var d = pyeval.eval('domain', this.build_domain());
+ var domain = []; //if there is no domain defined, fetch all the records
+
+ if (d.length) {
+ domain = ['|',['id', '=', this.get('value')]].concat(d);
+ }
+
+ if (! _.isEqual(domain, this.get("evaluated_selection_domain"))) {
+ this.set("evaluated_selection_domain", domain);
+ }
+ },
+ /** Get the selection and render it
+ * selection: [[identifier, value_to_display], ...]
+ * For selection fields: this is directly given by this.field.selection
+ * For many2one fields: perform a search on the relation of the many2one field
+ */
+ get_selection: function() {
+ var self = this;
+ var selection_unfolded = [];
+ var selection_folded = [];
+ var fold_field = this.options.fold_field;
+
+ var calculation = _.bind(function() {
+ if (this.field.type == "many2one") {
+ return self.get_distant_fields().then(function (fields) {
+ 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')) {
+ selection_folded.push([record.id, name]);
+ } else {
+ selection_unfolded.push([record.id, name]);
+ }
+ });
+ });
+ });
+ });
+ } else {
+ // For field type selection filter values according to
+ // statusbar_visible attribute of the field. For example:
+ // statusbar_visible="draft,open".
+ 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) {
+ selection_unfolded.push(select[i]);
+ }
+ }
+ return $.when();
+ }
+ }, this);
+ this.selection_dm.add(calculation()).then(function () {
+ var selection = {'unfolded': selection_unfolded, 'folded': selection_folded};
+ if (! _.isEqual(selection, self.get("selection"))) {
+ self.set("selection", selection);
+ }
+ });
+ },
+ /*
+ * :deprecated: this feature will probably be removed with OpenERP v8
+ */
+ get_distant_fields: function() {
+ var self = this;
+ if (! this.options.fold_field) {
+ this.distant_fields = {};
+ }
+ if (this.distant_fields) {
+ return $.when(this.distant_fields);
+ }
+ return new Model(self.field.relation).call("fields_get", [[this.options.fold_field]]).then(function(fields) {
+ self.distant_fields = fields;
+ return fields;
+ });
+ },
+ on_click_stage: function (ev) {
+ var self = this;
+ var $li = $(ev.currentTarget);
+ var val;
+ if (this.field.type == "many2one") {
+ val = parseInt($li.data("id"), 10);
+ }
+ else {
+ val = $li.data("id");
+ }
+ if (val != self.get('value')) {
+ this.view.recursive_save().done(function() {
+ var change = {};
+ change[self.name] = val;
+ self.view.dataset.write(self.view.datarecord.id, change).done(function() {
+ self.view.reload();
+ });
+ });
+ }
+ },
+});
+
+var FieldMonetary = FieldFloat.extend({
+ template: "FieldMonetary",
+ widget_class: 'oe_form_field_float oe_form_field_monetary',
+ init: function() {
+ this._super.apply(this, arguments);
+ this.set({"currency": false});
+ if (this.options.currency_field) {
+ this.field_manager.on("field_changed:" + this.options.currency_field, this, function() {
+ this.set({"currency": this.field_manager.get_field_value(this.options.currency_field)});
+ });
+ }
+ this.on("change:currency", this, this.get_currency_info);
+ this.get_currency_info();
+ this.ci_dm = new utils.DropMisordered();
+ },
+ start: function() {
+ var tmp = this._super();
+ this.on("change:currency_info", this, this.reinitialize);
+ return tmp;
+ },
+ get_currency_info: function() {
+ var self = this;
+ if (this.get("currency") === false) {
+ this.set({"currency_info": null});
+ return;
+ }
+ return this.ci_dm.add(self.alive(new Model("res.currency").query(["symbol", "position"])
+ .filter([["id", "=", self.get("currency")]]).first())).then(function(res) {
+ self.set({"currency_info": res});
+ });
+ },
+ parse_value: function(val, def) {
+ return formats.parse_value(val, {type: "float", digits: (this.node.attrs || {}).digits || this.field.digits}, def);
+ },
+ format_value: function(val, def) {
+ return formats.format_value(val, {type: "float", digits: (this.node.attrs || {}).digits || this.field.digits}, def);
+ },
+});
+
+/**
+ This widget is intended to be used on stat button numeric fields. It will display
+ the value many2many and one2many. It is a read-only field that will
+ display a simple string "<value of field> <label of the field>"
+*/
+var StatInfo = common.AbstractField.extend({
+ is_field_number: true,
+ init: function() {
+ this._super.apply(this, arguments);
+ this.internal_set_value(0);
+ },
+ set_value: function(value_) {
+ if (value_ === false || value_ === undefined) {
+ value_ = 0;
+ }
+ this._super.apply(this, [value_]);
+ },
+ render_value: function() {
+ var options = {
+ value: this.get("value") || 0,
+ };
+ if (! this.node.attrs.nolabel) {
+ if(this.options.label_field && this.view.datarecord[this.options.label_field]) {
+ options.text = this.view.datarecord[this.options.label_field];
+ }
+ else {
+ options.text = this.string;
+ }
+ }
+ this.$el.html(QWeb.render("StatInfo", options));
+ },
+
+});
+
+/**
+ This widget is intended to be used on boolean fields. It toggles a button
+ switching between a green bullet / gray bullet.
+*/
+var FieldToggleBoolean = common.AbstractField.extend({
+ 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));
+ },
+ set_toggle_button: function () {
+ var self = this;
+ var toggle_value = this.get_value() === false ? true: false;
+ if (this.view.get('actual_mode') == 'view') {
+ var rec_values = {};
+ rec_values[self.node.attrs.name] = toggle_value;
+ return this.view.dataset._model.call(
+ 'write', [
+ [this.view.datarecord.id],
+ rec_values,
+ self.view.dataset.get_context()
+ ]).done(function () { self.reload_record(); });
+ }
+ else {
+ this.set_value(toggle_value);
+ }
+ },
+ reload_record: function () {
+ this.view.reload();
+ },
+});
+
+
+/**
+ * Registry of form fields, called by :js:`instance.web.FormView`.
+ *
+ * All referenced classes must implement FieldInterface. Those represent the classes whose instances
+ * will substitute to the <field> tags as defined in OpenERP's views.
+ */
+core.form_widget_registry
+ .add('char', FieldChar)
+ .add('id', FieldID)
+ .add('email', FieldEmail)
+ .add('url', FieldUrl)
+ .add('text',FieldText)
+ .add('html', FieldTextHtml)
+ .add('char_domain', FieldCharDomain)
+ .add('date', FieldDate)
+ .add('datetime', FieldDatetime)
+ .add('selection', FieldSelection)
+ .add('radio', FieldRadio)
+ .add('reference', FieldReference)
+ .add('boolean', FieldBoolean)
+ .add('toggle_button', FieldToggleBoolean)
+ .add('float', FieldFloat)
+ .add('percentpie', FieldPercentPie)
+ .add('barchart', FieldBarChart)
+ .add('integer', FieldFloat)
+ .add('float_time', FieldFloat)
+ .add('progressbar', FieldProgressBar)
+ .add('image', FieldBinaryImage)
+ .add('binary', FieldBinaryFile)
+ .add('statusbar', FieldStatus)
+ .add('monetary', FieldMonetary)
+ .add('priority', Priority)
+ .add('kanban_state_selection', KanbanSelection)
+ .add('statinfo', StatInfo);
+
+
+/**
+ * Registry of widgets usable in the form view that can substitute to any possible
+ * tags defined in OpenERP's form views.
+ *
+ * Every referenced class should extend FormWidget.
+ */
+core.form_tag_registry.add('button', WidgetButton);
+
+return {
+ FieldChar: FieldChar,
+};
+
+});