[REM] Remove code for redirect page on refresh click.
[odoo/odoo.git] / addons / web / static / src / js / view_list_editable.js
1 /**
2  * handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
3  * @namespace
4  */
5 openerp.web.list_editable = function (openerp) {
6     var KEY_RETURN = 13,
7         KEY_ESCAPE = 27;
8
9     // editability status of list rows
10     openerp.web.ListView.prototype.defaults.editable = null;
11
12     // TODO: not sure second @lends on existing item is correct, to check
13     openerp.web.ListView.include(/** @lends openerp.web.ListView# */{
14         init: function () {
15             var self = this;
16             this._super.apply(this, arguments);
17             $(this.groups).bind({
18                 'edit': function (e, id, dataset) {
19                     self.do_edit(dataset.index, id, dataset);
20                 },
21                 'saved': function () {
22                     if (self.groups.get_selection().length) {
23                         return;
24                     }
25                     self.compute_aggregates();
26                 }
27             })
28         },
29         /**
30          * Handles the activation of a record in editable mode (making a record
31          * editable), called *after* the record has become editable.
32          *
33          * The default behavior is to setup the listview's dataset to match
34          * whatever dataset was provided by the editing List
35          *
36          * @param {Number} index index of the record in the dataset
37          * @param {Object} id identifier of the record being edited
38          * @param {openerp.web.DataSet} dataset dataset in which the record is available
39          */
40         do_edit: function (index, id, dataset) {
41             _.extend(this.dataset, dataset);
42         },
43         /**
44          * Sets editability status for the list, based on defaults, view
45          * architecture and the provided flag, if any.
46          *
47          * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
48          */
49         set_editable: function (force) {
50             // If ``force``, set editability to bottom
51             // otherwise rely on view default
52             // view' @editable is handled separately as we have not yet
53             // fetched and processed the view at this point.
54             this.options.editable = (
55                     (force && "bottom")
56                     || this.defaults.editable);
57         },
58         /**
59          * Replace do_actual_search to handle editability process
60          */
61         do_actual_search: function (results) {
62             this.set_editable(results.context['set_editable']);
63             this._super(results);
64         },
65         /**
66          * Replace do_add_record to handle editability (and adding new record
67          * as an editable row at the top or bottom of the list)
68          */
69         do_add_record: function () {
70             if (this.options.editable) {
71                 this.groups.new_record();
72             } else {
73                 this._super();
74             }
75         },
76         on_loaded: function (data, grouped) {
77             // tree/@editable takes priority on everything else if present.
78             this.options.editable = data.arch.attrs.editable || this.options.editable;
79             return this._super(data, grouped);
80         }
81     });
82
83     openerp.web.ListView.Groups.include(/** @lends openerp.web.ListView.Groups# */{
84         passtrough_events: openerp.web.ListView.Groups.prototype.passtrough_events + " edit saved",
85         new_record: function () {
86             // TODO: handle multiple children
87             this.children[null].new_record();
88         }
89     });
90
91     openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List# */{
92         row_clicked: function (event) {
93             if (!this.options.editable) {
94                 return this._super(event);
95             }
96             this.edit_record($(event.currentTarget).data('id'));
97         },
98         /**
99          * Checks if a record is being edited, and if so cancels it
100          */
101         cancel_pending_edition: function () {
102             var self = this, cancelled = $.Deferred();
103             if (!this.edition) {
104                 cancelled.resolve();
105                 return cancelled.promise();
106             }
107
108             if (this.edition_id != null) {
109                 this.reload_record(self.records.get(this.edition_id)).then(function () {
110                     cancelled.resolve();
111                 });
112             } else {
113                 cancelled.resolve();
114             }
115             cancelled.then(function () {
116                 self.view.unpad_columns();
117                 self.edition_form.stop();
118                 self.edition_form.$element.remove();
119                 delete self.edition_form;
120                 delete self.edition_id;
121                 delete self.edition;
122             });
123             return cancelled.promise();
124         },
125         /**
126          * Adapts this list's view description to be suitable to the inner form
127          * view of a row being edited.
128          *
129          * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
130          */
131         get_form_fields_view: function () {
132             // deep copy of view
133             var view = $.extend(true, {}, this.group.view.fields_view);
134             _(view.arch.children).each(function (widget) {
135                 widget.attrs.nolabel = true;
136                 if (widget.tag === 'button') {
137                     delete widget.attrs.string;
138                 }
139             });
140             view.arch.attrs.col = 2 * view.arch.children.length;
141             return view;
142         },
143         render_row_as_form: function (row) {
144             var self = this;
145             this.cancel_pending_edition().then(function () {
146                 var record_id = $(row).data('id');
147                 var $new_row = $('<tr>', {
148                         id: _.uniqueId('oe-editable-row-'),
149                         'data-id': record_id,
150                         'class': $(row).attr('class') + ' oe_forms',
151                         click: function (e) {e.stopPropagation();}
152                     })
153                     .delegate('button.oe-edit-row-save', 'click', function () {
154                         self.save_row();
155                     })
156                     .delegate('button.oe-edit-row-cancel', 'click', function () {
157                         self.cancel_edition();
158                     })
159                     .delegate('button', 'keyup', function (e) {
160                         e.stopImmediatePropagation();
161                     })
162                     .keyup(function (e) {
163                         switch (e.which) {
164                             case KEY_RETURN:
165                                 self.save_row(true);
166                                 break;
167                             case KEY_ESCAPE:
168                                 self.cancel_edition();
169                                 break;
170                             default:
171                                 return;
172                         }
173                     });
174                 if (row) {
175                     $new_row.replaceAll(row);
176                 } else if (self.options.editable === 'top') {
177                     self.$current.prepend($new_row);
178                 } else if (self.options.editable) {
179                     self.$current.append($new_row);
180                 }
181                 self.edition = true;
182                 self.edition_id = record_id;
183                 self.edition_form = _.extend(new openerp.web.ListEditableFormView(self, self.dataset, false), {
184                     form_template: 'ListView.row.form',
185                     registry: openerp.web.list.form.widgets,
186                     $element: $new_row
187                 });
188                 // HA HA
189                 self.edition_form.appendTo();
190                 $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
191                     // put in $.when just in case  FormView.on_loaded becomes asynchronous
192                     $new_row.find('td')
193                           .addClass('oe-field-cell')
194                           .removeAttr('width')
195                       .end()
196                       .find('td:first').removeClass('oe-field-cell').end()
197                       .find('td:last').removeClass('oe-field-cell').end();
198                     // pad in case of groupby
199                     _(self.columns).each(function (column) {
200                         if (column.meta) {
201                             $new_row.prepend('<td>');
202                         }
203                     });
204                     // Add columns for the cancel and save buttons, if
205                     // there are none in the list
206                     if (!self.options.selectable) {
207                         self.view.pad_columns(
208                             1, {except: $new_row, position: 'before'});
209                     }
210                     if (!self.options.deletable) {
211                         self.view.pad_columns(
212                             1, {except: $new_row});
213                     }
214
215                     self.edition_form.do_show();
216                 });
217             });
218         },
219         handle_onwrite: function (source_record_id) {
220             var self = this;
221             var on_write_callback = self.view.fields_view.arch.attrs.on_write;
222             if (!on_write_callback) { return; }
223             this.dataset.call(on_write_callback, [source_record_id], function (ids) {
224                 _(ids).each(function (id) {
225                     var record = self.records.get(id);
226                     if (!record) {
227                         // insert after the source record
228                         var index = self.records.indexOf(
229                             self.records.get(source_record_id)) + 1;
230                         record = new openerp.web.list.Record({id: id});
231                         self.records.add(record, {at: index});
232                         self.dataset.ids.splice(index, 0, id);
233                     }
234                     self.reload_record(record);
235                 });
236             });
237         },
238         /**
239          * Saves the current row, and triggers the edition of its following
240          * sibling if asked.
241          *
242          * @param {Boolean} [edit_next=false] should the next row become editable
243          */
244         save_row: function (edit_next) {
245             var self = this;
246             this.edition_form.do_save(function (result) {
247                 if (result.created && !self.edition_id) {
248                     self.records.add({id: result.result},
249                         {at: self.options.editable === 'top' ? 0 : null});
250                     self.edition_id = result.result;
251                 }
252                 var edited_record = self.records.get(self.edition_id),
253                     next_record = self.records.at(
254                             self.records.indexOf(edited_record) + 1);
255
256                 self.handle_onwrite(self.edition_id);
257                 self.cancel_pending_edition().then(function () {
258                     $(self).trigger('saved', [self.dataset]);
259                     if (!edit_next) {
260                         return;
261                     }
262                     if (result.created) {
263                         self.new_record();
264                         return;
265                     }
266                     var next_record_id;
267                     if (next_record) {
268                         next_record_id = next_record.get('id');
269                         self.dataset.index = _(self.dataset.ids)
270                                 .indexOf(next_record_id);
271                     } else {
272                         self.dataset.index = 0;
273                         next_record_id = self.records.at(0).get('id');
274                     }
275                     self.edit_record(next_record_id);
276                 });
277             }, this.options.editable === 'top');
278         },
279         /**
280          * Cancels the edition of the row for the current dataset index
281          */
282         cancel_edition: function () {
283             this.cancel_pending_edition();
284         },
285         /**
286          * Edits record currently selected via dataset
287          */
288         edit_record: function (record_id) {
289             this.render_row_as_form(
290                 this.$current.find('[data-id=' + record_id + ']'));
291             $(this).trigger(
292                 'edit',
293                 [record_id, this.dataset]);
294         },
295         new_record: function () {
296             this.dataset.index = null;
297             this.render_row_as_form();
298         }
299     });
300     if (!openerp.web.list) {
301         openerp.web.list = {};
302     }
303     if (!openerp.web.list.form) {
304         openerp.web.list.form = {};
305     }
306     openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
307         template: 'ListView.row.frame'
308     });
309     var form_widgets = openerp.web.form.widgets;
310     openerp.web.list.form.widgets = form_widgets.clone({
311         'frame': 'openerp.web.list.form.WidgetFrame'
312     });
313     // All form widgets inherit a problematic behavior from
314     // openerp.web.form.WidgetFrame: the cell itself is removed when invisible
315     // whether it's @invisible or @attrs[invisible]. In list view, only the
316     // former should completely remove the cell. We need to override update_dom
317     // on all widgets since we can't just hit on widget itself (I think)
318     var list_form_widgets = openerp.web.list.form.widgets;
319     _(list_form_widgets.map).each(function (widget_path, key) {
320         if (key === 'frame') { return; }
321         var new_path = 'openerp.web.list.form.' + key;
322
323         openerp.web.list.form[key] = (form_widgets.get_object(key)).extend({
324             update_dom: function () {
325                 this.$element.children().css('visibility', '');
326                 if (this.modifiers.tree_invisible) {
327                     var old_invisible = this.invisible;
328                     this.invisible = !!this.modifiers.tree_invisible;
329                     this._super();
330                     this.invisible = old_invisible;
331                 } else if (this.invisible) {
332                     this.$element.children().css('visibility', 'hidden');
333                 }
334             }
335         });
336         list_form_widgets.add(key, new_path);
337     });
338     
339     openerp.web.ListEditableFormView = openerp.web.FormView.extend({
340         init_view: function() {},
341         _render_and_insert: function () {
342             return this.start();
343         }
344     });
345 };