static/midcom.datamanager/autocomplete.js   F
last analyzed

Complexity

Total Complexity 69
Complexity/F 2.23

Size

Lines of Code 378
Function Count 31

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 69
eloc 259
c 0
b 0
f 0
dl 0
loc 378
rs 2.88
mnd 38
bc 38
fnc 31
bpm 1.2258
cpm 2.2258
noi 0

2 Functions

Rating   Name   Duplication   Size   Complexity  
C autocomplete.js ➔ filter_existing 0 8 9
F autocomplete.js ➔ remove_item 0 30 23

How to fix   Complexity   

Complexity

Complex classes like static/midcom.datamanager/autocomplete.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
$.widget( "custom.category_complete", $.ui.autocomplete, {
2
    _create: function() {
3
        this._super();
4
        this.widget().menu( "option", "items", "> :not(.ui-autocomplete-category)" );
5
    },
6
    _renderMenu: function(ul, items) {
7
        var self = this,
8
        currentCategory = "";
9
        items.forEach(function(item) {
10
            if (item.category !== currentCategory) {
11
                ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
12
                currentCategory = item.category;
13
            }
14
            self._renderItemData(ul, item);
15
        });
16
    }
17
});
18
19
window.midcom_helper_datamanager2_autocomplete = {
20
    get_default_options: function() {
21
        return {
22
            minLength: 2,
23
            source: midcom_helper_datamanager2_autocomplete.query,
24
            select: midcom_helper_datamanager2_autocomplete.select,
25
            position: {collision: 'flipfit'}
26
        };
27
    },
28
    query: function(request, response) {
29
        var identifier = $('.ui-autocomplete-loading').attr('id').replace(/_search_input$/, ''),
30
            query_options_var = identifier + '_handler_options',
31
            query_options = window[query_options_var],
32
            cache = $('.ui-autocomplete-loading').data('cache'),
33
            term = request.term;
34
35
        function filter_existing(data) {
36
            if ($('#' + identifier + '_selection_holder').length === 0) {
37
                return data;
38
            }
39
            return data.filter(function(element) {
40
                return $('#' + identifier + '_selection_holder span.autocomplete-selected[data-id="' + element.id + '"]').length === 0;
41
            });
42
        }
43
44
        if (cache === undefined) {
45
            cache = {};
46
        }
47
        if (term in cache) {
48
            response(filter_existing(cache[term]));
49
            return;
50
        }
51
52
        query_options.term = term;
53
        $('.ui-autocomplete-loading').removeClass('ui-autocomplete-error')
54
55
        $.ajax({
56
            url: query_options.handler_url,
57
            dataType: "json",
58
            method: query_options.method,
59
            data: query_options,
60
            success: function(data) {
61
                if (!$.isEmptyObject(data)) {
62
                    cache[term] = data;
63
                }
64
                data = filter_existing(data);
65
                $('.ui-autocomplete-loading').data('cache', cache);
66
                response(data);
67
            },
68
            error: function(jqXHR, textStatus, errorThrown) {
69
                $('.ui-autocomplete-loading')
70
                    .addClass('ui-autocomplete-error')
71
                    .prop('title', errorThrown);
72
                response();
73
            }
74
        });
75
    },
76
77
    select: function(event, ui) {
78
        var identifier = event.target.id.replace(/_search_input$/, '');
79
80
        if ($('#' + identifier + '_selection_holder').length > 0) {
81
            midcom_helper_datamanager2_autocomplete.update_selection(identifier, ui.item.id, 'add');
82
            midcom_helper_datamanager2_autocomplete.add_item(identifier, ui.item.id, ui.item.label, 'autocomplete-new');
83
            event.preventDefault();
84
        } else {
85
            $('#' + identifier + '_selection').val(JSON.stringify([ui.item.id]));
86
            $(event.target).data('selected', ui.item.label);
87
        }
88
    },
89
90
    open: function() {
91
        var offset = $(this).offset(),
92
        height = $(window).height() - (offset.top + $(this).height() + 10);
93
        $('ul.ui-autocomplete').css('maxHeight', height);
94
    },
95
96
    /**
97
     * Enable the creation mode
98
     */
99
    enable_creation_mode: function(identifier, creation_url) {
100
        var dialog_id = identifier + '_creation_dialog',
101
            handler_options = window[identifier + '_handler_options'],
102
            input = $('#' + identifier + '_search_input'),
103
            create_dialog = $('<div class="autocomplete_widget_creation_dialog" id="' + dialog_id + '"><div class="autocomplete_widget_creation_dialog_content_holder"></div></div>').insertAfter(input),
104
            create_button = $('<div class="autocomplete_widget_create_button" id="' + identifier + '_create_button"></div>').insertAfter(create_dialog);
105
106
        input.css({float: 'left'});
107
108
        create_button
109
            .button({
110
                icons: {
111
                    primary: 'ui-icon-plusthick'
112
                },
113
                text: false
114
            })
115
            .on('click', function() {
116
                var url = creation_url + '?chooser_widget_id=' + identifier;
117
                if (   $('#' + identifier + '_search_input').val() !== ''
118
                    && handler_options.creation_default_key !== undefined) {
119
                    url += '&defaults[' + handler_options.creation_default_key + ']=' + $('#' + identifier + '_search_input').val();
120
                }
121
122
                var iframe_html = '<iframe src="' + url + '" id="' + identifier + '_creation_dialog_content"'
123
                    + ' class="chooser_widget_creation_dialog_content"'
124
                    + ' frameborder="0"'
125
                    + ' width="100%"'
126
                    + ' height="100%"'
127
                    + ' scrolling="auto"></iframe>';
128
129
                make_dialog(create_dialog.html(iframe_html), {
130
                    height: 350,
131
                    width: 500
132
                });
133
            });
134
    },
135
136
    /**
137
     * Add creation result to form (from chooser-compatible data)
138
     */
139
    add_result_item: function(identifier, data) {
140
        var handler_options = window[identifier + '_handler_options'],
141
            input_value = '';
142
        handler_options.result_headers.forEach(function(value) {
143
            if (data[value.name] !== undefined) {
144
                input_value += data[value.name] + ', ';
145
            }
146
        });
147
        midcom_helper_datamanager2_autocomplete.update_selection(identifier, data[handler_options.id_field], 'add');
148
        midcom_helper_datamanager2_autocomplete.add_item(identifier, data[handler_options.id_field], input_value.replace(/, $/, ''), 'autocomplete-new');
149
    },
150
151
    create_dm2_widget: function(selector, min_length) {
152
        var identifier = selector.replace(/_search_input$/, ''),
153
            handler_options = window[identifier + '_handler_options'],
154
            dm2_defaults = {
155
                minLength: min_length,
156
                //Don't change input field during keyboard navigation:
157
                focus: function(event) {
158
                    event.preventDefault();
159
                }
160
            },
161
            options =  $.extend(dm2_defaults, midcom_helper_datamanager2_autocomplete.get_default_options()),
162
            input = $('#' + selector),
163
            readonly = input.attr('type') === 'hidden',
164
            selection_holder_class = 'autocomplete-selection-holder';
165
166
        function remove_item(item) {
167
            var animate_property = 'height',
168
                animation_config = {};
169
170
            midcom_helper_datamanager2_autocomplete.update_selection(identifier, item.data('id'), 'remove');
171
            if (handler_options.allow_multiple !== true) {
172
                input.show().focus();
173
                if (handler_options.creation_mode_enabled) {
174
                    $('#' + identifier + '_create_button').show();
175
                }
176
            }
177
            if (item.hasClass('autocomplete-saved')) {
178
                item.removeClass('autocomplete-selected');
179
                item.addClass('autocomplete-todelete');
180
            } else {
181
                if (handler_options.allow_multiple !== true) {
182
                    item.remove();
183
                } else {
184
                    if (   item.next().length > 0
185
                        && item.offset().top === item.next().offset().top) {
186
                        animate_property = 'width';
187
                    }
188
                    animation_config[animate_property] = 0;
189
                    item
190
                        .css('visibility', 'hidden')
191
                        .find('.autocomplete-item-label')
192
                        .animate(animation_config, {duration: 200, complete: function(){item.remove();}});
193
                }
194
            }
195
        }
196
197
        if (readonly) {
198
            selection_holder_class += ' autocomplete-selection-holder-readonly';
199
        }
200
201
        input.parent()
202
            .append('<span class="' + selection_holder_class + '" id="' + identifier + '_selection_holder"></span>')
203
            .addClass('autocomplete-widget');
204
        if (handler_options.creation_mode_enabled) {
205
            midcom_helper_datamanager2_autocomplete.enable_creation_mode(identifier, handler_options.creation_handler);
206
            input.parent().addClass('autocomplete-widget-creation-enabled');
207
        }
208
        handler_options.preset_order.forEach(function(id) {
209
            var text = handler_options.preset[id];
210
            if (handler_options.id_field === 'id') {
211
                id = parseInt(id);
212
            }
213
            midcom_helper_datamanager2_autocomplete.add_item(identifier, id, text, 'autocomplete-saved');
214
            if (input.is('[required]')) {
215
                input.prop('required', false);
216
                input.data('required', true)
217
            }
218
        });
219
        if (readonly) {
220
            return;
221
        }
222
223
        if (handler_options.categorize_by_parent_label !== false) {
224
            input.category_complete(options);
225
        } else {
226
            input
227
                .autocomplete(options)
228
                .autocomplete("instance")._renderItem = function(ul, item) {
229
                    var desc = item.description || '';
230
                    return $('<li>')
231
                        .append('<div><div class="item-label">' + item.label + '</div><span class="item-description">' + desc + "</span></div>" )
232
                        .appendTo( ul );
233
                };
234
        }
235
236
        input.parent().on('click', '.autocomplete-selection-holder .autocomplete-action-icon', function() {
237
            var item = $(this).parent(),
238
                item_id = item.data('id');
239
240
            if (item.hasClass('autocomplete-selected')) {
241
                remove_item(item);
242
            } else if (item.hasClass('autocomplete-todelete')) {
243
                midcom_helper_datamanager2_autocomplete.update_selection(identifier, item_id, 'add');
244
                midcom_helper_datamanager2_autocomplete.restore_item(identifier, item);
245
            } else {
246
                midcom_helper_datamanager2_autocomplete.hide_input(identifier, true);
247
            }
248
        });
249
250
        if (handler_options.sortable === true) {
251
            $("#" + identifier + "_selection_holder").sortable({
252
                items: "> span.autocomplete-item",
253
                placeholder: 'autocomplete-placeholder',
254
                forcePlaceholderSize: true,
255
                update: function() {
256
                    var result = [];
257
                    $("#" + identifier + "_selection_holder .autocomplete-item:not(.autocomplete-todelete)")
258
                        .each(function() {
259
                            result.push($(this).data("id"));
260
                        });
261
                    $("#" + identifier + "_selection").val(JSON.stringify(result));
262
                }
263
            });
264
265
            $("#" + identifier + "_search_input").on("autocompleteselect", function() {
266
                $("#" + identifier + "_selection_holder").sortable("refresh");
267
            });
268
        }
269
    },
270
271
    restore_item: function(identifier, item) {
272
        midcom_helper_datamanager2_autocomplete.hide_input(identifier, true);
273
274
        item.removeClass('autocomplete-todelete');
275
        item.addClass('autocomplete-selected');
276
    },
277
278
    hide_input: function(identifier, switch_focus) {
279
        var handler_options = window[identifier + '_handler_options'];
280
281
        if (handler_options.allow_multiple !== true) {
282
            $('#' + identifier + '_search_input').hide();
283
            if (handler_options.creation_mode_enabled) {
284
                $('#' + identifier + '_create_button').hide();
285
            }
286
            $('#' + identifier + '_selection_holder').find('.autocomplete-new').remove();
287
            if (switch_focus === true) {
288
                $('#' + identifier + '_search_input').closest('.form .element').nextAll().find(':focusable:visible').first().focus();
289
            }
290
        }
291
    },
292
293
    add_item: function(identifier, item_id, text, status) {
294
        var selection_holder = $('#' + identifier + '_selection_holder'),
295
            existing_item = selection_holder.find('[data-id="' + item_id + '"]'),
296
            selected = midcom_helper_datamanager2_autocomplete.is_selected(identifier, item_id),
297
            item;
298
299
        if (existing_item.length === 0) {
300
            midcom_helper_datamanager2_autocomplete.hide_input(identifier, status !== 'autocomplete-saved');
301
302
            status = (selected === true ? 'selected ' : 'todelete ') + status;
303
            item = $('<span class="autocomplete-item autocomplete-' + status + '" data-id="' + item_id + '"><span class="autocomplete-item-label" title="' + text + '">' + text + '</span></span>');
304
            if (!selection_holder.hasClass('autocomplete-selection-holder-readonly')) {
305
                item.append('<span class="autocomplete-action-icon"><i class="fa fa-check"></i><i class="fa fa-plus"></i><i class="fa fa-trash"></i></span>');
306
            }
307
            item.prependTo(selection_holder);
308
        } else if (existing_item.hasClass('autocomplete-todelete')) {
309
            midcom_helper_datamanager2_autocomplete.restore_item(identifier, existing_item);
310
        }
311
    },
312
    is_selected: function(identifier, item_id) {
313
        var selection = JSON.parse($('#' + identifier + '_selection').val());
314
        return selection.indexOf(item_id) !== -1;
315
    },
316
    update_selection: function(identifier, item_id, operation) {
317
        var selection = JSON.parse($('#' + identifier + '_selection').val()),
318
            handler_options = window[identifier + '_handler_options'],
319
            input = $('#' + identifier + '_search_input');
320
321
        if (operation === 'add') {
322
            if (handler_options.allow_multiple !== true) {
323
                selection = [];
324
            }
325
            if (selection.indexOf(item_id) === -1) {
326
                selection.push(item_id);
327
            }
328
        } else if (selection.indexOf(item_id) !== -1) {
329
            selection.splice(selection.indexOf(item_id), 1);
330
        }
331
        if (input.data('required')) {
332
            input.prop('required', selection.length === 0);
333
        }
334
        $('#' + identifier + '_selection')
335
            .val(JSON.stringify(selection))
336
            .trigger('change');
337
    },
338
339
    /**
340
     * Generate and attach HTML for autocomplete widget (for use outside of DM2)
341
     */
342
    create_widget: function(config, autocomplete_options) {
343
        var default_config = {
344
                id_field: 'guid',
345
                auto_wildcards: 'both',
346
                categorize_by_parent_label: false,
347
                placeholder: '',
348
                default_value: '',
349
                default_text: ''
350
            },
351
            default_value = config.default_value || default_config.default_value,
352
            default_text = config.default_text || default_config.default_text,
353
            placeholder = config.placeholder || default_config.placeholder,
354
            widget = config.input || null;
355
356
        if (widget === null) {
357
            widget = $('<input type="text" id="' + config.id + '_search_input" name="' + config.id + '_search_input" />');
358
            if (config.insertAfter !== undefined) {
359
                widget.insertAfter($(config.insertAfter));
360
            } else if (config.appendTo !== undefined) {
361
                widget.appendTo($(config.appendTo));
362
            }
363
        }
364
        widget
365
            .attr('placeholder', placeholder)
366
            .val(default_text)
367
            .after($('<input type="hidden" id="' + config.id + '_selection" name="' + config.id + '_selection" value="' + default_value + '" />'));
368
369
        autocomplete_options = $.extend({autoFocus: true}, midcom_helper_datamanager2_autocomplete.get_default_options(), autocomplete_options || {});
370
        window[config.id + '_handler_options'] = $.extend({}, default_config, config.widget_config);
371
372
        if (window[config.id + '_handler_options'].categorize_by_parent_label === true) {
373
            widget.category_complete(autocomplete_options);
374
        } else {
375
            widget.autocomplete(autocomplete_options);
376
        }
377
    }
378
};
379