Test Setup Failed
Push — master ( 11cd9d...8453e3 )
by
unknown
03:59
created

select2.js ➔ ... ➔ populate   F

Complexity

Conditions 9
Paths 385

Size

Total Lines 83

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
c 0
b 0
f 0
nc 385
dl 0
loc 83
rs 3.6555
nop 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
define(function(require) {
2
    'use strict';
3
4
    var $ = require('jquery');
5
    var _ = require('underscore');
6
    var Select2 = require('jquery.select2');
7
    require('oroui/js/select2-l10n');
8
9
    // disable scroll on IOS when select2 drop is visible
10
    $(document).on('wheel mousewheel touchmove keydown', '#select2-drop-mask', function(e) {
11
        e.preventDefault();
12
    });
13
14
    /**
15
     * An overload of populateResults method,
16
     * renders search results with collapsible groups
17
     *
18
     * @param {jQuery} container Dropdown container in jQuery object
19
     * @param {Object} results List of search result items
20
     * @param {Object} query Searched term
21
     * @this AbstractSelect2
22
     */
23
    function populateCollapsibleResults(container, results, query) {
24
        var opts = this.opts;
25
        var id = opts.id;
26
        var parent = container.parent();
27
        var selection = this.val();
28
29
        var populate = function(results, container, depth, parentStack) {
30
            var i;
31
            var l;
32
            var result;
33
            var selectable;
34
            var disabled;
35
            var compound;
36
            var node;
37
            var label;
38
            var innerContainer;
39
            var formatted;
40
            var subId;
41
            var parent;
42
            var resultId;
43
            results = opts.sortResults(results, container, query);
44
            parent = container.parent();
45
46
            for (i = 0, l = results.length; i < l; i = i + 1) {
47
                result = results[i];
48
                resultId = result.id;
49
50
                disabled = (result.disabled === true);
51
                selectable = (!disabled) && (id(result) !== undefined);
52
                compound = result.children && result.children.length > 0;
53
54
                node = $('<li></li>')
55
                    .addClass('select2-result')
56
                    .addClass('select2-results-dept-' + depth)
57
                    .addClass(selectable ? 'select2-result-selectable' : 'select2-result-unselectable')
58
                    .addClass(opts.formatResultCssClass(result));
59
                if (disabled) {
60
                    node.addClass('select2-disabled');
61
                }
62
                if (compound) {
63
                    node.addClass('select2-result-with-children');
64
                }
65
66
                label = $('<div></div>');
67
                label.addClass('select2-result-label');
68
69
                formatted = opts.formatResult(result, label, query, opts.escapeMarkup);
70
                if (formatted !== undefined) {
71
                    label.html(formatted);
72
                }
73
74
                if (compound) {
75
                    container.addClass('accordion');
76
                    subId = parent.attr('id') + '_' + depth + '_' + i;
77
78
                    innerContainer = $('<ul></ul>')
79
                        .addClass('select2-result-sub')
80
                        .wrap('<div class="accordion-body collapse" id="' + subId + '" />');
81
                    populate(result.children, innerContainer, depth + 1, parentStack.concat(innerContainer.parent()));
82
                    innerContainer = innerContainer.parent();
83
84
                    node.addClass('accordion-group')
85
                        .append(innerContainer);
86
87
                    if (query.term) {
88
                        innerContainer.addClass('in');
89
                    } else {
90
                        label.addClass('collapsed');
91
                    }
92
93
                    label = label.addClass('accordion-toggle')
94
                        .attr('data-toggle', 'collapse')
95
                        .attr('data-target', '#' + subId)
96
                        .attr('data-parent', '#' + parent.attr('id'))
97
                        .wrap('<div class="accordion-heading"/>')
98
                        .parent();
99
                }
100
101
                if (selection.indexOf(resultId) >= 0) {
102
                    $.each(parentStack, function() {
103
                        this.addClass('in');
104
                    });
105
                }
106
107
                node.prepend(label);
108
                node.data('select2-data', result);
109
                container.append(node);
110
            }
111
        };
112
113
        parent.attr('id', parent.attr('id') || ('select2container_' + Date.now()));
114
        container.on('click.collapse.data-api', '[data-toggle=collapse]', function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
115
            var $this = $(this);
116
            var target = $this.attr('data-target');
117
            var option = $(target).data('collapse') ? 'toggle' : $this.data();
118
            $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed');
119
            $(target).collapse(option);
120
        });
121
        populate(results, container, 0, []);
122
    }
123
    var overrideMethods = {
124
        processResult: function(original, data) {
125
            original.apply(this, _.rest(arguments));
126
            var results = _.result(data, 'results') || [];
127
            if (results.length > 0 && this.opts.dontSelectFirstOptionOnOpen) {
128
                this.results.find('.select2-highlighted').removeClass('select2-highlighted');
129
                this.dropdown.add(this.search).one('keydown', _.bind(function() {
130
                    delete this.opts.dontSelectFirstOptionOnOpen;
131
                }, this));
132
            }
133
        },
134
        moveHighlight: function(original) {
135
            if (this.highlight() === -1) {
136
                this.highlight(0);
137
            } else {
138
                original.apply(this, _.rest(arguments));
139
            }
140
        },
141
        initContainer: function(original) {
142
            original.apply(this, _.rest(arguments));
143
144
            this.focusser.off('keyup-change input');
145
            this.focusser.on('keyup-change input', this.bind(function(e) {
146
                var showSearch = this.results[0].children.length >= this.opts.minimumResultsForSearch;
147
148
                if (showSearch) {
149
                    e.stopPropagation();
150
                    if (this.opened()) {
151
                        return;
152
                    }
153
                    this.open();
154
                } else {
155
                    this.clearSearch();
156
                }
157
            }));
158
        },
159
        tokenize: function(original) {
160
            var opts = this.opts;
161
            var search = this.search;
162
            var results = this.results;
163
            if (opts.allowCreateNew && opts.createSearchChoice) {
164
                var def = opts.createSearchChoice.call(this, search.val(), []);
165
                if (def !== void 0 && def !== null && this.id(def) !== void 0 && this.id(def) !== null) {
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
166
                    results.empty();
167
                    if (search.val()) {
168
                        opts.populateResults.call(this, results, [def], {
169
                            term: search.val(),
170
                            page: this.resultsPage,
171
                            context: null
172
                        });
173
                        this.highlight(0);
174
                    }
175
                    if (opts.formatSearching) {
176
                        results.append('<li class="select2-searching">' + opts.formatSearching() + '</li>');
177
                    }
178
                    search.removeClass('select2-active');
179
                    this.positionDropdown();
180
                }
181
            }
182
            original.apply(this, _.rest(arguments));
183
        }
184
    };
185
186
    // Override methods of AbstractSelect2 class
187
    (function(prototype) {
188
        var select2DropBelowClassName = 'select2-drop-below';
189
        var positionDropdown = prototype.positionDropdown;
190
        var close = prototype.close;
191
        var prepareOpts = prototype.prepareOpts;
192
        var init = prototype.init;
193
        var destroy = prototype.destroy;
194
195
        prototype.prepareOpts = function(options) {
196
            if (options.collapsibleResults) {
197
                options.populateResults = populateCollapsibleResults;
198
                var matcher = options.matcher || $.fn.select2.defaults.matcher;
199
                options.matcher = function(term, text, option) {
200
                    return !option.children && matcher.apply(this, arguments);
201
                };
202
            }
203
204
            var additionalRequestParams = options.element.data('select2_query_additional_params');
205
            if (additionalRequestParams && options.ajax !== undefined) {
206
                options.ajax.url += (options.ajax.url.indexOf('?') < 0 ? '?' : '&') + $.param(additionalRequestParams);
207
            }
208
209
            return prepareOpts.call(this, options);
210
        };
211
212
        prototype.positionDropdown = function() {
213
            var $container = this.container;
214
            positionDropdown.apply(this, arguments);
215
            var dialogIsBelow = $container.hasClass('select2-dropdown-open') &&
216
                !$container.hasClass('select2-drop-above');
217
            if ($container.parent().hasClass(select2DropBelowClassName) !== dialogIsBelow) {
218
                $container.parent().toggleClass(select2DropBelowClassName, dialogIsBelow);
219
                this.opts.element.trigger('select2:dialogReposition');
220
            }
221
        };
222
223
        prototype.close = function() {
224
            close.apply(this, arguments);
225
            this.container.parent().removeClass(select2DropBelowClassName);
226
        };
227
228
        prototype.init = function() {
229
            init.apply(this, arguments);
230
            this.breadcrumbs = $('<ul class="select2-breadcrumbs"></ul>');
231
            this.breadcrumbs.on('click', '.select2-breadcrumb-item', $.proxy(function(e) {
232
                var data = $(e.currentTarget).data('select2-data');
233
                this.pagePath = data.pagePath;
234
                this.search.val('');
235
                this.updateResults();
236
                e.stopPropagation();
237
            }, this));
238
            this.dropdown.prepend(this.breadcrumbs);
239
        };
240
241
        prototype.destroy = function() {
242
            if (this.propertyObserver) {
243
                this.propertyObserver.disconnect();
244
                delete this.propertyObserver;
245
                this.propertyObserver = null;
246
            }
247
            destroy.call(this);
248
        };
249
250
        prototype.updateBreadcrumbs = function() {
251
            var breadcrumbs = this.breadcrumbs;
252
            var opts = this.opts;
253
            breadcrumbs.empty();
254
            if ($.isFunction(opts.formatBreadcrumbItem) && $.isFunction(opts.breadcrumbs)) {
255
                var items = opts.breadcrumbs(this.pagePath);
256
                $.each(items, function(i, item) {
257
                    var itemHTML = opts.formatBreadcrumbItem(item, {index: i, length: items.length});
258
                    var $item = $('<li class="select2-breadcrumb-item">' + itemHTML + '</li>');
259
                    $item.data('select2-data', {pagePath: item.pagePath});
260
                    breadcrumbs.append($item);
261
                });
262
            }
263
        };
264
265
        prototype.triggerChange = _.wrap(prototype.triggerChange, function(original, details) {
266
            details = details || {};
267
            if (this.changedManually) {
268
                details.manually = true;
269
            }
270
            original.apply(this, _.rest(arguments));
271
        });
272
    }(Select2['class'].abstract.prototype));
273
274
    (function(prototype) {
275
        var updateResults = prototype.updateResults;
276
        var clear = prototype.clear;
277
        var isPlaceholderOptionSelected = prototype.isPlaceholderOptionSelected;
278
279
        prototype.onSelect = _.wrap(prototype.onSelect, function(original, data, options) {
280
            if (data.id === undefined && data.pagePath) {
281
                this.pagePath = data.pagePath;
282
                this.search.val('');
283
                this.updateResults();
284
                return;
285
            }
286
287
            this.changedManually = true;
288
            original.apply(this, _.rest(arguments));
289
            delete this.changedManually;
290
291
            // @todo BAP-3928, remove this method override after upgrade select2 to v3.4.6, fix code is taken from there
292
            if ((!options || !options.noFocus) && this.opts.minimumResultsForSearch >= 0) {
293
                this.focusser.focus();
294
            }
295
        });
296
297
        // Overriding method to avoid bug with placeholder in version 3.4.1
298
        // see https://github.com/select2/select2/issues/1542
299
        // @todo remove after upgrade to version >= 3.4.2
300
        prototype.updateSelection = function(data) {
301
            var container = this.selection.find('.select2-chosen');
302
            var formatted;
303
            var cssClass;
304
305
            this.selection.data('select2-data', data);
306
307
            container.empty();
308
            if (data !== null && data !== []) {
309
                formatted = this.opts.formatSelection(data, container, this.opts.escapeMarkup);
310
            }
311
            if (formatted !== undefined) {
0 ignored issues
show
Bug introduced by
The variable formatted does not seem to be initialized in case data !== null && data !== [] on line 308 is false. Are you sure this can never be the case?
Loading history...
312
                container.append(formatted);
313
            }
314
            cssClass = this.opts.formatSelectionCssClass(data, container);
315
            if (cssClass !== undefined) {
316
                container.addClass(cssClass);
317
            }
318
319
            this.selection.removeClass('select2-default');
320
321
            if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
322
                this.container.addClass('select2-allowclear');
323
            }
324
        };
325
326
        // Overriding method to avoid bug with placeholder in version 3.4.1
327
        // see https://github.com/select2/select2/issues/1542
328
        // @todo remove after upgrade to version >= 3.4.2
329
        prototype.isPlaceholderOptionSelected = function() {
330
            if (!this.getPlaceholder()) {
331
                return false; // no placeholder specified so no option should be considered
332
            }
333
334
            return isPlaceholderOptionSelected.call(this);
335
        };
336
337
        prototype.updateResults = function(initial) {
338
            updateResults.apply(this, arguments);
339
            if (initial === true && this.opts.element.val()) {
340
                this.pagePath = this.opts.element.val();
341
            }
342
            this.updateBreadcrumbs();
343
            this.positionDropdown();
344
        };
345
346
        prototype.clear = function() {
347
            this.pagePath = '';
348
            clear.apply(this, arguments);
349
        };
350
351
        prototype.postprocessResults = _.wrap(prototype.postprocessResults, overrideMethods.processResult);
352
353
        prototype.moveHighlight = _.wrap(prototype.moveHighlight, overrideMethods.moveHighlight);
354
        prototype.initContainer = _.wrap(prototype.initContainer, overrideMethods.initContainer);
355
        prototype.tokenize = _.wrap(prototype.tokenize, overrideMethods.tokenize);
356
    }(Select2['class'].single.prototype));
357
358
    // Override methods of MultiSelect2 class
359
    // Fix is valid for version 3.4.1
360
    (function(prototype) {
361
        function killEvent(e) {
362
            e.preventDefault();
363
            e.stopPropagation();
364
        }
365
366
        function indexOf(value, array) {
367
            var i = 0;
368
            var l = array.length;
369
            for (; i < l; i = i + 1) {
370
                if (equal(value, array[i])) {
371
                    return i;
372
                }
373
            }
374
            return -1;
375
        }
376
377
        function equal(a, b) {
378
            if (a === b) {
379
                return true;
380
            }
381
            if (a === undefined || b === undefined) {
382
                return false;
383
            }
384
            if (a === null || b === null) {
385
                return false;
386
            }
387
            // Check whether 'a' or 'b' is a string (primitive or object).
388
            // The concatenation of an empty string (+'') converts its argument to a string's primitive.
389
            if (a.constructor === String) {
390
                return a + '' === b + '';
391
            }
392
            if (b.constructor === String) {
393
                return b + '' === a + '';
394
            }
395
            return false;
396
        }
397
398
        var resizeSearch = prototype.resizeSearch;
399
400
        prototype.resizeSearch = function() {
401
            this.selection.addClass('select2-search-resize');
402
            resizeSearch.apply(this, arguments);
403
            this.selection.removeClass('select2-search-resize');
404
            this.search.width(Math.floor($(this.search).width()) - 1);
405
        };
406
407
        prototype.updateSelection = function(data) {
408
            var ids = [];
409
            var filtered = [];
410
            var self = this;
411
412
            // filter out duplicates
413
            $(data).each(function() {
414
                if (indexOf(self.id(this), ids) < 0) {
415
                    ids.push(self.id(this));
416
                    filtered.push(this);
417
                }
418
            });
419
            data = filtered;
420
421
            this.selection.find('.select2-search-choice').remove();
422
            var val = this.getVal();
423
            $(data).each(function() {
424
                self.addSelectedChoiceOptimized(this, val);
425
            });
426
            this.setVal(val);
427
            self.postprocessResults();
428
        };
429
430
        /**
431
         * Makes it possible to render multiselect with 10 000 selected business units
432
         */
433
        prototype.addSelectedChoiceOptimized = function(data, val) {
434
            var enableChoice = !data.locked;
435
            var enabledItem = $(
436
                '<li class=\'select2-search-choice\'>' +
437
                    '<div></div>' +
438
                    '<a href=\'#\' onclick=\'return false;\' ' +
439
                        'class=\'select2-search-choice-close\' tabindex=\'-1\'></a>' +
440
                '</li>');
441
            var disabledItem = $(
442
                '<li class=\'select2-search-choice select2-locked\'>' +
443
                    '<div></div>' +
444
                    '</li>');
445
            var choice = enableChoice ? enabledItem : disabledItem;
446
            if (data.hidden) {
447
                choice.addClass('hide');
448
            }
449
            var id = this.id(data);
450
            var formatted;
451
452
            formatted = this.opts.formatSelection(data, choice.find('div'), this.opts.escapeMarkup);
453
            if (formatted !== undefined) {
454
                choice.find('div').replaceWith('<div>' + formatted + '</div>');
455
            }
456
            var cssClass = this.opts.formatSelectionCssClass(data, choice.find('div'));
457
            if (cssClass !== undefined) {
458
                choice.addClass(cssClass);
459
            }
460
461
            if (enableChoice) {
462
                choice.find('.select2-search-choice-close')
463
                    .on('mousedown', killEvent)
464
                    .on('click dblclick', this.bind(function(e) {
465
                        if (!this.isInterfaceEnabled()) {
466
                            return;
467
                        }
468
469
                        $(e.target).closest('.select2-search-choice').fadeOut('fast', this.bind(function() {
470
                            this.unselect($(e.target));
471
                            this.selection.find('.select2-search-choice-focus')
472
                                .removeClass('select2-search-choice-focus');
473
                            this.close();
474
                            this.focusSearch();
475
                        })).dequeue();
476
                        killEvent(e);
477
                    })).on('focus', this.bind(function() {
478
                        if (!this.isInterfaceEnabled()) {
479
                            return;
480
                        }
481
                        this.container.addClass('select2-container-active');
482
                        this.dropdown.addClass('select2-drop-active');
483
                    }));
484
            }
485
486
            choice.data('select2-data', data);
487
            choice.insertBefore(this.searchContainer);
488
489
            val.push(id);
490
        };
491
492
        prototype.postprocessResults = _.wrap(prototype.postprocessResults, overrideMethods.processResult);
493
494
        prototype.moveHighlight = _.wrap(prototype.moveHighlight, overrideMethods.moveHighlight);
495
    }(Select2['class'].multi.prototype));
496
});
497