[IMP] kanban :- donot scroll when draging record out side of page.
[odoo/odoo.git] / addons / web_kanban / static / src / js / kanban.js
1 openerp.web_kanban = function (openerp) {
2
3 var QWeb = openerp.web.qweb;
4 QWeb.add_template('/web_kanban/static/src/xml/web_kanban.xml');
5 openerp.web.views.add('kanban', 'openerp.web_kanban.KanbanView');
6 openerp.web_kanban.KanbanView = openerp.web.View.extend({
7     init: function (parent, dataset, view_id, options) {
8         this._super(parent);
9         this.set_default_options(options);
10         this.dataset = dataset;
11         this.model = dataset.model;
12         this.domain = dataset.domain;
13         this.context = dataset.context;
14         this.view_id = view_id;
15         this.fields_view = {};
16         this.group_by = [];
17         this.source_index = {};
18         this.all_display_data = false;
19         this.groups = [];
20         this.qweb = new QWeb2.Engine();
21         this.aggregates = {};
22         this.NO_OF_COLUMNS = 3;
23         if (this.options.action_views_ids.form) {
24             this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start();
25             this.form_dialog.on_form_dialog_saved.add_last(this.on_record_saved);
26         }
27     },
28     start: function() {
29         this._super();
30         return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type": "kanban"}, this.on_loaded);
31     },
32     on_loaded: function(data) {
33         var self = this;
34         this.fields_view = data;
35         this.add_qweb_template();
36         if (this.qweb.has_template('kanban-box')) {
37             this.do_actual_search();
38         }
39     },
40     add_qweb_template: function() {
41         var group_operator = ["avg", "max", "min", "sum", "count"]
42         for (var i=0, ii=this.fields_view.arch.children.length; i < ii; i++) {
43             var child = this.fields_view.arch.children[i];
44             if (child.tag === "field") {
45                 for(j=0, jj=group_operator.length; j < jj;  j++) {
46                     if (child.attrs[group_operator[j]]) {
47                         this.aggregates[child.attrs.name] = child.attrs[group_operator[j]];
48                         break;
49                     }
50                 }
51             }
52             if (child.tag === "templates") {
53                 this.transform_qweb_template(child);
54                 this.qweb.add_template(openerp.web.json_node_to_xml(child));
55                 break;
56             }
57         }
58     },
59     kanban_color: function(variable) {
60         var number_of_color_schemes = 8,
61             index = 0;
62         switch (typeof(variable)) {
63             case 'string':
64                 for (var i=0, ii=variable.length; i<ii; i++) {
65                     index += variable.charCodeAt(i);
66                 }
67                 break;
68             case 'number':
69                 index = Math.round(variable);
70                 break;
71             default:
72                 return '';
73         }
74         return 'oe_kanban_color_' + ((index % number_of_color_schemes) || number_of_color_schemes);
75     },
76     kanban_gravatar: function(email, size) {
77         size = size || 22;
78         var email_md5 = '2eb60ad22dadcf4dc456b28390a80268';
79         return 'http://www.gravatar.com/avatar/' + email_md5 + '.png?s=' + size;
80     },
81     transform_qweb_template: function(node) {
82         var qweb_prefix = QWeb.prefix;
83         switch (node.tag) {
84             case 'field':
85                 node.tag = 't';
86                 node.attrs['t-esc'] = 'record.' + node.attrs['name'] + '.value';
87                 break
88             case 'button':
89             case 'a':
90                 var type = node.attrs.type || '';
91                 if (_.indexOf('action,object,edit,delete,color'.split(','), type) !== -1) {
92                     _.each(node.attrs, function(v, k) {
93                         if (_.indexOf('icon,type,name,string,context,states'.split(','), k) != -1) {
94                             node.attrs['data-' + k] = v;
95                             delete(node.attrs[k]);
96                         }
97                     });
98                     if (node.attrs['data-states']) {
99                         var states = _.map(node.attrs['data-states'].split(','), function(state) {
100                             return "record.state.value == '" + _.trim(state) + "'";
101                         });
102                         node.attrs['t-if'] = states.join(' or ');
103                     }
104                     if (node.attrs['data-string']) {
105                         node.attrs.title = node.attrs['data-string'];
106                     }
107                     if (node.attrs['data-icon']) {
108                         node.children = [{
109                             tag: 'img',
110                             attrs: {
111                                 src: '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png',
112                                 width: '16',
113                                 height: '16'
114                             }
115                         }];
116                     }
117                     if (node.tag == 'a') {
118                         node.attrs.href = '#';
119                     } else {
120                         node.attrs.type = 'button';
121                     }
122                     node.attrs['class'] = (node.attrs['class'] || '') + ' oe_kanban_action oe_kanban_action_' + node.tag;
123                 }
124                 break;
125         }
126         if (node.children) {
127             for (var i = 0, ii = node.children.length; i < ii; i++) {
128                 this.transform_qweb_template(node.children[i]);
129             }
130         }
131     },
132     sort_group: function (first, second) {
133         if (first.header && second.header)
134         {
135             first = first.header.toLowerCase();
136             second = second.header.toLowerCase();
137             if (first > second) return 1;
138             else if (first < second) return -1;
139             else return 0;
140         }
141         else return 0;
142     },
143     on_show_data: function() {
144         var self = this;
145         if (!this.group_by.length) {
146             this.do_record_group();
147         }
148         self.all_display_data.sort(this.sort_group);
149         this.$element.html(QWeb.render("KanbanView", {"data": self.all_display_data}));
150         this.on_reload_kanban();
151         this.$element.find(".oe_vertical_text").hide();
152         var drag_handel = false;
153         if (this.$element.find(".oe_kanban_draghandle").length > 0) {
154             drag_handel = ".oe_kanban_draghandle";
155         }
156         if (!this.group_by.length) {
157             drag_handel = true;
158         }
159         this.$element.find(".oe_column").sortable({
160             connectWith: ".oe_column",
161             handle : drag_handel,
162             start: function(event, ui) {
163                 self.source_index['index'] = ui.item.index();
164                 self.source_index['column'] = ui.item.parent().attr('id');
165             },
166             stop: self.on_receive_record,
167             scroll: false
168         });
169         this.$element.find(".oe_column").disableSelection()
170         this.$element.find('button.oe_kanban_button_new').click(this.do_add_record);
171         this.$element.find(".fold-columns-icon").click(function(event) {
172             self.do_fold_unfold_columns(event, this.id);
173         });
174     },
175     do_fold_unfold_columns: function(event, element_id) {
176       var column_id = "column_" + element_id;
177       var column_element = this.$element.find("#" + column_id + " .oe_fold_column");
178       if (column_element.is(":hidden")) {
179           this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/minus-icon.png');
180           column_element.show();
181           this.$element.find("#" + column_id + ".oe_table_column").css("width",Math.round(99 / this.all_display_data.length) + "%");
182           this.$element.find("#" + column_id + ".oe_vertical_text").hide();
183       }
184       else{
185           this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/plus-icon.png');
186           column_element.hide();
187           this.$element.find("#" + column_id + ".oe_table_column").css("width","0.5%");
188           this.$element.find("#" + column_id + ".oe_vertical_text").show();
189       }
190
191     },
192     do_record_group: function() {
193         if (this.NO_OF_COLUMNS && this.all_display_data.length > 0) {
194             var records = this.all_display_data[0].records;
195             var record_per_group = Math.round((records).length / this.NO_OF_COLUMNS);
196             this.all_display_data = [];
197             for (var i=0, ii=this.NO_OF_COLUMNS; i < ii; i++) {
198                 this.all_display_data.push({'records': records.slice(0,record_per_group), 'value':i, 'header' : false, 'ids':[]});
199                 records.splice(0,record_per_group);
200             }
201         }
202     },
203     on_button_click: function (button_attrs, record_id) {
204         this.on_execute_button_click(this.dataset, button_attrs, record_id);
205     },
206     do_add_record: function() {
207         this.dataset.index = null;
208         this.do_switch_view('form');
209     },
210     do_edit_record: function(record_id) {
211         if (this.form_dialog) {
212             this.form_dialog.load_id(record_id);
213             this.form_dialog.open();
214         } else {
215             this.notification.warn("Kanban", "No form view defined for this object");
216         }
217     },
218     on_record_saved: function(r) {
219         var id = this.form_dialog.form.datarecord.id;
220         // TODO fme: reload record instead of all. need refactoring
221         this.do_actual_search();
222     },
223     do_change_color: function(record_id, $e) {
224         var self = this,
225             id = record_id,
226             colors = '#FFC7C7,#FFF1C7,#E3FFC7,#C7FFD5,#C7FFFF,#C7D5FF,#E3C7FF,#FFC7F1'.split(','),
227             $cpicker = $(QWeb.render('KanbanColorPicker', { colors : colors, columns: 2 }));
228         $e.after($cpicker);
229         $cpicker.mouseenter(function() {
230             clearTimeout($cpicker.data('timeoutId'));
231         }).mouseleave(function(evt) {
232             var timeoutId = setTimeout(function() { $cpicker.remove() }, 500);
233             $cpicker.data('timeoutId', timeoutId);
234         });
235         $cpicker.find('a').click(function() {
236             var data = {};
237             data[$e.data('name')] = $(this).data('color');
238             self.dataset.write(id, data, {}, function() {
239                 // TODO fme: reload record instead of all. need refactoring
240                 self.on_reload_record(id, data);
241             });
242             $cpicker.remove();
243         });
244     },
245     /**
246         Reload one record in view.
247         record_id : reload record id.
248         data : change value in particular record.
249     */
250     on_reload_record: function (record_id, data){
251         var self = this;
252         for (var i=0, ii=this.all_display_data.length; i < ii; i++) {
253             for(j=0, jj=this.all_display_data[i].records.length; j < jj;  j++) {
254                 if (this.all_display_data[i].records[j].id == record_id) {
255                     _.extend(this.all_display_data[i].records[j], data);
256                     this.$element.find("#main_" + record_id).children().remove();
257                     this.$element.find("#main_" + record_id).append(this.qweb.render('kanban-box', {
258                         record: this.do_transform_record(this.all_display_data[i].records[j]),
259                         kanban_color: this.kanban_color,
260                         kanban_gravatar: this.kanban_gravatar
261                     }));
262                     break;
263                 }
264             }
265         }
266         this.$element.find("#main_" + record_id + " .oe_kanban_action").click(this.on_action_clicked);
267         this.$element.find("#main_" + record_id + " .oe_kanban_box_show_onclick_trigger").click(function() {
268             $(this).parent("#main_" + record_id + " .oe_kanban_box").find(".oe_kanban_box_show_onclick").toggle();
269         });
270     },
271     do_delete: function (id) {
272         var self = this;
273         return $.when(this.dataset.unlink([id])).then(function () {
274             self.drop_records(id);
275         });
276     },
277     drop_records: function (id) {
278         var self = this;
279         _.each(self.all_display_data, function(data, index) {
280             _.each(data.records, function(record, index_row) {
281                 if (parseInt(record.id) == id) {
282                     self.all_display_data[index]['records'].splice(index_row, 1);
283                     self.all_display_data[index]['ids'].splice(index_row, 1);
284                     return false;
285                 }
286             });
287         });
288         self.$element.find("#main_" + id).remove();
289     },
290     on_execute_button_click: function (dataset, button_attrs, record_id) {
291         var self = this;
292         this.do_execute_action(
293             button_attrs, dataset,
294             record_id, function () {
295                 self.do_actual_search();
296             }
297         );
298     },
299     on_receive_record: function (event, ui) {
300         var self = this;
301         var from = ui.item.index();
302         var search_action = false;
303         var to = ui.item.prev().index() || 0;
304         if (!ui.item.attr("id")) {
305             return false;
306         }
307         // TODO fme: check what was this sequence
308         if (self.fields_view.fields.sequence != undefined && ((self.source_index.index >= 0 && self.source_index.index != from) ||
309                 (self.source_index.column && self.source_index.column != ui.item.parent().attr('id')))) {
310             var child_record = ui.item.parent().children();
311             var data, sequence = 1, index = to;
312             child_record.splice(0, to);
313             var flag = false;
314             if (to >= 0 && child_record) {
315                 var record_id = parseInt($(child_record).attr("id").split("_")[1]);
316                 if (record_id) {
317                     _.each(self.all_display_data, function(data, index) {
318                         _.each(data.records, function(record, index_row) {
319                             if(record_id == record.id && record.sequence) {
320                                 sequence = record.sequence;
321                                 flag = true;
322                                 return false;
323                             }
324                         });
325                         if(flag) {return false;}
326                     });
327                 }
328             }
329             _.each(child_record, function (child) {
330                 var child_id = parseInt($(child).attr("id").split("_")[1]);
331                 if (child_id) {
332                     flag = false;
333                     _.each(self.all_display_data, function(data, index) {
334                         _.each(data.records, function(record, index_row) {
335                             if(parseInt(record.id) == child_id) {
336                                 self.all_display_data[index]['records'][index_row]['sequence'] = sequence;
337                                 flag = true;
338                                 return false;
339                             }
340                         });
341                         if (flag) {return false;}
342                     });
343                     self.dataset.write(child_id, {sequence: sequence});
344                     sequence++;
345                     search_action = true;
346                 }
347             });
348         }
349         if (self.group_by.length > 0 && self.source_index.column && self.source_index.column != ui.item.parent().attr('id')) {
350             var value = ui.item.closest("td").attr("id");
351             if (value) {
352                 var data_val = {};
353                 var wirte_id = parseInt(ui.item.attr("id").split("_")[1]);
354                 value = value.split("_")[1];
355                 if (value == 'false') {
356                     value = false;
357                 }
358                 var update_record = false;
359                 _.each(self.all_display_data, function(data, index) {
360                     _.each(data.records, function(record, index_row) {
361                         if(parseInt(record.id) == wirte_id) {
362                             self.all_display_data[index]['records'][index_row][self.group_by[0]] = value;
363                             update_record = self.all_display_data[index]['records'].splice(index_row,1)
364                             return false;
365                         }
366                     });
367                     if (update_record) {return false;}
368                 });
369                 _.each(self.all_display_data, function(data, index) {
370                     if (data.value == value || (data.value == 'false' && value == false)) {
371                         self.all_display_data[index]['records'].push(update_record[0]);
372                     }
373                 });
374                 data_val[self.group_by[0]] = value;
375                 self.dataset.write(wirte_id, data_val);
376                 search_action = true;
377             }
378         }
379         if (search_action) {
380             self.on_reload_kanban();
381         }
382         this.source_index = {};
383     },
384     on_reload_kanban: function (){
385         var self = this;
386         _.each(self.all_display_data, function(data, index) {
387             if (data.records.length > 0){
388                 _.each(data.records, function(record) {
389                     self.$element.find("#main_" + record.id).children().remove();
390                     self.$element.find("#main_" + record.id).append(self.qweb.render('kanban-box', {
391                         record: self.do_transform_record(record),
392                         kanban_color: self.kanban_color,
393                         kanban_gravatar: self.kanban_gravatar
394                     }));
395                 });
396             }
397         });
398         this.$element.find('.oe_kanban_action').click(this.on_action_clicked);
399         this.$element.find('.oe_kanban_box_show_onclick_trigger').click(function() {
400             $(this).parent('.oe_kanban_box').find('.oe_kanban_box_show_onclick').toggle();
401         });
402     },
403     on_action_clicked: function(evt) {
404         var $action = $(evt.currentTarget),
405             record_id = parseInt($action.closest(".oe_kanban_record").attr("id").split('_')[1]),
406             type = $action.data('type');
407         if (type == 'delete') {
408             this.do_delete(record_id);
409         } else if (type == 'edit') {
410             this.do_edit_record(record_id);
411         } else if (type == 'color') {
412             this.do_change_color(record_id, $action);
413         } else {
414             var button_attrs = $action.data();
415             this.on_button_click(button_attrs, record_id);
416         }
417     },
418     do_transform_record: function(record) {
419         var self = this,
420             new_record = {};
421         _.each(record, function(value, name) {
422             var r = _.clone(self.fields_view.fields[name]);
423             r.raw_value = value;
424             r.value = openerp.web.format_value(value, r);
425             new_record[name] = r;
426         });
427         return new_record;
428     },
429     do_search: function (domains, contexts, group_by) {
430         var self = this;
431         this.rpc('/web/session/eval_domain_and_context', {
432             domains: [this.dataset.get_domain()].concat(domains),
433             contexts: [this.dataset.get_context()].concat(contexts),
434             group_by_seq: group_by
435         }, function (results) {
436             self.domain = results.domain;
437             self.context = results.context;
438             self.group_by = results.group_by;
439             self.do_actual_search();
440         });
441     },
442     do_actual_search : function () {
443         var self = this,
444             group_by = self.group_by;
445         if (!group_by.length && this.fields_view.arch.attrs.default_group_by) {
446             group_by = [this.fields_view.arch.attrs.default_group_by];
447             self.group_by = group_by;
448         }
449         self.datagroup = new openerp.web.DataGroup(self, self.model, self.domain, self.context, group_by);
450         self.datagroup.list([],
451             function (groups) {
452                 self.groups = groups;
453                 if (groups.length) {
454                     self.do_render_group(groups);
455                 }
456                 else {
457                     self.all_display_data = [];
458                     self.on_show_data();
459                 }
460             },
461             function (dataset) {
462                 self.groups = [];
463                 self.dataset.read_slice([], {'domain': self.domain, 'context': self.context}, function(records) {
464                     if (records.length) self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}];
465                     else self.all_display_data = [];
466                     self.$element.find(".oe_kanban_view").remove();
467                     self.on_show_data();
468                 });
469             }
470         );
471     },
472     do_render_group : function (datagroups) {
473         this.all_display_data = [];
474         var self = this;
475         _.each(datagroups, function (group) {
476             var group_name = group.value;
477             var group_value = group.value;
478             if (!group.value) {
479                 group_name = "Undefined";
480                 group_value = 'false';
481             } else if (group.value instanceof Array) {
482                 group_name = group.value[1];
483                 group_value = group.value[0];
484             }
485             var group_aggregates = '';
486             _.each(self.aggregates, function(value, key) {
487                 group_aggregates += value + ": " + group.aggregates[key];
488             });
489             self.dataset.read_slice([], {'domain': group.domain, 'context': group.context}, function(records) {
490                 self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids, 'aggregates': group_aggregates});
491                 if (datagroups.length == self.all_display_data.length) {
492                     self.$element.find(".oe_kanban_view").remove();
493                     self.on_show_data();
494                 }
495             });
496         });
497     },
498     do_show: function () {
499         this.$element.show();
500     },
501     do_hide: function () {
502         this.$element.hide();
503     }
504 });
505 };
506
507 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: