[imp] implemented quick_create_enabled an create_enabled
[odoo/odoo.git] / addons / web_kanban / static / src / js / kanban.js
1 openerp.web_kanban = function (instance) {
2
3 var _t = instance.web._t,
4    _lt = instance.web._lt;
5 var QWeb = instance.web.qweb;
6 instance.web.views.add('kanban', 'instance.web_kanban.KanbanView');
7
8 instance.web_kanban.KanbanView = instance.web.View.extend({
9     template: "KanbanView",
10     display_name: _lt('Kanban'),
11     default_nr_columns: 3,
12     view_type: "kanban",
13     init: function (parent, dataset, view_id, options) {
14         this._super(parent, dataset, view_id, options);
15         _.defaults(this.options, {"quick_creatable": true, "creatable": true});
16         this.fields_view = {};
17         this.fields_keys = [];
18         this.group_by = null;
19         this.state = {
20             groups : {},
21             records : {}
22         };
23         this.groups = [];
24         this.form_dialog = new instance.web.form.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start();
25         this.form_dialog.on_form_dialog_saved.add_last(this.do_reload);
26         this.aggregates = {};
27         this.group_operators = ['avg', 'max', 'min', 'sum', 'count'];
28         this.qweb = new QWeb2.Engine();
29         this.qweb.debug = instance.connection.debug;
30         this.qweb.default_dict = _.clone(QWeb.default_dict);
31         this.has_been_loaded = $.Deferred();
32         this.search_domain = this.search_context = this.search_group_by = null;
33         this.currently_dragging = {};
34         this.limit = options.limit || 80;
35         this.add_group_mutex = new $.Mutex();
36     },
37     on_loaded: function(data) {
38         this.fields_view = data;
39         this.$buttons = $(QWeb.render("KanbanView.buttons", {'widget': this}));
40         if (this.options.$buttons) {
41             this.$buttons.appendTo(this.options.$buttons);
42         } else {
43             this.$element.find('.oe_kanban_buttons').replaceWith(this.$buttons);
44         }
45         this.$buttons
46             .on('click','button.oe_kanban_button_new', this.do_add_record);
47         this.$groups = this.$element.find('.oe_kanban_groups tr');
48         this.fields_keys = _.keys(this.fields_view.fields);
49         this.add_qweb_template();
50         this.has_been_loaded.resolve();
51         return $.when();
52     },
53     _is_quick_create_enabled: function() {
54         if (! this.options.quick_creatable)
55             return false;
56         if (this.fields_view.arch.attrs.quick_create !== undefined)
57             return JSON.parse(this.fields_view.arch.attrs.quick_create);
58         return !! this.group_by;
59     },
60     _is_create_enabled: function() {
61         if (! this.options.creatable)
62             return false;
63         if (this.fields_view.arch.attrs.create !== undefined)
64             return JSON.parse(this.fields_view.arch.attrs.create);
65         return true;
66     },
67     add_qweb_template: function() {
68         for (var i=0, ii=this.fields_view.arch.children.length; i < ii; i++) {
69             var child = this.fields_view.arch.children[i];
70             if (child.tag === "templates") {
71                 this.transform_qweb_template(child);
72                 this.qweb.add_template(instance.web.json_node_to_xml(child));
73                 break;
74             } else if (child.tag === 'field') {
75                 this.extract_aggregates(child);
76             }
77         }
78     },
79     extract_aggregates: function(node) {
80         for (var j = 0, jj = this.group_operators.length; j < jj;  j++) {
81             if (node.attrs[this.group_operators[j]]) {
82                 this.aggregates[node.attrs.name] = node.attrs[this.group_operators[j]];
83                 break;
84             }
85         }
86     },
87     transform_qweb_template: function(node) {
88         var qweb_prefix = QWeb.prefix;
89         switch (node.tag) {
90             case 'field':
91                 node.tag = qweb_prefix;
92                 node.attrs[qweb_prefix + '-esc'] = 'record.' + node.attrs['name'] + '.value';
93                 this.extract_aggregates(node);
94                 break;
95             case 'button':
96             case 'a':
97                 var type = node.attrs.type || '';
98                 if (_.indexOf('action,object,edit,delete,color'.split(','), type) !== -1) {
99                     _.each(node.attrs, function(v, k) {
100                         if (_.indexOf('icon,type,name,args,string,context,states,kanban_states'.split(','), k) != -1) {
101                             node.attrs['data-' + k] = v;
102                             delete(node.attrs[k]);
103                         }
104                     });
105                     if (node.attrs['data-states']) {
106                         var states = _.map(node.attrs['data-states'].split(','), function(state) {
107                             return "record.state.raw_value == '" + _.str.trim(state) + "'";
108                         });
109                         node.attrs[qweb_prefix + '-if'] = states.join(' or ');
110                     }
111                     if (node.attrs['data-kanban_states']) {
112                         var states = _.map(node.attrs['data-kanban_states'].split(','), function(state) {
113                             return "record.kanban_state.raw_value == '" + _.str.trim(state) + "'";
114                         });
115                         node.attrs[qweb_prefix + '-if'] = states.join(' or ');
116                     }
117                     if (node.attrs['data-string']) {
118                         node.attrs.title = node.attrs['data-string'];
119                     }
120                     if (node.attrs['data-icon']) {
121                         node.children = [{
122                             tag: 'img',
123                             attrs: {
124                                 src: instance.connection.prefix + '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png',
125                                 width: '16',
126                                 height: '16'
127                             }
128                         }];
129                     }
130                     if (node.tag == 'a') {
131                         node.attrs.href = '#';
132                     } else {
133                         node.attrs.type = 'button';
134                     }
135                     node.attrs['class'] = (node.attrs['class'] || '') + ' oe_kanban_action oe_kanban_action_' + node.tag;
136                 }
137                 break;
138         }
139         if (node.children) {
140             for (var i = 0, ii = node.children.length; i < ii; i++) {
141                 this.transform_qweb_template(node.children[i]);
142             }
143         }
144     },
145     do_add_record: function() {
146         this.dataset.index = null;
147         this.do_switch_view('form');
148     },
149     do_search: function(domain, context, group_by) {
150         var self = this;
151         this.$element.find('.oe_view_nocontent').remove();
152         this.search_domain = domain;
153         this.search_context = context;
154         this.search_group_by = group_by;
155         $.when(this.has_been_loaded).then(function() {
156             self.group_by = group_by.length ? group_by[0] : self.fields_view.arch.attrs.default_group_by;
157             self.datagroup = new instance.web.DataGroup(self, self.dataset.model, domain, context, self.group_by ? [self.group_by] : []);
158             self.datagroup.list(self.fields_keys, self.do_process_groups, self.do_process_dataset);
159         });
160     },
161     do_process_groups: function(groups) {
162         var self = this;
163         this.add_group_mutex.exec(function() {
164             self.do_clear_groups();
165             self.dataset.ids = [];
166             var remaining = groups.length - 1,
167                 groups_array = [];
168             return $.when.apply(null, _.map(groups, function (group, index) {
169                 var dataset = new instance.web.DataSetSearch(self, self.dataset.model, group.context, group.domain);
170                 return dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit })
171                     .pipe(function(records) {
172                         self.dataset.ids.push.apply(self.dataset.ids, dataset.ids);
173                         groups_array[index] = new instance.web_kanban.KanbanGroup(self, records, group, dataset);
174                         if (!remaining--) {
175                             self.dataset.index = self.dataset.size() ? 0 : null;
176                             return self.do_add_groups(groups_array);
177                         }
178                 });
179             }));
180         });
181     },
182     do_process_dataset: function(dataset) {
183         var self = this;
184         this.add_group_mutex.exec(function() {
185             var def = $.Deferred();
186             self.do_clear_groups();
187             self.dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) {
188                 if (_.isEmpty(records)) {
189                     self.no_result();
190                     def.reject();
191                 } else {
192                     var kgroup = new instance.web_kanban.KanbanGroup(self, records, null, self.dataset);
193                     self.do_add_groups([kgroup]).then(function() {
194                         def.resolve();
195                     });
196                 }
197             }).then(null, function() {
198                 def.reject();
199             });
200             return def;
201         });
202     },
203     do_reload: function() {
204         this.do_search(this.search_domain, this.search_context, this.search_group_by);
205     },
206     do_clear_groups: function() {
207         _.each(this.groups, function(group) {
208             group.destroy();
209         });
210         this.groups = [];
211         this.$element.find('.oe_kanban_groups_headers, .oe_kanban_groups_records').empty();
212     },
213     do_add_groups: function(groups) {
214         var self = this;
215         _.each(groups, function(group) {
216             self.groups[group.undefined_title ? 'unshift' : 'push'](group);
217         });
218         var groups_started = _.map(this.groups, function(group) {
219             return group.appendTo(self.$element.find('.oe_kanban_groups_headers'));
220         });
221         return $.when.apply(null, groups_started).then(function () {
222             self.on_groups_started();
223         });
224     },
225     on_groups_started: function() {
226         var self = this;
227         this.compute_groups_width();
228         if (this.group_by) {
229             this.$element.find('.oe_kanban_column').sortable({
230                 connectWith: '.oe_kanban_column',
231                 handle : '.oe_kanban_draghandle',
232                 start: function(event, ui) {
233                     self.currently_dragging.index = ui.item.index();
234                     self.currently_dragging.group = ui.item.parents('.oe_kanban_column:first').data('widget');
235                 },
236                 stop: function(event, ui) {
237                     var record = ui.item.data('widget'),
238                         old_index = self.currently_dragging.index,
239                         new_index = ui.item.index(),
240                         old_group = self.currently_dragging.group,
241                         new_group = ui.item.parents('.oe_kanban_column:first').data('widget');
242                     if (!(old_group.title === new_group.title && old_group.value === new_group.value && old_index == new_index)) {
243                         self.on_record_moved(record, old_group, old_index, new_group, new_index);
244                     }
245                 },
246                 scroll: false
247             });
248         } else {
249             this.$element.find('.oe_kanban_draghandle').removeClass('oe_kanban_draghandle');
250         }
251     },
252     on_record_moved : function(record, old_group, old_index, new_group, new_index) {
253         var self = this;
254         $.fn.tipsy.clear();
255         $(old_group.$element).add(new_group.$element).find('.oe_kanban_aggregates, .oe_kanban_group_length').hide();
256         if (old_group === new_group) {
257             new_group.records.splice(old_index, 1);
258             new_group.records.splice(new_index, 0, record);
259             new_group.do_save_sequences();
260         } else {
261             old_group.records.splice(old_index, 1);
262             new_group.records.splice(new_index, 0, record);
263             record.group = new_group;
264             var data = {};
265             data[this.group_by] = new_group.value;
266             this.dataset.write(record.id, data, {}, function() {
267                 record.do_reload();
268                 new_group.do_save_sequences();
269             }).fail(function(error, evt) {
270                 evt.preventDefault();
271                 alert("An error has occured while moving the record to this group.");
272                 self.do_reload(); // TODO: use draggable + sortable in order to cancel the dragging when the rcp fails
273             });
274         }
275     },
276     compute_groups_width: function() {
277         var unfolded = 0;
278         _.each(this.groups, function(group) {
279             unfolded += group.state.folded ? 0 : 1;
280             group.$element.css('width', '');
281         });
282         _.each(this.groups, function(group) {
283             if (!group.state.folded) {
284                 group.$element.css('width', Math.round(100/unfolded) + '%');
285             }
286         });
287     },
288
289     do_show: function() {
290         if (this.$buttons) {
291             this.$buttons.show();
292         }
293         this.do_push_state({});
294         return this._super();
295     },
296     do_hide: function () {
297         if (this.$buttons) {
298             this.$buttons.hide();
299         }
300         return this._super();
301     },
302     open_record: function(id) {
303         if (this.dataset.select_id(id)) {
304             this.do_switch_view('form');
305         } else {
306             this.do_warn("Kanban: could not find id#" + id);
307         }
308     },
309     no_result: function() {
310         if (this.groups.group_by
311             || !this.options.action
312             || !this.options.action.help) {
313             return;
314         }
315         this.$element.find('.oe_view_nocontent').remove();
316         this.$element.prepend(
317             $('<div class="oe_view_nocontent">')
318                 .append($('<img>', { src: '/web/static/src/img/view_empty_arrow.png' }))
319                 .append($('<div>').html(this.options.action.help))
320         );
321     }
322 });
323
324 instance.web_kanban.KanbanGroup = instance.web.OldWidget.extend({
325     template: 'KanbanView.group_header',
326     init: function (parent, records, group, dataset) {
327         var self = this;
328         this._super(parent);
329         this.$has_been_started = $.Deferred();
330         this.view = parent;
331         this.group = group;
332         this.dataset = dataset;
333         this.dataset_offset = 0;
334         this.aggregates = {};
335         this.value = this.title = null;
336         if (this.group) {
337             this.value = group.value;
338             this.title = group.value;
339             if (this.value instanceof Array) {
340                 this.title = this.value[1];
341                 this.value = this.value[0];
342             }
343             var field = this.view.fields_view.fields[this.view.group_by];
344             if (field) {
345                 try {
346                     this.title = instance.web.format_value(group.value, field, false);
347                 } catch(e) {}
348             }
349             _.each(this.view.aggregates, function(value, key) {
350                 self.aggregates[value] = group.aggregates[key];
351             });
352         }
353
354         if (this.title === false) {
355             this.title = _t('Undefined');
356             this.undefined_title = true;
357         }
358         var key = this.view.group_by + '-' + this.value;
359         if (!this.view.state.groups[key]) {
360             this.view.state.groups[key] = {
361                 folded: false
362             }
363         }
364         this.state = this.view.state.groups[key];
365         this.$records = null;
366
367         this.records = [];
368         this.$has_been_started.then(function() {
369             self.do_add_records(records);
370         });
371     },
372     start: function() {
373         var self = this,
374             def = this._super();
375         if (! self.view.group_by) {
376             self.$element.addClass("oe_kanban_no_group");
377             self.quick = new instance.web_kanban.QuickCreate(this, self.dataset, {}, false)
378                 .on('added', self, self.proxy('quick_created'));
379             self.quick.replace($(".oe_kanban_no_group_qc_placeholder"));
380         }
381         this.$records = $(QWeb.render('KanbanView.group_records_container', { widget : this}));
382         this.$records.appendTo(this.view.$element.find('.oe_kanban_groups_records'));
383         this.$element.find(".oe_kanban_fold_icon").click(function() {
384             self.do_toggle_fold();
385             self.view.compute_groups_width();
386             return false;
387         });
388         this.$element.find('.oe_kanban_add').click(function () {
389             if (self.quick) { return; }
390             var ctx = {};
391             ctx['default_' + self.view.group_by] = self.value;
392             self.quick = new instance.web_kanban.QuickCreate(this, self.dataset, ctx, true)
393                 .on('added', self, self.proxy('quick_created'))
394                 .on('close', self, function() {
395                     this.quick.destroy();
396                     delete this.quick;
397                 });
398             self.quick.appendTo(self.$element.find('.oe_kanban_group_header'));
399             self.quick.focus();
400         });
401         this.$records.find('.oe_kanban_show_more').click(this.do_show_more);
402         if (this.state.folded) {
403             this.do_toggle_fold();
404         }
405         this.$element.data('widget', this);
406         this.$records.data('widget', this);
407         this.$has_been_started.resolve();
408         this.compute_cards_height();
409         return def;
410     },
411     compute_cards_height: function() {
412         var self = this;
413         var min_height = 0;
414         _.each(this.records, function(r) {
415             min_height = Math.max(min_height, r.$element.outerHeight());
416         });
417         _.each(this.records, function(r) {
418             r.$element.css('min-height', min_height);
419         });
420     },
421     destroy: function() {
422         this._super();
423         if (this.$records) {
424             this.$records.remove();
425         }
426     },
427     do_show_more: function(evt) {
428         var self = this;
429         this.dataset.read_slice(this.view.fields_keys.concat(['__last_update']), {
430             'limit': self.view.limit,
431             'offset': self.dataset_offset += self.view.limit
432         }).then(this.do_add_records);
433     },
434     do_add_records: function(records, prepend) {
435         var self = this;
436         _.each(records, function(record) {
437             var rec = new instance.web_kanban.KanbanRecord(self, record);
438             if (!prepend) {
439                 rec.insertBefore(self.$records.find('.oe_kanban_show_more'));
440                 self.records.push(rec);
441             } else {
442                 rec.prependTo(self.$records);
443                 self.records.unshift(rec);
444             }
445         });
446         this.$records.find('.oe_kanban_show_more').toggle(this.records.length < this.dataset.size())
447             .find('.oe_kanban_remaining').text(this.dataset.size() - this.records.length);
448     },
449     remove_record: function(id, remove_from_dataset) {
450         for (var i = 0, ii = this.records.length; i < ii; i++) {
451             if (this.records[i]['id'] === id) {
452                 this.records.splice(i, 1);
453             }
454         }
455     },
456     do_toggle_fold: function(compute_width) {
457         this.$element.add(this.$records).toggleClass('oe_kanban_group_folded');
458         this.state.folded = this.$element.is('.oe_kanban_group_folded');
459     },
460     do_save_sequences: function() {
461         var self = this;
462         if (_.indexOf(this.view.fields_keys, 'sequence') > -1) {
463             _.each(this.records, function(record, index) {
464                 self.view.dataset.write(record.id, { sequence : index });
465             });
466         }
467     },
468     /**
469      * Handles a non-erroneous response from name_create
470      *
471      * @param {(Id, String)} record name_get format for the newly created record
472      */
473     quick_created: function (record) {
474         var id = record[0], self = this;
475         this.dataset.read_ids([id], this.view.fields_keys)
476             .then(function (records) {
477                 self.view.dataset.ids.push(id);
478                 self.do_add_records(records, 'prepend');
479             });
480     }
481 });
482
483 instance.web_kanban.KanbanRecord = instance.web.OldWidget.extend({
484     template: 'KanbanView.record',
485     init: function (parent, record) {
486         this._super(parent);
487         this.group = parent;
488         this.view = parent.view;
489         this.id = null;
490         this.set_record(record);
491         if (!this.view.state.records[this.id]) {
492             this.view.state.records[this.id] = {
493                 folded: false
494             };
495         }
496         this.state = this.view.state.records[this.id];
497     },
498     set_record: function(record) {
499         this.id = record.id;
500         this.record = this.transform_record(record);
501     },
502     start: function() {
503         this._super();
504         this.$element.data('widget', this);
505         this.bind_events();
506     },
507     transform_record: function(record) {
508         var self = this,
509             new_record = {};
510         _.each(record, function(value, name) {
511             var r = _.clone(self.view.fields_view.fields[name] || {});
512             if ((r.type === 'date' || r.type === 'datetime') && value) {
513                 r.raw_value = instance.web.auto_str_to_date(value);
514             } else {
515                 r.raw_value = value;
516             }
517             r.value = instance.web.format_value(value, r);
518             new_record[name] = r;
519         });
520         return new_record;
521     },
522     render: function() {
523         this.qweb_context = {
524             record: this.record,
525             widget: this
526         };
527         for (var p in this) {
528             if (_.str.startsWith(p, 'kanban_')) {
529                 this.qweb_context[p] = _.bind(this[p], this);
530             }
531         }
532         return this._super({
533             'content': this.view.qweb.render('kanban-box', this.qweb_context)
534         });
535     },
536     bind_events: function() {
537         var self = this,
538             $show_on_click = self.$element.find('.oe_kanban_box_show_onclick');
539         $show_on_click.toggle(this.state.folded);
540         this.$element.find('.oe_kanban_box_show_onclick_trigger').click(function() {
541             $show_on_click.toggle();
542             self.state.folded = !self.state.folded;
543         });
544
545         this.$element.find('[tooltip]').tipsy({
546             delayIn: 500,
547             delayOut: 0,
548             fade: true,
549             title: function() {
550                 var template = $(this).attr('tooltip');
551                 if (!self.view.qweb.has_template(template)) {
552                     return false;
553                 }
554                 return self.view.qweb.render(template, self.qweb_context);
555             },
556             gravity: 's',
557             html: true,
558             opacity: 0.8,
559             trigger: 'hover'
560         });
561
562         this.$element.find('.oe_kanban_action').click(function() {
563             var $action = $(this),
564                 type = $action.data('type') || 'button',
565                 method = 'do_action_' + (type === 'action' ? 'object' : type);
566             if (_.str.startsWith(type, 'switch_')) {
567                 self.view.do_switch_view(type.substr(7));
568             } else if (typeof self[method] === 'function') {
569                 self[method]($action);
570             } else {
571                 self.do_warn("Kanban: no action for type : " + type);
572             }
573             return false;
574         });
575     },
576     do_action_delete: function($action) {
577         var self = this;
578         if (confirm(_t("Are you sure you want to delete this record ?"))) {
579             return $.when(this.view.dataset.unlink([this.id])).then(function() {
580                 self.group.remove_record(self.id);
581                 self.destroy();
582             });
583         }
584     },
585     do_action_edit: function($action) {
586         var self = this;
587         if ($action.attr('target') === 'dialog') {
588             this.view.form_dialog.select_id(this.id).then(function() {
589                 self.view.form_dialog.open();
590             });
591         } else {
592             this.view.open_record(this.id);
593         }
594     },
595     do_action_color: function($action) {
596         var self = this,
597             colors = '#FFFFFF,#CCCCCC,#FFC7C7,#FFF1C7,#E3FFC7,#C7FFD5,#C7FFFF,#C7D5FF,#E3C7FF,#FFC7F1'.split(','),
598             $cpicker = $(QWeb.render('KanbanColorPicker', { colors : colors, columns: 2 }));
599         $action.after($cpicker);
600         $cpicker.mouseenter(function() {
601             clearTimeout($cpicker.data('timeoutId'));
602         }).mouseleave(function(evt) {
603             var timeoutId = setTimeout(function() { $cpicker.remove() }, 500);
604             $cpicker.data('timeoutId', timeoutId);
605         });
606         $cpicker.find('a').click(function() {
607             var data = {};
608             data[$action.data('name')] = $(this).data('color');
609             self.view.dataset.write(self.id, data, {}, function() {
610                 self.record[$action.data('name')] = $(this).data('color');
611                 self.do_reload();
612             });
613             $cpicker.remove();
614             return false;
615         });
616     },
617     do_action_object: function ($action) {
618         var button_attrs = $action.data();
619         this.view.do_execute_action(button_attrs, this.view.dataset, this.id, this.do_reload);
620     },
621     do_reload: function() {
622         var self = this;
623         this.view.dataset.read_ids([this.id], this.view.fields_keys.concat(['__last_update'])).then(function(records) {
624             if (records.length) {
625                 self.set_record(records[0]);
626                 self.do_render();
627             } else {
628                 self.destroy();
629             }
630         });
631     },
632     do_render: function() {
633         this.$element.html(this.render());
634         this.bind_events();
635     },
636     kanban_color: function(variable) {
637         var number_of_color_schemes = 10,
638             index = 0;
639         switch (typeof(variable)) {
640             case 'string':
641                 for (var i=0, ii=variable.length; i<ii; i++) {
642                     index += variable.charCodeAt(i);
643                 }
644                 break;
645             case 'number':
646                 index = Math.round(variable);
647                 break;
648             default:
649                 return '';
650         }
651         var color = (index % number_of_color_schemes);
652         return 'oe_kanban_color_' + color;
653     },
654     kanban_gravatar: function(email, size) {
655         size = size || 22;
656         email = _.str.trim(email || '').toLowerCase();
657         var default_ = _.str.isBlank(email) ? 'mm' : 'identicon';
658         var email_md5 = $.md5(email);
659         return 'http://www.gravatar.com/avatar/' + email_md5 + '.png?s=' + size + '&d=' + default_;
660     },
661     kanban_image: function(model, field, id) {
662         id = id || '';
663         var url = instance.connection.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + id;
664         if (this.record.__last_update && this.record.__last_update.raw_value) {
665             var time = instance.web.str_to_datetime(this.record.__last_update.raw_value).getTime();
666             url += '&t=' + time;
667         }
668         return url;
669     },
670     kanban_text_ellipsis: function(s, size) {
671         size = size || 160;
672         if (!s) {
673             return '';
674         } else if (s.length <= size) {
675             return s;
676         } else {
677             return s.substr(0, size) + '...';
678         }
679     }
680 });
681
682 /**
683  * Quick creation view.
684  *
685  * Triggers a single event "added" with a single parameter "name", which is the
686  * name entered by the user
687  *
688  * @class
689  * @type {*}
690  */
691 instance.web_kanban.QuickCreate = instance.web.Widget.extend({
692     template: 'KanbanView.quick_create',
693     
694     /**
695      * close_btn: If true, the widget will display a "Close" button able to trigger
696      * a "close" event.
697      */
698     init: function(parent, dataset, context, buttons) {
699         this._super(parent);
700         this._dataset = dataset;
701         this._buttons = buttons || false;
702         this._context = context || {};
703     },
704     start: function () {
705         var self = this;
706         self.$input = this.$element.find('input');
707         self.$input.keyup(function(event){
708             if(event.keyCode == 13){
709                 self.quick_add();
710             }
711         });
712         $(".oe-kanban-quick_create_add", this.$element).click(function () {
713             self.quick_add();
714         });
715         $(".oe-kanban-quick_create_close", this.$element).click(function () {
716             self.trigger('close');
717         });
718     },
719     focus: function() {
720         this.$element.find('input').focus();
721     },
722     /**
723      * Handles user event from nested quick creation view
724      */
725     quick_add: function () {
726         var self = this;
727         this._dataset.call(
728             'name_create', [self.$input.val(), new instance.web.CompoundContext(
729                     this._dataset.get_context(), this._context)])
730             .pipe(function(record) {
731                 self.$input.val("");
732                 self.trigger('added', record);
733             }, function(error, event) {
734                 event.preventDefault();
735                 return self.slow_create();
736             });
737     },
738     slow_create: function() {
739         var self = this;
740         var pop = new instance.web.form.SelectCreatePopup(this);
741         pop.select_element(
742             self._dataset.model,
743             {
744                 title: _t("Create: ") + (this.string || this.name),
745                 initial_view: "form",
746                 disable_multiple_selection: true
747             },
748             [],
749             {"default_name": self.$input.val()}
750         );
751         pop.on_select_elements.add(function(element_ids) {
752             self.$input.val("");
753             self.trigger('added', element_ids);
754         });
755     }
756 });
757 };
758
759 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: