summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/fields
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2017-06-27 14:19:31 +0200
committerChristophe Simonis <chs@odoo.com>2017-06-27 14:19:31 +0200
commitdd43071638773dcb58376b79ec7b1456c8b0454d (patch)
treefd7935e2aa93de03fa3fe12507f8ef74405a255e /addons/web/static/src/js/fields
parent6501184e6294ffb06e24a0ec8e9666b79a80b59a (diff)
parent278e478d55209bd958836fde7fe1c82c555180dc (diff)
[MERGE] forward port branch saas-16 up to 278e478d552
Diffstat (limited to 'addons/web/static/src/js/fields')
-rw-r--r--addons/web/static/src/js/fields/abstract_field.js30
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js149
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js22
-rw-r--r--addons/web/static/src/js/fields/upgrade_fields.js6
4 files changed, 120 insertions, 87 deletions
diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js
index 492d9f26552..52e0aef77e4 100644
--- a/addons/web/static/src/js/fields/abstract_field.js
+++ b/addons/web/static/src/js/fields/abstract_field.js
@@ -142,6 +142,21 @@ var AbstractField = Widget.extend({
// this is the last value that was set by the user, unparsed. This is
// used to avoid setting the value twice in a row with the exact value.
this.lastSetValue = undefined;
+
+ // formatType is used to determine which format (and parse) functions
+ // to call to format the field's value to insert into the DOM (typically
+ // put into a span or an input), and to parse the value from the input
+ // to send it to the server. These functions are chosen according to
+ // the 'widget' attrs if is is given, and if it is a valid key, with a
+ // fallback on the field type, ensuring that the value is formatted and
+ // displayed according to the choosen widget, if any.
+ this.formatType = this.attrs.widget in field_utils.format ?
+ this.attrs.widget :
+ this.field.type;
+ // formatOptions (resp. parseOptions) is a dict of options passed to
+ // calls to the format (resp. parse) function.
+ this.formatOptions = {};
+ this.parseOptions = {};
},
/**
* When a field widget is appended to the DOM, its start method is called,
@@ -266,15 +281,15 @@ var AbstractField = Widget.extend({
//--------------------------------------------------------------------------
/**
- * convert the value from the field to a string representation
+ * Converts the value from the field to a string representation.
*
* @private
* @param {any} value (from the field type)
* @returns {string}
*/
_formatValue: function (value) {
- var options = _.extend({}, this.nodeOptions, { data: this.recordData });
- return field_utils.format[this.field.type](value, this.field, options);
+ var options = _.extend({}, this.nodeOptions, { data: this.recordData }, this.formatOptions);
+ return field_utils.format[this.formatType](value, this.field, options);
},
/**
* This method check if a value is the same as the current value of the
@@ -291,15 +306,14 @@ var AbstractField = Widget.extend({
return this.value === value;
},
/**
- * convert a string representation to a valid value, depending on the field
- * type.
+ * Converts a string representation to a valid value.
*
* @private
* @param {string} value
* @returns {any}
*/
_parseValue: function (value) {
- return field_utils.parse[this.field.type](value, this.field);
+ return field_utils.parse[this.formatType](value, this.field, this.parseOptions);
},
/**
* main rendering function. Override this if your widget has the same render
@@ -426,22 +440,18 @@ var AbstractField = Widget.extend({
this.trigger_up('navigation_move', {direction: 'cancel'});
break;
case $.ui.keyCode.UP:
- ev.preventDefault();
ev.stopPropagation();
this.trigger_up('navigation_move', {direction: 'up'});
break;
case $.ui.keyCode.RIGHT:
- ev.preventDefault();
ev.stopPropagation();
this.trigger_up('navigation_move', {direction: 'right'});
break;
case $.ui.keyCode.DOWN:
- ev.preventDefault();
ev.stopPropagation();
this.trigger_up('navigation_move', {direction: 'down'});
break;
case $.ui.keyCode.LEFT:
- ev.preventDefault();
ev.stopPropagation();
this.trigger_up('navigation_move', {direction: 'left'});
break;
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index d4013a8bcdb..82970255b3e 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -285,11 +285,15 @@ var InputField = DebouncedField.extend({
*/
_onNavigationMove: function (ev) {
this._super.apply(this, arguments);
- var input = this.$input[0];
- var selecting = (input.selectionEnd !== input.selectionStart);
- if ((ev.data.direction === "left" && (selecting || input.selectionStart !== 0))
- || (ev.data.direction === "right" && (selecting || input.selectionStart !== input.value.length))) {
- ev.stopPropagation();
+
+ // the following code only makes sense in edit mode, with an input
+ if (this.mode === 'edit') {
+ var input = this.$input[0];
+ var selecting = (input.selectionEnd !== input.selectionStart);
+ if ((ev.data.direction === "left" && (selecting || input.selectionStart !== 0))
+ || (ev.data.direction === "right" && (selecting || input.selectionStart !== input.value.length))) {
+ ev.stopPropagation();
+ }
}
},
});
@@ -322,6 +326,14 @@ var FieldDate = InputField.extend({
supportedFieldTypes: ['date'],
/**
+ * @override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ // use the session timezone when formatting dates
+ this.formatOptions.timezone = true;
+ },
+ /**
* In edit mode, instantiates a DateWidget datepicker and listen to changes.
*
* @override
@@ -396,17 +408,6 @@ var FieldDate = InputField.extend({
this.datewidget.setValue(this.value);
this.$input = this.datewidget.$input;
},
-
- /**
- * FieldDateTime overrides _formatValue to use the session timezone
- *
- * @override
- * @private
- * @param {Moment} value
- */
- _formatValue: function (value) {
- return field_utils.format.date(value, {timezone: true});
- },
});
var FieldDateTime = FieldDate.extend({
@@ -448,17 +449,6 @@ var FieldDateTime = FieldDate.extend({
this.datewidget.setValue(value);
this.$input = this.datewidget.$input;
},
-
- /**
- * FieldDateTime overrides _formatValue to use the session timezone
- *
- * @override
- * @private
- * @param {Moment} value
- */
- _formatValue: function (value) {
- return field_utils.format.datetime(value, {timezone: true});
- },
});
var FieldMonetary = InputField.extend({
@@ -490,6 +480,13 @@ var FieldMonetary = InputField.extend({
this.tagName = 'div';
this.className += ' o_input';
}
+
+ // use the formatFloat function in edit
+ if (this.mode === 'edit') {
+ this.formatType = 'float';
+ }
+ this.formatOptions.currency = this.currency;
+ this.formatOptions.digits = [16, 2];
},
//--------------------------------------------------------------------------
@@ -562,22 +559,6 @@ var FieldMonetary = InputField.extend({
var currencyID = this.record.data[currencyField] && this.record.data[currencyField].res_id;
this.currency = session.get_currency(currencyID);
},
- /**
- * FieldMonetary overrides _formatValue to use the format monetary method
- * in readonly.
- *
- * @override
- * @private
- * @param {float} value
- * @returns {string}
- */
- _formatValue: function (value) {
- var format = field_utils.format[this.mode === 'edit' ? "float" : "monetary"];
- return format(value, this.field, {
- digits: [16, 2],
- currency: this.currency,
- });
- },
});
var FieldBoolean = AbstractField.extend({
@@ -686,6 +667,11 @@ var FieldBoolean = AbstractField.extend({
this.$input.prop('checked', !this.value);
this._setValue(!this.value);
return;
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.RIGHT:
+ case $.ui.keyCode.DOWN:
+ case $.ui.keyCode.LEFT:
+ ev.preventDefault();
}
this._super.apply(this, arguments);
},
@@ -734,7 +720,7 @@ var FieldInteger = InputField.extend({
}
return value;
}
- return field_utils.format.integer(value, this.field);
+ return this._super.apply(this, arguments);
},
});
@@ -771,31 +757,11 @@ var FieldFloat = InputField.extend({
});
var FieldFloatTime = FieldFloat.extend({
- supportedFieldTypes: ['float'],
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Format the float time value into a human-readable time format.
- *
- * @override
- * @private
- */
- _formatValue: function (value) {
- return field_utils.format.float_time(value);
- },
-
- /**
- * Parse the human-readable formatted time value into a float.
- *
- * @override
- * @private
- */
- _parseValue: function (value) {
- return field_utils.parse.float_time(value);
- },
+ // this is not strictly necessary, as for this widget to be used, the 'widget'
+ // attrs must be set to 'float_time', so the formatType is automatically
+ // 'float_time', but for the sake of clarity, we explicitely define a
+ // FieldFloatTime widget with formatType = 'float_time'.
+ formatType: 'float_time',
});
var FieldText = InputField.extend(TranslatableFieldMixin, {
@@ -853,6 +819,19 @@ var FieldEmail = InputField.extend({
},
//--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Returns the associated link.
+ *
+ * @override
+ */
+ getFocusableElement: function () {
+ return this.mode === 'readonly' ? this.$el : this._super.apply(this, arguments);
+ },
+
+ //--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -887,6 +866,17 @@ var FieldPhone = FieldEmail.extend({
this.tagName = 'span';
}
},
+ /**
+ * Returns the associated link only if there is one.
+ *
+ * @override
+ */
+ getFocusableElement: function () {
+ if (this._canCall()) {
+ return this._super.apply(this, arguments);
+ }
+ return $();
+ },
//--------------------------------------------------------------------------
// Private
@@ -941,6 +931,19 @@ var UrlWidget = InputField.extend({
},
//--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Returns the associated link.
+ *
+ * @override
+ */
+ getFocusableElement: function () {
+ return this.mode === 'readonly' ? this.$el : this._super.apply(this, arguments);
+ },
+
+ //--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -1041,7 +1044,13 @@ var AbstractFieldBinary = AbstractField.extend({
set_filename: function (value) {
var filename = this.attrs.filename;
if (filename && filename in this.fields) {
- this.trigger_up('update_field', { name: filename, value: value });
+ var changes = {};
+ changes[filename] = value;
+ this.trigger_up('field_changed', {
+ dataPointID: this.dataPointID,
+ changes: changes,
+ viewType: this.viewType,
+ });
}
},
on_clear: function () {
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 3b47b8c64b0..419d1f5f646 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -124,7 +124,7 @@ var FieldMany2One = AbstractField.extend({
* @returns {jQuery}
*/
getFocusableElement: function () {
- return this.$input || $();
+ return this.mode === 'edit' && this.$input || this.$el;
},
/**
* TODO
@@ -206,6 +206,17 @@ var FieldMany2One = AbstractField.extend({
return [];
},
/**
+ * Returns the display_name from a string which contains it but was altered
+ * as a result of the show_address option using a horrible hack.
+ *
+ * @private
+ * @param {string} value
+ * @returns {string} display_name without show_address mess
+ */
+ _getDisplayName: function (value) {
+ return value.split('\n')[0];
+ },
+ /**
* @private
* @param {string} name
*/
@@ -244,7 +255,7 @@ var FieldMany2One = AbstractField.extend({
// and hope for the best that noone tries to uses this mechanism to do
// something else.
if (this.nodeOptions.always_reload) {
- value = value.split('\n')[0];
+ value = this._getDisplayName(value);
}
this.$input.val(value);
if (!this.autocomplete_bound) {
@@ -302,7 +313,7 @@ var FieldMany2One = AbstractField.extend({
.then(function (result) {
// possible selections for the m2o
var values = _.map(result, function (x) {
- x[1] = x[1].split("\n")[0];
+ x[1] = self._getDisplayName(x[1]);
return {
label: _.str.escapeHTML(x[1].trim()) || data.noDisplayContent,
value: x[1],
@@ -478,7 +489,7 @@ var FieldMany2One = AbstractField.extend({
_onInputKeyup: function () {
if (this.$input.val() === "") {
this.reinitialize(false);
- } else if (this.m2o_value !== this.$input.val()) {
+ } else if (this._getDisplayName(this.m2o_value) !== this.$input.val()) {
this.floating = true;
this._updateExternalButton();
}
@@ -1585,7 +1596,7 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({
var KanbanFieldMany2ManyTags = FieldMany2ManyTags.extend({
_render: function () {
var self = this;
- this.$el.addClass('o_field_many2manytags o_kanban_tags');
+ this.$el.empty().addClass('o_field_many2manytags o_kanban_tags');
_.each(this.value.data, function (m2m) {
// 10th color is invisible
if ('color' in m2m.data && m2m.data.color !== 10) {
@@ -1763,6 +1774,7 @@ var FieldSelection = AbstractField.extend({
this._super.apply(this, arguments);
if (this.field.type === 'many2one') {
this.values = this.record.specialData[this.name];
+ this.formatType = 'many2one';
} else {
this.values = _.reject(this.field.selection, function (v) {
return v[0] === false && v[1] === '';
diff --git a/addons/web/static/src/js/fields/upgrade_fields.js b/addons/web/static/src/js/fields/upgrade_fields.js
index ecf267e03c1..ae8cae56ef3 100644
--- a/addons/web/static/src/js/fields/upgrade_fields.js
+++ b/addons/web/static/src/js/fields/upgrade_fields.js
@@ -6,6 +6,7 @@ odoo.define('web.upgrade_widgets', function (require) {
* When checked, an upgrade popup is showed to the user.
*/
+var AbstractField = require('web.AbstractField');
var basic_fields = require('web.basic_fields');
var core = require('web.core');
var Dialog = require('web.Dialog');
@@ -58,6 +59,7 @@ var AbstractFieldUpgrade = {
* Opens the Upgrade dialog.
*
* @private
+ * @returns {Dialog} the instance of the opened Dialog
*/
_openDialog: function () {
var message = $(QWeb.render('EnterpriseUpgrade'));
@@ -75,7 +77,7 @@ var AbstractFieldUpgrade = {
},
];
- new Dialog(this, {
+ return new Dialog(this, {
size: 'medium',
buttons: buttons,
$content: $('<div>', {
@@ -122,7 +124,7 @@ var AbstractFieldUpgrade = {
var UpgradeBoolean = FieldBoolean.extend(AbstractFieldUpgrade, {
supportedFieldTypes: [],
- events: _.extend({}, FieldBoolean.prototype.events, {
+ events: _.extend({}, AbstractField.prototype.events, {
'click input': '_onInputClicked',
}),