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