flectra.define('web.search_inputs', function (require) { "use strict"; var Context = require('web.Context'); var core = require('web.core'); var Domain = require('web.Domain'); var field_utils = require('web.field_utils'); var pyeval = require('web.pyeval'); var time = require('web.time'); var utils = require('web.utils'); var Widget = require('web.Widget'); var _t = core._t; var _lt = core._lt; var Input = Widget.extend( /** @lends instance.web.search.Input# */{ /** * @constructs instance.web.search.Input * @extends instance.web.Widget * * @param parent */ init: function (parent) { this._super(parent); this.searchview = parent; this.load_attrs({}); }, /** * Fetch auto-completion values for the widget. * * The completion values should be an array of objects with keys category, * label, value prefixed with an object with keys type=section and label * * @param {String} value value to complete * @returns {jQuery.Deferred} */ complete: function (value) { return $.when(null); }, /** * Returns a Facet instance for the provided defaults if they apply to * this widget, or null if they don't. * * This default implementation will try calling * :js:func:`instance.web.search.Input#facet_for` if the widget's name * matches the input key * * @param {Object} defaults * @returns {jQuery.Deferred} */ facet_for_defaults: function (defaults) { if (!this.attrs || !(this.attrs.name in defaults && defaults[this.attrs.name])) { return $.when(null); } return this.facet_for(defaults[this.attrs.name]); }, get_context: function () { throw new Error( "get_context not implemented for widget " + this.attrs.type); }, get_groupby: function () { throw new Error( "get_groupby not implemented for widget " + this.attrs.type); }, get_domain: function () { throw new Error( "get_domain not implemented for widget " + this.attrs.type); }, load_attrs: function (attrs) { if (!_.isObject(attrs.modifiers)) { attrs.modifiers = attrs.modifiers ? JSON.parse(attrs.modifiers) : {}; } this.attrs = attrs; }, /** * Returns whether the input is "visible". The default behavior is to * query the ``modifiers.invisible`` flag on the input's description or * view node. * * @returns {Boolean} */ visible: function () { return !this.attrs.modifiers.invisible; }, }); var Field = Input.extend( /** @lends instance.web.search.Field# */ { template: 'SearchView.field', default_operator: '=', /** * @constructs instance.web.search.Field * @extends instance.web.search.Input * * @param view_section * @param field * @param parent */ init: function (view_section, field, parent) { this._super(parent); this.load_attrs(_.extend({}, field, view_section.attrs)); }, facet_for: function (value) { return $.when({ field: this, category: this.attrs.string || this.attrs.name, values: [{label: String(value), value: value}] }); }, value_from: function (facetValue) { return facetValue.get('value'); }, get_context: function (facet) { var self = this; // A field needs a context to send when active var context = this.attrs.context; if (_.isEmpty(context) || !facet.values.length) { return; } var contexts = facet.values.map(function (facetValue) { return new Context(context) .set_eval_context({self: self.value_from(facetValue)}); }); if (contexts.length === 1) { return contexts[0]; } return _.extend(new Context(), { __contexts: contexts }); }, get_groupby: function () { }, /** * Function creating the returned domain for the field, override this * methods in children if you only need to customize the field's domain * without more complex alterations or tests (and without the need to * change override the handling of filter_domain) * * @param {String} name the field's name * @param {String} operator the field's operator (either attribute-specified or default operator for the field * @param {Number|String} facet parsed value for the field * @returns {Array} domain to include in the resulting search */ make_domain: function (name, operator, facet) { return [[name, operator, this.value_from(facet)]]; }, get_domain: function (facet) { if (!facet.values.length) { return; } var value_to_domain; var self = this; var domain = this.attrs.filter_domain; if (domain) { value_to_domain = function (facetValue) { return Domain.prototype.stringToArray( domain, {self: self.value_from(facetValue), raw_value: facetValue.attributes.value} ); }; } else { value_to_domain = function (facetValue) { return self.make_domain( self.attrs.name, self.attrs.operator || self.default_operator, facetValue ); }; } var domains = facet.values.map(value_to_domain); if (domains.length === 1) { return domains[0]; } _.each(domains, Domain.prototype.normalizeArray); var ors = _.times(domains.length - 1, _.constant("|")); return ors.concat.apply(ors, domains); } }); /** * Implementation of the ``char`` OpenERP field type: * * * Default operator is ``ilike`` rather than ``=`` * * * The Javascript and the HTML values are identical (strings) * * @class * @extends instance.web.search.Field */ var CharField = Field.extend( /** @lends instance.web.search.CharField# */ { default_operator: 'ilike', complete: function (value) { if (_.isEmpty(value)) { return $.when(null); } var label = _.str.sprintf(_.str.escapeHTML( _t("Search %(field)s for: %(value)s")), { field: '' + _.escape(this.attrs.string) + '', value: '' + _.escape(value) + ''}); return $.when([{ label: label, facet: { category: this.attrs.string, field: this, values: [{label: value, value: value}] } }]); } }); var NumberField = Field.extend(/** @lends instance.web.search.NumberField# */{ complete: function (value) { var val = this.parse(value); if (isNaN(val)) { return $.when(); } var label = _.str.sprintf( _t("Search %(field)s for: %(value)s"), { field: '' + _.escape(this.attrs.string) + '', value: '' + _.escape(value) + ''}); return $.when([{ label: label, facet: { category: this.attrs.string, field: this, values: [{label: value, value: val}] } }]); }, }); /** * @class * @extends instance.web.search.NumberField */ var IntegerField = NumberField.extend(/** @lends instance.web.search.IntegerField# */{ error_message: _t("not a valid integer"), parse: function (value) { try { return field_utils.parse.integer(value); } catch (e) { return NaN; } } }); /** * @class * @extends instance.web.search.NumberField */ var FloatField = NumberField.extend(/** @lends instance.web.search.FloatField# */{ error_message: _t("not a valid number"), parse: function (value) { try { return field_utils.parse.float(value); } catch (e) { return NaN; } } }); /** * Utility function for m2o & selection fields taking a selection/name_get pair * (value, name) and converting it to a Facet descriptor * * @param {instance.web.search.Field} field holder field * @param {Array} pair pair value to convert */ function facet_from(field, pair) { return { field: field, category: field.attrs.string, values: [{label: pair[1], value: pair[0]}] }; } /** * @class * @extends instance.web.search.Field */ var SelectionField = Field.extend(/** @lends instance.web.search.SelectionField# */{ // This implementation is a basic