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