multiple types
[kivitendo-erp.git] / js / autocomplete_part.js
1 namespace('kivi', function(k){
2   k.PartPickerCache = { }
3   k.PartPicker = function($real, options) {
4     var o = $.extend({
5       limit: 20,
6       delay: 50,
7     }, options);
8     var STATES = {
9       UNIQUE: 1,
10       UNDEFINED: 0,
11     }
12     var real_id = $real.attr('id');
13     var $dummy  = $('#' + real_id + '_name');
14     var $type   = $('#' + real_id + '_type');
15     var $column = $('#' + real_id + '_column');
16     var state   = STATES.PICKED;
17     var last_real = $real.val();
18     var last_dummy = $dummy.val();
19     var open_dialog = function(){
20       open_jqm_window({
21         url: 'controller.pl?action=Part/part_picker_search',
22         data: $.extend({
23           real_id: real_id,
24         }, ajax_data($dummy.val())),
25         id: 'part_selection',
26       });
27       return true;
28     };
29
30     function ajax_data(term) {
31       return {
32         'filter.all:substr::ilike': term,
33         // due to a bug in $.param functions returning arrays don't get serialized correctly, so call it manually
34         'filter.type':  $type.val().split(','),
35         'filter.obsolete': 0,
36         column:   $column.val()===undefined ? '' : $column.val(),
37         current:  $real.val(),
38       }
39     }
40
41     function set_item (item) {
42       if (item.id) {
43         $real.val(item.id);
44         // autocomplete ui has name, ajax items have description
45         $dummy.val(item.name ? item.name : item.description);
46       } else {
47         $real.val('');
48         $dummy.val('');
49       }
50       state = STATES.PICKED;
51       last_real = $real.val();
52       last_dummy = $dummy.val();
53       $real.trigger('change');
54     }
55
56     function make_defined_state () {
57       if (state == STATES.PICKED)
58         return true
59       else if (state == STATES.UNDEFINED && $dummy.val() == '')
60         set_item({})
61       else
62         set_item({ id: last_real, name: last_dummy })
63     }
64
65     function update_results () {
66       $.ajax({
67         url: 'controller.pl?action=Part/part_picker_result',
68         data: $.extend({
69             'real_id': $real.val(),
70         }, ajax_data(function(){ var val = $('#part_picker_filter').val(); return val === undefined ? '' : val })),
71         success: function(data){ $('#part_picker_result').html(data) }
72       });
73     };
74
75     function close_popup() {
76       $('#part_selection').jqmClose()
77     };
78
79     $dummy.autocomplete({
80       source: function(req, rsp) {
81         $.ajax($.extend(o, {
82           url:      'controller.pl?action=Part/ajax_autocomplete',
83           dataType: "json",
84           data:     ajax_data(req.term),
85           success:  function (data){ rsp(data) }
86         }));
87       },
88       select: function(event, ui) {
89         set_item(ui.item);
90       },
91     });
92     /*  In case users are impatient and want to skip ahead:
93      *  Capture <enter> key events and check if it's a unique hit.
94      *  If it is, go ahead and assume it was selected. If it wasn't don't do
95      *  anything so that autocompletion kicks in.  For <tab> don't prevent
96      *  propagation. It would be nice to catch it, but javascript is too stupid
97      *  to fire a tab event later on, so we'd have to reimplement the "find
98      *  next active element in tabindex order and focus it".
99      */
100     $dummy.keypress(function(event){
101       if (event.keyCode == 13 || event.keyCode == 9) { // enter or tab or tab
102         // if string is empty assume they want to delete
103         if ($dummy.val() == '') {
104           set_item({});
105           return true;
106         } else if (state == STATES.PICKED) {
107           return true;
108         }
109         $.ajax({
110           url: 'controller.pl?action=Part/ajax_autocomplete',
111           dataType: "json",
112           data: $.extend( ajax_data($dummy.val()), { prefer_exact: 1 } ),
113           success: function (data){
114             if (data.length == 1) {
115               set_item(data[0]);
116               if (event.keyCode == 13)
117                 $('#update_button').click();
118             } else if (data.length > 1) {
119              if (event.keyCode == 13)
120                 open_dialog();
121               else
122                 make_defined_state();
123             } else {
124               if (event.keyCode == 9)
125                 make_defined_state();
126             }
127           }
128         });
129         if (event.keyCode == 13)
130           return false;
131       } else {
132         state = STATES.UNDEFINED;
133       }
134     });
135
136 //    $dummy.blur(make_defined_state);  // blur triggers also on open_jqm_dialog
137
138     // now add a picker div after the original input
139     var pcont  = $('<span>').addClass('position-absolute');
140     var picker = $('<div>');
141     $dummy.after(pcont);
142     pcont.append(picker);
143     picker.addClass('icon16 CRM--Schnellsuche').click(open_dialog);
144
145     return {
146       real:           function() { return $real },
147       dummy:          function() { return $dummy },
148       type:           function() { return $type },
149       column:         function() { return $column },
150       update_results: update_results,
151       set_item:       set_item,
152       reset:          make_defined_state,
153       init_results:    function () {
154         $('div.part_picker_part').each(function(){
155           $(this).click(function(){
156             set_item({
157               name: $(this).children('input.part_picker_description').val(),
158               id:   $(this).children('input.part_picker_id').val(),
159             });
160             close_popup();
161             return true;
162           });
163         });
164         $('#part_selection').keypress(function(e){
165            if (e.keyCode == 27) { // escape
166              close_popup();
167              $dummy.focus();
168            }
169         });
170       }
171     }
172   }
173 });
174
175 $(function(){
176   $('input.part_autocomplete').each(function(i,real){
177     kivi.PartPickerCache[real.id] = new kivi.PartPicker($(real));
178   })
179 });