-openerp.web.form = function (openerp) {
+openerp.web.form = function (instance) {
+var _t = instance.web._t,
+ _lt = instance.web._lt;
+var QWeb = instance.web.qweb;
-var _t = openerp.web._t,
- _lt = openerp.web._lt;
-var QWeb = openerp.web.qweb;
+/** @namespace */
+instance.web.form = {};
+
+/**
+ * Interface implemented by the form view or any other object
+ * able to provide the features necessary for the fields to work.
+ *
+ * Properties:
+ * - display_invalid_fields : if true, all fields where is_valid() return true should
+ * be displayed as invalid.
+ * Events:
+ * - view_content_has_changed : when the values of the fields have changed. When
+ * this event is triggered all fields should reprocess their modifiers.
+ */
+instance.web.form.FieldManagerMixin = {
+ /**
+ * Must return the asked field as in fields_get.
+ */
+ get_field: function(field_name) {},
+ /**
+ * Called by the field when the translate button is clicked.
+ */
+ open_translate_dialog: function(field) {},
+ /**
+ * Returns true when the view is in create mode.
+ */
+ is_create_mode: function() {},
+};
-openerp.web.views.add('form', 'openerp.web.FormView');
-openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# */{
+instance.web.views.add('form', 'instance.web.FormView');
+instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerMixin, {
/**
* Indicates that this view is not searchable, and thus that no search
* view should be displayed (if there is one active).
*/
searchable: false,
- readonly : false,
- form_template: "FormView",
+ template: "FormView",
display_name: _lt('Form'),
+ view_type: "form",
/**
- * @constructs openerp.web.FormView
- * @extends openerp.web.View
+ * @constructs instance.web.FormView
+ * @extends instance.web.View
*
- * @param {openerp.web.Session} session the current openerp session
- * @param {openerp.web.DataSet} dataset the dataset this view will work with
+ * @param {instance.web.Session} session the current openerp session
+ * @param {instance.web.DataSet} dataset the dataset this view will work with
* @param {String} view_id the identifier of the OpenERP view object
* @param {Object} options
- * - sidebar : [true|false]
* - resize_textareas : [true|false|max_height]
*
- * @property {openerp.web.Registry} registry=openerp.web.form.widgets widgets registry for this form view instance
+ * @property {instance.web.Registry} registry=instance.web.form.widgets widgets registry for this form view instance
*/
init: function(parent, dataset, view_id, options) {
this._super(parent);
this.model = dataset.model;
this.view_id = view_id || false;
this.fields_view = {};
- this.widgets = {};
- this.widgets_counter = 0;
this.fields = {};
this.fields_order = [];
this.datarecord = {};
this.default_focus_field = null;
this.default_focus_button = null;
- this.registry = openerp.web.form.widgets;
+ this.fields_registry = instance.web.form.widgets;
+ this.tags_registry = instance.web.form.tags;
this.has_been_loaded = $.Deferred();
- this.$form_header = null;
this.translatable_fields = [];
_.defaults(this.options, {
- "not_interactible_on_create": false
+ "not_interactible_on_create": false,
+ "initial_mode": "view",
});
this.is_initialized = $.Deferred();
this.mutating_mutex = new $.Mutex();
this.on_change_mutex = new $.Mutex();
this.reload_mutex = new $.Mutex();
- },
- start: function() {
- this._super();
- return this.init_view();
- },
- init_view: function() {
- if (this.embedded_view) {
- var def = $.Deferred().then(this.on_loaded);
- var self = this;
- $.async_when().then(function() {def.resolve(self.embedded_view);});
- return def.promise();
- } else {
- var context = new openerp.web.CompoundContext(this.dataset.get_context());
- return this.rpc("/web/view/load", {
- "model": this.model,
- "view_id": this.view_id,
- "view_type": "form",
- toolbar: this.options.sidebar,
- context: context
- }, this.on_loaded);
- }
- },
- stop: function() {
- if (this.sidebar) {
- this.sidebar.attachments.stop();
- this.sidebar.stop();
- }
- _.each(this.widgets, function(w) {
- w.stop();
+ this.__clicked_inside = false;
+ this.__blur_timeout = null;
+ this.rendering_engine = new instance.web.form.FormRenderingEngineReadonly(this);
+ this.qweb = null; // A QWeb instance will be created if the view is a QWeb template
+ },
+ destroy: function() {
+ _.each(this.get_widgets(), function(w) {
+ w.off('focused blurred');
+ w.destroy();
});
+ this.$element.off('.formBlur');
this._super();
},
- reposition: function ($e) {
- this.$element = $e;
- this.on_loaded();
- },
on_loaded: function(data) {
var self = this;
- if (data) {
- this.fields_order = [];
- this.fields_view = data;
- var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
+ if (!data) {
+ throw new Error("No data provided.");
+ }
+ if (this.arch) {
+ throw "Form view does not support multiple calls to on_loaded";
+ }
+ this.fields_order = [];
+ this.fields_view = data;
- this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'widget': this });
+ this.rendering_engine.set_fields_registry(this.fields_registry);
+ this.rendering_engine.set_tags_registry(this.tags_registry);
+ if (!this.extract_qweb_template(data)) {
+ this.rendering_engine.set_fields_view(data);
+ var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container');
+ this.rendering_engine.render_to($dest);
}
- this.$element.html(this.rendered);
- _.each(this.widgets, function(w) {
- w.start();
+
+ this.$element.on('mousedown.formBlur', function () {
+ self.__clicked_inside = true;
});
- this.$form_header = this.$element.find('.oe_form_header:first');
- this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
+
+ this.$buttons = $(QWeb.render("FormView.buttons", {'widget':self}));
+ if (this.options.$buttons) {
+ this.$buttons.appendTo(this.options.$buttons);
+ } else {
+ this.$element.find('.oe_form_buttons').replaceWith(this.$buttons);
+ }
+ this.$buttons.on('click','.oe_form_button_create',this.on_button_create);
+ this.$buttons.on('click','.oe_form_button_edit',this.on_button_edit);
+ this.$buttons.on('click','.oe_form_button_save',this.on_button_save);
+ this.$buttons.on('click','.oe_form_button_cancel',this.on_button_cancel);
+
+ this.$pager = $(QWeb.render("FormView.pager", {'widget':self}));
+ if (this.options.$pager) {
+ this.$pager.appendTo(this.options.$pager);
+ } else {
+ this.$element.find('.oe_form_pager').replaceWith(this.$pager);
+ }
+ this.$pager.on('click','a[data-pager-action]',function() {
var action = $(this).data('pager-action');
self.on_pager_action(action);
});
- this.$form_header.find('button.oe_form_button_save').click(this.on_button_save);
- this.$form_header.find('button.oe_form_button_cancel').click(this.on_button_cancel);
-
- if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) {
- this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
- this.sidebar.start();
- this.sidebar.do_unfold();
- this.sidebar.attachments = new openerp.web.form.SidebarAttachments(this.sidebar, this);
- this.sidebar.add_toolbar(this.fields_view.toolbar);
- this.set_common_sidebar_sections(this.sidebar);
-
- this.sidebar.add_section(_t('Customize'), 'customize');
- this.sidebar.add_items('customize', [{
- label: _t('Set Default'),
- form: this,
- callback: function (item) {
- item.form.open_defaults_dialog();
- }
- }]);
+ this.$sidebar = this.options.$sidebar || this.$element.find('.oe_form_sidebar');
+ if (!this.sidebar && this.options.$sidebar) {
+ this.sidebar = new instance.web.Sidebar(this);
+ this.sidebar.appendTo(this.$sidebar);
+ if(this.fields_view.toolbar) {
+ this.sidebar.add_toolbar(this.fields_view.toolbar);
+ }
+ this.sidebar.add_items('other', [
+ { label: _t('Delete'), callback: self.on_button_delete },
+ { label: _t('Duplicate'), callback: self.on_button_duplicate },
+ { label: _t('Set Default'), callback: function (item) { self.open_defaults_dialog(); } },
+ ]);
}
+ this.on("change:mode", this, this.switch_mode);
+ this.set({mode: this.options.initial_mode});
this.has_been_loaded.resolve();
+ return $.when();
+ },
+ extract_qweb_template: function(fvg) {
+ for (var i=0, ii=fvg.arch.children.length; i < ii; i++) {
+ var child = fvg.arch.children[i];
+ if (child.tag === "templates") {
+ this.qweb = new QWeb2.Engine();
+ this.qweb.add_template(instance.web.json_node_to_xml(child));
+ if (!this.qweb.has_template('form')) {
+ throw new Error("No QWeb template found for form view");
+ }
+ return true;
+ }
+ }
+ this.qweb = null;
+ return false;
+ },
+ get_fvg_from_qweb: function(record) {
+ var view = this.qweb.render('form', this.get_qweb_context(record));
+ var fvg = _.clone(this.fields_view);
+ fvg.arch = instance.web.xml_to_json(instance.web.str_to_xml(view).firstChild);
+ return fvg;
+ },
+ get_qweb_context: function(record) {
+ var self = this,
+ new_record = {};
+ _.each(record, function(value_, name) {
+ var r = _.clone(self.fields_view.fields[name] || {});
+ if ((r.type === 'date' || r.type === 'datetime') && value_) {
+ r.raw_value = instance.web.auto_str_to_date(value_);
+ } else {
+ r.raw_value = value_;
+ }
+ r.value = instance.web.format_value(value_, r);
+ new_record[name] = r;
+ });
+ return {
+ record : new_record,
+ new_record : !record.id
+ };
+ },
+ kill_current_form: function() {
+ _.each(this.getChildren(), function(el) {
+ el.destroy();
+ });
+ this.fields = {};
+ this.fields_order = [];
+ this.default_focus_field = null;
+ this.default_focus_button = null;
+ this.translatable_fields = [];
+ this.$element.find('.oe_form_container').empty();
+ },
+
+ widgetFocused: function() {
+ // Clear click flag if used to focus a widget
+ this.__clicked_inside = false;
+ if (this.__blur_timeout) {
+ clearTimeout(this.__blur_timeout);
+ this.__blur_timeout = null;
+ }
+ },
+ widgetBlurred: function() {
+ if (this.__clicked_inside) {
+ // clicked in an other section of the form (than the currently
+ // focused widget) => just ignore the blurring entirely?
+ this.__clicked_inside = false;
+ return;
+ }
+ var self = this;
+ // clear timeout, if any
+ this.widgetFocused();
+ this.__blur_timeout = setTimeout(function () {
+ self.trigger('blurred');
+ }, 0);
},
do_load_state: function(state, warm) {
}
}
},
-
- do_show: function () {
+ do_show: function (options) {
var self = this;
+ options = options || {};
+ if (this.sidebar) {
+ this.sidebar.$element.show();
+ }
+ if (this.$buttons) {
+ this.$buttons.show();
+ this.$buttons.find('.oe_form_button_save').removeClass('oe_form_button_save_dirty');
+ }
+ if (this.$pager) {
+ this.$pager.show();
+ }
this.$element.show().css('visibility', 'hidden');
this.$element.removeClass('oe_form_dirty');
return this.has_been_loaded.pipe(function() {
}).pipe(self.on_record_loaded);
}
result.pipe(function() {
+ if (options.editable) {
+ self.set({mode: "edit"});
+ }
self.$element.css('visibility', 'visible');
});
- if (self.sidebar) {
- self.sidebar.$element.show();
- }
return result;
});
},
do_hide: function () {
- this._super();
if (this.sidebar) {
this.sidebar.$element.hide();
}
+ if (this.$buttons) {
+ this.$buttons.hide();
+ }
+ if (this.$pager) {
+ this.$pager.hide();
+ }
+ this._super();
},
on_record_loaded: function(record) {
var self = this, set_values = [];
}
this.datarecord = record;
+ if (this.qweb) {
+ this.kill_current_form();
+ this.rendering_engine.set_fields_view(this.get_fvg_from_qweb(record));
+ var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container');
+ this.rendering_engine.render_to($dest);
+ }
+
_(this.fields).each(function (field, f) {
- field.reset();
+ field._dirty_flag = false;
var result = field.set_value(self.datarecord[f] || false);
set_values.push(result);
- $.when(result).then(function() {
- field.validate();
- });
});
return $.when.apply(null, set_values).pipe(function() {
if (!record.id) {
_.each(self.fields_order, function(field_name) {
if (record[field_name] !== undefined) {
var field = self.fields[field_name];
- field.dirty = true;
+ field._dirty_flag = true;
self.do_onchange(field);
}
});
self.is_initialized.resolve();
self.do_update_pager(record.id == null);
if (self.sidebar) {
- self.sidebar.attachments.do_update();
+ self.sidebar.do_attachement_update(self.dataset, self.datarecord.id);
}
if (self.default_focus_field) {
self.default_focus_field.focus();
self.do_push_state({id:record.id});
}
self.$element.removeClass('oe_form_dirty');
+ self.$buttons.find('.oe_form_button_save').removeClass('oe_form_button_save_dirty');
});
},
on_form_changed: function() {
- for (var w in this.widgets) {
- w = this.widgets[w];
- w.process_modifiers();
- if (w.field) {
- w.validate();
- }
- w.update_dom();
- }
+ this.trigger("view_content_has_changed");
},
do_notify_change: function() {
this.$element.addClass('oe_form_dirty');
+ this.$buttons.find('.oe_form_button_save').addClass('oe_form_button_save_dirty');
},
on_pager_action: function(action) {
if (this.can_be_discarded()) {
}
},
do_update_pager: function(hide_index) {
- var $pager = this.$form_header.find('div.oe_form_pager');
var index = hide_index ? '-' : this.dataset.index + 1;
- $pager.find('button').prop('disabled', this.dataset.ids.length < 2);
- $pager.find('span.oe_pager_index').html(index);
- $pager.find('span.oe_pager_count').html(this.dataset.ids.length);
+ this.$pager.find('button').prop('disabled', this.dataset.ids.length < 2).end()
+ .find('span.oe_pager_index').html(index).end()
+ .find('span.oe_pager_count').html(this.dataset.ids.length);
},
parse_on_change: function (on_change, widget) {
var self = this;
'None': function () {return null;},
'context': function (i) {
context_index = i;
- var ctx = new openerp.web.CompoundContext(self.dataset.get_context(), widget.build_context() ? widget.build_context() : {});
+ var ctx = new instance.web.CompoundContext(self.dataset.get_context(), widget.build_context() ? widget.build_context() : {});
return ctx;
}
};
}
// form field
if (self.fields[field]) {
- var value = self.fields[field].get_on_change_value();
- return value == null ? false : value;
+ var value_ = self.fields[field].get_value();
+ return value_ == null ? false : value_;
}
// parent field
var splitted = field.split('.');
}
if (widget.field['change_default']) {
- var fieldname = widget.name, value;
+ var fieldname = widget.name, value_;
if (response.value && (fieldname in response.value)) {
// Use value from onchange if onchange executed
- value = response.value[fieldname];
+ value_ = response.value[fieldname];
} else {
// otherwise get form value for field
- value = self.fields[fieldname].get_on_change_value();
+ value_ = self.fields[fieldname].get_value();
}
- var condition = fieldname + '=' + value;
+ var condition = fieldname + '=' + value_;
- if (value) {
+ if (value_) {
can_process_onchange = self.rpc({
url: '/web/dataset/call',
async: false
return self.on_processed_onchange(response, processed);
} catch(e) {
console.error(e);
+ instance.webclient.crashmanager.on_javascript_exception(e);
return $.Deferred().reject();
}
});
},
- on_processed_onchange: function(response, processed) {
+ on_processed_onchange: function(result, processed) {
try {
- var result = response;
if (result.value) {
for (var f in result.value) {
if (!result.value.hasOwnProperty(f)) { continue; }
var field = this.fields[f];
// If field is not defined in the view, just ignore it
if (field) {
- var value = result.value[f];
- if (field.get_value() != value) {
- field.set_value(value);
- field.dirty = true;
+ var value_ = result.value[f];
+ if (field.get_value() != value_) {
+ field.set_value(value_);
+ field._dirty_flag = true;
if (!_.contains(processed, field.name)) {
this.do_onchange(field, processed);
}
this.on_form_changed();
}
if (!_.isEmpty(result.warning)) {
- $(QWeb.render("CrashManagerWarning", result.warning)).dialog({
+ instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), {
+ title:result.warning.title,
modal: true,
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
}
if (result.domain) {
function edit_domain(node) {
+ if (typeof node !== "object") {
+ return;
+ }
var new_domain = result.domain[node.attrs.name];
if (new_domain) {
node.attrs.domain = new_domain;
return $.Deferred().resolve();
} catch(e) {
console.error(e);
+ instance.webclient.crashmanager.on_javascript_exception(e);
return $.Deferred().reject();
}
},
+ switch_mode: function() {
+ var self = this;
+ if(this.get("mode") == "view") {
+ self.$element.removeClass('oe_form_editable').addClass('oe_form_readonly');
+ self.$buttons.find('.oe_form_buttons_edit').hide();
+ self.$buttons.find('.oe_form_buttons_view').show();
+ self.$sidebar.show();
+ _.each(this.fields,function(field){
+ field.set({"force_readonly": true});
+ });
+ } else {
+ self.$element.removeClass('oe_form_readonly').addClass('oe_form_editable');
+ self.$buttons.find('.oe_form_buttons_edit').show();
+ self.$buttons.find('.oe_form_buttons_view').hide();
+ self.$sidebar.hide();
+ _.each(this.fields,function(field){
+ field.set({"force_readonly": false});
+ });
+ }
+ },
on_button_save: function() {
var self = this;
return this.do_save().then(function(result) {
- self.do_prev_view({'created': result.created, 'default': 'page'});
+ self.set({mode: "view"});
});
},
- on_button_cancel: function() {
+ on_button_cancel: function(event) {
if (this.can_be_discarded()) {
- return this.do_prev_view({'default': 'page'});
+ this.set({mode: "view"});
+ this.on_record_loaded(this.datarecord);
}
+ return false;
},
on_button_new: function() {
var self = this;
+ this.set({mode: "edit"});
var def = $.Deferred();
$.when(this.has_been_loaded).then(function() {
if (self.can_be_discarded()) {
});
return def.promise();
},
+ on_button_edit: function() {
+ return this.set({mode: "edit"});
+ },
+ on_button_create: function() {
+ this.dataset.index = null;
+ this.do_show();
+ },
+ on_button_duplicate: function() {
+ var self = this;
+ var def = $.Deferred();
+ $.when(this.has_been_loaded).then(function() {
+ self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) {
+ return self.on_created({ result : new_id });
+ }).then(function() {
+ return self.set({mode: "edit"});
+ }).then(function() {
+ def.resolve();
+ });
+ });
+ return def.promise();
+ },
+ on_button_delete: function() {
+ var self = this;
+ var def = $.Deferred();
+ $.when(this.has_been_loaded).then(function() {
+ if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) {
+ self.dataset.unlink([self.datarecord.id]).then(function() {
+ self.on_pager_action('next');
+ def.resolve();
+ });
+ } else {
+ $.async_when().then(function () {
+ def.reject();
+ })
+ }
+ });
+ return def.promise();
+ },
can_be_discarded: function() {
return !this.$element.is('.oe_form_dirty') || confirm(_t("Warning, the record has been modified, your changes will be discarded."));
},
* record or saving an existing one depending on whether the record
* already has an id property.
*
- * @param {Function} success callback on save success
+ * @param {Function} [success] callback on save success
* @param {Boolean} [prepend_on_create=false] if ``do_save`` creates a new record, should that record be inserted at the start of the dataset (by default, records are added at the end)
*/
do_save: function(success, prepend_on_create) {
f = self.fields[f];
if (!f.is_valid()) {
form_invalid = true;
- f.update_dom(true);
if (!first_invalid_field) {
first_invalid_field = f;
}
- } else if (f.name !== 'id' && !f.readonly && (!self.datarecord.id || f.is_dirty())) {
+ } else if (f.name !== 'id' && !f.get("readonly") && (!self.datarecord.id || f._dirty_flag)) {
// Special case 'id' field, do not save this field
// on 'create' : save all non readonly fields
// on 'edit' : save non readonly modified fields
}
}
if (form_invalid) {
+ self.set({'display_invalid_fields': true});
+ for (var f in self.fields) {
+ self.fields[f]._check_css_flags();
+ }
first_invalid_field.focus();
self.on_invalid();
return $.Deferred().reject();
} else {
+ self.set({'display_invalid_fields': false});
var save_deferral;
if (!self.datarecord.id) {
//console.log("FormView(", self, ") : About to create", values);
var msg = "<ul>";
_.each(this.fields, function(f) {
if (!f.is_valid()) {
- msg += "<li>" + f.string + "</li>";
+ msg += "<li>" + (f.node.attrs.string || f.field.string) + "</li>";
}
});
msg += "</ul>";
return $.Deferred().reject();
} else {
return $.when(this.reload()).pipe(function () {
- return $.when(r).then(success); }, null);
+ return r; })
+ .then(success);
}
},
/**
this.dataset.alter_ids(this.dataset.ids.concat([this.datarecord.id]));
this.dataset.index = this.dataset.ids.length - 1;
} else {
- this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids));
+ this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids));
this.dataset.index = 0;
}
this.do_update_pager();
if (this.sidebar) {
- this.sidebar.attachments.do_update();
+ this.sidebar.do_attachement_update(this.dataset, this.datarecord.id);
}
//openerp.log("The record has been created with id #" + this.datarecord.id);
- this.reload();
- return $.when(_.extend(r, {created: true})).then(success);
+ return $.when(this.reload()).pipe(function () {
+ return _.extend(r, {created: true}); })
+ .then(success);
}
},
on_action: function (action) {
reload: function() {
var self = this;
return this.reload_mutex.exec(function() {
+ if (self.dataset.index == null) {
+ self.do_prev_view();
+ return $.Deferred().reject().promise();
+ }
if (self.dataset.index == null || self.dataset.index < 0) {
return $.when(self.on_button_new());
} else {
}
});
},
+ get_widgets: function() {
+ return _.filter(this.getChildren(), function(obj) {
+ return obj instanceof instance.web.form.FormWidget;
+ });
+ },
get_fields_values: function(blacklist) {
blacklist = blacklist || [];
var values = {};
var ids = this.get_selected_ids();
values["id"] = ids.length > 0 ? ids[0] : false;
- _.each(this.fields, function(value, key) {
+ _.each(this.fields, function(value_, key) {
if (_.include(blacklist, key))
return;
- var val = value.get_value();
+ var val = value_.get_value();
values[key] = val;
});
return values;
});
},
is_dirty: function() {
- return _.any(this.fields, function (value) {
- return value.is_dirty();
+ return _.any(this.fields, function (value_) {
+ return value_._dirty_flag;
});
},
is_interactible_record: function() {
if (this.options.not_interactible_on_create)
return false;
} else if (typeof(id) === "string") {
- if(openerp.web.BufferedDataSet.virtual_id_regex.test(id))
+ if(instance.web.BufferedDataSet.virtual_id_regex.test(id))
return false;
}
return true;
// ignore fields which are empty, invisible, readonly, o2m
// or m2m
if (!value
- || field.invisible
- || field.readonly
+ || field.get('invisible')
+ || field.get("readonly")
|| field.field.type === 'one2many'
|| field.field.type === 'many2many') {
return false;
}
- var displayed;
- switch(field.field.type) {
+ var displayed = value;
+ switch (field.field.type) {
case 'selection':
displayed = _(field.values).find(function (option) {
return option[0] === value;
})[1];
break;
- case 'many2one':
- displayed = field.value[1] || value;
- break;
- default:
- displayed = value;
}
return {
name: name,
- string: field.string,
+ string: field.node.attrs.string || field.field.string,
value: value,
displayed: displayed,
// convert undefined to false
.filter(function (field) { return field.change_default; })
.value();
- var d = new openerp.web.Dialog(this, {
+ var d = new instance.web.Dialog(this, {
title: _t("Set Default"),
args: {
fields: fields,
var $defaults = d.$element.find('#formview_default_fields');
var field_to_set = $defaults.val();
if (!field_to_set) {
- $defaults.parent().addClass('invalid');
+ $defaults.parent().addClass('oe_form_invalid');
return;
}
var condition = d.$element.find('#formview_default_conditions').val(),
all_users = d.$element.find('#formview_default_all').is(':checked');
- new openerp.web.DataSet(self, 'ir.values').call(
+ new instance.web.DataSet(self, 'ir.values').call(
'set_default', [
self.dataset.model,
field_to_set,
});
d.template = 'FormView.set_default';
d.open();
- }
+ },
+ register_field: function(field, name) {
+ this.fields[name] = field;
+ this.fields_order.push(name);
+
+ field.on('focused', null, this.proxy('widgetFocused'))
+ .on('blurred', null, this.proxy('widgetBlurred'));
+ if (this.get_field(name).translate) {
+ this.translatable_fields.push(field);
+ }
+ field.on('changed_value', this, function() {
+ field._dirty_flag = true;
+ if (field.is_syntax_valid()) {
+ this.do_onchange(field);
+ this.on_form_changed(true);
+ this.do_notify_change();
+ }
+ });
+ },
+ get_field: function(field_name) {
+ return this.fields_view.fields[field_name];
+ },
+ is_create_mode: function() {
+ return !this.datarecord.id;
+ },
+});
+
+/**
+ * Interface to be implemented by rendering engines for the form view.
+ */
+instance.web.form.FormRenderingEngineInterface = instance.web.Class.extend({
+ set_fields_view: function(fields_view) {},
+ set_fields_registry: function(fields_registry) {},
+ render_to: function($element) {},
+});
+
+/**
+ * Default rendering engine for the form view.
+ *
+ * It is necessary to set the view using set_view() before usage.
+ */
+instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInterface.extend({
+ init: function(view) {
+ this.view = view;
+ },
+ set_fields_view: function(fvg) {
+ this.fvg = fvg;
+ this.version = parseFloat(this.fvg.arch.attrs.version);
+ if (isNaN(this.version)) {
+ this.version = 6.1;
+ }
+ },
+ set_tags_registry: function(tags_registry) {
+ this.tags_registry = tags_registry;
+ },
+ set_fields_registry: function(fields_registry) {
+ this.fields_registry = fields_registry;
+ },
+ // Backward compatibility tools, current default version: v6.1
+ process_version: function() {
+ if (this.version < 7.0) {
+ this.$form.find('form:first').wrapInner('<group col="4"/>');
+ this.$form.find('page').each(function() {
+ if (!$(this).parents('field').length) {
+ $(this).wrapInner('<group col="4"/>');
+ }
+ });
+ }
+ selector = 'form[version!="7.0"] page,form[version!="7.0"]';
+ },
+ render_to: function($target) {
+ var self = this;
+ this.$target = $target;
+
+ // TODO: I know this will save the world and all the kitten for a moment,
+ // but one day, we will have to get rid of xml2json
+ var xml = instance.web.json_node_to_xml(this.fvg.arch);
+ this.$form = $('<div class="oe_form">' + xml + '</div>');
+
+ this.process_version();
+
+ this.fields_to_init = [];
+ this.tags_to_init = [];
+ this.labels = {};
+ this.process(this.$form);
+
+ this.$form.appendTo(this.$target);
+
+ _.each(this.fields_to_init, function($elem) {
+ var name = $elem.attr("name");
+ if (!self.fvg.fields[name]) {
+ throw new Error("Field '" + name + "' specified in view could not be found.");
+ }
+ var obj = self.fields_registry.get_any([$elem.attr('widget'), self.fvg.fields[name].type]);
+ if (!obj) {
+ throw new Error("Widget type '"+ $elem.attr('widget') + "' is not implemented");
+ }
+ var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
+ var $label = self.labels[$elem.attr("name")];
+ if ($label) {
+ w.set_input_id($label.attr("for"));
+ }
+ self.alter_field(w);
+ self.view.register_field(w, $elem.attr("name"));
+ w.replace($elem);
+ });
+ _.each(this.tags_to_init, function($elem) {
+ var tag_name = $elem[0].tagName.toLowerCase();
+ var obj = self.tags_registry.get_object(tag_name);
+ var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
+ w.replace($elem);
+ });
+ // TODO: return a deferred
+ },
+ render_element: function(template /* dictionaries */) {
+ var dicts = [].slice.call(arguments).slice(1);
+ var dict = _.extend.apply(_, dicts);
+ dict['classnames'] = dict['class'] || ''; // class is a reserved word and might caused problem to Safari when used from QWeb
+ return $(QWeb.render(template, dict));
+ },
+ alter_field: function(field) {
+ },
+ toggle_layout_debugging: function() {
+ if (!this.$target.has('.oe_layout_debug_cell:first').length) {
+ this.$target.find('[title]').removeAttr('title');
+ this.$target.find('.oe_form_group_cell').each(function() {
+ var text = 'W:' + ($(this).attr('width') || '') + ' - C:' + $(this).attr('colspan');
+ $(this).attr('title', text);
+ });
+ }
+ this.$target.toggleClass('oe_layout_debugging');
+ },
+ process: function($tag) {
+ var self = this;
+ var tagname = $tag[0].nodeName.toLowerCase();
+ if (this.tags_registry.contains(tagname)) {
+ this.tags_to_init.push($tag);
+ return $tag;
+ }
+ var fn = self['process_' + tagname];
+ if (fn) {
+ var args = [].slice.call(arguments);
+ args[0] = $tag;
+ return fn.apply(self, args);
+ } else {
+ // generic tag handling, just process children
+ $tag.children().each(function() {
+ self.process($(this));
+ });
+ self.handle_common_properties($tag, $tag);
+ $tag.removeAttr("modifiers");
+ return $tag;
+ }
+ },
+ process_sheet: function($sheet) {
+ var $new_sheet = this.render_element('FormRenderingSheet', $sheet.getAttributes());
+ this.handle_common_properties($new_sheet, $sheet);
+ var $dst = $new_sheet.find('.oe_form_sheet');
+ $sheet.contents().appendTo($dst);
+ $sheet.before($new_sheet).remove();
+ this.process($new_sheet);
+ },
+ process_form: function($form) {
+ if ($form.find('> sheet').length === 0) {
+ $form.addClass('oe_form_nosheet');
+ }
+ var $new_form = this.render_element('FormRenderingForm', $form.getAttributes());
+ this.handle_common_properties($new_form, $form);
+ $form.contents().appendTo($new_form);
+ if ($form[0] === this.$form[0]) {
+ // If root element, replace it
+ this.$form = $new_form;
+ } else {
+ $form.before($new_form).remove();
+ }
+ this.process($new_form);
+ },
+ /*
+ * Used by direct <field> children of a <group> tag only
+ * This method will add the implicit <label...> for every field
+ * in the <group>
+ */
+ preprocess_field: function($field) {
+ var self = this;
+ var name = $field.attr('name'),
+ field_colspan = parseInt($field.attr('colspan'), 10),
+ field_modifiers = JSON.parse($field.attr('modifiers') || '{}');
+
+ if ($field.attr('nolabel') === '1')
+ return;
+ $field.attr('nolabel', '1');
+ var found = false;
+ this.$form.find('label[for="' + name + '"]').each(function(i ,el) {
+ $(el).parents().each(function(unused, tag) {
+ var name = tag.tagName.toLowerCase();
+ if (name === "field" || name in self.tags_registry.map)
+ found = true;
+ });
+ });
+ if (found)
+ return;
+
+ $label = $('<label/>').attr({
+ 'for' : name,
+ "modifiers": JSON.stringify({invisible: field_modifiers.invisible}),
+ "string": $field.attr('string'),
+ "help": $field.attr('help'),
+ "class": $field.attr('class'),
+ });
+ $label.insertBefore($field);
+ if (field_colspan > 1) {
+ $field.attr('colspan', field_colspan - 1);
+ }
+ return $label;
+ },
+ process_field: function($field) {
+ if ($field.parent().is('group')) {
+ // No implicit labels for normal fields, only for <group> direct children
+ var $label = this.preprocess_field($field);
+ if ($label) {
+ this.process($label);
+ }
+ }
+ this.fields_to_init.push($field);
+ return $field;
+ },
+ process_group: function($group) {
+ var self = this;
+ $group.children('field').each(function() {
+ self.preprocess_field($(this));
+ });
+ var $new_group = this.render_element('FormRenderingGroup', $group.getAttributes());
+ var $table;
+ if ($new_group.first().is('table.oe_form_group')) {
+ $table = $new_group;
+ } else if ($new_group.filter('table.oe_form_group').length) {
+ $table = $new_group.filter('table.oe_form_group').first();
+ } else {
+ $table = $new_group.find('table.oe_form_group').first();
+ }
+
+ var $tr, $td,
+ cols = parseInt($group.attr('col') || 2, 10),
+ row_cols = cols;
+
+ var children = [];
+ $group.children().each(function(a,b,c) {
+ var $child = $(this);
+ var colspan = parseInt($child.attr('colspan') || 1, 10);
+ var tagName = $child[0].tagName.toLowerCase();
+ var $td = $('<td/>').addClass('oe_form_group_cell').attr('colspan', colspan);
+ var newline = tagName === 'newline';
+
+ // Note FME: those classes are used in layout debug mode
+ if ($tr && row_cols > 0 && (newline || row_cols < colspan)) {
+ $tr.addClass('oe_form_group_row_incomplete');
+ if (newline) {
+ $tr.addClass('oe_form_group_row_newline');
+ }
+ }
+ if (newline) {
+ $tr = null;
+ return;
+ }
+ if (!$tr || row_cols < colspan) {
+ $tr = $('<tr/>').addClass('oe_form_group_row').appendTo($table);
+ row_cols = cols;
+ }
+ row_cols -= colspan;
+
+ // invisibility transfer
+ var field_modifiers = JSON.parse($child.attr('modifiers') || '{}');
+ var invisible = field_modifiers.invisible;
+ self.handle_common_properties($td, $("<dummy>").attr("modifiers", JSON.stringify({invisible: invisible})));
+
+ $tr.append($td.append($child));
+ children.push($child[0]);
+ });
+ if (row_cols && $td) {
+ $td.attr('colspan', parseInt($td.attr('colspan'), 10) + row_cols);
+ }
+ $group.before($new_group).remove();
+
+ $table.find('> tbody > tr').each(function() {
+ var to_compute = [],
+ row_cols = cols,
+ total = 100;
+ $(this).children().each(function() {
+ var $td = $(this),
+ $child = $td.children(':first');
+ switch ($child[0].tagName.toLowerCase()) {
+ case 'separator':
+ if ($child.attr('orientation') === 'vertical') {
+ $td.addClass('oe_vertical_separator').attr('width', '1');
+ $td.empty();
+ row_cols-= $td.attr('colspan') || 1;
+ total--;
+ }
+ break;
+ case 'label':
+ if ($child.attr('for')) {
+ $td.attr('width', '1%').addClass('oe_form_group_cell_label');
+ row_cols-= $td.attr('colspan') || 1;
+ total--;
+ }
+ break;
+ default:
+ var width = _.str.trim($child.attr('width') || ''),
+ iwidth = parseInt(width, 10);
+ if (iwidth) {
+ if (width.substr(-1) === '%') {
+ total -= iwidth;
+ width = iwidth + '%';
+ } else {
+ // Absolute width
+ $td.css('min-width', width + 'px');
+ }
+ $td.attr('width', width);
+ $child.removeAttr('width');
+ row_cols-= $td.attr('colspan') || 1;
+ } else {
+ to_compute.push($td);
+ }
+
+ }
+ });
+ if (row_cols) {
+ var unit = Math.floor(total / row_cols);
+ if (!$(this).is('.oe_form_group_row_incomplete')) {
+ _.each(to_compute, function($td, i) {
+ var width = parseInt($td.attr('colspan'), 10) * unit;
+ $td.attr('width', width + '%');
+ total -= width;
+ });
+ }
+ }
+ });
+ _.each(children, function(el) {
+ self.process($(el));
+ });
+ this.handle_common_properties($new_group, $group);
+ return $new_group;
+ },
+ process_notebook: function($notebook) {
+ var self = this;
+ var pages = [];
+ $notebook.find('> page').each(function() {
+ var $page = $(this);
+ var page_attrs = $page.getAttributes();
+ page_attrs.id = _.uniqueId('notebook_page_');
+ var $new_page = self.render_element('FormRenderingNotebookPage', page_attrs);
+ $page.contents().appendTo($new_page);
+ $page.before($new_page).remove();
+ var ic = self.handle_common_properties($new_page, $page).invisibility_changer;
+ page_attrs.__page = $new_page;
+ page_attrs.__ic = ic;
+ pages.push(page_attrs);
+
+ $new_page.children().each(function() {
+ self.process($(this));
+ });
+ });
+ var $new_notebook = this.render_element('FormRenderingNotebook', { pages : pages });
+ $notebook.contents().appendTo($new_notebook);
+ $notebook.before($new_notebook).remove();
+ self.process($($new_notebook.children()[0]));
+ //tabs and invisibility handling
+ $new_notebook.tabs();
+ _.each(pages, function(page, i) {
+ if (! page.__ic)
+ return;
+ page.__ic.on("change:effective_invisible", null, function() {
+ var current = $new_notebook.tabs("option", "selected");
+ if (! pages[current].__ic || ! pages[current].__ic.get("effective_invisible"))
+ return;
+ var first_visible = _.find(_.range(pages.length), function(i2) {
+ return (! pages[i2].__ic) || (! pages[i2].__ic.get("effective_invisible"));
+ });
+ if (first_visible !== undefined) {
+ $new_notebook.tabs('select', first_visible);
+ }
+ });
+ });
+
+ this.handle_common_properties($new_notebook, $notebook);
+ return $new_notebook;
+ },
+ process_separator: function($separator) {
+ var $new_separator = this.render_element('FormRenderingSeparator', $separator.getAttributes());
+ $separator.before($new_separator).remove();
+ this.handle_common_properties($new_separator, $separator);
+ return $new_separator;
+ },
+ process_label: function($label) {
+ var name = $label.attr("for"),
+ field_orm = this.fvg.fields[name];
+ var dict = {
+ string: $label.attr('string') || (field_orm || {}).string || '',
+ help: $label.attr('help') || (field_orm || {}).help || '',
+ _for: name ? _.uniqueId('oe-field-input-') : undefined,
+ };
+ var align = parseFloat(dict.align);
+ if (isNaN(align) || align === 1) {
+ align = 'right';
+ } else if (align === 0) {
+ align = 'left';
+ } else {
+ align = 'center';
+ }
+ dict.align = align;
+ var $new_label = this.render_element('FormRenderingLabel', dict);
+ $label.before($new_label).remove();
+ this.handle_common_properties($new_label, $label);
+ if (name) {
+ this.labels[name] = $new_label;
+ }
+ return $new_label;
+ },
+ handle_common_properties: function($new_element, $node) {
+ var str_modifiers = $node.attr("modifiers") || "{}"
+ var modifiers = JSON.parse(str_modifiers);
+ var ic = null;
+ if (modifiers.invisible !== undefined)
+ ic = new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element);
+ $new_element.addClass($node.attr("class") || "");
+ $new_element.attr('style', $node.attr('style'));
+ return {invisibility_changer: ic,};
+ },
});
-openerp.web.FormDialog = openerp.web.Dialog.extend({
+
+instance.web.form.FormRenderingEngineReadonly = instance.web.form.FormRenderingEngine.extend({
+ alter_field: function(field) {
+ field.set({"force_readonly": true});
+ },
+});
+
+instance.web.form.FormDialog = instance.web.Dialog.extend({
init: function(parent, options, view_id, dataset) {
this._super(parent, options);
this.dataset = dataset;
},
start: function() {
this._super();
- this.form = new openerp.web.FormView(this, this.dataset, this.view_id, {
- sidebar: false,
+ this.form = new instance.web.FormView(this, this.dataset, this.view_id, {
pager: false
});
this.form.appendTo(this.$element);
}
});
-/** @namespace */
-openerp.web.form = {};
-
-openerp.web.form.SidebarAttachments = openerp.web.OldWidget.extend({
- init: function(parent, form_view) {
- var $section = parent.add_section(_t('Attachments'), 'attachments');
- this.$div = $('<div class="oe-sidebar-attachments"></div>');
- $section.append(this.$div);
-
- this._super(parent, $section.attr('id'));
- this.view = form_view;
- },
- do_update: function() {
- if (!this.view.datarecord.id) {
- this.on_attachments_loaded([]);
- } else {
- (new openerp.web.DataSetSearch(
- this, 'ir.attachment', this.view.dataset.get_context(),
- [
- ['res_model', '=', this.view.dataset.model],
- ['res_id', '=', this.view.datarecord.id],
- ['type', 'in', ['binary', 'url']]
- ])).read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded);
- }
- },
- on_attachments_loaded: function(attachments) {
- this.attachments = attachments;
- this.$div.html(QWeb.render('FormView.sidebar.attachments', this));
- this.$element.find('.oe-binary-file').change(this.on_attachment_changed);
- this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
- },
- on_attachment_changed: function(e) {
- window[this.element_id + '_iframe'] = this.do_update;
- var $e = $(e.target);
- if ($e.val() != '') {
- this.$element.find('form.oe-binary-form').submit();
- $e.parent().find('input[type=file]').prop('disabled', true);
- $e.parent().find('button').prop('disabled', true).find('img, span').toggle();
- }
- },
- on_attachment_delete: function(e) {
- var self = this, $e = $(e.currentTarget);
- var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
- if (confirm(_.str.sprintf(_t("Do you really want to delete the attachment %s?"), name))) {
- this.rpc('/web/dataset/unlink', {
- model: 'ir.attachment',
- ids: [parseInt($e.attr('data-id'))]
- }, function(r) {
- $e.parent().remove();
- self.do_update()
- self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
- });
- }
- }
-});
-
-openerp.web.form.compute_domain = function(expr, fields) {
+instance.web.form.compute_domain = function(expr, fields) {
var stack = [];
for (var i = expr.length - 1; i >= 0; i--) {
var ex = expr[i];
_t("Unknown field %s in domain %s"),
ex[0], JSON.stringify(expr)));
}
- var field_value = field.get_value ? fields[ex[0]].get_value() : fields[ex[0]].value;
+ var field_value = field.get_value ? field.get_value() : field.value;
var op = ex[1];
var val = ex[2];
return _.all(stack, _.identity);
};
-openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.form.Widget# */{
- template: 'Widget',
+/**
+ * Must be applied over an class already possessing the PropertiesMixin.
+ *
+ * Apply the result of the "invisible" domain to this.$element.
+ */
+instance.web.form.InvisibilityChangerMixin = {
+ init: function(field_manager, invisible_domain) {
+ this._ic_field_manager = field_manager
+ this._ic_invisible_modifier = invisible_domain;
+ this._ic_field_manager.on("view_content_has_changed", this, function() {
+ var result = this._ic_invisible_modifier === undefined ? false :
+ instance.web.form.compute_domain(this._ic_invisible_modifier, this._ic_field_manager.fields);
+ this.set({"invisible": result});
+ });
+ this.set({invisible: this._ic_invisible_modifier === true, force_invisible: false});
+ var check = function() {
+ if (this.get("invisible") || this.get('force_invisible')) {
+ this.set({"effective_invisible": true});
+ } else {
+ this.set({"effective_invisible": false});
+ }
+ };
+ this.on('change:invisible', this, check);
+ this.on('change:force_invisible', this, check);
+ _.bind(check, this)();
+ },
+ start: function() {
+ this.on("change:effective_invisible", this, this._check_visibility);
+ this._check_visibility();
+ },
+ _check_visibility: function() {
+ this.$element.toggleClass('oe_form_invisible', this.get("effective_invisible"));
+ },
+};
+
+instance.web.form.InvisibilityChanger = instance.web.Class.extend(instance.web.PropertiesMixin, instance.web.form.InvisibilityChangerMixin, {
+ init: function(parent, field_manager, invisible_domain, $element) {
+ this.setParent(parent);
+ instance.web.PropertiesMixin.init.call(this);
+ instance.web.form.InvisibilityChangerMixin.init.call(this, field_manager, invisible_domain);
+ this.$element = $element;
+ this.start();
+ },
+});
+
+instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.InvisibilityChangerMixin, {
/**
- * @constructs openerp.web.form.Widget
- * @extends openerp.web.OldWidget
+ * @constructs instance.web.form.FormWidget
+ * @extends instance.web.Widget
*
* @param view
* @param node
*/
init: function(view, node) {
+ this._super(view);
this.view = view;
this.node = node;
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
- this.always_invisible = (this.modifiers.invisible && this.modifiers.invisible === true);
- this.type = this.type || node.tag;
- this.element_name = this.element_name || this.type;
- this.element_class = [
- 'formview', this.view.view_id, this.element_name,
- this.view.widgets_counter++].join("_");
+ instance.web.form.InvisibilityChangerMixin.init.call(this, view, this.modifiers.invisible);
- this._super(view);
-
- this.view.widgets[this.element_class] = this;
- this.children = node.children;
- this.colspan = parseInt(node.attrs.colspan || 1, 10);
- this.decrease_max_width = 0;
-
- this.string = this.string || node.attrs.string;
- this.help = this.help || node.attrs.help;
- this.invisible = this.modifiers['invisible'] === true;
- this.classname = 'oe_form_' + this.type;
-
- this.align = parseFloat(this.node.attrs.align);
- if (isNaN(this.align) || this.align === 1) {
- this.align = 'right';
- } else if (this.align === 0) {
- this.align = 'left';
- } else {
- this.align = 'center';
- }
-
-
- this.width = this.node.attrs.width;
+ this.view.on("view_content_has_changed", this, this.process_modifiers);
},
- start: function() {
- this.$element = this.view.$element.find(
- '.' + this.element_class.replace(/[^\r\n\f0-9A-Za-z_-]/g, "\\$&"));
+ renderElement: function() {
+ this._super();
+ this.$element.addClass(this.node.attrs["class"] || "");
},
- stop: function() {
- this._super.apply(this, arguments);
+ destroy: function() {
$.fn.tipsy.clear();
+ this._super.apply(this, arguments);
+ },
+ /**
+ * Sets up blur/focus forwarding from DOM elements to a widget (`this`)
+ *
+ * @param {jQuery} $e jQuery object of elements to bind focus/blur on
+ */
+ setupFocus: function ($e) {
+ var self = this;
+ $e.on({
+ focus: function () { self.trigger('focused'); },
+ blur: function () { self.trigger('blurred'); }
+ });
},
process_modifiers: function() {
- var compute_domain = openerp.web.form.compute_domain;
+ var compute_domain = instance.web.form.compute_domain;
+ var to_set = {};
for (var a in this.modifiers) {
- this[a] = compute_domain(this.modifiers[a], this.view.fields);
+ if (!_.include(["invisible"], a)) {
+ var val = compute_domain(this.modifiers[a], this.view.fields);
+ to_set[a] = val;
+ }
}
- },
- update_dom: function() {
- this.$element.toggle(!this.invisible);
- },
- render: function() {
- var template = this.template;
- return QWeb.render(template, { "widget": this });
+ this.set(to_set);
},
do_attach_tooltip: function(widget, trigger, options) {
widget = widget || this;
template = 'WidgetLabel.tooltip';
}
return QWeb.render(template, {
- debug: openerp.connection.debug,
+ debug: instance.connection.debug,
widget: widget
})},
gravity: $.fn.tipsy.autoBounds(50, 'nw'),
opacity: 0.85,
trigger: 'hover'
}, options || {});
- trigger.tipsy(options);
+ $(trigger).tipsy(options);
},
_build_view_fields_values: function(blacklist) {
var a_dataset = this.view.dataset;
},
_build_eval_context: function(blacklist) {
var a_dataset = this.view.dataset;
- return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
+ return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
},
/**
* Builds a new context usable for operations related to fields by merging
if (v_context.__ref || true) { //TODO: remove true
var fields_values = this._build_eval_context(blacklist);
- v_context = new openerp.web.CompoundContext(v_context).set_eval_context(fields_values);
+ v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values);
}
return v_context;
},
var final_domain = n_domain !== null ? n_domain : f_domain;
if (!(final_domain instanceof Array) || true) { //TODO: remove true
var fields_values = this._build_eval_context();
- final_domain = new openerp.web.CompoundDomain(final_domain).set_eval_context(fields_values);
+ final_domain = new instance.web.CompoundDomain(final_domain).set_eval_context(fields_values);
}
return final_domain;
}
});
-openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
- template: 'WidgetFrame',
- init: function(view, node) {
- this._super(view, node);
- this.columns = parseInt(node.attrs.col || 4, 10);
- this.x = 0;
- this.y = 0;
- this.table = [];
- this.add_row();
- for (var i = 0; i < node.children.length; i++) {
- var n = node.children[i];
- if (n.tag == "newline") {
- this.add_row();
- } else {
- this.handle_node(n);
- }
- }
- this.set_row_cells_with(this.table[this.table.length - 1]);
- },
- add_row: function(){
- if (this.table.length) {
- this.set_row_cells_with(this.table[this.table.length - 1]);
- }
- var row = [];
- this.table.push(row);
- this.x = 0;
- this.y += 1;
- return row;
- },
- set_row_cells_with: function(row) {
- var bypass = 0,
- max_width = 100,
- row_length = row.length;
- for (var i = 0; i < row.length; i++) {
- if (row[i].always_invisible) {
- row_length--;
- } else {
- bypass += row[i].width === undefined ? 0 : 1;
- max_width -= row[i].decrease_max_width;
- }
- }
- var size_unit = Math.round(max_width / (this.columns - bypass)),
- colspan_sum = 0;
- for (var i = 0; i < row.length; i++) {
- var w = row[i];
- if (w.always_invisible) {
- continue;
- }
- colspan_sum += w.colspan;
- if (w.width === undefined) {
- var width = (i === row_length - 1 && colspan_sum === this.columns) ? max_width : Math.round(size_unit * w.colspan);
- max_width -= width;
- w.width = width + '%';
- }
- }
- },
- handle_node: function(node) {
- var type = {};
- if (node.tag == 'field') {
- type = this.view.fields_view.fields[node.attrs.name] || {};
- if (node.attrs.widget == 'statusbar' && node.attrs.nolabel !== '1') {
- // This way we can retain backward compatibility between addons and old clients
- node.attrs.colspan = (parseInt(node.attrs.colspan, 10) || 1) + 1;
- node.attrs.nolabel = '1';
- }
- }
- var widget = new (this.view.registry.get_any(
- [node.attrs.widget, type.type, node.tag])) (this.view, node);
- if (node.tag == 'field') {
- if (!this.view.default_focus_field || node.attrs.default_focus == '1') {
- this.view.default_focus_field = widget;
- }
- if (node.attrs.nolabel != '1') {
- var label = new (this.view.registry.get_object('label')) (this.view, node);
- label["for"] = widget;
- this.add_widget(label, widget.colspan + 1);
- }
- }
- this.add_widget(widget);
- },
- add_widget: function(widget, colspan) {
- var current_row = this.table[this.table.length - 1];
- if (!widget.always_invisible) {
- colspan = colspan || widget.colspan;
- if (current_row.length && (this.x + colspan) > this.columns) {
- current_row = this.add_row();
- }
- this.x += widget.colspan;
- }
- current_row.push(widget);
- return widget;
- }
-});
-
-openerp.web.form.WidgetGroup = openerp.web.form.WidgetFrame.extend({
- template: 'WidgetGroup'
-}),
-
-openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
- template: 'WidgetNotebook',
- init: function(view, node) {
- this._super(view, node);
- this.pages = [];
- for (var i = 0; i < node.children.length; i++) {
- var n = node.children[i];
- if (n.tag == "page") {
- var page = new (this.view.registry.get_object('notebookpage'))(
- this.view, n, this, this.pages.length);
- this.pages.push(page);
- }
- }
- },
- start: function() {
- var self = this;
- this._super.apply(this, arguments);
- this.$element.find('> ul > li').each(function (index, tab_li) {
- var page = self.pages[index],
- id = _.uniqueId(self.element_name + '-');
- page.element_id = id;
- $(tab_li).find('a').attr('href', '#' + id);
- });
- this.$element.find('> div').each(function (index, page) {
- page.id = self.pages[index].element_id;
- });
- this.$element.tabs();
- this.view.on_button_new.add_first(this.do_select_first_visible_tab);
- if (openerp.connection.debug) {
- this.do_attach_tooltip(this, this.$element.find('ul:first'), {
- gravity: 's'
- });
- }
- },
- do_select_first_visible_tab: function() {
- for (var i = 0; i < this.pages.length; i++) {
- var page = this.pages[i];
- if (page.invisible === false) {
- this.$element.tabs('select', page.index);
- break;
- }
- }
- }
-});
-
-openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
- template: 'WidgetNotebookPage',
- init: function(view, node, notebook, index) {
- this.notebook = notebook;
- this.index = index;
- this.element_name = 'page_' + index;
- this._super(view, node);
- },
- start: function() {
- this._super.apply(this, arguments);
- this.$element_tab = this.notebook.$element.find(
- '> ul > li:eq(' + this.index + ')');
- },
- update_dom: function() {
- if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
- this.notebook.do_select_first_visible_tab();
- }
- this.$element_tab.toggle(!this.invisible);
- this.$element.toggle(!this.invisible);
- }
-});
-
-openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
- template: 'WidgetSeparator',
- init: function(view, node) {
- this._super(view, node);
- this.orientation = node.attrs.orientation || 'horizontal';
- if (this.orientation === 'vertical') {
- this.width = '1';
- }
- this.classname += '_' + this.orientation;
- }
-});
-
-openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
+instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
template: 'WidgetButton',
init: function(view, node) {
this._super(view, node);
this.force_disabled = false;
- if (this.string) {
- // We don't have button key bindings in the webclient
- this.string = this.string.replace(/_/g, '');
- }
- if (node.attrs.default_focus == '1') {
+ this.string = (this.node.attrs.string || '').replace(/_/g, '');
+ if (this.node.attrs.default_focus == '1') {
// TODO fme: provide enter key binding to widgets
this.view.default_focus_button = this;
}
+ this.view.on('view_content_has_changed', this, this.check_disable);
},
start: function() {
this._super.apply(this, arguments);
- this.$element.find("button").click(this.on_click);
- if (this.help || openerp.connection.debug) {
+ var $button = this.$element.find('button');
+ $button.click(this.on_click);
+ if (this.node.attrs.help || instance.connection.debug) {
this.do_attach_tooltip();
}
+ this.setupFocus($button);
},
on_click: function() {
var self = this;
var exec_action = function() {
if (self.node.attrs.confirm) {
var def = $.Deferred();
- var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
+ var dialog = instance.web.dialog($('<div/>').text(self.node.attrs.confirm), {
title: _t('Confirm'),
modal: true,
buttons: [
var context = this.node.attrs.context;
if (context && context.__ref) {
- context = new openerp.web.CompoundContext(context);
+ context = new instance.web.CompoundContext(context);
context.set_eval_context(this._build_eval_context());
}
self.view.reload();
});
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.check_disable();
- },
check_disable: function() {
- var disabled = (this.readonly || this.force_disabled || !this.view.is_interactible_record());
- this.$element.find('button').prop('disabled', disabled);
- this.$element.find("button").css('color', disabled ? 'grey' : '');
+ var disabled = (this.force_disabled || !this.view.is_interactible_record());
+ this.$element.prop('disabled', disabled);
+ this.$element.css('color', disabled ? 'grey' : '');
}
});
-openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
- template: 'WidgetLabel',
- init: function(view, node) {
- this.element_name = 'label_' + node.attrs.name;
-
- this._super(view, node);
-
- if (this.node.tag == 'label' && !this.string && this.node.children.length) {
- this.string = this.node.children[0];
- this.align = 'left';
- }
-
- if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) {
- this.template = "WidgetParagraph";
- this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
- // Widgets default to right-aligned, but paragraph defaults to
- // left-aligned
- if (isNaN(parseFloat(this.node.attrs.align))) {
- this.align = 'left';
- }
-
- this.multilines = this.string && _.str.lines(this.string).length > 1;
- } else {
- this.colspan = 1;
- this.width = '1%';
- this.decrease_max_width = 1;
- this.nowrap = true;
- }
- },
- render: function () {
- if (this['for'] && this.type !== 'label') {
- return QWeb.render(this.template, {widget: this['for']});
- }
- // Actual label widgets should not have a false and have type label
- return QWeb.render(this.template, {widget: this});
- },
- start: function() {
- this._super();
- var self = this;
- if (this['for'] && (this['for'].help || openerp.connection.debug)) {
- this.do_attach_tooltip(self['for']);
- }
- this.$element.find("label").dblclick(function() {
- var widget = self['for'] || self;
- openerp.log(widget.element_class , widget);
- window.w = widget;
- });
- }
-});
+/**
+ * Interface to be implemented by fields.
+ *
+ * Properties:
+ * - readonly: boolean. If set to true the field should appear in readonly mode.
+ * - force_readonly: boolean, When it is true, the field should always appear
+ * in read only mode, no matter what the value of the "readonly" property can be.
+ * Events:
+ * - changed_value: triggered to inform the view to check on_changes
+ *
+ */
+instance.web.form.FieldInterface = {
+ /**
+ * Constructor takes 2 arguments:
+ * - field_manager: Implements FieldManagerMixin
+ * - node: the "<field>" node in json form
+ */
+ init: function(field_manager, node) {},
+ /**
+ * Called by the form view to indicate the value of the field.
+ *
+ * set_value() may return an object that can be passed to $.when() that represents the moment when
+ * the field has finished all operations necessary before the user can effectively use the widget.
+ *
+ * Multiple calls to set_value() can occur at any time and must be handled correctly by the implementation,
+ * regardless of any asynchronous operation currently running and the status of any promise that a
+ * previous call to set_value() could have returned.
+ *
+ * set_value() must be able, at any moment, to handle the syntax returned by the "read" method of the
+ * osv class in the OpenERP server as well as the syntax used by the set_value() (see below). It must
+ * also be able to handle any other format commonly used in the _defaults key on the models in the addons
+ * as well as any format commonly returned in a on_change. It must be able to autodetect those formats as
+ * no information is ever given to know which format is used.
+ */
+ set_value: function(value_) {},
+ /**
+ * Get the current value of the widget.
+ *
+ * Must always return a syntaxically correct value to be passed to the "write" method of the osv class in
+ * the OpenERP server, although it is not assumed to respect the constraints applied to the field.
+ * For example if the field is marqued as "required", a call to get_value() can return false.
+ *
+ * get_value() can also be called *before* a call to set_value() and, in that case, is supposed to
+ * return a defaut value according to the type of field.
+ *
+ * This method is always assumed to perform synchronously, it can not return a promise.
+ *
+ * If there was no user interaction to modify the value of the field, it is always assumed that
+ * get_value() return the same semantic value than the one passed in the last call to set_value(),
+ * altough the syntax can be different. This can be the case for type of fields that have a different
+ * syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0).
+ */
+ get_value: function() {},
+ /**
+ * Inform the current object of the id it should use to match a html <label> that exists somewhere in the
+ * view.
+ */
+ set_input_id: function(id) {},
+ /**
+ * Returns true if is_syntax_valid() returns true and the value is semantically
+ * valid too according to the semantic restrictions applied to the field.
+ */
+ is_valid: function() {},
+ /**
+ * Returns true if the field holds a value which is syntaxically correct, ignoring
+ * the potential semantic restrictions applied to the field.
+ */
+ is_syntax_valid: function() {},
+ /**
+ * Must set the focus on the field.
+ */
+ focus: function() {},
+};
-openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.form.Field# */{
+/**
+ * Abstract class for classes implementing FieldInterface.
+ *
+ * Properties:
+ * - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending
+ * the values of the "readonly" property and the "force_readonly" property on the field manager.
+ * - value: useful property to hold the value of the field. By default, set_value() and get_value()
+ * set and retrieve the value property. Changing the value property also triggers automatically
+ * a 'changed_value' event that inform the view to trigger on_changes.
+ *
+ */
+instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.web.form.FieldInterface, {
/**
- * @constructs openerp.web.form.Field
- * @extends openerp.web.form.Widget
+ * @constructs instance.web.form.AbstractField
+ * @extends instance.web.form.FormWidget
*
- * @param view
+ * @param field_manager
* @param node
*/
- init: function(view, node) {
- this.name = node.attrs.name;
- this.value = undefined;
- view.fields[this.name] = this;
- view.fields_order.push(this.name);
- this.type = node.attrs.widget || view.fields_view.fields[node.attrs.name].type;
- this.element_name = "field_" + this.name + "_" + this.type;
-
- this._super(view, node);
-
- if (node.attrs.nolabel != '1' && this.colspan > 1) {
- this.colspan--;
- }
- this.field = view.fields_view.fields[node.attrs.name] || {};
- this.string = node.attrs.string || this.field.string;
- this.help = node.attrs.help || this.field.help;
- this.nolabel = (this.field.nolabel || node.attrs.nolabel) === '1';
- this.readonly = this.modifiers['readonly'] === true;
- this.required = this.modifiers['required'] === true;
- this.invalid = this.dirty = false;
-
- this.classname = 'oe_form_field_' + this.type;
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.field_manager = field_manager;
+ this.name = this.node.attrs.name;
+ this.set({'value': false});
+ this.field = this.field_manager.get_field(this.name);
+ this.set({required: this.modifiers['required'] === true});
+
+ // some events to make the property "effective_readonly" sync automatically with "readonly" and
+ // "force_readonly"
+ this.set({"readonly": this.modifiers['readonly'] === true});
+ var test_effective_readonly = function() {
+ this.set({"effective_readonly": this.get("readonly") || !!this.get("force_readonly")});
+ };
+ this.on("change:readonly", this, test_effective_readonly);
+ this.on("change:force_readonly", this, test_effective_readonly);
+ _.bind(test_effective_readonly, this)();
+
+ this.on("change:value", this, function() {
+ if (! this._inhibit_on_change)
+ this.trigger('changed_value');
+ this._check_css_flags();
+ });
},
- start: function() {
- this._super.apply(this, arguments);
+ renderElement: function() {
+ var self = this;
+ this._super();
if (this.field.translate) {
- this.view.translatable_fields.push(this);
this.$element.addClass('oe_form_field_translatable');
- this.$element.find('.oe_field_translate').click(this.on_translate);
+ this.$element.find('.oe_field_translate').click(_.bind(function() {
+ this.field_manager.open_translate_dialog(this);
+ }, this));
}
- if (this.nolabel && openerp.connection.debug) {
- this.do_attach_tooltip(this, this.$element);
+ this.$label = this.view.$element.find('label[for=' + this.id_for_label + ']');
+ if (instance.connection.debug) {
+ this.do_attach_tooltip(this, this.$label[0] || this.$element);
+ this.$label.off('dblclick').on('dblclick', function() {
+ console.log("Field '%s' of type '%s' in View: %o", self.name, (self.node.attrs.widget || self.field.type), self.view);
+ window.w = self;
+ console.log("window.w =", window.w);
+ });
}
+ if (!this.disable_utility_classes) {
+ this.off("change:required", this, this._set_required);
+ this.on("change:required", this, this._set_required);
+ this._set_required();
+ }
+ this._check_visibility();
+ this._check_css_flags();
},
- set_value: function(value) {
- this.value = value;
- this.invalid = false;
- this.update_dom();
- this.on_value_changed();
- },
- set_value_from_ui: function() {
- this.on_value_changed();
- },
- on_value_changed: function() {
+ /**
+ * Private. Do not use.
+ */
+ _set_required: function() {
+ this.$element.toggleClass('oe_form_required', this.get("required"));
},
- on_translate: function() {
- this.view.open_translate_dialog(this);
+ set_value: function(value_) {
+ this._inhibit_on_change = true;
+ this.set({'value': value_});
+ this._inhibit_on_change = false;
},
get_value: function() {
- return this.value;
+ return this.get('value');
},
is_valid: function() {
- return !this.invalid;
+ return this.is_syntax_valid() && !(this.get('required') && this.is_false());
},
- is_dirty: function() {
- return this.dirty && !this.readonly;
+ is_syntax_valid: function() {
+ return true;
},
- get_on_change_value: function() {
- return this.get_value();
+ /**
+ * Method useful to implement to ease validity testing. Must return true if the current
+ * value is similar to false in OpenERP.
+ */
+ is_false: function() {
+ return this.get('value') === false;
},
- update_dom: function(show_invalid) {
- this._super.apply(this, arguments);
+ _check_css_flags: function(show_invalid) {
if (this.field.translate) {
- this.$element.find('.oe_field_translate').toggle(!!this.view.datarecord.id);
+ this.$element.find('.oe_field_translate').toggle(!this.field_manager.is_create_mode());
}
if (!this.disable_utility_classes) {
- this.$element.toggleClass('disabled', this.readonly);
- this.$element.toggleClass('required', this.required);
- if (show_invalid) {
- this.$element.toggleClass('invalid', !this.is_valid());
+ if (this.field_manager.get('display_invalid_fields')) {
+ this.$element.toggleClass('oe_form_invalid', !this.is_valid());
}
}
},
- on_ui_change: function() {
- this.dirty = true;
- this.validate();
- if (this.is_valid()) {
- this.set_value_from_ui();
- this.view.do_onchange(this);
- this.view.on_form_changed(true);
- this.view.do_notify_change();
- } else {
- this.update_dom(true);
- }
- },
- validate: function() {
- this.invalid = false;
+ focus: function() {
},
- focus: function($element) {
- if ($element) {
- setTimeout(function() {
- $element.focus();
- }, 50);
- }
- },
- reset: function() {
- this.dirty = false;
+ /**
+ * Utility method to focus an element, but only after a small amount of time.
+ */
+ delay_focus: function($elem) {
+ setTimeout(function() {
+ $elem.focus();
+ }, 50);
},
+ /**
+ * Utility method to get the widget options defined in the field xml description.
+ */
get_definition_options: function() {
if (!this.definition_options) {
var str = this.node.attrs.options || '{}';
this.definition_options = JSON.parse(str);
}
return this.definition_options;
- }
+ },
+ set_input_id: function(id) {
+ this.id_for_label = id;
+ },
});
-openerp.web.form.FieldChar = openerp.web.form.Field.extend({
+/**
+ * A mixin to apply on any field that has to completely re-render when its readonly state
+ * switch.
+ */
+instance.web.form.ReinitializeFieldMixin = {
+ /**
+ * Default implementation of start(), use it or call explicitly initialize_field().
+ */
+ start: function() {
+ this._super();
+ this.initialize_field();
+ },
+ initialize_field: function() {
+ this.on("change:effective_readonly", this, function() {
+ this.destroy_content();
+ this.renderElement();
+ this.initialize_content();
+ this.render_value();
+ });
+ this.initialize_content();
+ this.render_value();
+ },
+ /**
+ * Called to destroy anything that could have been created previously, called before a
+ * re-initialization.
+ */
+ destroy_content: function() {},
+ /**
+ * Called to initialize the content.
+ */
+ initialize_content: function() {},
+ /**
+ * Called to render the value. Should also be explicitly called at the end of a set_value().
+ */
+ render_value: function() {},
+};
+
+instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldChar',
- init: function (view, node) {
- this._super(view, node);
+ widget_class: 'oe_form_field_char',
+ init: function (field_manager, node) {
+ this._super(field_manager, node);
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
},
- start: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').change(this.on_ui_change);
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- var show_value = openerp.web.format_value(value, this, '');
- this.$element.find('input').val(show_value);
- return show_value;
+ initialize_content: function() {
+ var self = this;
+ var $input = this.$element.find('input');
+ $input.change(function() {
+ self.set({'value': instance.web.parse_value($input.val(), self)});
+ });
+ this.setupFocus($input);
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').prop('readonly', this.readonly);
+ set_value: function(value_) {
+ this._super(value_);
+ this.render_value();
},
- set_value_from_ui: function() {
- this.value = openerp.web.parse_value(this.$element.find('input').val(), this);
- this._super();
+ render_value: function() {
+ var show_value = instance.web.format_value(this.get('value'), this, '');
+ if (!this.get("effective_readonly")) {
+ this.$element.find('input').val(show_value);
+ } else {
+ if (this.password) {
+ show_value = new Array(show_value.length + 1).join('*');
+ }
+ this.$element.text(show_value);
+ }
},
- validate: function() {
- this.invalid = false;
- try {
- var value = openerp.web.parse_value(this.$element.find('input').val(), this, '');
- this.invalid = this.required && value === '';
- } catch(e) {
- this.invalid = true;
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly")) {
+ try {
+ var value_ = instance.web.parse_value(this.$element.find('input').val(), this, '');
+ return true;
+ } catch(e) {
+ return false;
+ }
}
+ return true;
},
- focus: function($element) {
- this._super($element || this.$element.find('input:first'));
+ is_false: function() {
+ return this.get('value') === '' || this._super();
+ },
+ focus: function() {
+ this.delay_focus(this.$element.find('input:first'));
}
});
-openerp.web.form.FieldID = openerp.web.form.FieldChar.extend({
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').prop('readonly', true);
- }
+instance.web.form.FieldID = instance.web.form.FieldChar.extend({
+
});
-openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
+instance.web.form.FieldEmail = instance.web.form.FieldChar.extend({
template: 'FieldEmail',
- start: function() {
- this._super.apply(this, arguments);
- this.$element.find('button').click(this.on_button_clicked);
+ initialize_content: function() {
+ this._super();
+ var $button = this.$element.find('button');
+ $button.click(this.on_button_clicked);
+ this.setupFocus($button);
+ },
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this._super();
+ } else {
+ this.$element.find('a')
+ .attr('href', 'mailto:' + this.get('value'))
+ .text(this.get('value'));
+ }
},
on_button_clicked: function() {
- if (!this.value || !this.is_valid()) {
+ if (!this.get('value') || !this.is_syntax_valid()) {
this.do_warn("E-mail error", "Can't send email to invalid e-mail address");
} else {
- location.href = 'mailto:' + this.value;
+ location.href = 'mailto:' + this.get('value');
}
}
});
-openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
+instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({
template: 'FieldUrl',
- start: function() {
- this._super.apply(this, arguments);
- this.$element.find('button').click(this.on_button_clicked);
+ initialize_content: function() {
+ this._super();
+ var $button = this.$element.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+):(.+)/.exec(tmp);
+ if (!s) {
+ tmp = "http://" + this.get('value');
+ }
+ this.$element.find('a').attr('href', tmp).text(tmp);
+ }
},
on_button_clicked: function() {
- if (!this.value) {
+ if (!this.get('value')) {
this.do_warn("Resource error", "This resource is empty");
} else {
- window.open(this.value);
+ var url = $.trim(this.get('value'));
+ if(/^www\./i.test(url))
+ url = 'http://'+url;
+ window.open(url);
}
}
});
-openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
- init: function (view, node) {
- this._super(view, node);
- if (node.attrs.digits) {
- this.digits = py.eval(node.attrs.digits).toJSON();
+instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
+ is_field_number: true,
+ widget_class: 'oe_form_field_float',
+ init: function (field_manager, node) {
+ this._super(field_manager, node);
+ this.set({'value': 0});
+ if (this.node.attrs.digits) {
+ this.digits = this.node.attrs.digits;
} else {
- this.digits = view.fields_view.fields[node.attrs.name].digits;
+ this.digits = this.field.digits;
}
},
- set_value: function(value) {
- if (value === false || value === undefined) {
+ set_value: function(value_) {
+ if (value_ === false || value_ === undefined) {
// As in GTK client, floats default to 0
- value = 0;
+ value_ = 0;
}
- this._super.apply(this, [value]);
+ this._super.apply(this, [value_]);
}
});
-openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({
- template: "web.datetimepicker",
+instance.web.DateTimeWidget = instance.web.OldWidget.extend({
+ template: "web.datepicker",
jqueryui_object: 'datetimepicker',
type_of_date: "datetime",
init: function(parent) {
this.$input_picker = this.$element.find('input.oe_datepicker_container');
this.$input.change(this.on_change);
this.picker({
+ onClose: this.on_picker_select,
onSelect: this.on_picker_select,
changeMonth: true,
changeYear: true,
showButtonPanel: true
});
this.$element.find('img.oe_datepicker_trigger').click(function() {
- if (!self.readonly && !self.picker('widget').is(':visible')) {
- self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date());
- self.$input_picker.show();
- self.picker('show');
- self.$input_picker.hide();
+ if (self.get("effective_readonly") || self.picker('widget').is(':visible')) {
+ self.$input.focus();
+ return;
}
+ self.picker('setDate', self.value ? instance.web.auto_str_to_date(self.value) : new Date());
+ self.$input_picker.show();
+ self.picker('show');
+ self.$input_picker.hide();
});
this.set_readonly(false);
- this.value = false;
+ this.set({'value': false});
},
picker: function() {
return $.fn[this.jqueryui_object].apply(this.$input_picker, arguments);
},
- on_picker_select: function(text, instance) {
+ on_picker_select: function(text, instance_) {
var date = this.picker('getDate');
- this.$input.val(date ? this.format_client(date) : '').change();
+ this.$input
+ .val(date ? this.format_client(date) : '')
+ .change()
+ .focus();
},
- set_value: function(value) {
- this.value = value;
- this.$input.val(value ? this.format_client(value) : '');
+ set_value: function(value_) {
+ this.set({'value': value_});
+ this.$input.val(value_ ? this.format_client(value_) : '');
},
get_value: function() {
- return this.value;
+ return this.get('value');
},
- set_value_from_ui: function() {
- var value = this.$input.val() || false;
- this.value = this.parse_client(value);
+ set_value_from_ui_: function() {
+ var value_ = this.$input.val() || false;
+ this.set({'value': this.parse_client(value_)});
},
set_readonly: function(readonly) {
this.readonly = readonly;
this.$input.prop('readonly', this.readonly);
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
},
- is_valid: function(required) {
- var value = this.$input.val();
- if (value === "") {
- return !required;
+ is_valid_: function() {
+ var value_ = this.$input.val();
+ if (value_ === "") {
+ return true;
} else {
try {
- this.parse_client(value);
+ this.parse_client(value_);
return true;
} catch(e) {
return false;
}
},
parse_client: function(v) {
- return openerp.web.parse_value(v, {"widget": this.type_of_date});
+ return instance.web.parse_value(v, {"widget": this.type_of_date});
},
format_client: function(v) {
- return openerp.web.format_value(v, {"widget": this.type_of_date});
+ return instance.web.format_value(v, {"widget": this.type_of_date});
},
on_change: function() {
- if (this.is_valid()) {
- this.set_value_from_ui();
+ if (this.is_valid_()) {
+ this.set_value_from_ui_();
}
}
});
-openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
+instance.web.DateWidget = instance.web.DateTimeWidget.extend({
jqueryui_object: 'datepicker',
type_of_date: "date"
});
-openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
- template: "EmptyComponent",
+instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
+ template: "FieldDatetime",
build_widget: function() {
- return new openerp.web.DateTimeWidget(this);
+ return new instance.web.DateTimeWidget(this);
},
- start: function() {
- var self = this;
- this._super.apply(this, arguments);
- this.datewidget = this.build_widget();
- this.datewidget.on_change.add_last(this.on_ui_change);
- this.datewidget.appendTo(this.$element);
+ 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_change.add_last(_.bind(function() {
+ this.set({'value': this.datewidget.get_value()});
+ }, this));
+ this.datewidget.appendTo(this.$element);
+ this.setupFocus(this.datewidget.$input);
+ }
},
- set_value: function(value) {
- this._super(value);
- this.datewidget.set_value(value);
+ set_value: function(value_) {
+ this._super(value_);
+ this.render_value();
},
- get_value: function() {
- return this.datewidget.get_value();
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this.datewidget.set_value(this.get('value'));
+ } else {
+ this.$element.text(instance.web.format_value(this.get('value'), this, ''));
+ }
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.datewidget.set_readonly(this.readonly);
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly")) {
+ return this.datewidget.is_valid_();
+ }
+ return true;
},
- validate: function() {
- this.invalid = !this.datewidget.is_valid(this.required);
+ is_false: function() {
+ return this.get('value') === '' || this._super();
},
- focus: function($element) {
- this._super($element || this.datewidget.$input);
+ focus: function() {
+ if (this.datewidget && this.datewidget.$input)
+ this.delay_focus(this.datewidget.$input);
}
});
-openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
+instance.web.form.FieldDate = instance.web.form.FieldDatetime.extend({
+ template: "FieldDate",
build_widget: function() {
- return new openerp.web.DateWidget(this);
+ return new instance.web.DateWidget(this);
}
});
-openerp.web.form.FieldText = openerp.web.form.Field.extend({
+instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldText',
- start: function() {
- this._super.apply(this, arguments);
- this.$element.find('textarea').change(this.on_ui_change);
- this.resized = false;
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- var show_value = openerp.web.format_value(value, this, '');
- this.$element.find('textarea').val(show_value);
- if (!this.resized && this.view.options.resize_textareas) {
- this.do_resize(this.view.options.resize_textareas);
- this.resized = true;
+ initialize_content: function() {
+ this.$textarea = this.$element.find('textarea');
+ if (!this.get("effective_readonly")) {
+ this.$textarea.change(_.bind(function() {
+ this.set({'value': instance.web.parse_value(this.$textarea.val(), this)});
+ }, this));
+ } else {
+ this.$textarea.attr('disabled', 'disabled');
}
+ this.setupFocus(this.$textarea);
},
- update_dom: function() {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- this.$element.find('textarea').prop('readonly', this.readonly);
+ this.render_value();
},
- set_value_from_ui: function() {
- this.value = openerp.web.parse_value(this.$element.find('textarea').val(), this);
- this._super();
+ render_value: function() {
+ var show_value = instance.web.format_value(this.get('value'), this, '');
+ this.$textarea.val(show_value);
+ if (show_value && this.view.options.resize_textareas) {
+ this.do_resize(this.view.options.resize_textareas);
+ }
},
- validate: function() {
- this.invalid = false;
- try {
- var value = openerp.web.parse_value(this.$element.find('textarea').val(), this, '');
- this.invalid = this.required && value === '';
- } catch(e) {
- this.invalid = true;
+ is_syntax_valid: function() {
+ if (!this.get("effective_readonly")) {
+ try {
+ var value_ = instance.web.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($element) {
- this._super($element || this.$element.find('textarea:first'));
+ this.delay_focus(this.$textarea);
},
do_resize: function(max_height) {
max_height = parseInt(max_height, 10);
- var $input = this.$element.find('textarea'),
+ var $input = this.$textarea,
$div = $('<div style="position: absolute; z-index: 1000; top: 0"/>').width($input.width()),
new_height;
$div.text($input.val());
$div.remove();
$input.height(new_height);
},
- reset: function() {
- this.resized = false;
- }
});
-openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
+instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
template: 'FieldBoolean',
start: function() {
- var self = this;
this._super.apply(this, arguments);
- this.$element.find('input').click(self.on_ui_change);
+ this.$checkbox = $("input", this.$element);
+ this.setupFocus(this.$checkbox);
+ this.$element.click(_.bind(function() {
+ this.set({'value': this.$checkbox.is(':checked')});
+ }, this));
+ var check_readonly = function() {
+ this.$checkbox.prop('disabled', this.get("effective_readonly"));
+ };
+ this.on("change:effective_readonly", this, check_readonly);
+ _.bind(check_readonly, this)();
},
- set_value: function(value) {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- this.$element.find('input')[0].checked = value;
- },
- set_value_from_ui: function() {
- this.value = this.$element.find('input').is(':checked');
- this._super();
+ this.$checkbox[0].checked = value_;
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').prop('disabled', this.readonly);
- },
- focus: function($element) {
- this._super($element || this.$element.find('input:first'));
+ focus: function() {
+ this.delay_focus(this.$checkbox);
}
});
-openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
+instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({
template: 'FieldProgressBar',
start: function() {
this._super.apply(this, arguments);
- this.$element.find('div').progressbar({
- value: this.value,
- disabled: this.readonly
+ this.$element.progressbar({
+ value: this.get('value'),
+ disabled: this.get("effective_readonly")
});
},
- set_value: function(value) {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- var show_value = Number(value);
+ var show_value = Number(value_);
if (isNaN(show_value)) {
show_value = 0;
}
- var formatted_value = openerp.web.format_value(show_value, { type : 'float' }, '0');
- this.$element.find('div').progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
+ var formatted_value = instance.web.format_value(show_value, { type : 'float' }, '0');
+ this.$element.progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
}
});
-openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
+instance.web.form.FieldTextXml = instance.web.form.AbstractField.extend({
// to replace view editor
});
-openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
+instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldSelection',
- init: function(view, node) {
+ init: function(field_manager, node) {
var self = this;
- this._super(view, node);
+ this._super(field_manager, node);
this.values = _.clone(this.field.selection);
_.each(this.values, function(v, i) {
if (v[0] === false && v[1] === '') {
});
this.values.unshift([false, '']);
},
- start: function() {
+ 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
// changing the selected value), takes the action as validating the
// row
var ischanging = false;
- this._super.apply(this, arguments);
- this.$element.find('select')
- .change(this.on_ui_change)
+ var $select = this.$element.find('select')
+ .change(_.bind(function() {
+ this.set({'value': this.values[this.$element.find('select')[0].selectedIndex][0]});
+ }, this))
.change(function () { ischanging = true; })
.click(function () { ischanging = false; })
.keyup(function (e) {
e.stopPropagation();
ischanging = false;
});
- },
- set_value: function(value) {
- value = value === null ? false : value;
- value = value instanceof Array ? value[0] : value;
- this._super(value);
- var index = 0;
- for (var i = 0, ii = this.values.length; i < ii; i++) {
- if (this.values[i][0] === value) index = i;
+ this.setupFocus($select);
+ },
+ set_value: function(value_) {
+ value_ = value_ === null ? false : value_;
+ value_ = value_ instanceof Array ? value_[0] : value_;
+ this._super(value_);
+ this.render_value();
+ },
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ var index = 0;
+ for (var i = 0, ii = this.values.length; i < ii; i++) {
+ if (this.values[i][0] === this.get('value')) index = i;
+ }
+ this.$element.find('select')[0].selectedIndex = index;
+ } else {
+ var self = this;
+ var option = _(this.values)
+ .detect(function (record) { return record[0] === self.get('value'); });
+ this.$element.text(option ? option[1] : this.values[0][1]);
}
- this.$element.find('select')[0].selectedIndex = index;
- },
- set_value_from_ui: function() {
- this.value = this.values[this.$element.find('select')[0].selectedIndex][0];
- this._super();
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('select').prop('disabled', this.readonly);
- },
- validate: function() {
- var value = this.values[this.$element.find('select')[0].selectedIndex];
- this.invalid = !(value && !(this.required && value[0] === false));
+ is_syntax_valid: function() {
+ if (this.get("effective_readonly")) {
+ return true;
+ }
+ var value_ = this.values[this.$element.find('select')[0].selectedIndex];
+ return !! value_;
},
- focus: function($element) {
- this._super($element || this.$element.find('select:first'));
+ focus: function() {
+ this.delay_focus(this.$element.find('select:first'));
}
});
function filter( array, term ) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
- return $.grep( array, function(value) {
- return matcher.test( $( "<div>" ).html( value.label || value.value || value ).text() );
+ return $.grep( array, function(value_) {
+ return matcher.test( $( "<div>" ).html( value_.label || value_.value || value_ ).text() );
});
}
});
})();
-openerp.web.form.dialog = function(content, options) {
- options = _.extend({
- width: '90%',
- height: 'auto',
- min_width: '800px'
- }, options || {});
- var dialog = new openerp.web.Dialog(null, options, content).open();
- return dialog.$element;
-};
-
-openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
- template: 'FieldMany2One',
- init: function(view, node) {
- this._super(view, node);
+/**
+ * A mixin containing some useful methods to handle completion inputs.
+ */
+instance.web.form.CompletionFieldMixin = {
+ init: function() {
this.limit = 7;
- this.value = null;
- this.cm_id = _.uniqueId('m2o_cm_');
- this.last_search = [];
- this.tmp_value = undefined;
- },
- start: function() {
- this._super();
- var self = this;
- this.$input = this.$element.find("input");
- this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
- this.$menu_btn = this.$element.find(".oe-m2o-cm-button");
-
- // context menu
- var init_context_menu_def = $.Deferred().then(function(e) {
- var rdataset = new openerp.web.DataSetStatic(self, "ir.values", self.build_context());
- rdataset.call("get", ['action', 'client_action_relate',
- [[self.field.relation, false]], false, rdataset.get_context()], false, 0)
- .then(function(result) {
- self.related_entries = result;
-
- var $cmenu = $("#" + self.cm_id);
- $cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
- var bindings = {};
- bindings[self.cm_id + "_search"] = function() {
- if (self.readonly)
- return;
- self._search_create_popup("search");
- };
- bindings[self.cm_id + "_create"] = function() {
- if (self.readonly)
- return;
- self._search_create_popup("form");
- };
- bindings[self.cm_id + "_open"] = function() {
- if (!self.value) {
- return;
- }
- var pop = new openerp.web.form.FormOpenPopup(self.view);
- pop.show_element(
- self.field.relation,
- self.value[0],
- self.build_context(),
- {
- title: _t("Open: ") + (self.string || self.name)
- }
- );
- pop.on_write_completed.add_last(function() {
- self.set_value(self.value[0]);
- });
- };
- _.each(_.range(self.related_entries.length), function(i) {
- bindings[self.cm_id + "_related_" + i] = function() {
- self.open_related(self.related_entries[i]);
- };
- });
- var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'noRightClick': true,
- bindings: bindings, itemStyle: {"color": ""},
- onContextMenu: function() {
- if(self.value) {
- $("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").removeClass("oe-m2o-disabled-cm");
- } else {
- $("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").addClass("oe-m2o-disabled-cm");
- }
- if (!self.readonly) {
- $("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").removeClass("oe-m2o-disabled-cm");
- } else {
- $("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").addClass("oe-m2o-disabled-cm");
- }
- return true;
- }, menuStyle: {width: "200px"}
- });
- $.async_when().then(function() {self.$menu_btn.trigger(e);});
- });
- });
- var ctx_callback = function(e) {init_context_menu_def.resolve(e); e.preventDefault()};
- this.$menu_btn.click(ctx_callback);
-
- // some behavior for input
- this.$input.keyup(function() {
- if (self.$input.val() === "") {
- self._change_int_value(null);
- } else if (self.value === null || (self.value && self.$input.val() !== self.value[1])) {
- self._change_int_value(undefined);
- }
- });
- this.$drop_down.click(function() {
- if (self.readonly)
- return;
- if (self.$input.autocomplete("widget").is(":visible")) {
- self.$input.autocomplete("close");
- } else {
- if (self.value) {
- self.$input.autocomplete("search", "");
- } else {
- self.$input.autocomplete("search");
- }
- self.$input.focus();
- }
- });
- var anyoneLoosesFocus = function() {
- if (!self.$input.is(":focus") &&
- !self.$input.autocomplete("widget").is(":visible") &&
- !self.value) {
- if (self.value === undefined && self.last_search.length > 0) {
- self._change_int_ext_value(self.last_search[0]);
- } else {
- self._change_int_ext_value(null);
- }
- }
- };
- this.$input.focusout(anyoneLoosesFocus);
-
- var isSelecting = false;
- // autocomplete
- this.$input.autocomplete({
- source: function(req, resp) { self.get_search_result(req, resp); },
- select: function(event, ui) {
- isSelecting = true;
- var item = ui.item;
- if (item.id) {
- self._change_int_value([item.id, item.name]);
- } else if (item.action) {
- self._change_int_value(undefined);
- item.action();
- return false;
- }
- },
- focus: function(e, ui) {
- e.preventDefault();
- },
- html: true,
- close: anyoneLoosesFocus,
- minLength: 0,
- delay: 0
- });
- // used to correct a bug when selecting an element by pushing 'enter' in an editable list
- this.$input.keyup(function(e) {
- if (e.which === 13) {
- if (isSelecting)
- e.stopPropagation();
- }
- isSelecting = false;
- });
+ this.orderer = new instance.web.DropMisordered();
},
- // autocomplete component content handling
- get_search_result: function(request, response) {
- var search_val = request.term;
+ /**
+ * Call this method to search using a string.
+ */
+ get_search_result: function(search_val) {
var self = this;
- if (this.abort_last) {
- this.abort_last();
- delete this.abort_last;
- }
- var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.build_context());
+ var dataset = new instance.web.DataSet(this, this.field.relation, self.build_context());
+ var blacklist = this.get_search_blacklist();
- dataset.name_search(search_val, self.build_domain(), 'ilike',
- this.limit + 1, function(data) {
+ return this.orderer.add(dataset.name_search(
+ search_val, new instance.web.CompoundDomain(self.build_domain(), [["id", "not in", blacklist]]),
+ 'ilike', this.limit + 1)).pipe(function(data) {
self.last_search = data;
// possible selections for the m2o
var values = _.map(data, function(x) {
values.push({label: _t("<em>Â Â Â Search More...</em>"), action: function() {
dataset.name_search(search_val, self.build_domain(), 'ilike'
, false, function(data) {
- self._change_int_value(null);
self._search_create_popup("search", data);
});
}});
}
// quick create
var raw_result = _(data.result).map(function(x) {return x[1];});
- if (search_val.length > 0 &&
- !_.include(raw_result, search_val) &&
- (!self.value || search_val !== self.value[1])) {
+ if (search_val.length > 0 && !_.include(raw_result, search_val)) {
values.push({label: _.str.sprintf(_t('<em>Â Â Â Create "<strong>%s</strong>"</em>'),
$('<span />').text(search_val).html()), action: function() {
self._quick_create(search_val);
}
// create...
values.push({label: _t("<em>Â Â Â Create and Edit...</em>"), action: function() {
- self._change_int_value(null);
- self._search_create_popup("form", undefined, {"default_name": search_val});
+ self._search_create_popup("form", undefined, {});
}});
- response(values);
+ return values;
});
- this.abort_last = dataset.abort_last;
+ },
+ get_search_blacklist: function() {
+ return [];
},
_quick_create: function(name) {
var self = this;
var slow_create = function () {
- self._change_int_value(null);
self._search_create_popup("form", undefined, {"default_name": name});
};
if (self.get_definition_options().quick_create === undefined || self.get_definition_options().quick_create) {
- var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.build_context());
- dataset.name_create(name, function(data) {
- self._change_int_ext_value(data);
- }).fail(function(error, event) {
- event.preventDefault();
- slow_create();
- });
+ new instance.web.DataSet(this, this.field.relation, self.build_context())
+ .name_create(name, function(data) {
+ self.add_id(data[0]);
+ }).fail(function(error, event) {
+ event.preventDefault();
+ slow_create();
+ });
} else
slow_create();
},
// all search/create popup handling
_search_create_popup: function(view, ids, context) {
var self = this;
- var pop = new openerp.web.form.SelectCreatePopup(this);
+ var pop = new instance.web.form.SelectCreatePopup(this);
pop.select_element(
self.field.relation,
{
disable_multiple_selection: true
},
self.build_domain(),
- new openerp.web.CompoundContext(self.build_context(), context || {})
+ new instance.web.CompoundContext(self.build_context(), context || {})
);
pop.on_select_elements.add(function(element_ids) {
- var dataset = new openerp.web.DataSetStatic(self, self.field.relation, self.build_context());
- dataset.name_get([element_ids[0]], function(data) {
- self._change_int_ext_value(data[0]);
- });
+ self.add_id(element_ids[0]);
+ self.focus();
+ });
+ },
+ /**
+ * To implement.
+ */
+ add_id: function(id) {},
+};
+
+instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, instance.web.form.ReinitializeFieldMixin, {
+ template: "FieldMany2One",
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ instance.web.form.CompletionFieldMixin.init.call(this);
+ this.set({'value': false});
+ this.display_value = {};
+ this.last_search = [];
+ this.floating = false;
+ this.inhibit_on_change = false;
+ },
+ start: function() {
+ this._super();
+ instance.web.form.ReinitializeFieldMixin.start.call(this);
+ this.on("change:value", this, function() {
+ this.floating = false;
+ this.render_value();
});
},
- _change_int_ext_value: function(value) {
- this._change_int_value(value);
- this.$input.val(this.value ? this.value[1] : "");
+ initialize_content: function() {
+ if (!this.get("effective_readonly"))
+ this.render_editable();
+ this.render_value();
+ },
+ render_editable: function() {
+ var self = this;
+ this.$input = this.$element.find("input");
+
+ self.$input.tipsy({
+ title: function() {
+ return "No element was selected, you should create or select one from the dropdown list.";
+ },
+ trigger:'manual',
+ fade: true,
+ });
+
+ this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
+ this.$follow_button = $(".oe-m2o-cm-button", this.$element);
+
+ this.$follow_button.click(function() {
+ if (!self.get('value')) {
+ self.focus();
+ return;
+ }
+ var pop = new instance.web.form.FormOpenPopup(self.view);
+ pop.show_element(
+ self.field.relation,
+ self.get("value"),
+ self.build_context(),
+ {
+ title: _t("Open: ") + (self.string || self.name)
+ }
+ );
+ pop.on_write_completed.add_last(function() {
+ self.display_value = {};
+ self.render_value();
+ self.focus();
+ });
+ });
+
+ // some behavior for input
+ this.$input.keyup(function() {
+ if (self.$input.val() === "") {
+ self.set({value: false});
+ } else {
+ self.floating = true;
+ }
+ });
+ this.$drop_down.click(function() {
+ if (self.$input.autocomplete("widget").is(":visible")) {
+ self.$input.autocomplete("close");
+ self.$input.focus();
+ } else {
+ if (self.get("value") && ! self.floating) {
+ self.$input.autocomplete("search", "");
+ } else {
+ self.$input.autocomplete("search");
+ }
+ }
+ });
+ var tip_def = $.Deferred();
+ var untip_def = $.Deferred();
+ var tip_delay = 200;
+ var tip_duration = 3000;
+ var anyoneLoosesFocus = function() {
+ var used = false;
+ if (self.floating) {
+ if (self.last_search.length > 0) {
+ if (self.last_search[0][0] != self.get("value")) {
+ self.display_value = {};
+ self.display_value["" + self.last_search[0][0]] = self.last_search[0][1];
+ self.set({value: self.last_search[0][0]});
+ } else {
+ used = true;
+ self.render_value();
+ }
+ } else {
+ used = true;
+ self.set({value: false});
+ self.render_value();
+ }
+ self.floating = false;
+ }
+ if (used) {
+ tip_def.reject();
+ untip_def.reject();
+ tip_def = $.Deferred();
+ tip_def.then(function() {
+ self.$input.tipsy("show");
+ });
+ setTimeout(function() {
+ tip_def.resolve();
+ untip_def.reject();
+ untip_def = $.Deferred();
+ untip_def.then(function() {
+ self.$input.tipsy("hide");
+ });
+ setTimeout(function() {untip_def.resolve();}, tip_duration);
+ }, tip_delay);
+ } else {
+ tip_def.reject();
+ }
+ };
+ this.$input.focusout(anyoneLoosesFocus);
+
+ var isSelecting = false;
+ // autocomplete
+ this.$input.autocomplete({
+ source: function(req, resp) {
+ self.get_search_result(req.term).then(function(result) {
+ resp(result);
+ });
+ },
+ select: function(event, ui) {
+ isSelecting = true;
+ var item = ui.item;
+ if (item.id) {
+ self.display_value = {};
+ self.display_value["" + item.id] = item.name;
+ self.set({value: item.id});
+ } else if (item.action) {
+ self.floating = true;
+ item.action();
+ return false;
+ }
+ },
+ focus: function(e, ui) {
+ e.preventDefault();
+ },
+ html: true,
+ // disabled to solve a bug, but may cause others
+ //close: anyoneLoosesFocus,
+ minLength: 0,
+ delay: 0
+ });
+ this.$input.autocomplete("widget").addClass("openerp");
+ // used to correct a bug when selecting an element by pushing 'enter' in an editable list
+ this.$input.keyup(function(e) {
+ if (e.which === 13) {
+ if (isSelecting)
+ e.stopPropagation();
+ }
+ isSelecting = false;
+ });
+ this.setupFocus(this.$input.add(this.$follow_button));
},
- _change_int_value: function(value) {
- this.value = value;
- var back_orig_value = this.original_value;
- if (this.value === null || this.value) {
- this.original_value = this.value;
+
+ render_value: function(no_recurse) {
+ var self = this;
+ if (! this.get("value")) {
+ this.display_string("");
+ return;
}
- if (back_orig_value === undefined) { // first use after a set_value()
+ var display = this.display_value["" + this.get("value")];
+ if (display) {
+ this.display_string(display);
return;
}
- if (this.value !== undefined && ((back_orig_value ? back_orig_value[0] : null)
- !== (this.value ? this.value[0] : null))) {
- this.on_ui_change();
+ if (! no_recurse) {
+ var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
+ dataset.name_get([self.get("value")], function(data) {
+ self.display_value["" + self.get("value")] = data[0][1];
+ self.render_value(true);
+ });
}
},
- set_value: function(value) {
- value = value || null;
- this.invalid = false;
+ display_string: function(str) {
var self = this;
- this.tmp_value = value;
- self.update_dom();
- self.on_value_changed();
- var real_set_value = function(rval) {
- self.tmp_value = undefined;
- self.value = rval;
- self.original_value = undefined;
- self._change_int_ext_value(rval);
- };
- if (value && !(value instanceof Array)) {
- // name_get in a m2o does not use the context of the field
- var dataset = new openerp.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
- dataset.name_get([value], function(data) {
- real_set_value(data[0]);
- }).fail(function() {self.tmp_value = undefined;});
+ if (!this.get("effective_readonly")) {
+ this.$input.val(str);
} else {
- $.async_when().then(function() {real_set_value(value);});
- }
- },
- get_value: function() {
- if (this.tmp_value !== undefined) {
- if (this.tmp_value instanceof Array) {
- return this.tmp_value[0];
- }
- return this.tmp_value ? this.tmp_value : false;
+ this.$element.find('a')
+ .unbind('click')
+ .text(str)
+ .click(function () {
+ self.do_action({
+ type: 'ir.actions.act_window',
+ res_model: self.field.relation,
+ res_id: self.get("value"),
+ context: self.build_context(),
+ views: [[false, 'form']],
+ target: 'current'
+ });
+ return false;
+ });
}
- if (this.value === undefined)
- return this.original_value ? this.original_value[0] : false;
- return this.value ? this.value[0] : false;
},
- validate: function() {
- this.invalid = false;
- var val = this.tmp_value !== undefined ? this.tmp_value : this.value;
- if (val === null) {
- this.invalid = this.required;
+ set_value: function(value_) {
+ var self = this;
+ if (value_ instanceof Array) {
+ this.display_value = {};
+ this.display_value["" + value_[0]] = value_[1];
+ value_ = value_[0];
}
+ value_ = value_ || false;
+ this.inhibit_on_change = true;
+ this._super(value_);
+ this.inhibit_on_change = false;
},
- open_related: function(related) {
- var self = this;
- if (!self.value)
- return;
- var additional_context = {
- active_id: self.value[0],
- active_ids: [self.value[0]],
- active_model: self.field.relation
- };
- self.rpc("/web/action/load", {
- action_id: related[2].id,
- context: additional_context
- }, function(result) {
- result.result.context = _.extend(result.result.context || {}, additional_context);
- self.do_action(result.result);
- });
+ add_id: function(id) {
+ this.display_value = {};
+ this.set({value: id});
},
- focus: function ($element) {
- this._super($element || this.$input);
+ is_false: function() {
+ return ! this.get("value");
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$input.prop('readonly', this.readonly);
+ focus: function () {
+ this.delay_focus(this.$input);
}
});
return [6, false, ids];
}
};
-openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
- template: 'FieldOne2Many',
+instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
multi_selection: false,
- init: function(view, node) {
- this._super(view, node);
+ disable_utility_classes: true,
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ lazy_build_o2m_kanban_view();
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
this.is_setted = $.Deferred();
this.form_last_update = $.Deferred();
this.init_form_last_update = this.form_last_update;
- this.disable_utility_classes = true;
},
start: function() {
this._super.apply(this, arguments);
+ this.$element.addClass('oe_form_field_one2many');
var self = this;
- this.dataset = new openerp.web.form.One2ManyDataSet(this, this.field.relation);
+ this.dataset = new instance.web.form.One2ManyDataSet(this, this.field.relation);
this.dataset.o2m = this;
this.dataset.parent_view = this.view;
this.dataset.child_name = this.name;
- //this.dataset.child_name =
this.dataset.on_change.add_last(function() {
self.trigger_on_change();
});
this.is_setted.then(function() {
self.load_views();
});
+ this.is_loaded.then(function() {
+ self.on("change:effective_readonly", self, function() {
+ self.is_loaded = self.is_loaded.pipe(function() {
+ self.viewmanager.destroy();
+ return $.when(self.load_views()).then(function() {
+ self.reload_current_view();
+ });
+ });
+ });
+ });
},
trigger_on_change: function() {
var tmp = this.doing_on_change;
this.doing_on_change = true;
- this.on_ui_change();
+ this.trigger('changed_value');
this.doing_on_change = tmp;
},
- is_readonly: function() {
- return this.readonly || this.force_readonly;
- },
load_views: function() {
var self = this;
modes = !!modes ? modes.split(",") : ["tree"];
var views = [];
_.each(modes, function(mode) {
+ if (! _.include(["list", "tree", "graph", "kanban"], mode)) {
+ try {
+ throw new Error(_.str.sprintf("View type '%s' is not supported in One2Many.", mode));
+ } catch(e) {
+ instance.webclient.crashmanager.on_javascript_exception(e)
+ }
+ }
var view = {
view_id: false,
view_type: mode == "tree" ? "list" : mode,
- options: { sidebar : false }
+ options: {}
};
if (self.field.views && self.field.views[mode]) {
view.embedded_view = self.field.views[mode];
}
if(view.view_type === "list") {
view.options.selectable = self.multi_selection;
- if (self.is_readonly()) {
+ view.options.sortable = false;
+ if (self.get("effective_readonly")) {
view.options.addable = null;
view.options.deletable = null;
- view.options.isClarkGable = false;
+ view.options.reorderable = false;
}
} else if (view.view_type === "form") {
- if (self.is_readonly()) {
- view.view_type = 'page';
+ if (self.get("effective_readonly")) {
+ view.view_type = 'form';
}
view.options.not_interactible_on_create = true;
+ } else if (view.view_type === "kanban") {
+ view.options.confirm_on_delete = false;
+ if (self.get("effective_readonly")) {
+ view.options.action_buttons = false;
+ view.options.quick_creatable = false;
+ view.options.creatable = false;
+ view.options.read_only_mode = true;
+ }
}
views.push(view);
});
this.views = views;
- this.viewmanager = new openerp.web.ViewManager(this, this.dataset, views, {});
- this.viewmanager.template = 'One2Many.viewmanager';
- this.viewmanager.registry = openerp.web.views.extend({
- list: 'openerp.web.form.One2ManyListView',
- form: 'openerp.web.form.One2ManyFormView',
- page: 'openerp.web.PageView'
- });
+ this.viewmanager = new instance.web.form.One2ManyViewManager(this, this.dataset, views, {});
+ this.viewmanager.$element.addClass("oe_view_manager_one2many");
+ this.viewmanager.o2m = self;
var once = $.Deferred().then(function() {
self.init_form_last_update.resolve();
});
self.initial_is_loaded.resolve();
});
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
+ controller.o2m = self;
if (view_type == "list") {
- controller.o2m = self;
- if (self.is_readonly())
+ if (self.get("effective_readonly"))
controller.set_editable(false);
- } else if (view_type == "form" || view_type == 'page') {
- if (view_type == 'page' || self.is_readonly()) {
+ } else if (view_type === "form") {
+ if (self.get("effective_readonly")) {
$(".oe_form_buttons", controller.$element).children().remove();
}
controller.on_record_loaded.add_last(function() {
var view = self.viewmanager.views[active_view].controller;
if(active_view === "list") {
return view.reload_content();
- } else if (active_view === "form" || active_view === 'page') {
+ } else if (active_view === "form") {
if (self.dataset.index === null && self.dataset.ids.length >= 1) {
self.dataset.index = 0;
}
};
self.form_last_update = self.form_last_update.pipe(act, act);
return self.form_last_update;
- } else if (active_view === "graph") {
+ } else if (view.do_search) {
return view.do_search(self.build_domain(), self.dataset.get_context(), []);
}
}, undefined);
},
- set_value: function(value) {
- value = value || [];
+ set_value: function(value_) {
+ value_ = value_ || [];
var self = this;
this.dataset.reset_ids([]);
- if(value.length >= 1 && value[0] instanceof Array) {
+ if(value_.length >= 1 && value_[0] instanceof Array) {
var ids = [];
- _.each(value, function(command) {
+ _.each(value_, function(command) {
var obj = {values: command[2]};
switch (command[0]) {
case commands.CREATE:
});
this._super(ids);
this.dataset.set_ids(ids);
- } else if (value.length >= 1 && typeof(value[0]) === "object") {
+ } else if (value_.length >= 1 && typeof(value_[0]) === "object") {
var ids = [];
this.dataset.delete_all = true;
- _.each(value, function(command) {
+ _.each(value_, function(command) {
var obj = {values: command};
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
obj.defaults = {};
this._super(ids);
this.dataset.set_ids(ids);
} else {
- this._super(value);
- this.dataset.reset_ids(value);
+ this._super(value_);
+ this.dataset.reset_ids(value_);
}
if (this.dataset.index === null && this.dataset.ids.length > 0) {
this.dataset.index = 0;
return false;
}, this));
},
- is_valid: function() {
- this.validate();
- return this._super();
- },
- validate: function() {
- this.invalid = false;
+ is_syntax_valid: function() {
if (!this.viewmanager.views[this.viewmanager.active_view])
- return;
+ return true;
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
- if (this.viewmanager.active_view === "form") {
- for (var f in view.fields) {
- f = view.fields[f];
- if (!f.is_valid()) {
- this.invalid = true;
- return;
- }
- }
+ switch (this.viewmanager.active_view) {
+ case 'form':
+ return _(view.fields).chain()
+ .invoke('is_valid')
+ .all(_.identity)
+ .value();
+ break;
+ case 'list':
+ return view.is_valid();
}
+ return true;
},
- is_dirty: function() {
- this.save_any_view();
- return this._super();
+});
+
+instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
+ template: 'One2Many.viewmanager',
+ init: function(parent, dataset, views, flags) {
+ this._super(parent, dataset, views, _.extend({}, flags, {$sidebar: false}));
+ this.registry = this.registry.extend({
+ list: 'instance.web.form.One2ManyListView',
+ form: 'instance.web.form.One2ManyFormView',
+ kanban: 'instance.web.form.One2ManyKanbanView',
+ });
},
- update_dom: function() {
- this._super.apply(this, arguments);
+ switch_view: function(mode, unused) {
+ if (mode !== 'form') {
+ return this._super(mode, unused);
+ }
var self = this;
- if (this.previous_readonly !== this.readonly) {
- this.previous_readonly = this.readonly;
- if (this.viewmanager) {
- this.is_loaded = this.is_loaded.pipe(function() {
- self.viewmanager.stop();
- return $.when(self.load_views()).then(function() {
- self.reload_current_view();
- });
+ var id = self.o2m.dataset.index !== null ? self.o2m.dataset.ids[self.o2m.dataset.index] : null;
+ var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
+ pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
+ title: _t("Open: ") + self.name,
+ create_function: function(data) {
+ return self.o2m.dataset.create(data).then(function(r) {
+ self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
+ self.o2m.dataset.on_change();
});
- }
- }
- }
-});
-
-openerp.web.form.One2ManyDataSet = openerp.web.BufferedDataSet.extend({
- get_context: function() {
- this.context = this.o2m.build_context([this.o2m.name]);
- return this.context;
- }
-});
-
-openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
+ },
+ write_function: function(id, data, options) {
+ return self.o2m.dataset.write(id, data, {}).then(function() {
+ self.o2m.reload_current_view();
+ });
+ },
+ alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
+ parent_view: self.o2m.view,
+ child_name: self.o2m.name,
+ read_function: function() {
+ return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
+ },
+ form_view_options: {'not_interactible_on_create':true},
+ readonly: self.o2m.get("effective_readonly")
+ });
+ pop.on_select_elements.add_last(function() {
+ self.o2m.reload_current_view();
+ });
+ },
+});
+
+instance.web.form.One2ManyDataSet = instance.web.BufferedDataSet.extend({
+ get_context: function() {
+ this.context = this.o2m.build_context([this.o2m.name]);
+ return this.context;
+ }
+});
+
+instance.web.form.One2ManyListView = instance.web.ListView.extend({
_template: 'One2Many.listview',
+ init: function (parent, dataset, view_id, options) {
+ this._super(parent, dataset, view_id, _.extend(options || {}, {
+ ListType: instance.web.form.One2ManyList
+ }));
+ },
+ is_valid: function () {
+ var form;
+ // A list not being edited is always valid
+ if (!(form = this.first_edition_form())) {
+ return true;
+ }
+ // If the form has not been modified, the view can only be valid
+ // NB: is_dirty will also be set on defaults/onchanges/whatever?
+ // oe_form_dirty seems to only be set on actual user actions
+ if (!form.$element.is('.oe_form_dirty')) {
+ return true;
+ }
+
+ // Otherwise validate internal form
+ return _(form.fields).chain()
+ .invoke(function () {
+ this._check_css_flag();
+ return this.is_valid();
+ })
+ .all(_.identity)
+ .value();
+ },
+ first_edition_form: function () {
+ var get_form = function (group_or_list) {
+ if (group_or_list.edition) {
+ return group_or_list.edition_form;
+ }
+ return _(group_or_list.children).chain()
+ .map(get_form)
+ .compact()
+ .first()
+ .value();
+ };
+ return get_form(this.groups);
+ },
do_add_record: function () {
if (this.options.editable) {
this._super.apply(this, arguments);
} else {
var self = this;
- var pop = new openerp.web.form.SelectCreatePopup(this);
- pop.on_default_get.add(self.dataset.on_default_get);
+ var pop = new instance.web.form.SelectCreatePopup(this);
pop.select_element(
self.o2m.field.relation,
{
},
do_activate_record: function(index, id) {
var self = this;
- var pop = new openerp.web.form.FormOpenPopup(self.o2m.view);
+ var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
title: _t("Open: ") + self.name,
- auto_write: false,
+ write_function: function(id, data) {
+ return self.o2m.dataset.write(id, data, {}, function(r) {
+ self.o2m.reload_current_view();
+ });
+ },
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
parent_view: self.o2m.view,
child_name: self.o2m.name,
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
},
form_view_options: {'not_interactible_on_create':true},
- readonly: self.o2m.is_readonly()
- });
- pop.on_write.add(function(id, data) {
- self.o2m.dataset.write(id, data, {}, function(r) {
- self.o2m.reload_current_view();
- });
+ readonly: self.o2m.get("effective_readonly")
});
},
do_button_action: function (name, id, callback) {
+ var _super = _.bind(this._super, this);
+
+ this.o2m.view.do_save().then(function () {
+ _super(name, id, callback);
+ });
+ }
+});
+instance.web.form.One2ManyList = instance.web.ListView.List.extend({
+ KEY_RETURN: 13,
+ // blurring caused by hitting the [Return] key, should skip the
+ // autosave-on-blur and let the handler for [Return] do its thing
+ __return_blur: false,
+ render_row_as_form: function () {
var self = this;
- var def = $.Deferred().then(callback).then(function() {self.o2m.view.reload();});
- return this._super(name, id, _.bind(def.resolve, def));
+ return this._super.apply(this, arguments).then(function () {
+ // Replace the "Save Row" button with "Cancel Edition"
+ self.edition_form.$element
+ .undelegate('button.oe-edit-row-save', 'click')
+ .delegate('button.oe-edit-row-save', 'click', function () {
+ self.cancel_pending_edition();
+ });
+
+ // Overload execute_action on the edition form to perform a simple
+ // reload_record after the action is done, rather than fully
+ // reload the parent view (or something)
+ var _execute_action = self.edition_form.do_execute_action;
+ self.edition_form.do_execute_action = function (action, dataset, record_id, _callback) {
+ return _execute_action.call(this, action, dataset, record_id, function () {
+ self.view.reload_record(
+ self.view.records.get(record_id));
+ });
+ };
+
+ self.edition_form.on('blurred', null, function () {
+ if (self.__return_blur) {
+ delete self.__return_blur;
+ return;
+ }
+ if (!self.edition_form.widget_is_stopped) {
+ self.view.ensure_saved();
+ }
+ });
+ });
+ },
+ on_row_keyup: function (e) {
+ if (e.which === this.KEY_RETURN) {
+ this.__return_blur = true;
+ }
+ this._super(e);
}
});
-openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
+instance.web.form.One2ManyFormView = instance.web.FormView.extend({
form_template: 'One2Many.formview',
on_loaded: function(data) {
this._super(data);
var self = this;
- this.$form_header.find('button.oe_form_button_create').click(function() {
+ this.$buttons.find('button.oe_form_button_create').click(function() {
self.do_save().then(self.on_button_new);
});
},
}
});
-openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
- template: 'FieldMany2Many',
+var lazy_build_o2m_kanban_view = function() {
+if (! instance.web_kanban || instance.web.form.One2ManyKanbanView)
+ return;
+instance.web.form.One2ManyKanbanView = instance.web_kanban.KanbanView.extend({
+});
+}
+
+instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, instance.web.form.ReinitializeFieldMixin, {
+ template: "FieldMany2ManyTags",
+ init: function() {
+ this._super.apply(this, arguments);
+ instance.web.form.CompletionFieldMixin.init.call(this);
+ this.set({"value": []});
+ this._display_orderer = new instance.web.DropMisordered();
+ this._drop_shown = false;
+ },
+ start: function() {
+ this._super();
+ instance.web.form.ReinitializeFieldMixin.start.call(this);
+ this.on("change:value", this, this.render_value);
+ },
+ initialize_content: function() {
+ if (this.get("effective_readonly"))
+ return;
+ var self = this;
+ self.$text = $("textarea", this.$element);
+ self.$text.textext({
+ plugins : 'tags arrow autocomplete',
+ autocomplete: {
+ render: function(suggestion) {
+ return $('<span class="text-label"/>').
+ data('index', suggestion['index']).html(suggestion['label']);
+ }
+ },
+ ext: {
+ autocomplete: {
+ selectFromDropdown: function() {
+ $(this).trigger('hideDropdown');
+ var index = Number(this.selectedSuggestionElement().children().children().data('index'));
+ var data = self.search_result[index];
+ if (data.id) {
+ self.add_id(data.id);
+ } else {
+ data.action();
+ }
+ },
+ },
+ tags: {
+ isTagAllowed: function(tag) {
+ if (! tag.name)
+ return false;
+ return true;
+ },
+ removeTag: function(tag) {
+ var id = tag.data("id");
+ self.set({"value": _.without(self.get("value"), id)});
+ },
+ renderTag: function(stuff) {
+ return $.fn.textext.TextExtTags.prototype.renderTag.
+ call(this, stuff).data("id", stuff.id);
+ },
+ },
+ itemManager: {
+ itemToString: function(item) {
+ return item.name;
+ },
+ },
+ },
+ }).bind('getSuggestions', function(e, data) {
+ var _this = this;
+ var str = !!data ? data.query || '' : '';
+ self.get_search_result(str).then(function(result) {
+ self.search_result = result;
+ $(_this).trigger('setSuggestions', {result : _.map(result, function(el, i) {
+ return _.extend(el, {index:i});
+ })});
+ });
+ }).bind('hideDropdown', function() {
+ self._drop_shown = false;
+ }).bind('showDropdown', function() {
+ self._drop_shown = true;
+ });
+ self.tags = self.$text.textext()[0].tags();
+ $("textarea", this.$element).focusout(function() {
+ self.$text.trigger("setInputData", "");
+ }).keydown(function(e) {
+ if (event.keyCode === 9 && self._drop_shown) {
+ self.$text.textext()[0].autocomplete().selectFromDropdown();
+ }
+ });
+ },
+ set_value: function(value_) {
+ value_ = value_ || [];
+ if (value_.length >= 1 && value_[0] instanceof Array) {
+ value_ = value_[0][2];
+ }
+ this._super(value_);
+ },
+ get_value: function() {
+ var tmp = [commands.replace_with(this.get("value"))];
+ return tmp;
+ },
+ get_search_blacklist: function() {
+ return this.get("value");
+ },
+ render_value: function() {
+ var self = this;
+ var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
+ var handle_names = function(data) {
+ var indexed = {};
+ _.each(data, function(el) {
+ indexed[el[0]] = el;
+ });
+ data = _.map(self.get("value"), function(el) { return indexed[el]; });
+ if (! self.get("effective_readonly")) {
+ self.tags.containerElement().children().remove();
+ $("textarea", self.$element).css("padding-left", "3px");
+ self.tags.addTags(_.map(data, function(el) {return {name: el[1], id:el[0]};}));
+ } else {
+ self.$element.html(QWeb.render("FieldMany2ManyTags.box", {elements: data}));
+ }
+ };
+ if (! self.get('values') || self.get('values').length > 0) {
+ this._display_orderer.add(dataset.name_get(self.get("value"))).then(handle_names);
+ } else {
+ handle_names([]);
+ }
+ },
+ add_id: function(id) {
+ this.set({'value': _.uniq(this.get('value').concat([id]))});
+ },
+});
+
+/*
+ * TODO niv: clean those deferred stuff, it could be better
+ */
+instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
multi_selection: false,
- init: function(view, node) {
- this._super(view, node);
- this.list_id = _.uniqueId("many2many");
+ disable_utility_classes: true,
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
this.is_setted = $.Deferred();
},
start: function() {
this._super.apply(this, arguments);
+ this.$element.addClass('oe_form_field_many2many');
var self = this;
- this.dataset = new openerp.web.form.Many2ManyDataSet(this, this.field.relation);
+ this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
this.dataset.m2m = this;
this.dataset.on_unlink.add_last(function(ids) {
- self.on_ui_change();
+ self.dataset_changed();
});
-
+
this.is_setted.then(function() {
self.load_view();
});
+ this.is_loaded.then(function() {
+ self.on("change:effective_readonly", self, function() {
+ self.is_loaded = self.is_loaded.pipe(function() {
+ self.list_view.destroy();
+ return $.when(self.load_view()).then(function() {
+ self.reload_content();
+ });
+ });
+ });
+ });
},
- set_value: function(value) {
- value = value || [];
- if (value.length >= 1 && value[0] instanceof Array) {
- value = value[0][2];
+ set_value: function(value_) {
+ value_ = value_ || [];
+ if (value_.length >= 1 && value_[0] instanceof Array) {
+ value_ = value_[0][2];
}
- this._super(value);
- this.dataset.set_ids(value);
+ this._super(value_);
+ this.dataset.set_ids(value_);
var self = this;
self.reload_content();
this.is_setted.resolve();
},
get_value: function() {
- return [commands.replace_with(this.dataset.ids)];
+ return [commands.replace_with(this.get('value'))];
},
- validate: function() {
- this.invalid = false;
- },
- is_readonly: function() {
- return this.readonly || this.force_readonly;
+
+ is_false: function () {
+ return _(this.dataset.ids).isEmpty();
},
load_view: function() {
var self = this;
- this.list_view = new openerp.web.form.Many2ManyListView(this, this.dataset, false, {
- 'addable': self.is_readonly() ? null : _t("Add"),
- 'deletable': self.is_readonly() ? false : true,
+ this.list_view = new instance.web.form.Many2ManyListView(this, this.dataset, false, {
+ 'addable': self.get("effective_readonly") ? null : _t("Add"),
+ 'deletable': self.get("effective_readonly") ? false : true,
'selectable': self.multi_selection,
- 'isClarkGable': self.is_readonly() ? false : true
+ 'sortable': false,
+ 'reorderable': false,
});
var embedded = (this.field.views || {}).tree;
if (embedded) {
loaded.resolve();
});
$.async_when().then(function () {
- self.list_view.appendTo($("#" + self.list_id));
+ self.list_view.appendTo(self.$element);
});
return loaded;
},
return self.list_view.reload_content();
});
},
- update_dom: function() {
+ dataset_changed: function() {
+ this.set({'value': this.dataset.ids});
+ },
+});
+
+instance.web.form.Many2ManyDataSet = instance.web.DataSetStatic.extend({
+ get_context: function() {
+ this.context = this.m2m.build_context();
+ return this.context;
+ }
+});
+
+/**
+ * @class
+ * @extends instance.web.ListView
+ */
+instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends instance.web.form.Many2ManyListView# */{
+ do_add_record: function () {
+ var pop = new instance.web.form.SelectCreatePopup(this);
+ pop.select_element(
+ this.model,
+ {
+ title: _t("Add: ") + this.name
+ },
+ new instance.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]),
+ this.m2m_field.build_context()
+ );
+ var self = this;
+ pop.on_select_elements.add(function(element_ids) {
+ _.each(element_ids, function(one_id) {
+ if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
+ self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
+ self.m2m_field.dataset_changed();
+ self.reload_content();
+ }
+ });
+ });
+ },
+ do_activate_record: function(index, id) {
+ var self = this;
+ var pop = new instance.web.form.FormOpenPopup(this);
+ pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
+ title: _t("Open: ") + this.name,
+ readonly: this.getParent().get("effective_readonly")
+ });
+ pop.on_write_completed.add_last(function() {
+ self.reload_content();
+ });
+ }
+});
+
+instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, {
+ disable_utility_classes: true,
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ instance.web.form.CompletionFieldMixin.init.call(this);
+ m2m_kanban_lazy_init();
+ this.is_loaded = $.Deferred();
+ this.initial_is_loaded = this.is_loaded;
+ this.is_setted = $.Deferred();
+ },
+ start: function() {
this._super.apply(this, arguments);
+
var self = this;
- if (this.previous_readonly !== this.readonly) {
- this.previous_readonly = this.readonly;
- if (this.list_view) {
- this.is_loaded = this.is_loaded.pipe(function() {
- self.list_view.stop();
+
+ this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
+ this.dataset.m2m = this;
+ this.dataset.on_unlink.add_last(function(ids) {
+ self.dataset_changed();
+ });
+
+ this.is_setted.then(function() {
+ self.load_view();
+ });
+ this.is_loaded.then(function() {
+ self.on("change:effective_readonly", self, function() {
+ self.is_loaded = self.is_loaded.pipe(function() {
+ self.kanban_view.destroy();
return $.when(self.load_view()).then(function() {
self.reload_content();
});
});
- }
+ });
+ })
+ },
+ set_value: function(value_) {
+ value_ = value_ || [];
+ if (value_.length >= 1 && value_[0] instanceof Array) {
+ value_ = value_[0][2];
}
- }
+ this._super(value_);
+ this.dataset.set_ids(value_);
+ var self = this;
+ self.reload_content();
+ this.is_setted.resolve();
+ },
+ load_view: function() {
+ var self = this;
+ this.kanban_view = new instance.web.form.Many2ManyKanbanView(this, this.dataset, false, {
+ 'create_text': _t("Add"),
+ 'creatable': self.get("effective_readonly") ? false : true,
+ 'quick_creatable': self.get("effective_readonly") ? false : true,
+ 'read_only_mode': self.get("effective_readonly") ? true : false,
+ 'confirm_on_delete': false,
+ });
+ var embedded = (this.field.views || {}).kanban;
+ if (embedded) {
+ this.kanban_view.set_embedded_view(embedded);
+ }
+ this.kanban_view.m2m = this;
+ var loaded = $.Deferred();
+ this.kanban_view.on_loaded.add_last(function() {
+ self.initial_is_loaded.resolve();
+ loaded.resolve();
+ });
+ this.kanban_view.do_switch_view.add_last(_.bind(this.open_popup, this));
+ $.async_when().then(function () {
+ self.kanban_view.appendTo(self.$element);
+ });
+ return loaded;
+ },
+ reload_content: function() {
+ var self = this;
+ this.is_loaded = this.is_loaded.pipe(function() {
+ return self.kanban_view.do_search(self.build_domain(), self.dataset.get_context(), []);
+ });
+ },
+ dataset_changed: function() {
+ this.set({'value': [commands.replace_with(this.dataset.ids)]});
+ },
+ open_popup: function(type, unused) {
+ if (type !== "form")
+ return;
+ var self = this;
+ if (this.dataset.index === null) {
+ var pop = new instance.web.form.SelectCreatePopup(this);
+ pop.select_element(
+ this.field.relation,
+ {
+ title: _t("Add: ") + this.name
+ },
+ new instance.web.CompoundDomain(this.build_domain(), ["!", ["id", "in", this.dataset.ids]]),
+ this.build_context()
+ );
+ pop.on_select_elements.add(function(element_ids) {
+ _.each(element_ids, function(one_id) {
+ if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
+ self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
+ self.dataset_changed();
+ self.reload_content();
+ }
+ });
+ });
+ } else {
+ var id = self.dataset.ids[self.dataset.index];
+ var pop = new instance.web.form.FormOpenPopup(self.view);
+ pop.show_element(self.field.relation, id, self.build_context(), {
+ title: _t("Open: ") + self.name,
+ write_function: function(id, data, options) {
+ return self.dataset.write(id, data, {}).then(function() {
+ self.reload_content();
+ });
+ },
+ alternative_form_view: self.field.views ? self.field.views["form"] : undefined,
+ parent_view: self.view,
+ child_name: self.name,
+ readonly: self.get("effective_readonly")
+ });
+ }
+ },
+ add_id: function(id) {
+ this.quick_create.add_id(id);
+ },
});
-openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({
- get_context: function() {
- this.context = this.m2m.build_context();
- return this.context;
- }
+function m2m_kanban_lazy_init() {
+if (instance.web.form.Many2ManyKanbanView)
+ return;
+instance.web.form.Many2ManyKanbanView = instance.web_kanban.KanbanView.extend({
+ quick_create_class: 'instance.web.form.Many2ManyQuickCreate',
+ _is_quick_create_enabled: function() {
+ return this._super() && ! this.group_by;
+ },
+});
+instance.web.form.Many2ManyQuickCreate = instance.web.Widget.extend({
+ template: 'Many2ManyKanban.quick_create',
+
+ /**
+ * close_btn: If true, the widget will display a "Close" button able to trigger
+ * a "close" event.
+ */
+ init: function(parent, dataset, context, buttons) {
+ this._super(parent);
+ this.m2m = this.getParent().view.m2m;
+ this.m2m.quick_create = this;
+ this._dataset = dataset;
+ this._buttons = buttons || false;
+ this._context = context || {};
+ },
+ start: function () {
+ var self = this;
+ self.$text = this.$element.find('input').css("width", "200px");
+ self.$text.textext({
+ plugins : 'arrow autocomplete',
+ autocomplete: {
+ render: function(suggestion) {
+ return $('<span class="text-label"/>').
+ data('index', suggestion['index']).html(suggestion['label']);
+ }
+ },
+ ext: {
+ autocomplete: {
+ selectFromDropdown: function() {
+ $(this).trigger('hideDropdown');
+ var index = Number(this.selectedSuggestionElement().children().children().data('index'));
+ var data = self.search_result[index];
+ if (data.id) {
+ self.add_id(data.id);
+ } else {
+ data.action();
+ }
+ },
+ },
+ itemManager: {
+ itemToString: function(item) {
+ return item.name;
+ },
+ },
+ },
+ }).bind('getSuggestions', function(e, data) {
+ var _this = this;
+ var str = !!data ? data.query || '' : '';
+ self.m2m.get_search_result(str).then(function(result) {
+ self.search_result = result;
+ $(_this).trigger('setSuggestions', {result : _.map(result, function(el, i) {
+ return _.extend(el, {index:i});
+ })});
+ });
+ });
+ self.$text.focusout(function() {
+ self.$text.val("");
+ });
+ },
+ focus: function() {
+ this.$text.focus();
+ },
+ add_id: function(id) {
+ var self = this;
+ self.$text.val("");
+ self.trigger('added', id);
+ this.m2m.dataset_changed();
+ },
+});
+}
+
+/**
+ * Class with everything which is common between FormOpenPopup and SelectCreatePopup.
+ */
+instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
+ template: "AbstractFormPopup.render",
+ /**
+ * options:
+ * -readonly: only applicable when not in creation mode, default to false
+ * - alternative_form_view
+ * - write_function
+ * - read_function
+ * - create_function
+ * - parent_view
+ * - child_name
+ * - form_view_options
+ */
+ init_popup: function(model, row_id, domain, context, options) {
+ this.row_id = row_id;
+ this.model = model;
+ this.domain = domain || [];
+ this.context = context || {};
+ this.options = options;
+ _.defaults(this.options, {
+ });
+ },
+ init_dataset: function() {
+ var self = this;
+ this.created_elements = [];
+ this.dataset = new instance.web.ProxyDataSet(this, this.model, this.context);
+ this.dataset.read_function = this.options.read_function;
+ this.dataset.create_function = function(data, sup) {
+ var fct = self.options.create_function || sup;
+ return fct.call(this, data).then(function(r) {
+ self.created_elements.push(r.result);
+ });
+ };
+ this.dataset.write_function = function(id, data, options, sup) {
+ var fct = self.options.write_function || sup;
+ return fct.call(this, id, data, options).then(self.on_write_completed);
+ };
+ this.dataset.parent_view = this.options.parent_view;
+ this.dataset.child_name = this.options.child_name;
+ },
+ display_popup: function() {
+ var self = this;
+ this.renderElement();
+ new instance.web.Dialog(this, {
+ width: '90%',
+ min_width: '800px',
+ close: function() {
+ self.check_exit(true);
+ },
+ title: this.options.title || "",
+ }, this.$element).open();
+ this.start();
+ },
+ on_write_completed: function() {},
+ setup_form_view: function() {
+ var self = this;
+ if (this.row_id) {
+ this.dataset.ids = [this.row_id];
+ this.dataset.index = 0;
+ } else {
+ this.dataset.index = null;
+ }
+ var options = _.clone(self.options.form_view_options) || {};
+ if (this.row_id !== null) {
+ options.initial_mode = this.options.readonly ? "view" : "edit";
+ }
+ this.view_form = new instance.web.FormView(this, this.dataset, false, options);
+ if (this.options.alternative_form_view) {
+ this.view_form.set_embedded_view(this.options.alternative_form_view);
+ }
+ this.view_form.appendTo(this.$element.find(".oe-form-view-popup-form-placeholder"));
+ this.view_form.on_loaded.add_last(function() {
+ var $buttons = self.view_form.$element.find(".oe_form_buttons");
+ var multi_select = self.row_id === null && ! self.options.disable_multiple_selection;
+ $buttons.html(QWeb.render("AbstractFormPopup.buttons", {multi_select: multi_select}));
+ var $snbutton = $buttons.find(".oe_abstractformpopup-form-save-new");
+ $snbutton.click(function() {
+ $.when(self.view_form.do_save()).then(function() {
+ self.view_form.reload_mutex.exec(function() {
+ self.view_form.on_button_new();
+ });
+ });
+ });
+ var $sbutton = $buttons.find(".oe_abstractformpopup-form-save");
+ $sbutton.click(function() {
+ $.when(self.view_form.do_save()).then(function() {
+ self.view_form.reload_mutex.exec(function() {
+ self.check_exit();
+ });
+ });
+ });
+ var $cbutton = $buttons.find(".oe_abstractformpopup-form-close");
+ $cbutton.click(function() {
+ self.check_exit();
+ });
+ if (self.row_id !== null && self.options.readonly) {
+ $snbutton.hide();
+ $sbutton.hide();
+ $cbutton.text(_t("Close"));
+ }
+ self.view_form.do_show();
+ });
+ },
+ on_select_elements: function(element_ids) {
+ },
+ check_exit: function(no_destroy) {
+ if (this.created_elements.length > 0) {
+ this.on_select_elements(this.created_elements);
+ this.created_elements = [];
+ }
+ this.destroy();
+ },
+ destroy: function () {
+ this.$element.dialog('close');
+ this._super();
+ },
});
/**
- * @class
- * @extends openerp.web.ListView
+ * Class to display a popup containing a form view.
*/
-openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends openerp.web.form.Many2ManyListView# */{
- do_add_record: function () {
- var pop = new openerp.web.form.SelectCreatePopup(this);
- pop.select_element(
- this.model,
- {
- title: _t("Add: ") + this.name
- },
- new openerp.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]),
- this.m2m_field.build_context()
- );
- var self = this;
- pop.on_select_elements.add(function(element_ids) {
- _.each(element_ids, function(element_id) {
- if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) {
- self.dataset.set_ids([].concat(self.dataset.ids, [element_id]));
- self.m2m_field.on_ui_change();
- self.reload_content();
- }
- });
+instance.web.form.FormOpenPopup = instance.web.form.AbstractFormPopup.extend({
+ show_element: function(model, row_id, context, options) {
+ this.init_popup(model, row_id, [], context, options);
+ _.defaults(this.options, {
});
+ this.display_popup();
+ },
+ start: function() {
+ this._super();
+ this.init_dataset();
+ this.setup_form_view();
},
- do_activate_record: function(index, id) {
- var self = this;
- var pop = new openerp.web.form.FormOpenPopup(this);
- pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
- title: _t("Open: ") + this.name,
- readonly: this.widget_parent.is_readonly()
- });
- pop.on_write_completed.add_last(function() {
- self.reload_content();
- });
- }
});
/**
- * @class
- * @extends openerp.web.OldWidget
+ * Class to display a popup to display a list to search a row. It also allows
+ * to switch to a form view to create a new row.
*/
-openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.SelectCreatePopup# */{
- template: "SelectCreatePopup",
+instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend({
/**
* options:
* - initial_ids
* - initial_view: form or search (default search)
* - disable_multiple_selection
- * - alternative_form_view
- * - create_function (defaults to a naive saving behavior)
- * - parent_view
- * - child_name
- * - form_view_options
* - list_view_options
- * - read_function
*/
select_element: function(model, options, domain, context) {
+ this.init_popup(model, null, domain, context, options);
var self = this;
- this.model = model;
- this.domain = domain || [];
- this.context = context || {};
- this.options = _.defaults(options || {}, {"initial_view": "search", "create_function": function() {
- return self.create_row.apply(self, arguments);
- }, read_function: null});
- this.initial_ids = this.options.initial_ids;
- this.created_elements = [];
- this.render_element();
- openerp.web.form.dialog(this.$element, {
- close: function() {
- self.check_exit();
- },
- title: options.title || ""
+ _.defaults(this.options, {
+ initial_view: "search",
});
- this.start();
+ this.initial_ids = this.options.initial_ids;
+ this.display_popup();
},
start: function() {
- this._super();
var self = this;
- this.dataset = new openerp.web.ProxyDataSet(this, this.model,
- this.context);
- this.dataset.create_function = function() {
- return self.options.create_function.apply(null, arguments).then(function(r) {
- self.created_elements.push(r.result);
- });
- };
- this.dataset.write_function = function() {
- return self.write_row.apply(self, arguments);
- };
- this.dataset.read_function = this.options.read_function;
- this.dataset.parent_view = this.options.parent_view;
- this.dataset.child_name = this.options.child_name;
- this.dataset.on_default_get.add(this.on_default_get);
+ this.init_dataset();
if (this.options.initial_view == "search") {
self.rpc('/web/session/eval_domain_and_context', {
domains: [],
contexts: [this.context]
}, function (results) {
var search_defaults = {};
- _.each(results.context, function (value, key) {
+ _.each(results.context, function (value_, key) {
var match = /^search_default_(.*)$/.exec(key);
if (match) {
- search_defaults[match[1]] = value;
+ search_defaults[match[1]] = value_;
}
});
self.setup_search_view(search_defaults);
this.new_object();
}
},
- stop: function () {
- this.$element.dialog('close');
- this._super();
- },
setup_search_view: function(search_defaults) {
var self = this;
if (this.searchview) {
- this.searchview.stop();
+ this.searchview.destroy();
}
- this.searchview = new openerp.web.SearchView(this,
+ this.searchview = new instance.web.SearchView(this,
this.dataset, false, search_defaults);
this.searchview.on_search.add(function(domains, contexts, groupbys) {
if (self.initial_ids) {
}
});
this.searchview.on_loaded.add_last(function () {
- self.view_list = new openerp.web.form.SelectCreateListView(self,
+ self.view_list = new instance.web.form.SelectCreateListView(self,
self.dataset, false,
_.extend({'deletable': false,
- 'selectable': !self.options.disable_multiple_selection
+ 'selectable': !self.options.disable_multiple_selection,
+ 'read_only': true,
}, self.options.list_view_options || {}));
self.view_list.popup = self;
- self.view_list.appendTo($("#" + self.element_id + "_view_list")).pipe(function() {
+ self.view_list.appendTo($(".oe-select-create-popup-view-list", self.$element)).pipe(function() {
self.view_list.do_show();
}).pipe(function() {
self.searchview.do_search();
$buttons.prepend(QWeb.render("SelectCreatePopup.search.buttons"));
var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
$cbutton.click(function() {
- self.stop();
+ self.destroy();
});
var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
if(self.options.disable_multiple_selection) {
}
$sbutton.click(function() {
self.on_select_elements(self.selected_ids);
- self.stop();
+ self.destroy();
});
});
});
- this.searchview.appendTo($("#" + this.element_id + "_search"));
+ this.searchview.appendTo($(".oe-select-create-popup-view-list", self.$element));
},
do_search: function(domains, contexts, groupbys) {
var self = this;
self.view_list.do_search(results.domain, results.context, results.group_by);
});
},
- create_row: function() {
- var self = this;
- var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
- wdataset.parent_view = this.options.parent_view;
- wdataset.child_name = this.options.child_name;
- return wdataset.create.apply(wdataset, arguments);
- },
- write_row: function() {
- var self = this;
- var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
- wdataset.parent_view = this.options.parent_view;
- wdataset.child_name = this.options.child_name;
- return wdataset.write.apply(wdataset, arguments);
- },
- on_select_elements: function(element_ids) {
- },
on_click_element: function(ids) {
this.selected_ids = ids || [];
if(this.selected_ids.length > 0) {
}
},
new_object: function() {
- var self = this;
if (this.searchview) {
this.searchview.hide();
}
if (this.view_list) {
this.view_list.$element.hide();
}
- this.dataset.index = null;
- this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options);
- if (this.options.alternative_form_view) {
- this.view_form.set_embedded_view(this.options.alternative_form_view);
- }
- this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form"));
- this.view_form.on_loaded.add_last(function() {
- var $buttons = self.view_form.$element.find(".oe_form_buttons");
- $buttons.html(QWeb.render("SelectCreatePopup.form.buttons", {widget:self}));
- var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save-new");
- $nbutton.click(function() {
- $.when(self.view_form.do_save()).then(function() {
- self.view_form.reload_mutex.exec(function() {
- self.view_form.on_button_new();
- });
- });
- });
- var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save");
- $nbutton.click(function() {
- $.when(self.view_form.do_save()).then(function() {
- self.view_form.reload_mutex.exec(function() {
- self.check_exit();
- });
- });
- });
- var $cbutton = $buttons.find(".oe_selectcreatepopup-form-close");
- $cbutton.click(function() {
- self.check_exit();
- });
- });
- this.view_form.do_show();
- },
- check_exit: function() {
- if (this.created_elements.length > 0) {
- this.on_select_elements(this.created_elements);
- }
- this.stop();
+ this.setup_form_view();
},
- on_default_get: function(res) {}
});
-openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({
+instance.web.form.SelectCreateListView = instance.web.ListView.extend({
do_add_record: function () {
this.popup.new_object();
},
select_record: function(index) {
this.popup.on_select_elements([this.dataset.ids[index]]);
- this.popup.stop();
+ this.popup.destroy();
},
do_select: function(ids, records) {
this._super(ids, records);
}
});
-/**
- * @class
- * @extends openerp.web.OldWidget
- */
-openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.FormOpenPopup# */{
- template: "FormOpenPopup",
- /**
- * options:
- * - alternative_form_view
- * - auto_write (default true)
- * - read_function
- * - parent_view
- * - child_name
- * - form_view_options
- * - readonly
- */
- show_element: function(model, row_id, context, options) {
- this.model = model;
- this.row_id = row_id;
- this.context = context || {};
- this.options = _.defaults(options || {}, {"auto_write": true});
- this.render_element();
- this.$element.dialog({
- title: options.title || '',
- modal: true,
- width: 960,
- height: 600
- });
- this.start();
- },
- start: function() {
- this._super();
- this.dataset = new openerp.web.form.FormOpenDataset(this, this.model, this.context);
- this.dataset.fop = this;
- this.dataset.ids = [this.row_id];
- this.dataset.index = 0;
- this.dataset.parent_view = this.options.parent_view;
- this.dataset.child_name = this.options.child_name;
- this.setup_form_view();
- },
- on_write: function(id, data) {
- if (!this.options.auto_write)
- return;
- var self = this;
- var wdataset = new openerp.web.DataSetSearch(this, this.model, this.context, this.domain);
- wdataset.parent_view = this.options.parent_view;
- wdataset.child_name = this.options.child_name;
- wdataset.write(id, data, {}, function(r) {
- self.on_write_completed();
- });
- },
- on_write_completed: function() {},
- setup_form_view: function() {
- var self = this;
- var FormClass = this.options.readonly
- ? openerp.web.views.get_object('page')
- : openerp.web.views.get_object('form');
- this.view_form = new FormClass(this, this.dataset, false, self.options.form_view_options);
- if (this.options.alternative_form_view) {
- this.view_form.set_embedded_view(this.options.alternative_form_view);
- }
- this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form"));
- this.view_form.on_loaded.add_last(function() {
- var $buttons = self.view_form.$element.find(".oe_form_buttons");
- $buttons.html(QWeb.render("FormOpenPopup.form.buttons"));
- var $nbutton = $buttons.find(".oe_formopenpopup-form-save");
- $nbutton.click(function() {
- self.view_form.do_save().then(function() {
- self.stop();
- });
- });
- var $cbutton = $buttons.find(".oe_formopenpopup-form-close");
- $cbutton.click(function() {
- self.stop();
- });
- if (self.options.readonly) {
- $nbutton.hide();
- $cbutton.text(_t("Close"));
- }
- self.view_form.do_show();
- });
- this.dataset.on_write.add(this.on_write);
- }
-});
-
-openerp.web.form.FormOpenDataset = openerp.web.ProxyDataSet.extend({
- read_ids: function() {
- if (this.fop.options.read_function) {
- return this.fop.options.read_function.apply(null, arguments);
- } else {
- return this._super.apply(this, arguments);
- }
- }
-});
-
-openerp.web.form.FieldReference = openerp.web.form.Field.extend({
+instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldReference',
- init: function(view, node) {
- this._super(view, node);
- this.fields_view = {
- fields: {
- selection: {
- selection: view.fields_view.fields[this.name].selection
- },
- m2o: {
- relation: null
- }
- }
- };
- this.get_fields_values = view.get_fields_values;
- this.get_selected_ids = view.get_selected_ids;
- this.do_onchange = this.on_form_changed = this.do_notify_change = this.on_nop;
- this.dataset = this.view.dataset;
- this.widgets_counter = 0;
- this.view_id = 'reference_' + _.uniqueId();
- this.widgets = {};
- this.fields = {};
- this.fields_order = [];
- this.selection = new openerp.web.form.FieldSelection(this, { attrs: {
- name: 'selection',
- widget: 'selection'
- }});
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.reference_ready = true;
- this.selection.on_value_changed.add_last(this.on_selection_changed);
- this.m2o = new openerp.web.form.FieldMany2One(this, { attrs: {
- name: 'm2o',
- widget: 'many2one'
- }});
- this.m2o.on_ui_change.add_last(this.on_ui_change);
},
on_nop: function() {
},
if (this.reference_ready) {
var sel = this.selection.get_value();
this.m2o.field.relation = sel;
- this.m2o.set_value(null);
+ this.m2o.set_value(false);
this.m2o.$element.toggle(sel !== false);
}
},
- start: function() {
- this._super();
+ destroy_content: function() {
+ if (this.selection) {
+ this.selection.destroy();
+ this.selection = undefined;
+ }
+ if (this.m2o) {
+ this.m2o.destroy();
+ this.m2o = undefined;
+ }
+ },
+ initialize_content: function() {
+ var self = this;
+ this.selection = new instance.web.form.FieldSelection(this, { attrs: {
+ name: 'selection'
+ }});
+ this.selection.view = this.view;
+ this.selection.set({force_readonly: this.get('effective_readonly')});
+ this.selection.on("change:value", this, this.on_selection_changed);
+ this.selection.$element = $(".oe_form_view_reference_selection", this.$element);
+ this.selection.renderElement();
this.selection.start();
+ this.selection
+ .on('focused', null, function () {self.trigger('focused')})
+ .on('blurred', null, function () {self.trigger('blurred')});
+
+ this.m2o = new instance.web.form.FieldMany2One(this, { attrs: {
+ name: 'm2o'
+ }});
+ this.m2o.view = this.view;
+ this.m2o.set({force_readonly: this.get("effective_readonly")});
+ this.m2o.on("change:value", this, this.data_changed);
+ this.m2o.$element = $(".oe_form_view_reference_m2o", this.$element);
+ this.m2o.renderElement();
this.m2o.start();
+ this.m2o
+ .on('focused', null, function () {self.trigger('focused')})
+ .on('blurred', null, function () {self.trigger('blurred')});
},
- is_valid: function() {
- return this.required === false || typeof(this.get_value()) === 'string';
+ is_false: function() {
+ return typeof(this.get_value()) !== 'string';
},
- is_dirty: function() {
- return this.selection.is_dirty() || this.m2o.is_dirty();
+ set_value: function(value_) {
+ this._super(value_);
+ this.render_value();
},
- set_value: function(value) {
- this._super(value);
+ render_value: function() {
this.reference_ready = false;
var vals = [], sel_val, m2o_val;
- if (typeof(value) === 'string') {
- vals = value.split(',');
+ if (typeof(this.get('value')) === 'string') {
+ vals = this.get('value').split(',');
}
sel_val = vals[0] || false;
- m2o_val = vals[1] ? parseInt(vals[1], 10) : false;
- this.selection.set_value(sel_val);
+ m2o_val = vals[1] ? parseInt(vals[1], 10) : vals[1];
+ if (!this.get("effective_readonly")) {
+ this.selection.set_value(sel_val);
+ }
this.m2o.field.relation = sel_val;
this.m2o.set_value(m2o_val);
- this.m2o.$element.toggle(sel_val !== false);
this.reference_ready = true;
},
- get_value: function() {
+ data_changed: function() {
var model = this.selection.get_value(),
id = this.m2o.get_value();
if (typeof(model) === 'string' && typeof(id) === 'number') {
- return model + ',' + id;
+ this.set({'value': model + ',' + id});
} else {
- return false;
+ this.set({'value': false});
}
- }
+ },
+ get_field: function(name) {
+ if (name === "selection") {
+ return {
+ selection: this.view.fields_view.fields[this.name].selection,
+ type: "selection",
+ };
+ } else if (name === "m2o") {
+ return {
+ relation: null,
+ type: "many2one",
+ };
+ }
+ throw Exception("Should not happen");
+ },
});
-openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.iframe = this.element_id + '_iframe';
+instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
+ init: function(field_manager, node) {
+ var self = this;
+ this._super(field_manager, node);
this.binary_value = false;
+ 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);
+ });
},
- start: function() {
+ stop: function() {
+ $(window).off(this.fileupload_id);
this._super.apply(this, arguments);
+ },
+ initialize_content: function() {
this.$element.find('input.oe-binary-file').change(this.on_file_change);
- this.$element.find('button.oe-binary-file-save').click(this.on_save_as);
+ this.$element.find('button.oe_binary_file_save').click(this.on_save_as);
this.$element.find('.oe-binary-file-clear').click(this.on_clear);
},
human_filesize : function(size) {
// TODO: on modern browsers, we could directly read the file locally on client ready to be used on image cropper
// http://www.html5rocks.com/tutorials/file/dndfiles/
// http://deepliquid.com/projects/Jcrop/demos.php?demo=handler
- window[this.iframe] = this.on_file_uploaded;
- if ($(e.target).val() != '') {
+
+ if ($(e.target).val() !== '') {
this.$element.find('form.oe-binary-form input[name=session_id]').val(this.session.session_id);
this.$element.find('form.oe-binary-form').submit();
this.$element.find('.oe-binary-progress').show();
}
},
on_file_uploaded: function(size, name, content_type, file_base64) {
- delete(window[this.iframe]);
if (size === false) {
this.do_warn("File Upload", "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.on_ui_change();
}
this.$element.find('.oe-binary-progress').hide();
this.$element.find('.oe-binary').show();
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
},
- on_save_as: function() {
- $.blockUI();
- 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: (this.node.attrs.filename || ''),
- context: this.view.dataset.get_context()
- })},
- complete: $.unblockUI,
- error: openerp.webclient.crashmanager.on_rpc_error
- });
+ 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 if (this._dirty_flag) {
+ var link = this.$('.oe_binary_file_save_data')[0];
+ link.download = this.filename || "download.bin"; // Works on only on Google Chrome
+ //link.target = '_blank';
+ link.href = "data:application/octet-stream;base64," + value;
+ } else {
+ $.blockUI();
+ 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: (this.node.attrs.filename || ''),
+ context: this.view.dataset.get_context()
+ })},
+ complete: $.unblockUI,
+ error: instance.webclient.crashmanager.on_rpc_error
+ });
+ ev.stopPropagation();
+ return false;
+ }
},
set_filename: function(value) {
var filename = this.node.attrs.filename;
}
},
on_clear: function() {
- if (this.value !== false) {
- this.value = false;
+ if (this.get('value') !== false) {
this.binary_value = false;
- this.on_ui_change();
+ this.set({'value': false});
}
return false;
}
});
-openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
+instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
template: 'FieldBinaryFile',
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('.oe-binary-file-set, .oe-binary-file-clear').toggle(!this.readonly);
- this.$element.find('input[type=text]').prop('readonly', this.readonly);
+ initialize_content: function() {
+ this._super();
+ if (this.get("effective_readonly")) {
+ var self = this;
+ this.$element.find('a').click(function() {
+ if (self.get('value')) {
+ self.on_save_as();
+ }
+ return false;
+ });
+ }
},
- set_value: function(value) {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- var show_value;
- if (this.node.attrs.filename) {
- show_value = this.view.datarecord[this.node.attrs.filename] || '';
+ this.render_value();
+ },
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ var show_value;
+ if (this.node.attrs.filename) {
+ show_value = this.view.datarecord[this.node.attrs.filename] || '';
+ } else {
+ show_value = (this.get('value') != null && this.get('value') !== false) ? this.get('value') : '';
+ }
+ this.$element.find('input').eq(0).val(show_value);
} else {
- show_value = (value != null && value !== false) ? value : '';
+ this.$element.find('a').show(!!this.get('value'));
+ if (this.get('value')) {
+ var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || '');
+ this.$element.find('a').text(show_value);
+ }
}
- this.$element.find('input').eq(0).val(show_value);
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
- this.value = file_base64;
this.binary_value = true;
+ this.set({'value': file_base64});
var show_value = name + " (" + this.human_filesize(size) + ")";
this.$element.find('input').eq(0).val(show_value);
this.set_filename(name);
},
+ set_filename: function(value_) {
+ var filename = this.node.attrs.filename;
+ if (this.view.fields[filename]) {
+ this.view.fields[filename].set({value: value_});
+ }
+ },
on_clear: function() {
this._super.apply(this, arguments);
this.$element.find('input').eq(0).val('');
}
});
-openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
+instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
template: 'FieldBinaryImage',
- start: function() {
- this._super.apply(this, arguments);
- this.$image = this.$element.find('img.oe-binary-image');
- },
- update_dom: function() {
+ set_value: function(value_) {
this._super.apply(this, arguments);
- this.$element.find('.oe-binary').toggle(!this.readonly);
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- this.set_image_maxwidth();
- var url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
- this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
- this.$image.attr('src', url);
- },
- set_image_maxwidth: function() {
- this.$image.css('max-width', this.$element.width());
+ this.render_value();
+ },
+ render_value: function() {
+ var url;
+ if (this.get('value') && this.get('value').substr(0, 10).indexOf(' ') == -1) {
+ url = 'data:image/png;base64,' + this.get('value');
+ } else if (this.get('value')) {
+ url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
+ this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
+ } else {
+ url = "/web/static/src/img/placeholder.png";
+ }
+ var img = QWeb.render("FieldBinaryImage-img", { widget: this, url: url });
+ this.$element.find('> img').remove();
+ this.$element.prepend(img);
},
on_file_change: function() {
- this.set_image_maxwidth();
+ this.render_value();
this._super.apply(this, arguments);
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
- this.value = file_base64;
+ this.set({'value': file_base64});
this.binary_value = true;
- this.$image.attr('src', 'data:' + (content_type || 'image/png') + ';base64,' + file_base64);
- this.set_filename(name);
+ this.render_value();
},
on_clear: function() {
this._super.apply(this, arguments);
- this.$image.attr('src', '/web/static/src/img/placeholder.png');
- this.set_filename('');
+ this.render_value();
}
});
-openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
- template: "EmptyComponent",
+instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
+ tagName: "span",
start: function() {
this._super();
this.selected_value = null;
+ // preview in start only for selection fields, because of the dynamic behavior of many2one fields.
+ if (this.field.type in ['selection']) {
+ this.render_list();
+ }
+ },
+ set_value: function(value_) {
+ var self = this;
+ this._super(value_);
+ // find selected value:
+ // - many2one: [2, "New"] -> 2
+ // - selection: new -> new
+ if (this.field.type == "many2one") {
+ this.selected_value = value_[0];
+ } else {
+ this.selected_value = value_;
+ }
+ // trick to be sure all values are loaded in the form, therefore
+ // enabling the evaluation of dynamic domains
+ $.async_when().then(function() {
+ return self.render_list();
+ });
+ },
- this.render_list();
+ /** Get the status list and render them
+ * to_show: [[identifier, value_to_display]] where
+ * - identifier = key for a selection, id for a many2one
+ * - display_val = label that will be displayed
+ * - ex: [[0, "New"]] (many2one) or [["new", "In Progress"]] (selection)
+ */
+ render_list: function() {
+ var self = this;
+ // get selection values, filter them and render them
+ var selection_done = this.get_selection().pipe(self.proxy('filter_selection')).pipe(self.proxy('render_elements'));
},
- set_value: function(value) {
- this._super(value);
- this.selected_value = value;
- this.render_list();
+ /** Get the selection list to be displayed in the statusbar widget.
+ * For selection fields: this is directly given by this.field.selection
+ * For many2one fields :
+ * - perform a search on the relation of the many2one field (given by
+ * field.relation )
+ * - get the field domain for the search
+ * - self.build_domain() gives the domain given by the view or by
+ * the field
+ * - if the optional statusbar_fold attribute is set to true, make
+ * an AND with build_domain to hide all 'fold=true' columns
+ * - make an OR with current value, to be sure it is displayed,
+ * with the correct order, even if it is folded
+ */
+ get_selection: function() {
+ var self = this;
+ if (this.field.type == "many2one") {
+ this.selection = [];
+ // get fold information from widget
+ var fold = ((this.node.attrs || {}).statusbar_fold || true);
+ // build final domain: if fold option required, add the
+ if (fold == true) {
+ var domain = new instance.web.CompoundDomain(['|'], ['&'], self.build_domain(), [['fold', '=', false]], [['id', '=', self.selected_value]]);
+ } else {
+ var domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]);
+ }
+ // get a DataSetSearch on the current field relation (ex: crm.lead.stage_id -> crm.case.stage)
+ var model_ext = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain);
+ // fetch selection
+ var read_defer = model_ext.read_slice(['name'], {}).pipe( function (records) {
+ _(records).each(function (record) {
+ self.selection.push([record.id, record.name]);
+ });
+ });
+ } else {
+ this.selection = this.field.selection;
+ var read_defer = new $.Deferred().resolve();
+ }
+ return read_defer;
},
- render_list: function() {
+
+ /** Filters this.selection, according to values coming from the statusbar_visible
+ * attribute of the field. For example: statusbar_visible="draft,open"
+ * Currently, the key of (key, label) pairs has to be used in the
+ * selection of visible items. This feature is not meant to be used
+ * with many2one fields.
+ */
+ filter_selection: function() {
var self = this;
var shown = _.map(((this.node.attrs || {}).statusbar_visible || "").split(","),
function(x) { return _.str.trim(x); });
shown = _.select(shown, function(x) { return x.length > 0; });
-
+
if (shown.length == 0) {
- this.to_show = this.field.selection;
+ this.to_show = this.selection;
} else {
- this.to_show = _.select(this.field.selection, function(x) {
+ this.to_show = _.select(this.selection, function(x) {
return _.indexOf(shown, x[0]) !== -1 || x[0] === self.selected_value;
});
}
+ },
- var content = openerp.web.qweb.render("FieldStatus.content", {widget: this, _:_});
+ /** Renders the widget. This function also checks for statusbar_colors='{"pending": "blue"}'
+ * attribute in the widget. This allows to set a given color to a given
+ * state (given by the key of (key, label)).
+ */
+ render_elements: function () {
+ var content = instance.web.qweb.render("FieldStatus.content", {widget: this, _:_});
this.$element.html(content);
var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}");
var color = colors[this.selected_value];
if (color) {
- var elem = this.$element.find("li.oe-arrow-list-selected span");
- elem.css("border-color", color);
- if (this.check_white(color))
- elem.css("color", "white");
- elem = this.$element.find("li.oe-arrow-list-selected .oe-arrow-list-before");
- elem.css("border-left-color", "transparent");
- elem = this.$element.find("li.oe-arrow-list-selected .oe-arrow-list-after");
- elem.css("border-color", "transparent");
- elem.css("border-left-color", color);
- }
- },
- check_white: function(color) {
- var div = $("<div></div>");
- div.css("display", "none");
- div.css("color", color);
- div.appendTo($("body"));
- var ncolor = div.css("color");
- div.remove();
- var res = /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/.exec(ncolor);
- if (!res) {
- return false;
- }
- var comps = [parseInt(res[1]), parseInt(res[2]), parseInt(res[3])];
- var lum = comps[0] * 0.3 + comps[1] * 0.59 + comps[1] * 0.11;
- if (lum < 128) {
- return true;
+ var elem = this.$element.find("li.oe_form_steps_active span");
+ elem.css("color", color);
}
- return false;
- }
-});
-
-openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
- render: function () {
- var $root = $('<div class="oe_form_html_view">');
- this.render_children(this, $root);
- return $root.html();
},
- render_children: function (object, $into) {
- var self = this,
- fields = this.view.fields_view.fields;
- _(object.children).each(function (child) {
- if (typeof child === 'string') {
- $into.text(child);
- } else if (child.tag === 'field') {
- $into.append(
- new (self.view.registry.get_object('frame'))(
- self.view, {tag: 'ueule', attrs: {}, children: [child] })
- .render());
- } else {
- var $child = $(document.createElement(child.tag))
- .attr(child.attrs)
- .appendTo($into);
- self.render_children(child, $child);
- }
- });
- }
});
-
/**
- * Registry of form widgets, called by :js:`openerp.web.FormView`
+ * 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.
*/
-openerp.web.form.widgets = new openerp.web.Registry({
- 'frame' : 'openerp.web.form.WidgetFrame',
- 'group' : 'openerp.web.form.WidgetGroup',
- 'notebook' : 'openerp.web.form.WidgetNotebook',
- 'notebookpage' : 'openerp.web.form.WidgetNotebookPage',
- 'separator' : 'openerp.web.form.WidgetSeparator',
- 'label' : 'openerp.web.form.WidgetLabel',
- 'button' : 'openerp.web.form.WidgetButton',
- 'char' : 'openerp.web.form.FieldChar',
- 'id' : 'openerp.web.form.FieldID',
- 'email' : 'openerp.web.form.FieldEmail',
- 'url' : 'openerp.web.form.FieldUrl',
- 'text' : 'openerp.web.form.FieldText',
- 'date' : 'openerp.web.form.FieldDate',
- 'datetime' : 'openerp.web.form.FieldDatetime',
- 'selection' : 'openerp.web.form.FieldSelection',
- 'many2one' : 'openerp.web.form.FieldMany2One',
- 'many2many' : 'openerp.web.form.FieldMany2Many',
- 'one2many' : 'openerp.web.form.FieldOne2Many',
- 'one2many_list' : 'openerp.web.form.FieldOne2Many',
- 'reference' : 'openerp.web.form.FieldReference',
- 'boolean' : 'openerp.web.form.FieldBoolean',
- 'float' : 'openerp.web.form.FieldFloat',
- 'integer': 'openerp.web.form.FieldFloat',
- 'float_time': 'openerp.web.form.FieldFloat',
- 'progressbar': 'openerp.web.form.FieldProgressBar',
- 'image': 'openerp.web.form.FieldBinaryImage',
- 'binary': 'openerp.web.form.FieldBinaryFile',
- 'statusbar': 'openerp.web.form.FieldStatus',
- 'html': 'openerp.web.form.WidgetHtml'
+instance.web.form.widgets = new instance.web.Registry({
+ 'char' : 'instance.web.form.FieldChar',
+ 'id' : 'instance.web.form.FieldID',
+ 'email' : 'instance.web.form.FieldEmail',
+ 'url' : 'instance.web.form.FieldUrl',
+ 'text' : 'instance.web.form.FieldText',
+ 'date' : 'instance.web.form.FieldDate',
+ 'datetime' : 'instance.web.form.FieldDatetime',
+ 'selection' : 'instance.web.form.FieldSelection',
+ 'many2one' : 'instance.web.form.FieldMany2One',
+ 'many2many' : 'instance.web.form.FieldMany2Many',
+ 'many2many_tags' : 'instance.web.form.FieldMany2ManyTags',
+ 'many2many_kanban' : 'instance.web.form.FieldMany2ManyKanban',
+ 'one2many' : 'instance.web.form.FieldOne2Many',
+ 'one2many_list' : 'instance.web.form.FieldOne2Many',
+ 'reference' : 'instance.web.form.FieldReference',
+ 'boolean' : 'instance.web.form.FieldBoolean',
+ 'float' : 'instance.web.form.FieldFloat',
+ 'integer': 'instance.web.form.FieldFloat',
+ 'float_time': 'instance.web.form.FieldFloat',
+ 'progressbar': 'instance.web.form.FieldProgressBar',
+ 'image': 'instance.web.form.FieldBinaryImage',
+ 'binary': 'instance.web.form.FieldBinaryFile',
+ 'statusbar': 'instance.web.form.FieldStatus'
});
+/**
+ * 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.
+ */
+instance.web.form.tags = new instance.web.Registry({
+ 'button' : 'instance.web.form.WidgetButton',
+});
};