diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js
index 1cf78618817..6782de9ed56 100644
--- a/addons/base/static/src/js/chrome.js
+++ b/addons/base/static/src/js/chrome.js
@@ -2,7 +2,7 @@
* OpenERP base library
*---------------------------------------------------------*/
-openerp.base$chrome = function(openerp) {
+openerp.addons.base.chrome = function(openerp) {
openerp.base.callback = function(obj, method) {
// openerp.base.callback( obj, methods, [arg1, arg2, ... ] )
diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js
new file mode 100644
index 00000000000..d25bead7b7a
--- /dev/null
+++ b/addons/base/static/src/js/data.js
@@ -0,0 +1,277 @@
+
+openerp.addons.base.data = function(openerp) {
+/**
+ * Management interface between views and the collection of selected OpenERP
+ * records (represents the view's state?)
+ */
+openerp.base.DataGroup = openerp.base.Controller.extend({
+ init: function(session) {
+ this._super(session, null);
+ },
+});
+
+/**
+ * Management interface between views and the collection of selected OpenERP
+ * records (represents the view's state?)
+ */
+openerp.base.DataSet = openerp.base.Controller.extend({
+ init: function(session, model) {
+ this._super(session);
+ this.model = model;
+
+ this._fields = null;
+
+ this._ids = [];
+ this._active_ids = null;
+ this._active_id_index = 0;
+
+ this._sort = [];
+ this._domain = [];
+ this._context = {};
+ },
+ start: function() {
+ // TODO: fields_view_get fields selection?
+ this.rpc("/base/dataset/fields", {"model":this.model}, this.on_fields);
+ },
+ on_fields: function(result) {
+ this._fields = result._fields;
+ this.on_ready();
+ },
+
+ /**
+ * Fetch all the records selected by this DataSet, based on its domain
+ * and context.
+ *
+ * Fires the on_ids event.
+ *
+ * @param {Number} [offset=0] The index from which selected records should be returned
+ * @param {Number} [limit=null] The maximum number of records to return
+ * @returns itself
+ */
+ // ADD CALLBACK
+ fetch: function (offset, limit) {
+ offset = offset || 0;
+ limit = limit || null;
+ this.rpc('/base/dataset/find', {
+ model: this.model,
+ fields: this._fields,
+ domain: this._domain,
+ context: this._context,
+ sort: this._sort,
+ offset: offset,
+ limit: limit
+ }, _.bind(function (records) {
+ var data_records = _.map(
+ records, function (record) {
+ return new openerp.base.DataRecord(
+ this.session, this.model,
+ this._fields, record);
+ }, this);
+
+ this.on_fetch(data_records, {
+ offset: offset,
+ limit: limit,
+ domain: this._domain,
+ context: this._context,
+ sort: this._sort
+ });
+ }, this));
+ return this;
+ },
+
+
+ /** REMOVE
+ * @event
+ *
+ * Fires after the DataSet fetched the records matching its internal ids selection
+ *
+ * @param {Array} records An array of the DataRecord fetched
+ * @param event The on_fetch event object
+ * @param {Number} event.offset the offset with which the original DataSet#fetch call was performed
+ * @param {Number} event.limit the limit set on the original DataSet#fetch call
+ * @param {Array} event.domain the domain set on the DataSet before DataSet#fetch was called
+ * @param {Object} event.context the context set on the DataSet before DataSet#fetch was called
+ * @param {Array} event.sort the sorting criteria used to get the ids
+ */
+ on_fetch: function (records, event) { },
+
+ /**
+ * Fetch all the currently active records for this DataSet (records selected via DataSet#select)
+ *
+ * @returns itself
+ */
+ // ADD fields and callback
+ active_ids: function () {
+ this.rpc('/base/dataset/get', {
+ ids: this.get_active_ids(),
+ model: this.model
+ }, _.bind(function (records) {
+ this.on_active_ids(_.map(
+ records, function (record) {
+ return new openerp.base.DataRecord(
+ this.session, this.model,
+ this._fields, record);
+ }, this));
+ }, this));
+ return this;
+ },
+
+
+ /** REMIVE
+ * @event
+ *
+ * Fires after the DataSet fetched the records matching its internal active ids selection
+ *
+ * @param {Array} records An array of the DataRecord fetched
+ */
+ on_active_ids: function (records) { },
+
+ /**
+ * Fetches the current active record for this DataSet
+ *
+ * @returns itself
+ */
+ // ADD fields and callback
+ active_id: function () {
+ this.rpc('/base/dataset/get', {
+ ids: [this.get_active_id()],
+ model: this.model
+ }, _.bind(function (records) {
+ var record = records[0];
+ this.on_active_id(
+ record && new openerp.base.DataRecord(
+ this.session, this.model,
+ this._fields, record));
+ }, this));
+ return this;
+ },
+
+
+ /** REMOVE
+ * Fires after the DataSet fetched the record matching the current active record
+ *
+ * @param record the record matching the provided id, or null if there is no record for this id
+ */
+ on_active_id: function (record) {
+
+ },
+
+ /**
+ * Configures the DataSet
+ *
+ * @param options DataSet options
+ * @param {Array} options.domain the domain to assign to this DataSet for filtering
+ * @param {Object} options.context the context this DataSet should use during its calls
+ * @param {Array} options.sort the sorting criteria for this DataSet
+ * @returns itself
+ */
+ set: function (options) {
+ if (options.domain) {
+ this._domain = _.clone(options.domain);
+ }
+ if (options.context) {
+ this._context = _.clone(options.context);
+ }
+ if (options.sort) {
+ this._sort = _.clone(options.sort);
+ }
+ return this;
+ },
+
+ /**
+ * Activates the previous id in the active sequence. If there is no previous id, wraps around to the last one
+ * @returns itself
+ */
+ prev: function () {
+ this._active_id_index -= 1;
+ if (this._active_id_index < 0) {
+ this._active_id_index = this._active_ids.length - 1;
+ }
+ return this;
+ },
+ /**
+ * Activates the next id in the active sequence. If there is no next id, wraps around to the first one
+ * @returns itself
+ */
+ next: function () {
+ this._active_id_index += 1;
+ if (this._active_id_index >= this._active_ids.length) {
+ this._active_id_index = 0;
+ }
+ return this;
+ },
+
+ /**
+ * Sets active_ids by value:
+ *
+ * * Activates all ids part of the current selection
+ * * Sets active_id to be the first id of the selection
+ *
+ * @param {Array} ids the list of ids to activate
+ * @returns itself
+ */
+ select: function (ids) {
+ this._active_ids = ids;
+ this._active_id_index = 0;
+ return this;
+ },
+ /**
+ * Fetches the ids of the currently selected records, if any.
+ */
+ get_active_ids: function () {
+ return this._active_ids;
+ },
+ /**
+ * Sets the current active_id by value
+ *
+ * If there are no active_ids selected, selects the provided id as the sole active_id
+ *
+ * If there are ids selected and the provided id is not in them, raise an error
+ *
+ * @param {Object} id the id to activate
+ * @returns itself
+ */
+ activate: function (id) {
+ if(!this._active_ids) {
+ this._active_ids = [id];
+ this._active_id_index = 0;
+ } else {
+ var index = _.indexOf(this._active_ids, id);
+ if (index == -1) {
+ throw new Error(
+ "Could not find id " + id +
+ " in array [" + this._active_ids.join(', ') + "]");
+ }
+ this._active_id_index = index;
+ }
+ return this;
+ },
+ /**
+ * Fetches the id of the current active record, if any.
+ *
+ * @returns record? record id or null
+ */
+ get_active_id: function () {
+ if (!this._active_ids) {
+ return null;
+ }
+ return this._active_ids[this._active_id_index];
+ }
+});
+
+openerp.base.DataRecord = openerp.base.Controller.extend({
+ init: function(session, model, fields, values) {
+ this._super(session, null);
+ this.model = model;
+ this.id = values.id || null;
+ this.fields = fields;
+ this.values = values;
+ },
+ on_change: function() {
+ },
+ on_reload: function() {
+ }
+});
+};
+
+// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js
new file mode 100644
index 00000000000..7e4ff3dde02
--- /dev/null
+++ b/addons/base/static/src/js/form.js
@@ -0,0 +1,539 @@
+
+openerp.addons.base.form = function (openerp) {
+
+openerp.base.FormView = openerp.base.Controller.extend({
+ init: function(session, element_id, dataset, view_id) {
+ this._super(session, element_id);
+ this.dataset = dataset;
+ this.model = dataset.model;
+ this.view_id = view_id;
+ this.fields_views = {};
+ this.widgets = {};
+ this.widgets_counter = 0;
+ this.fields = {};
+ this.datarecord = {};
+ this.ready = false;
+ },
+ start: function() {
+ //this.log('Starting FormView '+this.model+this.view_id)
+ this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
+ },
+ on_loaded: function(data) {
+ var self = this;
+ this.fields_view = data.fields_view;
+ //this.log(this.fields_view);
+
+ var frame = new openerp.base.WidgetFrame(this, this.fields_view.arch);
+
+ this.$element.html(QWeb.render("FormView", { "frame": frame, "view": this }));
+ _.each(this.widgets, function(w) {
+ w.start();
+ });
+ this.$element.find('button.form_save').click(this.do_save);
+
+// this.dataset.on_active_id.add(this.on_record_loaded);
+// this.dataset.active_id(fields of the form, this.on_record_loaded);
+ },
+ on_next: function() {
+// this.dataset.next();
+// this.dataset.active_id(fields of the form, this.on_record_loaded);
+ },
+ on_prev: function() {
+
+// this.dataset.prev();
+// this.dataset.active_id(fields of the form, this.on_record_loaded);
+ },
+ on_record_loaded: function(record) {
+ this.datarecord = record;
+ for (var f in this.fields) {
+ this.fields[f].set_value(this.datarecord.values[f]);
+ }
+ this.on_form_changed();
+ this.ready = true;
+ },
+ on_form_changed: function(widget) {
+ for (var w in this.widgets) {
+ w = this.widgets[w];
+ w.process_attrs();
+ w.update_dom();
+ }
+ if (widget) {
+ // TODO: check if and trigger
+ // if (onchange for this field) {
+ // this.ready = false;
+ // rpc - process.callback ( this.ready = true; )
+ // }
+ }
+ },
+ do_save: function() {
+ if (!this.ready) {
+ return false;
+ }
+ var invalid = false;
+ var values = {};
+ for (var f in this.fields) {
+ f = this.fields[f];
+ if (f.invalid) {
+ invalid = true;
+ } else {
+ values[f.name] = f.value;
+ }
+ }
+ if (invalid) {
+ this.on_invalid();
+ } else {
+ console.log("Save form", values);
+ // TODO: save values via datarecord
+ // rpc - save.callbacl on_saved
+ }
+ },
+ on_invalid: function() {
+ },
+ on_saved: function() {
+ // Check response for exceptions, display error
+ }
+});
+
+openerp.base.Widget = openerp.base.Controller.extend({
+ // TODO Change this to init: function(view, node) { and use view.session and a new element_id for the super
+ // it means that widgets are special controllers
+ init: function(view, node) {
+ this.view = view;
+ this.node = node;
+ this.attrs = eval('(' + (this.node.attrs.attrs || '{}') + ')');
+ this.type = this.type || node.tag;
+ this.element_name = this.element_name || this.type;
+ this.element_id = [this.view.element_id, this.element_name, this.view.widgets_counter++].join("_");
+
+ this._super(this.view.session, this.element_id);
+
+ this.view.widgets[this.element_id] = this;
+ this.children = node.children;
+ this.colspan = parseInt(node.attrs.colspan || 1);
+ this.template = "Widget";
+
+ this.string = this.string || node.attrs.string;
+ this.help = this.help || node.attrs.help;
+ this.invisible = (node.attrs.invisible == '1');
+ },
+ start: function() {
+ this.$element = $('#' + this.element_id);
+ },
+ process_attrs: function() {
+ for (var a in this.attrs) {
+ this[a] = this.eval_attrs(this.attrs[a]);
+ }
+ },
+ eval_attrs: function(expr) {
+ var stack = [];
+ for (var i = 0; i < expr.length; i++) {
+ var ex = expr[i];
+ if (ex.length == 1) {
+ stack.push(ex[0]);
+ continue;
+ }
+
+ var field = this.view.fields[ex[0]].value;
+ var op = ex[1];
+ var val = ex[2];
+
+ switch (op.toLowerCase()) {
+ case '=':
+ case '==':
+ stack.push(field == val);
+ break;
+ case '!=':
+ case '<>':
+ stack.push(field != val);
+ break;
+ case '<':
+ stack.push(field < val);
+ break;
+ case '>':
+ stack.push(field > val);
+ break;
+ case '<=':
+ stack.push(field <= val);
+ break;
+ case '>=':
+ stack.push(field >= val);
+ break;
+ case 'in':
+ stack.push(_.indexOf(val, field) > -1);
+ break;
+ case 'not in':
+ stack.push(_.indexOf(val, field) == -1);
+ break;
+ default:
+ this.log("Unsupported operator in attrs :", op);
+ }
+ }
+
+ for (var j = stack.length-1; j >- 1; j--) {
+ switch (stack[j]) {
+ case '|':
+ var result = stack[j + 1] || stack[j + 2];
+ stack.splice(j, 3, result);
+ break;
+ case '&':
+ var result = stack[j + 1] && stack[j + 2];
+ stack.splice(j, 3, result);
+ break;
+ }
+ }
+ return _.indexOf(stack, false) == -1;
+ },
+ update_dom: function() {
+ this.$element.toggle(!this.invisible);
+ },
+ render: function() {
+ var template = this.template;
+ return QWeb.render(template, { "widget": this });
+ }
+});
+
+openerp.base.WidgetFrame = openerp.base.Widget.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "WidgetFrame";
+ this.columns = node.attrs.col || 4;
+ 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) {
+ for (var i = 0; i < row.length; i++) {
+ var w = row[i];
+ if (w.is_field_label) {
+ w.width = "1%";
+ if (row[i + 1]) {
+ row[i + 1].width = Math.round((100 / this.columns) * (w.colspan + 1) - 1) + '%';
+ }
+ } else if (w.width === undefined) {
+ w.width = Math.round((100 / this.columns) * w.colspan) + '%';
+ }
+ }
+ },
+ handle_node: function(n) {
+ var type = this.view.fields_view.fields[n.attrs.name] || {};
+ var widget_type = n.attrs.widget || type.type || n.tag;
+ if (openerp.base.widgets[widget_type]) {
+ var widget = new openerp.base.widgets[widget_type](this.view, n);
+ if (n.tag == 'field' && n.attrs.nolabel != '1') {
+ var label = new openerp.base.widgets['label'](this.view, n);
+ label["for"] = widget;
+ this.add_widget(label);
+ }
+ this.add_widget(widget);
+ } else {
+ this.log("Unhandled widget type : " + widget_type, n);
+ }
+ },
+ add_widget: function(w) {
+ if (!w.invisible) {
+ var current_row = this.table[this.table.length - 1];
+ if (current_row.length && (this.x + w.colspan) > this.columns) {
+ current_row = this.add_row();
+ }
+ current_row.push(w);
+ this.x += w.colspan;
+ }
+ return w;
+ }
+});
+
+openerp.base.WidgetNotebook = openerp.base.Widget.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "WidgetNotebook";
+ this.pages = [];
+ for (var i = 0; i < node.children.length; i++) {
+ var n = node.children[i];
+ if (n.tag == "page") {
+ var page = new openerp.base.WidgetFrame(this.view, n);
+ this.pages.push(page);
+ }
+ }
+ },
+ start: function() {
+ this._super.apply(this, arguments);
+ this.$element.tabs();
+ }
+});
+
+openerp.base.WidgetSeparator = openerp.base.Widget.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "WidgetSeparator";
+ }
+});
+
+openerp.base.WidgetButton = openerp.base.Widget.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "WidgetButton";
+ }
+});
+
+openerp.base.WidgetLabel = openerp.base.Widget.extend({
+ init: function(view, node) {
+ this.is_field_label = true;
+ this.element_name = 'label_' + node.attrs.name;
+
+ this._super(view, node);
+
+ this.template = "WidgetLabel";
+ this.colspan = 1;
+ },
+ 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});
+ }
+});
+
+openerp.base.Field = openerp.base.Widget.extend({
+ init: function(view, node) {
+ this.name = node.attrs.name;
+ this.value = undefined;
+ view.fields[this.name] = this;
+ 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.datarecord = this.view.datarecord ??
+ 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 = (node.attrs.nolabel == '1');
+ this.readonly = (node.attrs.readonly == '1');
+ this.required = (node.attrs.required == '1');
+ this.invalid = false;
+ },
+ set_value: function(value) {
+ this.value = value;
+ },
+ get_value: function(value) {
+ return value;
+ },
+ update_dom: function() {
+ this._super.apply(this, arguments);
+ this.$element.toggleClass('disabled', this.readonly);
+ this.$element.toggleClass('required', this.required);
+ },
+ on_ui_change: function() {
+ this.view.on_form_changed(this);
+ }
+});
+
+openerp.base.FieldChar = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldChar";
+ },
+ 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);
+ if (value != null && value !== false) {
+ this.$element.find('input').val(value);
+ }
+ },
+ get_value: function() {
+ },
+ update_dom: function() {
+ this._super.apply(this, arguments);
+ this.$element.find('input').attr({
+ 'disabled' : this.readonly,
+ 'required' : this.required
+ });
+ },
+ on_ui_change: function() {
+ this.value = this.$element.find('input').val();
+ this.invalid = this.required && this.value == "";
+ this._super.apply(this, arguments);
+ }
+});
+
+openerp.base.FieldEmail = openerp.base.FieldChar.extend({
+});
+
+openerp.base.FieldUrl = openerp.base.FieldChar.extend({
+});
+
+openerp.base.FieldFloat = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldChar";
+ },
+ 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);
+ if (value != null && value !== false) {
+ this.$element.find('input').val(value.toFixed(2));
+ }
+ },
+ get_value: function() {
+ },
+ update_dom: function() {
+ this._super.apply(this, arguments);
+ this.$element.find('input').attr({
+ 'disabled' : this.readonly,
+ 'required' : this.required
+ });
+ },
+ on_ui_change: function() {
+ this.value = this.$element.find('input').val();
+ this.invalid = this.required && this.value == "";
+ this._super.apply(this, arguments);
+ }
+});
+
+openerp.base.FieldText = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldText";
+ },
+ set_value: function(value) {
+ this._super.apply(this, arguments);
+ if (value != null && value !== false) {
+ this.$element.find('textarea').val(value);
+ }
+ },
+ get_value: function() {
+ return this.$element.find('textarea').val();
+ }
+});
+
+openerp.base.FieldBoolean = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldBoolean";
+ },
+ set_value: function(value) {
+ this._super.apply(this, arguments);
+ this.$element.find('input')[0].checked = value;
+ },
+ get_value: function() {
+ this.$element.find('input')[0].checked;
+ }
+});
+
+openerp.base.FieldDate = openerp.base.FieldChar.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldDate";
+ }
+});
+
+openerp.base.FieldDatetime = openerp.base.FieldChar.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldDatetime";
+ }
+});
+
+openerp.base.FieldTextXml = openerp.base.Field.extend({
+// to replace view editor
+});
+
+openerp.base.FieldSelection = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldSelection";
+ },
+ set_value: function(value) {
+ this._super.apply(this, arguments);
+ if (value != null && value !== false) {
+ this.$element.find('select').val(value);
+ }
+ },
+ get_value: function() {
+ return this.$element.find('select').val();
+ }
+});
+
+openerp.base.FieldMany2One = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldMany2One";
+ }
+});
+
+openerp.base.FieldOne2Many = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldOne2Many";
+ }
+});
+
+openerp.base.FieldMany2Many = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldMany2Many";
+ }
+});
+
+openerp.base.FieldReference = openerp.base.Field.extend({
+ init: function(view, node) {
+ this._super(view, node);
+ this.template = "FieldReference";
+ }
+});
+
+openerp.base.widgets = {
+ 'group' : openerp.base.WidgetFrame,
+ 'notebook' : openerp.base.WidgetNotebook,
+ 'separator' : openerp.base.WidgetSeparator,
+ 'label' : openerp.base.WidgetLabel,
+ 'char' : openerp.base.FieldChar,
+ 'email' : openerp.base.FieldEmail,
+ 'url' : openerp.base.FieldUrl,
+ 'text' : openerp.base.FieldText,
+ 'date' : openerp.base.FieldDate,
+ 'datetime' : openerp.base.FieldDatetime,
+ 'selection' : openerp.base.FieldSelection,
+ 'many2one' : openerp.base.FieldMany2One,
+ 'many2many' : openerp.base.FieldMany2Many,
+ 'one2many' : openerp.base.FieldOne2Many,
+ 'one2many_list' : openerp.base.FieldOne2Many,
+ 'reference' : openerp.base.FieldReference,
+ 'boolean' : openerp.base.FieldBoolean,
+ 'float' : openerp.base.FieldFloat,
+ 'button' : openerp.base.WidgetButton
+};
+
+};
+
+// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js
new file mode 100644
index 00000000000..79e9d171550
--- /dev/null
+++ b/addons/base/static/src/js/list.js
@@ -0,0 +1,83 @@
+
+openerp.addons.base.form = function (openerp) {
+
+openerp.base.ListView = openerp.base.Controller.extend({
+ init: function(session, element_id, dataset, view_id) {
+ this._super(session, element_id);
+ this.dataset = dataset;
+ this.model = dataset.model;
+ this.view_id = view_id;
+ this.name = "";
+
+ this.cols = [];
+
+ this.$table = null;
+ this.colnames = [];
+ this.colmodel = [];
+
+ this.event_loading = false; // TODO in the future prevent abusive click by masking
+ },
+ start: function() {
+ //this.log('Starting ListView '+this.model+this.view_id)
+ this.rpc("/base/listview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded);
+ },
+ on_loaded: function(data) {
+ this.fields_view = data.fields_view;
+ //this.log(this.fields_view);
+ this.name = "" + this.fields_view.arch.attrs.string;
+ this.$element.html(QWeb.render("ListView", {"fields_view": this.fields_view}));
+ this.$table = this.$element.find("table");
+ this.cols = [];
+ this.colnames = [];
+ this.colmodel = [];
+ // TODO uss a object for each col, fill it with view and fallback to dataset.model_field
+ var tree = this.fields_view.arch.children;
+ for(var i = 0; i < tree.length; i++) {
+ var col = tree[i];
+ if(col.tag == "field") {
+ this.cols.push(col.attrs.name);
+ this.colnames.push(col.attrs.name);
+ this.colmodel.push({ name: col.attrs.name, index: col.attrs.name });
+ }
+ }
+ this.dataset.fields = this.cols;
+ this.dataset.on_fetch.add(this.do_fill_table);
+
+ var width = this.$element.width();
+ this.$table.jqGrid({
+ datatype: "local",
+ height: "100%",
+ rowNum: 100,
+ //rowList: [10,20,30],
+ colNames: this.colnames,
+ colModel: this.colmodel,
+ //pager: "#plist47",
+ viewrecords: true,
+ caption: this.name
+ }).setGridWidth(width);
+
+ var self = this;
+ $(window).bind('resize', function() {
+ self.$element.children().hide();
+ self.$table.setGridWidth(self.$element.width());
+ self.$element.children().show();
+ }).trigger('resize');
+ },
+ do_fill_table: function(records) {
+ this.log("do_fill_table");
+
+ this.$table
+ .clearGridData()
+ .addRowData('id', _.map(records, function (record) {
+ return record.values;
+ }));
+
+ }
+});
+
+openerp.base.TreeView = openerp.base.Controller.extend({
+});
+
+};
+
+// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js
index 4b5f69a967a..238df7cb9ea 100644
--- a/addons/base/static/src/js/search.js
+++ b/addons/base/static/src/js/search.js
@@ -1,500 +1,4 @@
-/*---------------------------------------------------------
- * OpenERP base library
- *---------------------------------------------------------*/
-
-openerp.base$views = function(openerp) {
-
-// process all kind of actions
-openerp.base.ActionManager = openerp.base.Controller.extend({
- init: function(session, element_id) {
- this._super(session, element_id);
- this.action = null;
- this.viewmanager = null;
- },
- /**
- * Process an action
- * Supported actions: act_window
- */
- do_action: function(action) {
- // instantiate the right controllers by understanding the action
- this.action = action;
- if(action.type == "ir.actions.act_window") {
- this.viewmanager = new openerp.base.ViewManager(this.session,this.element_id);
- this.viewmanager.do_action_window(action);
- this.viewmanager.start();
- }
- }
-});
-
-// This will be ViewManager Abstract/Common
-openerp.base.ViewManager = openerp.base.Controller.extend({
- init: function(session, element_id) {
- this._super(session, element_id);
- this.action = null;
- this.dataset = null;
- this.searchview_id = false;
- this.searchview = null;
- this.search_visible = true;
- // this.views = { "list": { "view_id":1234, "controller": instance} }
- this.views = {};
- },
- start: function() {
- },
- on_mode_switch: function(view_type) {
- for (var i in this.views) {
- this.views[i].controller.$element.toggle(i === view_type);
- }
- },
- /**
- * Extract search view defaults from the current action's context.
- *
- * These defaults are of the form {search_default_*: value}
- *
- * @returns {Object} a clean defaults mapping of {field_name: value}
- */
- search_defaults: function () {
- var defaults = {};
- _.each(this.action.context, function (value, key) {
- var match = /^search_default_(.*)$/.exec(key);
- if (match) {
- defaults[match[1]] = value;
- }
- });
- return defaults;
- },
- do_action_window: function(action) {
- var self = this;
- var prefix_id = "#" + this.element_id;
- this.action = action;
- this.dataset = new openerp.base.DataSet(this.session, action.res_model);
- this.dataset.start();
-
- this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: action.views}));
-
- this.searchview_id = false;
- if(this.search_visible && action.search_view_id) {
- this.searchview_id = action.search_view_id[0];
- var searchview = this.searchview = new openerp.base.SearchView(
- this.session, this.element_id + "_search",
- this.dataset, this.searchview_id,
- this.search_defaults());
- searchview.on_search.add(this.do_search);
- searchview.start();
-
- if (action['auto_search']) {
- searchview.on_loaded.add_last(
- searchview.do_search);
- }
- }
- for(var i = 0; i < action.views.length; i++) {
- var view_id, controller;
- view_id = action.views[i][0];
- if(action.views[i][1] == "tree") {
- controller = new openerp.base.ListView(this.session, this.element_id + "_view_tree", this.dataset, view_id);
- controller.start();
- this.views.tree = { view_id: view_id, controller: controller };
- this.$element.find(prefix_id + "_button_tree").bind('click',function(){
- self.on_mode_switch("tree");
- });
- } else if(action.views[i][1] == "form") {
- controller = new openerp.base.FormView(this.session, this.element_id + "_view_form", this.dataset, view_id);
- controller.start();
- this.views.form = { view_id: view_id, controller: controller };
- this.$element.find(prefix_id + "_button_form").bind('click',function(){
- self.on_mode_switch("form");
- });
- }
- }
- // switch to the first one in sequence
- this.on_mode_switch("tree");
- },
- // create when root, also add to parent when o2m
- on_create: function() {
- },
- on_remove: function() {
- },
- on_edit: function() {
- },
- do_search: function (domains, contexts, groupbys) {
- var self = this;
- this.rpc('/base/session/eval_domain_and_context', {
- domains: domains,
- contexts: contexts,
- group_by_seq: groupbys
- }, function (results) {
- // TODO: handle non-empty results.group_by with read_group
- self.dataset.set({
- context: results.context,
- domain: results.domain
- }).fetch(0, self.action.limit);
- });
- }
-});
-
-// Extends view manager
-openerp.base.ViewManagerRoot = openerp.base.Controller.extend({
-});
-
-// Extends view manager
-openerp.base.ViewManagerUsedAsAMany2One = openerp.base.Controller.extend({
-});
-
-/**
- * Management interface between views and the collection of selected OpenERP
- * records (represents the view's state?)
- */
-openerp.base.DataGroup = openerp.base.Controller.extend({
- init: function(session) {
- this._super(session, null);
- },
-});
-
-/**
- * Management interface between views and the collection of selected OpenERP
- * records (represents the view's state?)
- */
-openerp.base.DataSet = openerp.base.Controller.extend({
- init: function(session, model) {
- this._super(session);
- this.model = model;
-
- this._fields = null;
-
- this._ids = [];
- this._active_ids = null;
- this._active_id_index = 0;
-
- this._sort = [];
- this._domain = [];
- this._context = {};
- },
- start: function() {
- // TODO: fields_view_get fields selection?
- this.rpc("/base/dataset/fields", {"model":this.model}, this.on_fields);
- },
- on_fields: function(result) {
- this._fields = result._fields;
- this.on_ready();
- },
-
- /**
- * Fetch all the records selected by this DataSet, based on its domain
- * and context.
- *
- * Fires the on_ids event.
- *
- * @param {Number} [offset=0] The index from which selected records should be returned
- * @param {Number} [limit=null] The maximum number of records to return
- * @returns itself
- */
- fetch: function (offset, limit) {
- offset = offset || 0;
- limit = limit || null;
- this.rpc('/base/dataset/find', {
- model: this.model,
- fields: this._fields,
- domain: this._domain,
- context: this._context,
- sort: this._sort,
- offset: offset,
- limit: limit
- }, _.bind(function (records) {
- var data_records = _.map(
- records, function (record) {
- return new openerp.base.DataRecord(
- this.session, this.model,
- this._fields, record);
- }, this);
-
- this.on_fetch(data_records, {
- offset: offset,
- limit: limit,
- domain: this._domain,
- context: this._context,
- sort: this._sort
- });
- }, this));
- return this;
- },
- /**
- * @event
- *
- * Fires after the DataSet fetched the records matching its internal ids selection
- *
- * @param {Array} records An array of the DataRecord fetched
- * @param event The on_fetch event object
- * @param {Number} event.offset the offset with which the original DataSet#fetch call was performed
- * @param {Number} event.limit the limit set on the original DataSet#fetch call
- * @param {Array} event.domain the domain set on the DataSet before DataSet#fetch was called
- * @param {Object} event.context the context set on the DataSet before DataSet#fetch was called
- * @param {Array} event.sort the sorting criteria used to get the ids
- */
- on_fetch: function (records, event) { },
-
- /**
- * Fetch all the currently active records for this DataSet (records selected via DataSet#select)
- *
- * @returns itself
- */
- active_ids: function () {
- this.rpc('/base/dataset/get', {
- ids: this.get_active_ids(),
- model: this.model
- }, _.bind(function (records) {
- this.on_active_ids(_.map(
- records, function (record) {
- return new openerp.base.DataRecord(
- this.session, this.model,
- this._fields, record);
- }, this));
- }, this));
- return this;
- },
- /**
- * @event
- *
- * Fires after the DataSet fetched the records matching its internal active ids selection
- *
- * @param {Array} records An array of the DataRecord fetched
- */
- on_active_ids: function (records) { },
-
- /**
- * Fetches the current active record for this DataSet
- *
- * @returns itself
- */
- active_id: function () {
- this.rpc('/base/dataset/get', {
- ids: [this.get_active_id()],
- model: this.model
- }, _.bind(function (records) {
- var record = records[0];
- this.on_active_id(
- record && new openerp.base.DataRecord(
- this.session, this.model,
- this._fields, record));
- }, this));
- return this;
- },
- /**
- * Fires after the DataSet fetched the record matching the current active record
- *
- * @param record the record matching the provided id, or null if there is no record for this id
- */
- on_active_id: function (record) {
-
- },
-
- /**
- * Configures the DataSet
- *
- * @param options DataSet options
- * @param {Array} options.domain the domain to assign to this DataSet for filtering
- * @param {Object} options.context the context this DataSet should use during its calls
- * @param {Array} options.sort the sorting criteria for this DataSet
- * @returns itself
- */
- set: function (options) {
- if (options.domain) {
- this._domain = _.clone(options.domain);
- }
- if (options.context) {
- this._context = _.clone(options.context);
- }
- if (options.sort) {
- this._sort = _.clone(options.sort);
- }
- return this;
- },
-
- /**
- * Activates the previous id in the active sequence. If there is no previous id, wraps around to the last one
- * @returns itself
- */
- prev: function () {
- this._active_id_index -= 1;
- if (this._active_id_index < 0) {
- this._active_id_index = this._active_ids.length - 1;
- }
- return this;
- },
- /**
- * Activates the next id in the active sequence. If there is no next id, wraps around to the first one
- * @returns itself
- */
- next: function () {
- this._active_id_index += 1;
- if (this._active_id_index >= this._active_ids.length) {
- this._active_id_index = 0;
- }
- return this;
- },
-
- /**
- * Sets active_ids by value:
- *
- * * Activates all ids part of the current selection
- * * Sets active_id to be the first id of the selection
- *
- * @param {Array} ids the list of ids to activate
- * @returns itself
- */
- select: function (ids) {
- this._active_ids = ids;
- this._active_id_index = 0;
- return this;
- },
- /**
- * Fetches the ids of the currently selected records, if any.
- */
- get_active_ids: function () {
- return this._active_ids;
- },
- /**
- * Sets the current active_id by value
- *
- * If there are no active_ids selected, selects the provided id as the sole active_id
- *
- * If there are ids selected and the provided id is not in them, raise an error
- *
- * @param {Object} id the id to activate
- * @returns itself
- */
- activate: function (id) {
- if(!this._active_ids) {
- this._active_ids = [id];
- this._active_id_index = 0;
- } else {
- var index = _.indexOf(this._active_ids, id);
- if (index == -1) {
- throw new Error(
- "Could not find id " + id +
- " in array [" + this._active_ids.join(', ') + "]");
- }
- this._active_id_index = index;
- }
- return this;
- },
- /**
- * Fetches the id of the current active record, if any.
- *
- * @returns record? record id or null
- */
- get_active_id: function () {
- if (!this._active_ids) {
- return null;
- }
- return this._active_ids[this._active_id_index];
- }
-});
-
-openerp.base.DataRecord = openerp.base.Controller.extend({
- init: function(session, model, fields, values) {
- this._super(session, null);
- this.model = model;
- this.id = values.id || null;
- this.fields = fields;
- this.values = values;
- },
- on_change: function() {
- },
- on_reload: function() {
- }
-});
-
-/**
- * Base class for widgets. Handle rendering (based on a QWeb template), identifier
- * generation, parenting and destruction of the widget.
- */
-openerp.base.BaseWidget = openerp.base.Controller.extend({
- /**
- * The name of the QWeb template that will be used for rendering. Must be redifined
- * in subclasses or the render() method can not be used.
- *
- * @type string
- */
- template: null,
- /**
- * The prefix used to generate an id automatically. Should be redifined in subclasses.
- * If it is not defined, a default identifier will be used.
- *
- * @type string
- */
- identifier_prefix: 'generic-identifier',
- /**
- * Contructor. Also initialize the identifier.
- *
- * @params {openerp.base.search.BaseWidget} parent The parent widget.
- */
- init: function (parent) {
- this.children = [];
- this.parent = null;
- this.set_parent(parent);
- this.make_id(this.identifier_prefix);
- },
- /**
- * Sets and returns a globally unique identifier for the widget.
- *
- * If a prefix is appended, the identifier will be appended to it.
- *
- * @params sections prefix sections, empty/falsy sections will be removed
- */
- make_id: function () {
- this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
- return this.element_id;
- },
- /**
- * "Starts" the widgets. Called at the end of the rendering, this allows
- * to get a jQuery object referring to the DOM ($element attribute).
- */
- start: function () {
- this._super();
- var tmp = document.getElementById(this.element_id)
- this.$element = tmp ? $(tmp) : null;
- },
- /**
- * "Stops" the widgets. Called when the view destroys itself, this
- * lets the widgets clean up after themselves.
- */
- stop: function () {
- var tmp_children = this.children;
- this.children = [];
- _.each(tmp_children, function(x) {
- x.stop();
- });
- if(this.$element != null) {
- this.$element.remove();
- }
- this.set_parent(null);
- this._super();
- },
- /**
- * Set the parent of this component, also unregister the previous parent if there
- * was one.
- *
- * @param {openerp.base.BaseWidget} parent The new parent.
- */
- set_parent: function(parent) {
- if(this.parent) {
- this.parent.children = _.without(this.parent.children, this);
- }
- this.parent = parent;
- if(this.parent) {
- parent.children.push(this);
- }
- },
- /**
- * Render the widget. This.template must be defined.
- * The content of the current object is passed as context to the template.
- *
- * @param {object} additional Additional context arguments to pass to the template.
- */
- render: function (additional) {
- return QWeb.render(this.template, _.extend({}, this,
- additional != null ? additional : {}));
- }
-});
+openerp.addons.base.views = function(openerp) {
openerp.base.SearchView = openerp.base.Controller.extend({
init: function(session, element_id, dataset, view_id, defaults) {
@@ -1238,630 +742,6 @@ openerp.base.search.ManyToManyField = openerp.base.search.IntegerField.extend({
// TODO: .related_columns (Array), .context, .domain
});
-openerp.base.FormView = openerp.base.Controller.extend({
- init: function(session, element_id, dataset, view_id) {
- this._super(session, element_id);
- this.dataset = dataset;
- this.model = dataset.model;
- this.view_id = view_id;
- this.fields_views = {};
- this.widgets = {};
- this.widgets_counter = 0;
- this.fields = {};
- this.datarecord = {};
- this.ready = false;
- },
- start: function() {
- //this.log('Starting FormView '+this.model+this.view_id)
- this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
- },
- on_loaded: function(data) {
- var self = this;
- this.fields_view = data.fields_view;
- //this.log(this.fields_view);
-
- var frame = new openerp.base.WidgetFrame(this, this.fields_view.arch);
-
- this.$element.html(QWeb.render("FormView", { "frame": frame, "view": this }));
- _.each(this.widgets, function(w) {
- w.start();
- });
- this.$element.find('button.form_save').click(this.do_save);
-
- this.dataset.on_active_id.add(this.on_record_loaded);
- },
- on_record_loaded: function(record) {
- this.datarecord = record;
- for (var f in this.fields) {
- this.fields[f].set_value(this.datarecord.values[f]);
- }
- this.on_form_changed();
- this.ready = true;
- },
- on_form_changed: function(widget) {
- for (var w in this.widgets) {
- w = this.widgets[w];
- w.process_attrs();
- w.update_dom();
- }
- if (widget) {
- // TODO: check if and trigger
- // if (onchange for this field) {
- // this.ready = false;
- // rpc - process.callback ( this.ready = true; )
- // }
- }
- },
- do_save: function() {
- if (!this.ready) {
- return false;
- }
- var invalid = false;
- var values = {};
- for (var f in this.fields) {
- f = this.fields[f];
- if (f.invalid) {
- invalid = true;
- } else {
- values[f.name] = f.value;
- }
- }
- if (invalid) {
- this.on_invalid();
- } else {
- console.log("Save form", values);
- // TODO: save values via datarecord
- // rpc - save.callbacl on_saved
- }
- },
- on_invalid: function() {
- },
- on_saved: function() {
- // Check response for exceptions, display error
- }
-});
-
-openerp.base.ListView = openerp.base.Controller.extend({
- init: function(session, element_id, dataset, view_id) {
- this._super(session, element_id);
- this.dataset = dataset;
- this.model = dataset.model;
- this.view_id = view_id;
- this.name = "";
-
- this.cols = [];
-
- this.$table = null;
- this.colnames = [];
- this.colmodel = [];
-
- this.event_loading = false; // TODO in the future prevent abusive click by masking
- },
- start: function() {
- //this.log('Starting ListView '+this.model+this.view_id)
- this.rpc("/base/listview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded);
- },
- on_loaded: function(data) {
- this.fields_view = data.fields_view;
- //this.log(this.fields_view);
- this.name = "" + this.fields_view.arch.attrs.string;
- this.$element.html(QWeb.render("ListView", {"fields_view": this.fields_view}));
- this.$table = this.$element.find("table");
- this.cols = [];
- this.colnames = [];
- this.colmodel = [];
- // TODO uss a object for each col, fill it with view and fallback to dataset.model_field
- var tree = this.fields_view.arch.children;
- for(var i = 0; i < tree.length; i++) {
- var col = tree[i];
- if(col.tag == "field") {
- this.cols.push(col.attrs.name);
- this.colnames.push(col.attrs.name);
- this.colmodel.push({ name: col.attrs.name, index: col.attrs.name });
- }
- }
- this.dataset.fields = this.cols;
- this.dataset.on_fetch.add(this.do_fill_table);
-
- var width = this.$element.width();
- this.$table.jqGrid({
- datatype: "local",
- height: "100%",
- rowNum: 100,
- //rowList: [10,20,30],
- colNames: this.colnames,
- colModel: this.colmodel,
- //pager: "#plist47",
- viewrecords: true,
- caption: this.name
- }).setGridWidth(width);
-
- var self = this;
- $(window).bind('resize', function() {
- self.$element.children().hide();
- self.$table.setGridWidth(self.$element.width());
- self.$element.children().show();
- }).trigger('resize');
- },
- do_fill_table: function(records) {
- this.log("do_fill_table");
-
- this.$table
- .clearGridData()
- .addRowData('id', _.map(records, function (record) {
- return record.values;
- }));
-
- }
-});
-
-openerp.base.TreeView = openerp.base.Controller.extend({
-});
-
-
-
-openerp.base.Widget = openerp.base.Controller.extend({
- // TODO Change this to init: function(view, node) { and use view.session and a new element_id for the super
- // it means that widgets are special controllers
- init: function(view, node) {
- this.view = view;
- this.node = node;
- this.attrs = eval('(' + (this.node.attrs.attrs || '{}') + ')');
- this.type = this.type || node.tag;
- this.element_name = this.element_name || this.type;
- this.element_id = [this.view.element_id, this.element_name, this.view.widgets_counter++].join("_");
-
- this._super(this.view.session, this.element_id);
-
- this.view.widgets[this.element_id] = this;
- this.children = node.children;
- this.colspan = parseInt(node.attrs.colspan || 1);
- this.template = "Widget";
-
- this.string = this.string || node.attrs.string;
- this.help = this.help || node.attrs.help;
- this.invisible = (node.attrs.invisible == '1');
- },
- start: function() {
- this.$element = $('#' + this.element_id);
- },
- process_attrs: function() {
- for (var a in this.attrs) {
- this[a] = this.eval_attrs(this.attrs[a]);
- }
- },
- eval_attrs: function(expr) {
- var stack = [];
- for (var i = 0; i < expr.length; i++) {
- var ex = expr[i];
- if (ex.length == 1) {
- stack.push(ex[0]);
- continue;
- }
-
- var field = this.view.fields[ex[0]].value;
- var op = ex[1];
- var val = ex[2];
-
- switch (op.toLowerCase()) {
- case '=':
- case '==':
- stack.push(field == val);
- break;
- case '!=':
- case '<>':
- stack.push(field != val);
- break;
- case '<':
- stack.push(field < val);
- break;
- case '>':
- stack.push(field > val);
- break;
- case '<=':
- stack.push(field <= val);
- break;
- case '>=':
- stack.push(field >= val);
- break;
- case 'in':
- stack.push(_.indexOf(val, field) > -1);
- break;
- case 'not in':
- stack.push(_.indexOf(val, field) == -1);
- break;
- default:
- this.log("Unsupported operator in attrs :", op);
- }
- }
-
- for (var j = stack.length-1; j >- 1; j--) {
- switch (stack[j]) {
- case '|':
- var result = stack[j + 1] || stack[j + 2];
- stack.splice(j, 3, result);
- break;
- case '&':
- var result = stack[j + 1] && stack[j + 2];
- stack.splice(j, 3, result);
- break;
- }
- }
- return _.indexOf(stack, false) == -1;
- },
- update_dom: function() {
- this.$element.toggle(!this.invisible);
- },
- render: function() {
- var template = this.template;
- return QWeb.render(template, { "widget": this });
- }
-});
-
-openerp.base.WidgetFrame = openerp.base.Widget.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "WidgetFrame";
- this.columns = node.attrs.col || 4;
- 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) {
- for (var i = 0; i < row.length; i++) {
- var w = row[i];
- if (w.is_field_label) {
- w.width = "1%";
- if (row[i + 1]) {
- row[i + 1].width = Math.round((100 / this.columns) * (w.colspan + 1) - 1) + '%';
- }
- } else if (w.width === undefined) {
- w.width = Math.round((100 / this.columns) * w.colspan) + '%';
- }
- }
- },
- handle_node: function(n) {
- var type = this.view.fields_view.fields[n.attrs.name] || {};
- var widget_type = n.attrs.widget || type.type || n.tag;
- if (openerp.base.widgets[widget_type]) {
- var widget = new openerp.base.widgets[widget_type](this.view, n);
- if (n.tag == 'field' && n.attrs.nolabel != '1') {
- var label = new openerp.base.widgets['label'](this.view, n);
- label["for"] = widget;
- this.add_widget(label);
- }
- this.add_widget(widget);
- } else {
- this.log("Unhandled widget type : " + widget_type, n);
- }
- },
- add_widget: function(w) {
- if (!w.invisible) {
- var current_row = this.table[this.table.length - 1];
- if (current_row.length && (this.x + w.colspan) > this.columns) {
- current_row = this.add_row();
- }
- current_row.push(w);
- this.x += w.colspan;
- }
- return w;
- }
-});
-
-openerp.base.WidgetNotebook = openerp.base.Widget.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "WidgetNotebook";
- this.pages = [];
- for (var i = 0; i < node.children.length; i++) {
- var n = node.children[i];
- if (n.tag == "page") {
- var page = new openerp.base.WidgetFrame(this.view, n);
- this.pages.push(page);
- }
- }
- },
- start: function() {
- this._super.apply(this, arguments);
- this.$element.tabs();
- }
-});
-
-openerp.base.WidgetSeparator = openerp.base.Widget.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "WidgetSeparator";
- }
-});
-
-openerp.base.WidgetButton = openerp.base.Widget.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "WidgetButton";
- }
-});
-
-openerp.base.WidgetLabel = openerp.base.Widget.extend({
- init: function(view, node) {
- this.is_field_label = true;
- this.element_name = 'label_' + node.attrs.name;
-
- this._super(view, node);
-
- this.template = "WidgetLabel";
- this.colspan = 1;
- },
- 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});
- }
-});
-
-openerp.base.Field = openerp.base.Widget.extend({
- init: function(view, node) {
- this.name = node.attrs.name;
- this.value = undefined;
- view.fields[this.name] = this;
- 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.datarecord = this.view.datarecord ??
- 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 = (node.attrs.nolabel == '1');
- this.readonly = (node.attrs.readonly == '1');
- this.required = (node.attrs.required == '1');
- this.invalid = false;
- },
- set_value: function(value) {
- this.value = value;
- },
- get_value: function(value) {
- return value;
- },
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.toggleClass('disabled', this.readonly);
- this.$element.toggleClass('required', this.required);
- },
- on_ui_change: function() {
- this.view.on_form_changed(this);
- }
-});
-
-openerp.base.FieldChar = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldChar";
- },
- 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);
- if (value != null && value !== false) {
- this.$element.find('input').val(value);
- }
- },
- get_value: function() {
- },
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').attr({
- 'disabled' : this.readonly,
- 'required' : this.required
- });
- },
- on_ui_change: function() {
- this.value = this.$element.find('input').val();
- this.invalid = this.required && this.value == "";
- this._super.apply(this, arguments);
- }
-});
-
-openerp.base.FieldEmail = openerp.base.FieldChar.extend({
-});
-
-openerp.base.FieldUrl = openerp.base.FieldChar.extend({
-});
-
-openerp.base.FieldFloat = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldChar";
- },
- 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);
- if (value != null && value !== false) {
- this.$element.find('input').val(value.toFixed(2));
- }
- },
- get_value: function() {
- },
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').attr({
- 'disabled' : this.readonly,
- 'required' : this.required
- });
- },
- on_ui_change: function() {
- this.value = this.$element.find('input').val();
- this.invalid = this.required && this.value == "";
- this._super.apply(this, arguments);
- }
-});
-
-openerp.base.FieldText = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldText";
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- if (value != null && value !== false) {
- this.$element.find('textarea').val(value);
- }
- },
- get_value: function() {
- return this.$element.find('textarea').val();
- }
-});
-
-openerp.base.FieldBoolean = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldBoolean";
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- this.$element.find('input')[0].checked = value;
- },
- get_value: function() {
- this.$element.find('input')[0].checked;
- }
-});
-
-openerp.base.FieldDate = openerp.base.FieldChar.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldDate";
- }
-});
-
-openerp.base.FieldDatetime = openerp.base.FieldChar.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldDatetime";
- }
-});
-
-openerp.base.FieldTextXml = openerp.base.Field.extend({
-// to replace view editor
-});
-
-openerp.base.FieldSelection = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldSelection";
- },
- set_value: function(value) {
- this._super.apply(this, arguments);
- if (value != null && value !== false) {
- this.$element.find('select').val(value);
- }
- },
- get_value: function() {
- return this.$element.find('select').val();
- }
-});
-
-openerp.base.FieldMany2One = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldMany2One";
- }
-});
-
-openerp.base.FieldOne2Many = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldOne2Many";
- }
-});
-
-openerp.base.FieldMany2Many = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldMany2Many";
- }
-});
-
-openerp.base.FieldReference = openerp.base.Field.extend({
- init: function(view, node) {
- this._super(view, node);
- this.template = "FieldReference";
- }
-});
-
-openerp.base.widgets = {
- 'group' : openerp.base.WidgetFrame,
- 'notebook' : openerp.base.WidgetNotebook,
- 'separator' : openerp.base.WidgetSeparator,
- 'label' : openerp.base.WidgetLabel,
- 'char' : openerp.base.FieldChar,
- 'email' : openerp.base.FieldEmail,
- 'url' : openerp.base.FieldUrl,
- 'text' : openerp.base.FieldText,
- 'date' : openerp.base.FieldDate,
- 'datetime' : openerp.base.FieldDatetime,
- 'selection' : openerp.base.FieldSelection,
- 'many2one' : openerp.base.FieldMany2One,
- 'many2many' : openerp.base.FieldMany2Many,
- 'one2many' : openerp.base.FieldOne2Many,
- 'one2many_list' : openerp.base.FieldOne2Many,
- 'reference' : openerp.base.FieldReference,
- 'boolean' : openerp.base.FieldBoolean,
- 'float' : openerp.base.FieldFloat,
- 'button' : openerp.base.WidgetButton
};
-openerp.base.CalendarView = openerp.base.Controller.extend({
-// Dhtmlx scheduler ?
-});
-
-openerp.base.GanttView = openerp.base.Controller.extend({
-// Dhtmlx gantt ?
-});
-
-openerp.base.DiagramView = openerp.base.Controller.extend({
-//
-});
-
-openerp.base.GraphView = openerp.base.Controller.extend({
-});
-
-openerp.base.ProcessView = openerp.base.Controller.extend({
-});
-
-openerp.base.HelpView = openerp.base.Controller.extend({
-});
-
-};
-
-// DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner'))
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/tests.js b/addons/base/static/src/js/tests.js
new file mode 100644
index 00000000000..39c8f213fe6
--- /dev/null
+++ b/addons/base/static/src/js/tests.js
@@ -0,0 +1,351 @@
+/**
+ * @function
+ * Defines a module scope (which lasts until the next call to module).
+ *
+ * This module scopes implies setup and teardown callbacks running for each test.
+ *
+ * @param {String} name the name of the module
+ * @param {Object} [lifecycle] callbacks to run before and after each test of the module
+ * @param {Function} lifecycle.setup function running before each test of this module
+ * @param {Function} lifecycle.teardown function running after each test of this module
+ */
+var module;
+/**
+ * @function
+ * Defines a given test to run. Runs all the assertions present in the test
+ *
+ * @param {String} name the name of the test
+ * @param {Number} [expected] number of assertions expected to run in this test (useful for asynchronous tests)
+ * @param {Function} test the testing code to run, holding a sequence of assertions (at least one)
+ */
+var test;
+/**
+ * @function
+ * Defines an asynchronous test: equivalent to calling stop() at the start of
+ * a normal test().
+ *
+ * The test code needs to restart the test runner via start()
+ *
+ * @param {String} name the name of the test
+ * @param {Number} [expected] number of assertions expected to run in this test (useful for asynchronous tests)
+ * @param {Function} test the testing code to run, holding a sequence of assertions (at least one)
+ */
+var asyncTest;
+/**
+ * @function
+ * The most basic boolean assertion (~assertTrue or assert).
+ *
+ * Passes if its argument is truthy
+ *
+ * @param {Boolean} state an arbitrary expression, evaluated in a boolean context
+ * @param {String} [message] the message to output with the assertion result
+ */
+var ok;
+/**
+ * @function
+ * Equality assertion (~assertEqual)
+ *
+ * Passes if both arguments are equal (via ==
)
+ *
+ * @param {Object} actual the object to check for correctness (processing result)
+ * @param {Object} expected the object to check against
+ * @param {String} [message] message output with the assertion result
+ */
+var equal;
+/**
+ * @function
+ * Inequality assertion (~assertNotEqual)
+ *
+ * Passes if the arguments are different (via !=
)
+ *
+ * @param {Object} actual the object to check for correctness (processing result)
+ * @param {Object} expected the object to check against
+ * @param {String} [message] message output with the assertion result
+ */
+var notEqual;
+/**
+ * @function
+ * Recursive equality assertion.
+ *
+ * Works on primitive types using ===
and traversing through
+ * Objects and Arrays as well checking their components
+ *
+ * @param {Object} actual the object to check for correctness (processing result)
+ * @param {Object} expected the object to check against
+ * @param {String} [message] message output with the assertion result
+ */
+var deepEqual;
+/**
+ * @function
+ * Recursive inequality assertion.
+ *
+ * Works on primitive types using !==
and traversing through
+ * Objects and Arrays as well checking their components
+ *
+ * @param {Object} actual the object to check for correctness (processing result)
+ * @param {Object} expected the object to check against
+ * @param {String} [message] message output with the assertion result
+ */
+var notDeepEqual;
+/**
+ * @function
+ * Strict equality assertion (~assertEqual)
+ *
+ * Passes if both arguments are identical (via ===
)
+ *
+ * @param {Object} actual the object to check for correctness (processing result)
+ * @param {Object} expected the object to check against
+ * @param {String} [message] message output with the assertion result
+ */
+var strictEqual;
+/**
+ * @function
+ * Strict inequality assertion (~assertNotEqual)
+ *
+ * Passes if both arguments are identical (via !==
)
+ *
+ * @param {Object} actual the object to check for correctness (processing result)
+ * @param {Object} expected the object to check against
+ * @param {String} [message] message output with the assertion result
+ */
+var notStrictEqual;
+/**
+ * @function
+ * Passes if the provided block raised an exception.
+ *
+ * The expect
argument can be provided to perform further assertion checks on the exception itself:
+ * * If it's a RegExp
test the exception against the regexp (message?)
+ * * If it's a constructor, check if the exception is an instance of it
+ * * If it's an other type of function, call it with the exception as first parameter
+ * - If the function returns true, the assertion validates
+ * - Otherwise it fails
+ *
+ * @param {Function} block function which should raise an exception when called
+ * @param {Object} [expect] a RegExp, a constructor or a Function
+ * @param {String} [message] message output with the assertion result
+ */
+var raises;
+/**
+ * @function
+ * Starts running the test runner again from the point where it was
+ * stop
ped.
+ *
+ * Used to resume testing after a callback.
+ */
+var start;
+/**
+ * @function
+ * Stops the test runner in order to wait for an asynchronous test to run
+ *
+ * @param {Number} [timeout] fails the test after the timeout triggers, only for debugging tests
+ */
+var stop;
+
+var Session = function () {
+ return {
+ rpc: function (_url, params, on_success) {
+ setTimeout(on_success);
+ }
+ };
+};
+$(document).ready(function () {
+ var openerp;
+ module("ids_callback", {
+ setup: function () {
+ openerp = window.openerp.init();
+ }
+ });
+ asyncTest("Baseline event attributes", 6, function () {
+ var dataset = new openerp.base.DataSet(
+ new Session());
+ dataset.on_fetch.add(function (records, event) {
+ deepEqual(records, [], 'No records returned');
+ equal(event.offset, 0, 'No offset set in call');
+ equal(event.limit, null, 'No limit set in call');
+ deepEqual(event.domain, [], 'No domain on the dataset');
+ deepEqual(event.context, {}, 'No context on the dataset');
+ deepEqual(event.sort, [], 'The dataset is not sorted');
+ start();
+ });
+ dataset.fetch();
+ });
+ asyncTest("Offset and limit", 2, function () {
+ var dataset = new openerp.base.DataSet(
+ new Session());
+ dataset.on_fetch.add(function (records, event) {
+ equal(event.offset, 20);
+ equal(event.limit, 42);
+ start();
+ });
+ dataset.fetch(20, 42);
+ });
+ asyncTest("Domain and context propagation", 3, function () {
+ var dataset = new openerp.base.DataSet(
+ new Session());
+ var domain_value = [['foo', '=', 'bar']];
+ var context_value= {active_id:3, active_ids:42};
+ var sort_value = ['foo'];
+ dataset.on_fetch.add(function (records, event) {
+ deepEqual(event.domain, domain_value);
+ deepEqual(event.context, context_value);
+ deepEqual(event.sort, sort_value);
+ start();
+ });
+ dataset.set({
+ domain: domain_value,
+ context: context_value,
+ sort: sort_value
+ });
+ dataset.fetch();
+ });
+ asyncTest("Data records", function () {
+ var dataset = new openerp.base.DataSet({
+ rpc: function (url, _params, on_success) {
+ equal('/base/dataset/find', url);
+ _.delay(on_success, 0, [
+ {id: 1, sequence: 3, name: "dummy", age: 42},
+ {id: 5, sequence: 7, name: "whee", age: 55}
+ ]);
+ }
+ });
+ dataset.on_fetch.add(function (records) {
+ equal(records.length, 2, "I loaded two virtual records");
+ var d1 = records[0],
+ d2 = records[1];
+ ok(d1 instanceof openerp.base.DataRecord);
+ ok(d2 instanceof openerp.base.DataRecord);
+ start();
+ });
+ dataset.fetch();
+ });
+
+ var dataset;
+ module("set", {
+ setup: function () {
+ var openerp = window.openerp.init();
+ dataset = new openerp.base.DataSet();
+ }
+ });
+ test('Basic properties setting', function () {
+ var domain_value = [['foo', '=', 'bar']];
+ var result = dataset.set({
+ domain: domain_value
+ });
+ ok(dataset === result);
+ deepEqual(domain_value, dataset._domain);
+ });
+ test("Ensure changes don't stick", function () {
+ var domain = [['foo', '=', 'bar']];
+ dataset.set({
+ domain: domain
+ });
+ domain.pop();
+ deepEqual([['foo', '=', 'bar']], dataset._domain);
+ });
+
+ module('ids_activation', {
+ setup: function () {
+ var openerp = window.openerp.init();
+ dataset = new openerp.base.DataSet();
+ }
+ });
+ test('activate id', function () {
+ dataset.activate(1);
+ deepEqual(dataset.get_active_ids(), [1]);
+ equal(dataset.get_active_id(), 1);
+ });
+ test('set active_ids', function () {
+ dataset.select([1, 2, 3]);
+ deepEqual(dataset.get_active_ids(), [1, 2, 3],
+ "selecting an ids range");
+ equal(dataset.get_active_id(), 1);
+ });
+ test('activate incorrect id', function () {
+ dataset.select([1, 2, 3]);
+ raises(function () { dataset.activate(42); },
+ "Activating an id not present in the selection is an error");
+ });
+ test('reset active id on set active ids', function () {
+ dataset.select([1, 2, 3]).activate(3).select([1, 2, 3]);
+ equal(dataset.get_active_id(), 1,
+ "selecting an ids range resets the active id");
+ });
+
+ module('active_id_iteration', {
+ setup: function () {
+ var openerp = window.openerp.init();
+ dataset = new openerp.base.DataSet();
+ dataset.select([1, 2, 3]);
+ }
+ });
+ test('step forward', function () {
+ dataset.activate(1);
+ dataset.next();
+ equal(dataset.get_active_id(), 2);
+ });
+ test('wraparound forward', function () {
+ dataset.activate(3);
+ dataset.next();
+ equal(dataset.get_active_id(), 1);
+ });
+ test('step back', function () {
+ dataset.activate(3);
+ dataset.prev();
+ equal(dataset.get_active_id(), 2);
+ });
+ test('wraparound back', function () {
+ dataset.activate(1);
+ dataset.prev();
+ equal(dataset.get_active_id(), 3);
+ });
+
+ var ResponseAssertSession = function (response_ids) {
+ return {
+ rpc: function (url, params, on_success) {
+ equal(url, '/base/dataset/get');
+ deepEqual(params.ids, response_ids);
+ _.delay(on_success, 0, _.map(
+ params.ids, function (id) {
+ return {id: id, sequence: id, name: 'foo'+id};
+ }
+ ));
+ }
+ };
+ };
+
+ module('active_ids', {
+ setup: function () {
+ openerp = window.openerp.init();
+ }
+ });
+ asyncTest('Get pre-set active_ids', 6, function () {
+ var dataset = new openerp.base.DataSet(
+ new ResponseAssertSession([1, 2, 3]));
+ dataset.select([1, 2, 3]);
+ dataset.on_active_ids.add(function (data_records) {
+ equal(data_records.length, 3);
+ equal(data_records[0].values.id, 1);
+ equal(data_records[1].values.id, 2);
+ equal(data_records[2].values.id, 3);
+ start();
+ });
+ dataset.active_ids();
+ });
+
+ module('active_id', {
+ setup: function () {
+ openerp = window.openerp.init();
+ }
+ });
+ test('Get pre-set active_id', 3, function () {
+ var dataset = new openerp.base.DataSet(
+ new ResponseAssertSession([42]));
+ stop(500);
+ dataset.select([1, 2, 3, 42]).activate(42);
+ dataset.on_active_id.add(function (data_record) {
+ equal(data_record.values.id, 42);
+ start();
+ });
+ dataset.active_id();
+ });
+});
diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js
new file mode 100644
index 00000000000..155e8811e17
--- /dev/null
+++ b/addons/base/static/src/js/views.js
@@ -0,0 +1,257 @@
+/*---------------------------------------------------------
+ * OpenERP base library
+ *---------------------------------------------------------*/
+
+openerp.addons.base.views = function(openerp) {
+
+// process all kind of actions
+openerp.base.ActionManager = openerp.base.Controller.extend({
+ init: function(session, element_id) {
+ this._super(session, element_id);
+ this.action = null;
+ this.viewmanager = null;
+ },
+ /**
+ * Process an action
+ * Supported actions: act_window
+ */
+ do_action: function(action) {
+ // instantiate the right controllers by understanding the action
+ this.action = action;
+ if(action.type == "ir.actions.act_window") {
+ this.viewmanager = new openerp.base.ViewManager(this.session,this.element_id);
+ this.viewmanager.do_action_window(action);
+ this.viewmanager.start();
+ }
+ }
+});
+
+// This will be ViewManager Abstract/Common
+openerp.base.ViewManager = openerp.base.Controller.extend({
+ init: function(session, element_id) {
+ this._super(session, element_id);
+ this.action = null;
+ this.dataset = null;
+ this.searchview_id = false;
+ this.searchview = null;
+ this.search_visible = true;
+ // this.views = { "list": { "view_id":1234, "controller": instance} }
+ this.views = {};
+ },
+ start: function() {
+ },
+ on_mode_switch: function(view_type) {
+ for (var i in this.views) {
+ this.views[i].controller.$element.toggle(i === view_type);
+ }
+ },
+ /**
+ * Extract search view defaults from the current action's context.
+ *
+ * These defaults are of the form {search_default_*: value}
+ *
+ * @returns {Object} a clean defaults mapping of {field_name: value}
+ */
+ search_defaults: function () {
+ var defaults = {};
+ _.each(this.action.context, function (value, key) {
+ var match = /^search_default_(.*)$/.exec(key);
+ if (match) {
+ defaults[match[1]] = value;
+ }
+ });
+ return defaults;
+ },
+ do_action_window: function(action) {
+ var self = this;
+ var prefix_id = "#" + this.element_id;
+ this.action = action;
+ this.dataset = new openerp.base.DataSet(this.session, action.res_model);
+ this.dataset.start();
+
+ this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: action.views}));
+
+ this.searchview_id = false;
+ if(this.search_visible && action.search_view_id) {
+ this.searchview_id = action.search_view_id[0];
+ var searchview = this.searchview = new openerp.base.SearchView(
+ this.session, this.element_id + "_search",
+ this.dataset, this.searchview_id,
+ this.search_defaults());
+ searchview.on_search.add(this.do_search);
+ searchview.start();
+
+ if (action['auto_search']) {
+ searchview.on_loaded.add_last(
+ searchview.do_search);
+ }
+ }
+ for(var i = 0; i < action.views.length; i++) {
+ var view_id, controller;
+ view_id = action.views[i][0];
+ if(action.views[i][1] == "tree") {
+ controller = new openerp.base.ListView(this.session, this.element_id + "_view_tree", this.dataset, view_id);
+ controller.start();
+ this.views.tree = { view_id: view_id, controller: controller };
+ this.$element.find(prefix_id + "_button_tree").bind('click',function(){
+ self.on_mode_switch("tree");
+ });
+ } else if(action.views[i][1] == "form") {
+ controller = new openerp.base.FormView(this.session, this.element_id + "_view_form", this.dataset, view_id);
+ controller.start();
+ this.views.form = { view_id: view_id, controller: controller };
+ this.$element.find(prefix_id + "_button_form").bind('click',function(){
+ self.on_mode_switch("form");
+ });
+ }
+ }
+ // switch to the first one in sequence
+ this.on_mode_switch("tree");
+ },
+ // create when root, also add to parent when o2m
+ on_create: function() {
+ },
+ on_remove: function() {
+ },
+ on_edit: function() {
+ },
+ do_search: function (domains, contexts, groupbys) {
+ var self = this;
+ this.rpc('/base/session/eval_domain_and_context', {
+ domains: domains,
+ contexts: contexts,
+ group_by_seq: groupbys
+ }, function (results) {
+ // TODO: handle non-empty results.group_by with read_group
+ self.dataset.set({
+ context: results.context,
+ domain: results.domain
+ }).fetch(0, self.action.limit);
+ });
+ }
+});
+
+// Extends view manager
+openerp.base.ViewManagerRoot = openerp.base.Controller.extend({
+});
+
+// Extends view manager
+openerp.base.ViewManagerUsedAsAMany2One = openerp.base.Controller.extend({
+});
+
+/**
+ * Base class for widgets. Handle rendering (based on a QWeb template), identifier
+ * generation, parenting and destruction of the widget.
+ */
+openerp.base.BaseWidget = openerp.base.Controller.extend({
+ /**
+ * The name of the QWeb template that will be used for rendering. Must be redifined
+ * in subclasses or the render() method can not be used.
+ *
+ * @type string
+ */
+ template: null,
+ /**
+ * The prefix used to generate an id automatically. Should be redifined in subclasses.
+ * If it is not defined, a default identifier will be used.
+ *
+ * @type string
+ */
+ identifier_prefix: 'generic-identifier',
+ /**
+ * Contructor. Also initialize the identifier.
+ *
+ * @params {openerp.base.search.BaseWidget} parent The parent widget.
+ */
+ init: function (parent) {
+ this.children = [];
+ this.parent = null;
+ this.set_parent(parent);
+ this.make_id(this.identifier_prefix);
+ },
+ /**
+ * Sets and returns a globally unique identifier for the widget.
+ *
+ * If a prefix is appended, the identifier will be appended to it.
+ *
+ * @params sections prefix sections, empty/falsy sections will be removed
+ */
+ make_id: function () {
+ this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
+ return this.element_id;
+ },
+ /**
+ * "Starts" the widgets. Called at the end of the rendering, this allows
+ * to get a jQuery object referring to the DOM ($element attribute).
+ */
+ start: function () {
+ this._super();
+ var tmp = document.getElementById(this.element_id)
+ this.$element = tmp ? $(tmp) : null;
+ },
+ /**
+ * "Stops" the widgets. Called when the view destroys itself, this
+ * lets the widgets clean up after themselves.
+ */
+ stop: function () {
+ var tmp_children = this.children;
+ this.children = [];
+ _.each(tmp_children, function(x) {
+ x.stop();
+ });
+ if(this.$element != null) {
+ this.$element.remove();
+ }
+ this.set_parent(null);
+ this._super();
+ },
+ /**
+ * Set the parent of this component, also unregister the previous parent if there
+ * was one.
+ *
+ * @param {openerp.base.BaseWidget} parent The new parent.
+ */
+ set_parent: function(parent) {
+ if(this.parent) {
+ this.parent.children = _.without(this.parent.children, this);
+ }
+ this.parent = parent;
+ if(this.parent) {
+ parent.children.push(this);
+ }
+ },
+ /**
+ * Render the widget. This.template must be defined.
+ * The content of the current object is passed as context to the template.
+ *
+ * @param {object} additional Additional context arguments to pass to the template.
+ */
+ render: function (additional) {
+ return QWeb.render(this.template, _.extend({}, this,
+ additional != null ? additional : {}));
+ }
+});
+openerp.base.CalendarView = openerp.base.Controller.extend({
+// Dhtmlx scheduler ?
+});
+
+openerp.base.GanttView = openerp.base.Controller.extend({
+// Dhtmlx gantt ?
+});
+
+openerp.base.DiagramView = openerp.base.Controller.extend({
+//
+});
+
+openerp.base.GraphView = openerp.base.Controller.extend({
+});
+
+openerp.base.ProcessView = openerp.base.Controller.extend({
+});
+
+openerp.base.HelpView = openerp.base.Controller.extend({
+});
+
+};
+
+// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: