summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/fields/basic_fields.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/fields/basic_fields.js')
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js586
1 files changed, 445 insertions, 141 deletions
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index b51fa49ed9a..aac91081c3c 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -114,7 +114,7 @@ var DebouncedField = AbstractField.extend({
*/
commitChanges: function () {
if (this._isDirty && this.mode === 'edit') {
- this._doAction();
+ return this._doAction();
}
},
@@ -135,7 +135,7 @@ var DebouncedField = AbstractField.extend({
// do anything (commitChanges ensures that if it has local changes, they
// are triggered up before the widget is destroyed, if necessary).
if (!this.isDestroyed()) {
- this._setValue(this._getValue());
+ return this._setValue(this._getValue());
}
},
/**
@@ -206,6 +206,13 @@ var InputField = DebouncedField.extend({
this.isDirty = false;
}
if (this.isDirty || (event && event.target === this && event.data.changes[this.name] === this.value)) {
+ if (this.attrs.decorations) {
+ // if a field is modified, then it could have triggered an onchange
+ // which changed some of its decorations. Since we bypass the
+ // render function, we need to apply decorations here to make
+ // sure they are recomputed.
+ this._applyDecorations();
+ }
return $.when();
} else {
return this._render();
@@ -236,14 +243,20 @@ var InputField = DebouncedField.extend({
_prepareInput: function ($input) {
this.$input = $input || $("<input/>");
this.$input.addClass('o_input');
- this.$input.attr({
- type: this.nodeOptions.isPassword ? 'password' : 'text',
- placeholder: this.attrs.placeholder || "",
- autocomplete: this.nodeOptions.isPassword ?
- 'new-password' :
- this.attrs.autocomplete,
- });
- this.$input.val(this._formatValue(this.value));
+
+ var inputAttrs = { placeholder: this.attrs.placeholder || "" };
+ var inputVal;
+ if (this.nodeOptions.isPassword) {
+ inputAttrs = _.extend(inputAttrs, { type: 'password', autocomplete: 'new-password' });
+ inputVal = this.value || '';
+ } else {
+ inputAttrs = _.extend(inputAttrs, { type: 'text', autocomplete: this.attrs.autocomplete });
+ inputVal = this._formatValue(this.value);
+ }
+
+ this.$input.attr(inputAttrs);
+ this.$input.val(inputVal);
+
return this.$input;
},
/**
@@ -321,7 +334,8 @@ var InputField = DebouncedField.extend({
}
if (ev.data.direction ==='next' &&
this.attrs.modifiersValue &&
- this.attrs.modifiersValue.required) {
+ this.attrs.modifiersValue.required &&
+ this.viewType !== 'list') {
if (!this.$input.val()){
this.setInvalidClass();
ev.stopPropagation();
@@ -333,6 +347,64 @@ var InputField = DebouncedField.extend({
},
});
+var NumericField = InputField.extend({
+ tagName: 'span',
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * For numeric fields, 0 is a valid value.
+ *
+ * @override
+ */
+ isSet: function () {
+ return this.value === 0 || this._super.apply(this, arguments);
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Format numerical value (integer or float)
+ *
+ * Note: We have to overwrite this method to skip the format if we are into
+ * edit mode on a input type number.
+ *
+ * @override
+ * @private
+ */
+ _formatValue: function (value) {
+ if (this.mode === 'edit' && this.nodeOptions.type === 'number') {
+ return value;
+ }
+ return this._super.apply(this, arguments);
+ },
+
+ /**
+ * Formats an input element for edit mode. This is in a separate function so
+ * extending widgets can use it on their input without having input as tagName.
+ *
+ * Note: We have to overwrite this method to set the input's type to number if
+ * option setted into the field.
+ *
+ * @override
+ * @private
+ */
+ _prepareInput: function ($input) {
+ var result = this._super.apply(this, arguments);
+ if (this.nodeOptions.type === 'number') {
+ this.$input.attr({type: 'number'});
+ }
+ if (this.nodeOptions.step) {
+ this.$input.attr({step: this.nodeOptions.step});
+ }
+ return result;
+ }
+});
+
var FieldChar = InputField.extend(TranslatableFieldMixin, {
className: 'o_field_char',
tagName: 'span',
@@ -390,9 +462,10 @@ var LinkButton = AbstractField.extend({
if (this.value) {
var className = this.attrs.icon || 'fa-globe';
- this.$el.html("<span />");
+ this.$el.html("<span role='img'/>");
this.$el.addClass("fa "+ className);
this.$el.attr('title', this.value);
+ this.$el.attr('aria-label', this.value);
}
},
@@ -428,6 +501,7 @@ var FieldDate = InputField.extend({
// use the session timezone when formatting dates
this.formatOptions.timezone = true;
this.datepickerOptions = _.defaults(
+ {},
this.nodeOptions.datepicker || {},
{defaultDate: this.value}
);
@@ -458,6 +532,25 @@ var FieldDate = InputField.extend({
},
//--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Asks the datepicker widget to activate the input, instead of doing it
+ * ourself, such that 'input' events triggered by the lib are correctly
+ * intercepted, and don't produce unwanted 'field_changed' events.
+ *
+ * @override
+ */
+ activate: function () {
+ if (this.isFocusable() && this.datewidget) {
+ this.datewidget.focus();
+ return true;
+ }
+ return false;
+ },
+
+ //--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -700,8 +793,8 @@ var FieldBoolean = AbstractField.extend({
// The formatValue of boolean fields renders HTML elements similar to
// the one rendered by the widget itself. Even though the event might
// have been fired on the non-widget version of this field, we can still
- // test the presence of its o_checkbox class.
- if (activated && options && options.event && $(options.event.target).parents('.o_checkbox').length) {
+ // test the presence of its custom class.
+ if (activated && options && options.event && $(options.event.target).closest('.custom-control.custom-checkbox').length) {
this._setValue(!this.value); // Toggle the checkbox
}
return activated;
@@ -736,6 +829,15 @@ var FieldBoolean = AbstractField.extend({
}
return rendered;
},
+ /**
+ * Associates the 'for' attribute of the internal label.
+ *
+ * @override
+ */
+ setIDForLabel: function (id) {
+ this._super.apply(this, arguments);
+ this.$('.custom-control-label').attr('for', id);
+ },
//--------------------------------------------------------------------------
// Private
@@ -795,25 +897,11 @@ var FieldBoolean = AbstractField.extend({
},
});
-var FieldInteger = InputField.extend({
+var FieldInteger = NumericField.extend({
className: 'o_field_integer o_field_number',
- tagName: 'span',
supportedFieldTypes: ['integer'],
//--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * For integer fields, 0 is a valid value.
- *
- * @override
- */
- isSet: function () {
- return this.value === 0 || this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -842,9 +930,8 @@ var FieldInteger = InputField.extend({
},
});
-var FieldFloat = InputField.extend({
+var FieldFloat = NumericField.extend({
className: 'o_field_float o_field_number',
- tagName: 'span',
supportedFieldTypes: ['float'],
/**
@@ -859,27 +946,159 @@ var FieldFloat = InputField.extend({
this.nodeOptions.digits = JSON.parse(this.attrs.digits);
}
},
+});
+
+var FieldFloatTime = FieldFloat.extend({
+ // 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',
+
+ init: function () {
+ this._super.apply(this, arguments);
+ this.formatType = 'float_time';
+ }
+});
+
+var FieldFloatFactor = FieldFloat.extend({
+ supportedFieldTypes: ['float'],
+ className: 'o_field_float_factor',
+ formatType: 'float_factor',
+
+ /**
+ * @constructor
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ // default values
+ if (!this.nodeOptions.factor){
+ this.nodeOptions.factor = 1;
+ }
+ // use as format and parse options
+ this.parseOptions = this.nodeOptions;
+ }
+});
+
+/**
+ * The goal of this widget is to replace the input field by a button containing a
+ * range of possible values (given in the options). Each click allows the user to loop
+ * in the range. The purpose here is to restrict the field value to a predefined selection.
+ * Also, the widget support the factor conversion as the *float_factor* widget (Range values
+ * should be the result of the conversion).
+ **/
+var FieldFloatToggle = AbstractField.extend({
+ supportedFieldTypes: ['float'],
+ formatType: 'float_factor',
+ className: 'o_field_float_toggle',
+ tagName: 'span',
+ events: {
+ click: '_onClick'
+ },
+
+ /**
+ * @constructor
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+
+ this.formatType = 'float_factor';
+
+ if (this.mode === 'edit') {
+ this.tagName = 'button';
+ }
+
+ // we don't inherit Float Field
+ if (this.attrs.digits) {
+ this.nodeOptions.digits = JSON.parse(this.attrs.digits);
+ }
+ // default values
+ if (!this.nodeOptions.factor){
+ this.nodeOptions.factor = 1;
+ }
+ if (!this.nodeOptions.range){
+ this.nodeOptions.range = [0.0, 0.5, 1.0];
+ }
+
+ // use as format and parse options
+ this.parseOptions = this.nodeOptions;
+ },
//--------------------------------------------------------------------------
- // Public
+ // Private
//--------------------------------------------------------------------------
/**
- * For float fields, 0 is a valid value.
+ * Get the display value but in real type to use it in calculations
+ *
+ * @private
+ * @returns {float} The current formatted value
+ */
+ _getDisplayedValue: function () {
+ return parseFloat(this._formatValue(this.value));
+ },
+ /**
+ * Formats the HTML input tag for edit mode and stores selection status.
*
* @override
+ * @private
*/
- isSet: function () {
- return this.value === 0 || this._super.apply(this, arguments);
+ _renderEdit: function () {
+ // Keep a reference to the input so $el can become something else
+ // without losing track of the actual input.
+ this.$el.text(this._formatValue(this.value));
+ },
+ /**
+ * Resets the content to the formated value in readonly mode.
+ *
+ * @override
+ * @private
+ */
+ _renderReadonly: function () {
+ this.$el.text(this._formatValue(this.value));
+ },
+ /**
+ * Get the next value in the range, from the current one. If the current
+ * one is not in the range, the next value of the closest one will be chosen.
+ *
+ * @private
+ * @returns {number} The next formatted value in the range
+ */
+ _nextValue: function () {
+ var range = this.nodeOptions.range;
+ var val = utils.closestNumber(this._getDisplayedValue(), range);
+ var index = _.indexOf(range, val);
+ if (index !== -1) {
+ if (index + 1 < range.length) {
+ return range[index + 1];
+ }
+ }
+ return range[0];
},
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Clicking on the button triggers the change of value; the next one of
+ * the range will be displayed.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onClick: function(ev) {
+ if (this.mode === 'edit') {
+ ev.stopPropagation(); // only stop propagation in edit mode
+ var next_val = this._nextValue();
+ this._setValue(next_val.toString()); // will be parsed in _setValue
+ }
+ },
+
});
-var FieldFloatTime = FieldFloat.extend({
- // 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 FieldPercentage = FieldFloat.extend({
+ formatType:'percentage',
});
var FieldText = InputField.extend(TranslatableFieldMixin, {
@@ -910,7 +1129,19 @@ var FieldText = InputField.extend(TranslatableFieldMixin, {
}
return this._super();
},
-
+ /**
+ * Override to force a resize of the textarea when its value has changed
+ *
+ * @override
+ */
+ reset: function () {
+ var self = this;
+ return $.when(this._super.apply(this, arguments)).then(function () {
+ if (self.mode === 'edit') {
+ self.$input.trigger('change');
+ }
+ });
+ },
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
@@ -1093,13 +1324,88 @@ var UrlWidget = InputField.extend({
},
});
+var CopyClipboard = {
+
+ /**
+ * @override
+ */
+ destroy: function () {
+ this._super.apply(this, arguments);
+ this.clipboard.destroy();
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Instatiates the Clipboad lib.
+ */
+ _initClipboard: function () {
+ var self = this;
+ var $clipboardBtn = this.$('.o_clipboard_button');
+ $clipboardBtn.tooltip({title: _t('Copied !'), trigger: 'manual', placement: 'right'});
+ this.clipboard = new ClipboardJS($clipboardBtn[0], {
+ text: function (_) {
+ return self.value.trim();
+ },
+ // Container added because of Bootstrap modal that give the focus to another element.
+ // We need to give to correct focus to ClipboardJS (see in ClipboardJS doc)
+ // https://github.com/zenorocha/clipboard.js/issues/155
+ container: self.$el[0]
+ });
+ this.clipboard.on('success', function () {
+ _.defer(function () {
+ $clipboardBtn.tooltip('show');
+ _.delay(function () {
+ $clipboardBtn.tooltip('hide');
+ }, 800);
+ });
+ });
+ },
+};
+
+var TextCopyClipboard = FieldText.extend(CopyClipboard, {
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _render: function() {
+ this._super.apply(this, arguments);
+ this.$el.addClass('o_field_copy');
+ this.$el.append($(qweb.render('CopyClipboardText')));
+ this._initClipboard();
+ }
+});
+
+var CharCopyClipboard = FieldChar.extend(CopyClipboard, {
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _render: function() {
+ this._super.apply(this, arguments);
+ this.$el.addClass('o_field_copy');
+ this.$el.append($(qweb.render('CopyClipboardChar')));
+ this._initClipboard();
+ }
+});
+
var AbstractFieldBinary = AbstractField.extend({
events: _.extend({}, AbstractField.prototype.events, {
'change .o_input_file': 'on_file_change',
'click .o_select_file_button': function () {
this.$('.o_input_file').click();
},
- 'click .o_clear_file_button': 'on_clear',
+ 'click .o_clear_file_button': '_onClearClick',
}),
init: function (parent, name, record) {
this._super.apply(this, arguments);
@@ -1185,11 +1491,33 @@ var AbstractFieldBinary = AbstractField.extend({
});
}
},
- on_clear: function () {
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+ /**
+ * Clear the file input
+ *
+ * @private
+ */
+ _clearFile: function (){
this.set_filename('');
this._setValue(false);
this._render();
},
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+ /**
+ * On "clear file" button click
+ *
+ * @param {MouseEvent} ev
+ * @private
+ */
+ _onClearClick: function (ev) {
+ this._clearFile();
+ },
});
var FieldBinaryImage = AbstractFieldBinary.extend({
@@ -1207,12 +1535,19 @@ var FieldBinaryImage = AbstractFieldBinary.extend({
},
}),
supportedFieldTypes: ['binary'],
+ file_type_magic_word: {
+ '/': 'jpg',
+ 'R': 'gif',
+ 'i': 'png',
+ 'P': 'svg+xml',
+ },
_render: function () {
var self = this;
var url = this.placeholder;
if (this.value) {
if (!utils.is_bin_size(this.value)) {
- url = 'data:image/png;base64,' + this.value;
+ // Use magic-word technique for detecting image type
+ url = 'data:image/' + (this.file_type_magic_word[this.value[0]] || 'png') + ';base64,' + this.value;
} else {
url = session.url('/web/image', {
model: this.model,
@@ -1239,7 +1574,7 @@ var FieldBinaryImage = AbstractFieldBinary.extend({
this.$('> img').remove();
this.$el.prepend($img);
$img.on('error', function () {
- self.on_clear();
+ self._clearFile();
$img.attr('src', self.placeholder);
self.do_warn(_t("Image"), _t("Could not display the selected image."));
});
@@ -1348,9 +1683,6 @@ var FieldPdfViewer = FieldBinaryFile.extend({
* @param {DOMElement} iframe
*/
_disableButtons: function (iframe) {
- if (this.mode === 'readonly') {
- $(iframe).contents().find('button#download').hide();
- }
$(iframe).contents().find('button#openFile').hide();
},
/**
@@ -1499,6 +1831,7 @@ var PriorityWidget = AbstractField.extend({
_renderStar: function (tag, isFull, index, tip) {
return $(tag)
.attr('title', tip)
+ .attr('aria-label', tip)
.attr('data-index', index)
.addClass('o_priority_star fa')
.toggleClass('fa-star', isFull)
@@ -1568,7 +1901,8 @@ var AttachmentImage = AbstractField.extend({
if (this.value) {
this.$el.empty().append($('<img>/', {
src: "/web/image/" + this.value.data.id + "?unique=1",
- title: this.value.data.display_name
+ title: this.value.data.display_name,
+ alt: _t("Image")
}));
}
}
@@ -1577,10 +1911,7 @@ var AttachmentImage = AbstractField.extend({
var StateSelectionWidget = AbstractField.extend({
template: 'FormSelection',
events: {
- 'click a': function (e) {
- e.preventDefault();
- },
- 'click li': '_setSelection'
+ 'click .dropdown-item': '_setSelection',
},
supportedFieldTypes: ['selection'],
@@ -1637,7 +1968,9 @@ var StateSelectionWidget = AbstractField.extend({
this.$('.o_status')
.removeClass('o_status_red o_status_green')
.addClass(currentState.state_class)
- .prop('special_click', true);
+ .prop('special_click', true)
+ .parent().attr('title', currentState.state_name)
+ .attr('aria-label', currentState.state_name);
// Render "FormSelection.Items" and move it into "FormSelection"
var $items = $(qweb.render('FormSelection.items', {
@@ -1659,13 +1992,12 @@ var StateSelectionWidget = AbstractField.extend({
* @param {MouseEvent} ev
*/
_setSelection: function (ev) {
- var li = $(ev.target).closest('li');
- if (li.length) {
- var value = String(li.data('value'));
- this._setValue(value);
- if (this.mode === 'edit') {
- this._render();
- }
+ ev.preventDefault();
+ var $item = $(ev.currentTarget);
+ var value = String($item.data('value'));
+ this._setValue(value);
+ if (this.mode === 'edit') {
+ this._render();
}
},
});
@@ -1701,8 +2033,9 @@ var FavoriteWidget = AbstractField.extend({
* @private
*/
_render: function () {
- var template = this.attrs.nolabel ? '<a href="#"><i class="fa %s" title="%s"></i></a>' : '<a href="#"><i class="fa %s"></i> %s</a>';
- this.$el.empty().append(_.str.sprintf(template, this.value ? 'fa-star' : 'fa-star-o', this.value ? _t('Remove from Favorites') : _t('Add to Favorites')));
+ var tip = this.value ? _t('Remove from Favorites') : _t('Add to Favorites');
+ var template = this.attrs.nolabel ? '<a href="#"><i class="fa %s" title="%s" aria-label="%s" role="img"></i></a>' : '<a href="#"><i class="fa %s" role="img" aria-label="%s"></i> %s</a>';
+ this.$el.empty().append(_.str.sprintf(template, this.value ? 'fa-star' : 'fa-star-o', tip, tip));
},
//--------------------------------------------------------------------------
@@ -1740,7 +2073,7 @@ var LabelSelection = AbstractField.extend({
_render: function () {
this.classes = this.nodeOptions && this.nodeOptions.classes || {};
var labelClass = this.classes[this.value] || 'primary';
- this.$el.addClass('label label-' + labelClass).text(this._formatValue(this.value));
+ this.$el.addClass('badge badge-' + labelClass).text(this._formatValue(this.value));
},
});
@@ -1807,32 +2140,10 @@ var FieldBooleanButton = AbstractField.extend({
});
var BooleanToggle = FieldBoolean.extend({
+ className: FieldBoolean.prototype.className + ' o_boolean_toggle',
events: {
'click': '_onClick'
},
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @private
- */
- _render: function () {
- this._super.apply(this, arguments);
- this._renderToggleSwitch();
- },
-
- /**
- * Display toggle switch
- *
- * @private
- */
- _renderToggleSwitch: function () {
- this.$el.addClass("o_boolean_toggle");
- var $div = $('<div class="slider"></div>');
- $div.insertAfter(this.$("input[type=checkbox]"));
- },
//--------------------------------------------------------------------------
// Handlers
@@ -1849,7 +2160,6 @@ var BooleanToggle = FieldBoolean.extend({
this._setValue(!this.value);
this.$el.closest(".o_data_row").toggleClass('text-muted', this.value);
},
-
});
var StatInfo = AbstractField.extend({
@@ -2082,7 +2392,10 @@ var FieldProgressBar = AbstractField.extend({
widthComplete = 100;
}
- this.$('.o_progress').toggleClass('o_progress_overflow', value > max_value);
+ this.$('.o_progress').toggleClass('o_progress_overflow', value > max_value)
+ .attr('aria-valuemin', '0')
+ .attr('aria-valuemax', max_value)
+ .attr('aria-valuenow', value);
this.$('.o_progressbar_complete').css('width', widthComplete + '%');
if (!this.write_mode) {
@@ -2151,6 +2464,7 @@ var FieldToggleBoolean = AbstractField.extend({
.toggleClass('text-muted', !this.value);
var title = this.value ? this.attrs.options.active : this.attrs.options.inactive;
this.$el.attr('title', title);
+ this.$el.attr('aria-pressed', this.value);
},
//--------------------------------------------------------------------------
@@ -2198,6 +2512,25 @@ var JournalDashboardGraph = AbstractField.extend({
}
this._super.apply(this, arguments);
},
+ /**
+ * The widget view uses the nv(d3) lib to render the graph. This lib
+ * requires that the rendering is done directly into the DOM (so that it can
+ * correctly compute positions). However, the views are always rendered in
+ * fragments, and appended to the DOM once ready (to prevent them from
+ * flickering). We here use the on_attach_callback hook, called when the
+ * widget is attached to the DOM, to perform the rendering. This ensures
+ * that the rendering is always done in the DOM.
+ */
+ on_attach_callback: function () {
+ this._isInDOM = true;
+ this._renderInDOM();
+ },
+ /**
+ * Called when the field is detached from the DOM.
+ */
+ on_detach_callback: function () {
+ this._isInDOM = false;
+ },
//--------------------------------------------------------------------------
// Private
@@ -2218,9 +2551,25 @@ var JournalDashboardGraph = AbstractField.extend({
}
},
/**
+ * Render the widget only when it is in the DOM. This is because nvd3
+ * renders graph only when it is in DOM, apparently to compute available
+ * height and width for instance.
+ *
+ * @override
* @private
*/
_render: function () {
+ if (this._isInDOM) {
+ return this._renderInDOM();
+ }
+ return $.when();
+ },
+ /**
+ * Render the widget. This function assumes that it is attached to the DOM.
+ *
+ * @private
+ */
+ _renderInDOM: function () {
// note: the rendering of this widget is aynchronous as nvd3 does a
// setTimeout(0) before executing the callback given to addGraph
var self = this;
@@ -2644,56 +2993,6 @@ var AceEditor = DebouncedField.extend({
},
});
-var ImageSelection = AbstractField.extend({
- supportedFieldTypes: ['selection'],
- events: _.extend({}, AbstractField.prototype.events, {
- 'click img': '_onImgClicked',
- }),
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @private
- */
- _render: function () {
- var self = this;
- this.$el.empty();
- _.each(this.nodeOptions, function (val, key) {
- var $container = $('<div>').addClass('col-xs-3 text-center');
- var $img = $('<img>')
- .addClass('img img-responsive img-thumbnail ml16')
- .toggleClass('btn-info', key === self.value)
- .attr('src', val.image_link)
- .data('key', key);
- $container.append($img);
- if (val.preview_link) {
- var $previewLink = $('<a>')
- .text('Preview')
- .attr('href', val.preview_link)
- .attr('target', '_blank');
- $container.append($previewLink);
- }
- self.$el.append($container);
- });
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @private
- * @param {MouseEvent} event
- */
- _onImgClicked: function (event) {
- this._setValue($(event.currentTarget).data('key'));
- },
-});
-
return {
TranslatableFieldMixin: TranslatableFieldMixin,
DebouncedField: DebouncedField,
@@ -2711,6 +3010,9 @@ return {
FieldDomain: FieldDomain,
FieldFloat: FieldFloat,
FieldFloatTime: FieldFloatTime,
+ FieldFloatFactor: FieldFloatFactor,
+ FieldFloatToggle: FieldFloatToggle,
+ FieldPercentage : FieldPercentage,
FieldInteger: FieldInteger,
FieldMonetary: FieldMonetary,
FieldPercentPie: FieldPercentPie,
@@ -2720,14 +3022,16 @@ return {
FieldToggleBoolean: FieldToggleBoolean,
HandleWidget: HandleWidget,
InputField: InputField,
+ NumericField: NumericField,
AttachmentImage: AttachmentImage,
- ImageSelection: ImageSelection,
LabelSelection: LabelSelection,
StateSelectionWidget: StateSelectionWidget,
FavoriteWidget: FavoriteWidget,
PriorityWidget: PriorityWidget,
StatInfo: StatInfo,
UrlWidget: UrlWidget,
+ TextCopyClipboard: TextCopyClipboard,
+ CharCopyClipboard: CharCopyClipboard,
JournalDashboardGraph: JournalDashboardGraph,
AceEditor: AceEditor,
};