vendor/assets/javascripts/select2-full.js in select2-rails-4.0.13 vs vendor/assets/javascripts/select2-full.js in select2-rails-4.1.0.pre.beta.1

- old
+ new

@@ -1,7 +1,7 @@ /*! - * Select2 4.0.13 + * Select2 4.1.0-beta.1 * https://select2.github.io * * Released under the MIT license * https://github.com/select2/select2/blob/master/LICENSE.md */ @@ -753,48 +753,36 @@ return String(markup).replace(/[&<>"'\/\\]/g, function (match) { return replaceMap[match]; }); }; - // Append an array of jQuery nodes to a given element. - Utils.appendMany = function ($element, $nodes) { - // jQuery 1.7.x does not support $.fn.append() with an array - // Fall back to a jQuery object collection using $.fn.add() - if ($.fn.jquery.substr(0, 3) === '1.7') { - var $jqNodes = $(); - - $.map($nodes, function (node) { - $jqNodes = $jqNodes.add(node); - }); - - $nodes = $jqNodes; - } - - $element.append($nodes); - }; - // Cache objects in Utils.__cache instead of $.data (see #4346) Utils.__cache = {}; var id = 0; Utils.GetUniqueElementId = function (element) { // Get a unique element Id. If element has no id, // creates a new unique number, stores it in the id - // attribute and returns the new id. - // If an id already exists, it simply returns it. + // attribute and returns the new id with a prefix. + // If an id already exists, it simply returns it with a prefix. var select2Id = element.getAttribute('data-select2-id'); - if (select2Id == null) { - // If element has id, use it. - if (element.id) { - select2Id = element.id; - element.setAttribute('data-select2-id', select2Id); - } else { - element.setAttribute('data-select2-id', ++id); - select2Id = id.toString(); - } + + if (select2Id != null) { + return select2Id; } + + // If element has id, use it. + if (element.id) { + select2Id = 'select2-data-' + element.id; + } else { + select2Id = 'select2-data-' + (++id).toString() + + '-' + Utils.generateChars(4); + } + + element.setAttribute('data-select2-id', select2Id); + return select2Id; }; Utils.StoreData = function (element, name, value) { // Stores an item in the cache for a specified element. @@ -834,10 +822,32 @@ } element.removeAttribute('data-select2-id'); }; + Utils.copyNonInternalCssClasses = function (dest, src) { + var classes; + + var destinationClasses = dest.getAttribute('class').trim().split(/\s+/); + + destinationClasses = destinationClasses.filter(function (clazz) { + // Save all Select2 classes + return clazz.indexOf('select2-') === 0; + }); + + var sourceClasses = src.getAttribute('class').trim().split(/\s+/); + + sourceClasses = sourceClasses.filter(function (clazz) { + // Only copy non-Select2 classes + return clazz.indexOf('select2-') !== 0; + }); + + var replacements = destinationClasses.concat(sourceClasses); + + dest.setAttribute('class', replacements.join(' ')); + }; + return Utils; }); S2.define('select2/results',[ 'jquery', @@ -938,13 +948,13 @@ return sorter(data); }; Results.prototype.highlightFirstItem = function () { var $options = this.$results - .find('.select2-results__option[aria-selected]'); + .find('.select2-results__option--selectable'); - var $selected = $options.filter('[aria-selected=true]'); + var $selected = $options.filter('.select2-results__option--selected'); // Check if there are any selected options if ($selected.length > 0) { // If there are selected options, highlight the first $selected.first().trigger('mouseenter'); @@ -959,29 +969,31 @@ Results.prototype.setClasses = function () { var self = this; this.data.current(function (selected) { - var selectedIds = $.map(selected, function (s) { + var selectedIds = selected.map(function (s) { return s.id.toString(); }); var $options = self.$results - .find('.select2-results__option[aria-selected]'); + .find('.select2-results__option--selectable'); $options.each(function () { var $option = $(this); var item = Utils.GetData(this, 'data'); // id needs to be converted to a string when comparing var id = '' + item.id; if ((item.element != null && item.element.selected) || - (item.element == null && $.inArray(id, selectedIds) > -1)) { + (item.element == null && selectedIds.indexOf(id) > -1)) { + this.classList.add('select2-results__option--selected'); $option.attr('aria-selected', 'true'); } else { + this.classList.remove('select2-results__option--selected'); $option.attr('aria-selected', 'false'); } }); }); @@ -1007,29 +1019,31 @@ this.$results.find('.loading-results').remove(); }; Results.prototype.option = function (data) { var option = document.createElement('li'); - option.className = 'select2-results__option'; + option.classList.add('select2-results__option'); + option.classList.add('select2-results__option--selectable'); var attrs = { - 'role': 'option', - 'aria-selected': 'false' + 'role': 'option' }; var matches = window.Element.prototype.matches || window.Element.prototype.msMatchesSelector || window.Element.prototype.webkitMatchesSelector; if ((data.element != null && matches.call(data.element, ':disabled')) || (data.element == null && data.disabled)) { - delete attrs['aria-selected']; attrs['aria-disabled'] = 'true'; + + option.classList.remove('select2-results__option--selectable'); + option.classList.add('select2-results__option--disabled'); } if (data.id == null) { - delete attrs['aria-selected']; + option.classList.remove('select2-results__option--selectable'); } if (data._resultId != null) { option.id = data._resultId; } @@ -1039,11 +1053,13 @@ } if (data.children) { attrs.role = 'group'; attrs['aria-label'] = data.text; - delete attrs['aria-selected']; + + option.classList.remove('select2-results__option--selectable'); + option.classList.add('select2-results__option--group'); } for (var attr in attrs) { var val = attrs[attr]; @@ -1054,11 +1070,10 @@ var $option = $(option); var label = document.createElement('strong'); label.className = 'select2-results__group'; - var $label = $(label); this.template(data, label); var $children = []; for (var c = 0; c < data.children.length; c++) { @@ -1173,11 +1188,11 @@ return; } var data = Utils.GetData($highlighted[0], 'data'); - if ($highlighted.attr('aria-selected') == 'true') { + if ($highlighted.hasClass('select2-results__option--selected')) { self.trigger('close', {}); } else { self.trigger('select', { data: data }); @@ -1185,11 +1200,11 @@ }); container.on('results:previous', function () { var $highlighted = self.getHighlightedResults(); - var $options = self.$results.find('[aria-selected]'); + var $options = self.$results.find('.select2-results__option--selectable'); var currentIndex = $options.index($highlighted); // If we are already at the top, don't move further // If no options, currentIndex will be -1 @@ -1220,11 +1235,11 @@ }); container.on('results:next', function () { var $highlighted = self.getHighlightedResults(); - var $options = self.$results.find('[aria-selected]'); + var $options = self.$results.find('.select2-results__option--selectable'); var currentIndex = $options.index($highlighted); var nextIndex = currentIndex + 1; @@ -1248,11 +1263,12 @@ self.$results.scrollTop(nextOffset); } }); container.on('results:focus', function (params) { - params.element.addClass('select2-results__option--highlighted'); + params.element[0].classList.add('select2-results__option--highlighted'); + params.element[0].setAttribute('aria-selected', 'true'); }); container.on('results:message', function (params) { self.displayMessage(params); }); @@ -1280,17 +1296,17 @@ e.stopPropagation(); } }); } - this.$results.on('mouseup', '.select2-results__option[aria-selected]', + this.$results.on('mouseup', '.select2-results__option--selectable', function (evt) { var $this = $(this); var data = Utils.GetData(this, 'data'); - if ($this.attr('aria-selected') === 'true') { + if ($this.hasClass('select2-results__option--selected')) { if (self.options.get('multiple')) { self.trigger('unselect', { originalEvent: evt, data: data }); @@ -1305,16 +1321,17 @@ originalEvent: evt, data: data }); }); - this.$results.on('mouseenter', '.select2-results__option[aria-selected]', + this.$results.on('mouseenter', '.select2-results__option--selectable', function (evt) { var data = Utils.GetData(this, 'data'); self.getHighlightedResults() - .removeClass('select2-results__option--highlighted'); + .removeClass('select2-results__option--highlighted') + .attr('aria-selected', 'false'); self.trigger('results:focus', { data: data, element: $(this) }); @@ -1337,11 +1354,11 @@ if ($highlighted.length === 0) { return; } - var $options = this.$results.find('[aria-selected]'); + var $options = this.$results.find('.select2-results__option--selectable'); var currentIndex = $options.index($highlighted); var currentOffset = this.$results.offset().top; var nextTop = $highlighted.offset().top; @@ -1593,11 +1610,11 @@ Utils.Extend(SingleSelection, BaseSelection); SingleSelection.prototype.render = function () { var $selection = SingleSelection.__super__.render.call(this); - $selection.addClass('select2-selection--single'); + $selection[0].classList.add('select2-selection--single'); $selection.html( '<span class="select2-selection__rendered"></span>' + '<span class="select2-selection__arrow" role="presentation">' + '<b role="presentation"></b>' + @@ -1700,11 +1717,11 @@ Utils.Extend(MultipleSelection, BaseSelection); MultipleSelection.prototype.render = function () { var $selection = MultipleSelection.__super__.render.call(this); - $selection.addClass('select2-selection--multiple'); + $selection[0].classList.add('select2-selection--multiple'); $selection.html( '<ul class="select2-selection__rendered"></ul>' ); @@ -1714,10 +1731,13 @@ MultipleSelection.prototype.bind = function (container, $container) { var self = this; MultipleSelection.__super__.bind.apply(this, arguments); + var id = container.id + '-container'; + this.$selection.find('.select2-selection__rendered').attr('id', id); + this.$selection.on('click', function (evt) { self.trigger('toggle', { originalEvent: evt }); }); @@ -1740,10 +1760,23 @@ originalEvent: evt, data: data }); } ); + + this.$selection.on( + 'keydown', + '.select2-selection__choice__remove', + function (evt) { + // Ignore the event if it is disabled + if (self.isDisabled()) { + return; + } + + evt.stopPropagation(); + } + ); }; MultipleSelection.prototype.clear = function () { var $rendered = this.$selection.find('.select2-selection__rendered'); $rendered.empty(); @@ -1758,13 +1791,15 @@ }; MultipleSelection.prototype.selectionContainer = function () { var $container = $( '<li class="select2-selection__choice">' + - '<span class="select2-selection__choice__remove" role="presentation">' + - '&times;' + - '</span>' + + '<button type="button" class="select2-selection__choice__remove" ' + + 'tabindex="-1">' + + '<span aria-hidden="true">&times;</span>' + + '</button>' + + '<span class="select2-selection__choice__display"></span>' + '</li>' ); return $container; }; @@ -1776,40 +1811,61 @@ return; } var $selections = []; + var selectionIdPrefix = this.$selection.find('.select2-selection__rendered') + .attr('id') + '-choice-'; + for (var d = 0; d < data.length; d++) { var selection = data[d]; var $selection = this.selectionContainer(); var formatted = this.display(selection, $selection); - $selection.append(formatted); + var selectionId = selectionIdPrefix + Utils.generateChars(4) + '-'; + if (selection.id) { + selectionId += selection.id; + } else { + selectionId += Utils.generateChars(4); + } + + $selection.find('.select2-selection__choice__display') + .append(formatted) + .attr('id', selectionId); + var title = selection.title || selection.text; if (title) { $selection.attr('title', title); } + var removeItem = this.options.get('translations').get('removeItem'); + + var $remove = $selection.find('.select2-selection__choice__remove'); + + $remove.attr('title', removeItem()); + $remove.attr('aria-label', removeItem()); + $remove.attr('aria-describedby', selectionId); + Utils.StoreData($selection[0], 'data', selection); $selections.push($selection); } var $rendered = this.$selection.find('.select2-selection__rendered'); - Utils.appendMany($rendered, $selections); + $rendered.append($selections); }; return MultipleSelection; }); S2.define('select2/selection/placeholder',[ - '../utils' -], function (Utils) { + +], function () { function Placeholder (decorated, $element, options) { this.placeholder = this.normalizePlaceholder(options.get('placeholder')); decorated.call(this, $element, options); } @@ -1827,12 +1883,12 @@ Placeholder.prototype.createPlaceholder = function (decorated, placeholder) { var $placeholder = this.selectionContainer(); $placeholder.html(this.display(placeholder)); - $placeholder.addClass('select2-selection__placeholder') - .removeClass('select2-selection__choice'); + $placeholder[0].classList.add('select2-selection__placeholder'); + $placeholder[0].classList.remove('select2-selection__choice'); return $placeholder; }; Placeholder.prototype.update = function (decorated, data) { @@ -1947,25 +2003,33 @@ }; AllowClear.prototype.update = function (decorated, data) { decorated.call(this, data); + this.$selection.find('.select2-selection__clear').remove(); + if (this.$selection.find('.select2-selection__placeholder').length > 0 || data.length === 0) { return; } + var selectionId = this.$selection.find('.select2-selection__rendered') + .attr('id'); + var removeAll = this.options.get('translations').get('removeAllItems'); var $remove = $( - '<span class="select2-selection__clear" title="' + removeAll() +'">' + - '&times;' + - '</span>' + '<button type="button" class="select2-selection__clear" tabindex="-1">' + + '<span aria-hidden="true">&times;</span>' + + '</button>' ); + $remove.attr('title', removeAll()); + $remove.attr('aria-label', removeAll()); + $remove.attr('aria-describedby', selectionId); Utils.StoreData($remove[0], 'data', data); - this.$selection.find('.select2-selection__rendered').prepend($remove); + this.$selection.prepend($remove); }; return AllowClear; }); @@ -1978,41 +2042,48 @@ decorated.call(this, $element, options); } Search.prototype.render = function (decorated) { var $search = $( - '<li class="select2-search select2-search--inline">' + + '<span class="select2-search select2-search--inline">' + '<input class="select2-search__field" type="search" tabindex="-1"' + - ' autocomplete="off" autocorrect="off" autocapitalize="none"' + + ' autocorrect="off" autocapitalize="none"' + ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' + - '</li>' + '</span>' ); this.$searchContainer = $search; this.$search = $search.find('input'); + this.$search.prop('autocomplete', this.options.get('autocomplete')); + var $rendered = decorated.call(this); this._transferTabIndex(); + $rendered.append(this.$searchContainer); return $rendered; }; Search.prototype.bind = function (decorated, container, $container) { var self = this; var resultsId = container.id + '-results'; + var selectionId = container.id + '-container'; decorated.call(this, container, $container); + self.$search.attr('aria-describedby', selectionId); + container.on('open', function () { self.$search.attr('aria-controls', resultsId); self.$search.trigger('focus'); }); container.on('close', function () { self.$search.val(''); + self.resizeSearch(); self.$search.removeAttr('aria-controls'); self.$search.removeAttr('aria-activedescendant'); self.$search.trigger('focus'); }); @@ -2054,12 +2125,12 @@ self._keyUpPrevented = evt.isDefaultPrevented(); var key = evt.which; if (key === KEYS.BACKSPACE && self.$search.val() === '') { - var $previousChoice = self.$searchContainer - .prev('.select2-selection__choice'); + var $previousChoice = self.$selection + .find('.select2-selection__choice').last(); if ($previousChoice.length > 0) { var item = Utils.GetData($previousChoice[0], 'data'); self.searchRemoveChoice(item); @@ -2153,13 +2224,10 @@ this.$search.attr('placeholder', ''); decorated.call(this, data); - this.$selection.find('.select2-selection__rendered') - .append(this.$searchContainer); - this.resizeSearch(); if (searchHadFocus) { this.$search.trigger('focus'); } }; @@ -2188,15 +2256,13 @@ }; Search.prototype.resizeSearch = function () { this.$search.css('width', '25px'); - var width = ''; + var width = '100%'; - if (this.$search.attr('placeholder') !== '') { - width = this.$selection.find('.select2-selection__rendered').width(); - } else { + if (this.$search.attr('placeholder') === '') { var minimumWidth = this.$search.val().length + 1; width = (minimumWidth * 0.75) + 'em'; } @@ -2204,10 +2270,34 @@ }; return Search; }); +S2.define('select2/selection/selectionCss',[ + '../utils' +], function (Utils) { + function SelectionCSS () { } + + SelectionCSS.prototype.render = function (decorated) { + var $selection = decorated.call(this); + + var selectionCssClass = this.options.get('selectionCssClass') || ''; + + if (selectionCssClass.indexOf(':all:') !== -1) { + selectionCssClass = selectionCssClass.replace(':all:', ''); + + Utils.copyNonInternalCssClasses($selection[0], this.$element[0]); + } + + $selection.addClass(selectionCssClass); + + return $selection; + }; + + return SelectionCSS; +}); + S2.define('select2/selection/eventRelay',[ 'jquery' ], function ($) { function EventRelay () { } @@ -2227,11 +2317,11 @@ decorated.call(this, container, $container); container.on('*', function (name, params) { // Ignore events that should not be relayed - if ($.inArray(name, relayEvents) === -1) { + if (relayEvents.indexOf(name) === -1) { return; } // The parameters should always be an object params = params || {}; @@ -2242,11 +2332,11 @@ }); self.$element.trigger(evt); // Only handle preventable events if it was one - if ($.inArray(name, preventableEvents) === -1) { + if (preventableEvents.indexOf(name) === -1) { return; } params.prevented = evt.isDefaultPrevented(); }); @@ -3197,31 +3287,31 @@ } Utils.Extend(SelectAdapter, BaseAdapter); SelectAdapter.prototype.current = function (callback) { - var data = []; var self = this; - this.$element.find(':selected').each(function () { - var $option = $(this); + var data = Array.prototype.map.call( + this.$element[0].querySelectorAll(':checked'), + function (selectedElement) { + return self.item($(selectedElement)); + } + ); - var option = self.item($option); - - data.push(option); - }); - callback(data); }; SelectAdapter.prototype.select = function (data) { var self = this; data.selected = true; // If data.element is a DOM node, use it instead - if ($(data.element).is('option')) { + if ( + data.element != null && data.element.tagName.toLowerCase() === 'option' + ) { data.element.selected = true; this.$element.trigger('input').trigger('change'); return; @@ -3235,11 +3325,11 @@ data.push.apply(data, currentData); for (var d = 0; d < data.length; d++) { var id = data[d].id; - if ($.inArray(id, val) === -1) { + if (val.indexOf(id) === -1) { val.push(id); } } self.$element.val(val); @@ -3260,11 +3350,14 @@ return; } data.selected = false; - if ($(data.element).is('option')) { + if ( + data.element != null && + data.element.tagName.toLowerCase() === 'option' + ) { data.element.selected = false; this.$element.trigger('input').trigger('change'); return; @@ -3274,11 +3367,11 @@ var val = []; for (var d = 0; d < currentData.length; d++) { var id = currentData[d].id; - if (id !== data.id && $.inArray(id, val) === -1) { + if (id !== data.id && val.indexOf(id) === -1) { val.push(id); } } self.$element.val(val); @@ -3314,16 +3407,19 @@ var self = this; var $options = this.$element.children(); $options.each(function () { - var $option = $(this); - - if (!$option.is('option') && !$option.is('optgroup')) { + if ( + this.tagName.toLowerCase() !== 'option' && + this.tagName.toLowerCase() !== 'optgroup' + ) { return; } + var $option = $(this); + var option = self.item($option); var matches = self.matches(params, option); if (matches !== null) { @@ -3335,11 +3431,11 @@ results: data }); }; SelectAdapter.prototype.addOptions = function ($options) { - Utils.appendMany(this.$element, $options); + this.$element.append($options); }; SelectAdapter.prototype.option = function (data) { var option; @@ -3370,19 +3466,17 @@ if (data.title) { option.title = data.title; } - var $option = $(option); - var normalizedData = this._normalizeItem(data); normalizedData.element = option; // Override the option's data with the combined data Utils.StoreData(option, 'data', normalizedData); - return $option; + return $(option); }; SelectAdapter.prototype.item = function ($option) { var data = {}; @@ -3390,19 +3484,21 @@ if (data != null) { return data; } - if ($option.is('option')) { + var option = $option[0]; + + if (option.tagName.toLowerCase() === 'option') { data = { id: $option.val(), text: $option.text(), disabled: $option.prop('disabled'), selected: $option.prop('selected'), title: $option.prop('title') }; - } else if ($option.is('optgroup')) { + } else if (option.tagName.toLowerCase() === 'optgroup') { data = { text: $option.prop('label'), children: [], title: $option.prop('title') }; @@ -3522,11 +3618,11 @@ for (var d = 0; d < data.length; d++) { var item = this._normalizeItem(data[d]); // Skip items which were pre-loaded, only merge the data - if ($.inArray(item.id, existingIds) >= 0) { + if (existingIds.indexOf(item.id) >= 0) { var $existingOption = $existing.filter(onlyItem(item)); var existingData = this.item($existingOption); var newData = $.extend(true, {}, item, existingData); @@ -3540,11 +3636,11 @@ var $option = this.option(item); if (item.children) { var $children = this.convertToOptions(item.children); - Utils.appendMany($option, $children); + $option.append($children); } $options.push($option); } @@ -3624,11 +3720,11 @@ var $request = options.transport(options, function (data) { var results = self.processResults(data, params); if (self.options.get('debug') && window.console && console.error) { // Check to make sure that the response included a `results` key. - if (!results || !results.results || !$.isArray(results.results)) { + if (!results || !results.results || !Array.isArray(results.results)) { console.error( 'Select2: The AJAX results did not return an array in the ' + '`results` key of the response.' ); } @@ -3683,11 +3779,11 @@ this.insertTag = insertTag; } decorated.call(this, $element, options); - if ($.isArray(tags)) { + if (Array.isArray(tags)) { for (var t = 0; t < tags.length; t++) { var tag = tags[t]; var item = this._normalizeItem(tag); var $option = this.option(item); @@ -3759,12 +3855,16 @@ decorated.call(this, params, wrapper); }; Tags.prototype.createTag = function (decorated, params) { - var term = $.trim(params.term); + if (params.term == null) { + return null; + } + var term = params.term.trim(); + if (term === '') { return null; } return { @@ -3874,11 +3974,11 @@ }; while (i < term.length) { var termChar = term[i]; - if ($.inArray(termChar, separators) === -1) { + if (separators.indexOf(termChar) === -1) { i++; continue; } @@ -4069,29 +4169,30 @@ return Dropdown; }); S2.define('select2/dropdown/search',[ - 'jquery', - '../utils' -], function ($, Utils) { + 'jquery' +], function ($) { function Search () { } Search.prototype.render = function (decorated) { var $rendered = decorated.call(this); var $search = $( '<span class="select2-search select2-search--dropdown">' + '<input class="select2-search__field" type="search" tabindex="-1"' + - ' autocomplete="off" autocorrect="off" autocapitalize="none"' + + ' autocorrect="off" autocapitalize="none"' + ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' + '</span>' ); this.$searchContainer = $search; this.$search = $search.find('input'); + this.$search.prop('autocomplete', this.options.get('autocomplete')); + $rendered.prepend($search); return $rendered; }; @@ -4149,13 +4250,13 @@ container.on('results:all', function (params) { if (params.query.term == null || params.query.term === '') { var showSearch = self.showSearch(params); if (showSearch) { - self.$searchContainer.removeClass('select2-search--hide'); + self.$searchContainer[0].classList.remove('select2-search--hide'); } else { - self.$searchContainer.addClass('select2-search--hide'); + self.$searchContainer[0].classList.add('select2-search--hide'); } } }); container.on('results:focus', function (params) { @@ -4363,12 +4464,12 @@ AttachBody.prototype.position = function (decorated, $dropdown, $container) { // Clone all of the container classes $dropdown.attr('class', $container.attr('class')); - $dropdown.removeClass('select2'); - $dropdown.addClass('select2-container--open'); + $dropdown[0].classList.remove('select2'); + $dropdown[0].classList.add('select2-container--open'); $dropdown.css({ position: 'absolute', top: -999999 }); @@ -4470,12 +4571,14 @@ }; AttachBody.prototype._positionDropdown = function () { var $window = $(window); - var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above'); - var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below'); + var isCurrentlyAbove = this.$dropdown[0].classList + .contains('select2-dropdown--above'); + var isCurrentlyBelow = this.$dropdown[0].classList + .contains('select2-dropdown--below'); var newDirection = null; var offset = this.$container.offset(); @@ -4543,16 +4646,17 @@ (isCurrentlyAbove && newDirection !== 'below')) { css.top = container.top - parentOffset.top - dropdown.height; } if (newDirection != null) { - this.$dropdown - .removeClass('select2-dropdown--below select2-dropdown--above') - .addClass('select2-dropdown--' + newDirection); - this.$container - .removeClass('select2-container--below select2-container--above') - .addClass('select2-container--' + newDirection); + this.$dropdown[0].classList.remove('select2-dropdown--below'); + this.$dropdown[0].classList.remove('select2-dropdown--above'); + this.$dropdown[0].classList.add('select2-dropdown--' + newDirection); + + this.$container[0].classList.remove('select2-container--below'); + this.$container[0].classList.remove('select2-container--above'); + this.$container[0].classList.add('select2-container--' + newDirection); } this.$dropdownContainer.css(css); }; @@ -4705,10 +4809,34 @@ }; return CloseOnSelect; }); +S2.define('select2/dropdown/dropdownCss',[ + '../utils' +], function (Utils) { + function DropdownCSS () { } + + DropdownCSS.prototype.render = function (decorated) { + var $dropdown = decorated.call(this); + + var dropdownCssClass = this.options.get('dropdownCssClass') || ''; + + if (dropdownCssClass.indexOf(':all:') !== -1) { + dropdownCssClass = dropdownCssClass.replace(':all:', ''); + + Utils.copyNonInternalCssClasses($dropdown[0], this.$element[0]); + } + + $dropdown.addClass(dropdownCssClass); + + return $dropdown; + }; + + return DropdownCSS; +}); + S2.define('select2/i18n/en',[],function () { // English return { errorLoading: function () { return 'The results could not be loaded.'; @@ -4749,25 +4877,28 @@ searching: function () { return 'Searching…'; }, removeAllItems: function () { return 'Remove all items'; + }, + removeItem: function () { + return 'Remove item'; } }; }); S2.define('select2/defaults',[ 'jquery', - 'require', './results', './selection/single', './selection/multiple', './selection/placeholder', './selection/allowClear', './selection/search', + './selection/selectionCss', './selection/eventRelay', './utils', './translation', './diacritics', @@ -4787,26 +4918,28 @@ './dropdown/infiniteScroll', './dropdown/attachBody', './dropdown/minimumResultsForSearch', './dropdown/selectOnClose', './dropdown/closeOnSelect', + './dropdown/dropdownCss', './i18n/en' -], function ($, require, +], function ($, ResultsList, SingleSelection, MultipleSelection, Placeholder, AllowClear, - SelectionSearch, EventRelay, + SelectionSearch, SelectionCSS, EventRelay, Utils, Translation, DIACRITICS, SelectData, ArrayData, AjaxData, Tags, Tokenizer, MinimumInputLength, MaximumInputLength, MaximumSelectionLength, Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll, AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect, + DropdownCSS, EnglishTranslation) { function Defaults () { this.reset(); } @@ -4852,28 +4985,10 @@ options.dataAdapter = Utils.Decorate( options.dataAdapter, Tokenizer ); } - - if (options.query != null) { - var Query = require(options.amdBase + 'compat/query'); - - options.dataAdapter = Utils.Decorate( - options.dataAdapter, - Query - ); - } - - if (options.initSelection != null) { - var InitSelection = require(options.amdBase + 'compat/initSelection'); - - options.dataAdapter = Utils.Decorate( - options.dataAdapter, - InitSelection - ); - } } if (options.resultsAdapter == null) { options.resultsAdapter = ResultsList; @@ -4920,17 +5035,11 @@ options.dropdownAdapter, CloseOnSelect ); } - if ( - options.dropdownCssClass != null || - options.dropdownCss != null || - options.adaptDropdownCssClass != null - ) { - var DropdownCSS = require(options.amdBase + 'compat/dropdownCss'); - + if (options.dropdownCssClass != null) { options.dropdownAdapter = Utils.Decorate( options.dropdownAdapter, DropdownCSS ); } @@ -4968,20 +5077,14 @@ options.selectionAdapter, SelectionSearch ); } - if ( - options.containerCssClass != null || - options.containerCss != null || - options.adaptContainerCssClass != null - ) { - var ContainerCSS = require(options.amdBase + 'compat/containerCss'); - + if (options.selectionCssClass != null) { options.selectionAdapter = Utils.Decorate( options.selectionAdapter, - ContainerCSS + SelectionCSS ); } options.selectionAdapter = Utils.Decorate( options.selectionAdapter, @@ -5026,11 +5129,11 @@ return text.replace(/[^\u0000-\u007E]/g, match); } function matcher (params, data) { // Always return the object if there is nothing to compare - if ($.trim(params.term) === '') { + if (params.term == null || params.term.trim() === '') { return data; } // Do a recursive check for options with children if (data.children && data.children.length > 0) { @@ -5070,12 +5173,12 @@ // If it doesn't contain the term, don't return anything return null; } this.defaults = { - amdBase: './', amdLanguageBase: './i18n/', + autocomplete: 'off', closeOnSelect: true, debug: false, dropdownAutoWidth: false, escapeMarkup: Utils.escapeMarkup, language: {}, @@ -5131,11 +5234,11 @@ return [language]; } var languages; - if (!$.isArray(language)) { + if (!Array.isArray(language)) { languages = [language]; } else { languages = language; } @@ -5212,15 +5315,14 @@ return defaults; }); S2.define('select2/options',[ - 'require', 'jquery', './defaults', './utils' -], function (require, $, Defaults, Utils) { +], function ($, Defaults, Utils) { function Options (options, $element) { this.options = options; if ($element != null) { this.fromElement($element); @@ -5229,19 +5331,10 @@ if ($element != null) { this.options = Defaults.applyFromElement(this.options, $element); } this.options = Defaults.apply(this.options); - - if ($element && $element.is('input')) { - var InputCompat = require(this.get('amdBase') + 'compat/inputData'); - - this.options.dataAdapter = Utils.Decorate( - this.options.dataAdapter, - InputCompat - ); - } } Options.prototype.fromElement = function ($e) { var excludedData = ['select2']; @@ -5251,10 +5344,14 @@ if (this.options.disabled == null) { this.options.disabled = $e.prop('disabled'); } + if (this.options.autocomplete == null && $e.prop('autocomplete')) { + this.options.autocomplete = $e.prop('autocomplete'); + } + if (this.options.dir == null) { if ($e.prop('dir')) { this.options.dir = $e.prop('dir'); } else if ($e.closest('[dir]').prop('dir')) { this.options.dir = $e.closest('[dir]').prop('dir'); @@ -5329,11 +5426,11 @@ var data = $.extend(true, {}, Utils.GetData($e[0]), dataset); data = Utils._convertData(data); for (var key in data) { - if ($.inArray(key, excludedData) > -1) { + if (excludedData.indexOf(key) > -1) { continue; } if ($.isPlainObject(this.options[key])) { $.extend(this.options[key], data[key]); @@ -5433,11 +5530,11 @@ data: initialData }); }); // Hide the original select - $element.addClass('select2-hidden-accessible'); + $element[0].classList.add('select2-hidden-accessible'); $element.attr('aria-hidden', 'true'); // Synchronize any monitored attributes this._syncAttributes(); @@ -5553,46 +5650,19 @@ }); this._syncA = Utils.bind(this._syncAttributes, this); this._syncS = Utils.bind(this._syncSubtree, this); - if (this.$element[0].attachEvent) { - this.$element[0].attachEvent('onpropertychange', this._syncA); - } - - var observer = window.MutationObserver || - window.WebKitMutationObserver || - window.MozMutationObserver - ; - - if (observer != null) { - this._observer = new observer(function (mutations) { - self._syncA(); - self._syncS(null, mutations); - }); - this._observer.observe(this.$element[0], { - attributes: true, - childList: true, - subtree: false - }); - } else if (this.$element[0].addEventListener) { - this.$element[0].addEventListener( - 'DOMAttrModified', - self._syncA, - false - ); - this.$element[0].addEventListener( - 'DOMNodeInserted', - self._syncS, - false - ); - this.$element[0].addEventListener( - 'DOMNodeRemoved', - self._syncS, - false - ); - } + this._observer = new window.MutationObserver(function (mutations) { + self._syncA(); + self._syncS(mutations); + }); + this._observer.observe(this.$element[0], { + attributes: true, + childList: true, + subtree: false + }); }; Select2.prototype._registerDataEvents = function () { var self = this; @@ -5612,11 +5682,11 @@ this.selection.on('focus', function (params) { self.focus(params); }); this.selection.on('*', function (name, params) { - if ($.inArray(name, nonRelayEvents) !== -1) { + if (nonRelayEvents.indexOf(name) !== -1) { return; } self.trigger(name, params); }); @@ -5640,27 +5710,27 @@ Select2.prototype._registerEvents = function () { var self = this; this.on('open', function () { - self.$container.addClass('select2-container--open'); + self.$container[0].classList.add('select2-container--open'); }); this.on('close', function () { - self.$container.removeClass('select2-container--open'); + self.$container[0].classList.remove('select2-container--open'); }); this.on('enable', function () { - self.$container.removeClass('select2-container--disabled'); + self.$container[0].classList.remove('select2-container--disabled'); }); this.on('disable', function () { - self.$container.addClass('select2-container--disabled'); + self.$container[0].classList.add('select2-container--disabled'); }); this.on('blur', function () { - self.$container.removeClass('select2-container--focus'); + self.$container[0].classList.remove('select2-container--focus'); }); this.on('query', function (params) { if (!self.isOpen()) { self.trigger('open', {}); @@ -5732,53 +5802,34 @@ } else { this.trigger('enable', {}); } }; - Select2.prototype._isChangeMutation = function (evt, mutations) { - var changed = false; + Select2.prototype._isChangeMutation = function (mutations) { var self = this; - // Ignore any mutation events raised for elements that aren't options or - // optgroups. This handles the case when the select element is destroyed - if ( - evt && evt.target && ( - evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP' - ) - ) { - return; - } - - if (!mutations) { - // If mutation events aren't supported, then we can only assume that the - // change affected the selections - changed = true; - } else if (mutations.addedNodes && mutations.addedNodes.length > 0) { + if (mutations.addedNodes && mutations.addedNodes.length > 0) { for (var n = 0; n < mutations.addedNodes.length; n++) { var node = mutations.addedNodes[n]; if (node.selected) { - changed = true; + return true; } } } else if (mutations.removedNodes && mutations.removedNodes.length > 0) { - changed = true; - } else if ($.isArray(mutations)) { - $.each(mutations, function(evt, mutation) { - if (self._isChangeMutation(evt, mutation)) { - // We've found a change mutation. - // Let's escape from the loop and continue - changed = true; - return false; - } + return true; + } else if (Array.isArray(mutations)) { + return mutations.some(function (mutation) { + return self._isChangeMutation(mutation); }); } - return changed; + + return false; }; - Select2.prototype._syncSubtree = function (evt, mutations) { - var changed = this._isChangeMutation(evt, mutations); + Select2.prototype._syncSubtree = function (mutations) { + var changed = this._isChangeMutation(mutations); var self = this; // Only re-pull the data if we think there is a change if (changed) { this.dataAdapter.current(function (currentData) { @@ -5879,24 +5930,24 @@ Select2.prototype.isDisabled = function () { return this.options.get('disabled'); }; Select2.prototype.isOpen = function () { - return this.$container.hasClass('select2-container--open'); + return this.$container[0].classList.contains('select2-container--open'); }; Select2.prototype.hasFocus = function () { - return this.$container.hasClass('select2-container--focus'); + return this.$container[0].classList.contains('select2-container--focus'); }; Select2.prototype.focus = function (data) { // No need to re-trigger focus events if we are already focused if (this.hasFocus()) { return; } - this.$container.addClass('select2-container--focus'); + this.$container[0].classList.add('select2-container--focus'); this.trigger('focus', {}); }; Select2.prototype.enable = function (args) { if (this.options.get('debug') && window.console && console.warn) { @@ -5946,46 +5997,33 @@ return this.$element.val(); } var newVal = args[0]; - if ($.isArray(newVal)) { - newVal = $.map(newVal, function (obj) { + if (Array.isArray(newVal)) { + newVal = newVal.map(function (obj) { return obj.toString(); }); } this.$element.val(newVal).trigger('input').trigger('change'); }; Select2.prototype.destroy = function () { this.$container.remove(); - if (this.$element[0].detachEvent) { - this.$element[0].detachEvent('onpropertychange', this._syncA); - } + this._observer.disconnect(); + this._observer = null; - if (this._observer != null) { - this._observer.disconnect(); - this._observer = null; - } else if (this.$element[0].removeEventListener) { - this.$element[0] - .removeEventListener('DOMAttrModified', this._syncA, false); - this.$element[0] - .removeEventListener('DOMNodeInserted', this._syncS, false); - this.$element[0] - .removeEventListener('DOMNodeRemoved', this._syncS, false); - } - this._syncA = null; this._syncS = null; this.$element.off('.select2'); this.$element.attr('tabindex', Utils.GetData(this.$element[0], 'old-tabindex')); - this.$element.removeClass('select2-hidden-accessible'); + this.$element[0].classList.remove('select2-hidden-accessible'); this.$element.attr('aria-hidden', 'false'); Utils.RemoveData(this.$element[0]); this.$element.removeData('select2'); this.dataAdapter.destroy(); @@ -6009,420 +6047,21 @@ $container.attr('dir', this.options.get('dir')); this.$container = $container; - this.$container.addClass('select2-container--' + this.options.get('theme')); + this.$container[0].classList + .add('select2-container--' + this.options.get('theme')); Utils.StoreData($container[0], 'element', this.$element); return $container; }; return Select2; }); -S2.define('select2/compat/utils',[ - 'jquery' -], function ($) { - function syncCssClasses ($dest, $src, adapter) { - var classes, replacements = [], adapted; - - classes = $.trim($dest.attr('class')); - - if (classes) { - classes = '' + classes; // for IE which returns object - - $(classes.split(/\s+/)).each(function () { - // Save all Select2 classes - if (this.indexOf('select2-') === 0) { - replacements.push(this); - } - }); - } - - classes = $.trim($src.attr('class')); - - if (classes) { - classes = '' + classes; // for IE which returns object - - $(classes.split(/\s+/)).each(function () { - // Only adapt non-Select2 classes - if (this.indexOf('select2-') !== 0) { - adapted = adapter(this); - - if (adapted != null) { - replacements.push(adapted); - } - } - }); - } - - $dest.attr('class', replacements.join(' ')); - } - - return { - syncCssClasses: syncCssClasses - }; -}); - -S2.define('select2/compat/containerCss',[ - 'jquery', - './utils' -], function ($, CompatUtils) { - // No-op CSS adapter that discards all classes by default - function _containerAdapter (clazz) { - return null; - } - - function ContainerCSS () { } - - ContainerCSS.prototype.render = function (decorated) { - var $container = decorated.call(this); - - var containerCssClass = this.options.get('containerCssClass') || ''; - - if ($.isFunction(containerCssClass)) { - containerCssClass = containerCssClass(this.$element); - } - - var containerCssAdapter = this.options.get('adaptContainerCssClass'); - containerCssAdapter = containerCssAdapter || _containerAdapter; - - if (containerCssClass.indexOf(':all:') !== -1) { - containerCssClass = containerCssClass.replace(':all:', ''); - - var _cssAdapter = containerCssAdapter; - - containerCssAdapter = function (clazz) { - var adapted = _cssAdapter(clazz); - - if (adapted != null) { - // Append the old one along with the adapted one - return adapted + ' ' + clazz; - } - - return clazz; - }; - } - - var containerCss = this.options.get('containerCss') || {}; - - if ($.isFunction(containerCss)) { - containerCss = containerCss(this.$element); - } - - CompatUtils.syncCssClasses($container, this.$element, containerCssAdapter); - - $container.css(containerCss); - $container.addClass(containerCssClass); - - return $container; - }; - - return ContainerCSS; -}); - -S2.define('select2/compat/dropdownCss',[ - 'jquery', - './utils' -], function ($, CompatUtils) { - // No-op CSS adapter that discards all classes by default - function _dropdownAdapter (clazz) { - return null; - } - - function DropdownCSS () { } - - DropdownCSS.prototype.render = function (decorated) { - var $dropdown = decorated.call(this); - - var dropdownCssClass = this.options.get('dropdownCssClass') || ''; - - if ($.isFunction(dropdownCssClass)) { - dropdownCssClass = dropdownCssClass(this.$element); - } - - var dropdownCssAdapter = this.options.get('adaptDropdownCssClass'); - dropdownCssAdapter = dropdownCssAdapter || _dropdownAdapter; - - if (dropdownCssClass.indexOf(':all:') !== -1) { - dropdownCssClass = dropdownCssClass.replace(':all:', ''); - - var _cssAdapter = dropdownCssAdapter; - - dropdownCssAdapter = function (clazz) { - var adapted = _cssAdapter(clazz); - - if (adapted != null) { - // Append the old one along with the adapted one - return adapted + ' ' + clazz; - } - - return clazz; - }; - } - - var dropdownCss = this.options.get('dropdownCss') || {}; - - if ($.isFunction(dropdownCss)) { - dropdownCss = dropdownCss(this.$element); - } - - CompatUtils.syncCssClasses($dropdown, this.$element, dropdownCssAdapter); - - $dropdown.css(dropdownCss); - $dropdown.addClass(dropdownCssClass); - - return $dropdown; - }; - - return DropdownCSS; -}); - -S2.define('select2/compat/initSelection',[ - 'jquery' -], function ($) { - function InitSelection (decorated, $element, options) { - if (options.get('debug') && window.console && console.warn) { - console.warn( - 'Select2: The `initSelection` option has been deprecated in favor' + - ' of a custom data adapter that overrides the `current` method. ' + - 'This method is now called multiple times instead of a single ' + - 'time when the instance is initialized. Support will be removed ' + - 'for the `initSelection` option in future versions of Select2' - ); - } - - this.initSelection = options.get('initSelection'); - this._isInitialized = false; - - decorated.call(this, $element, options); - } - - InitSelection.prototype.current = function (decorated, callback) { - var self = this; - - if (this._isInitialized) { - decorated.call(this, callback); - - return; - } - - this.initSelection.call(null, this.$element, function (data) { - self._isInitialized = true; - - if (!$.isArray(data)) { - data = [data]; - } - - callback(data); - }); - }; - - return InitSelection; -}); - -S2.define('select2/compat/inputData',[ - 'jquery', - '../utils' -], function ($, Utils) { - function InputData (decorated, $element, options) { - this._currentData = []; - this._valueSeparator = options.get('valueSeparator') || ','; - - if ($element.prop('type') === 'hidden') { - if (options.get('debug') && console && console.warn) { - console.warn( - 'Select2: Using a hidden input with Select2 is no longer ' + - 'supported and may stop working in the future. It is recommended ' + - 'to use a `<select>` element instead.' - ); - } - } - - decorated.call(this, $element, options); - } - - InputData.prototype.current = function (_, callback) { - function getSelected (data, selectedIds) { - var selected = []; - - if (data.selected || $.inArray(data.id, selectedIds) !== -1) { - data.selected = true; - selected.push(data); - } else { - data.selected = false; - } - - if (data.children) { - selected.push.apply(selected, getSelected(data.children, selectedIds)); - } - - return selected; - } - - var selected = []; - - for (var d = 0; d < this._currentData.length; d++) { - var data = this._currentData[d]; - - selected.push.apply( - selected, - getSelected( - data, - this.$element.val().split( - this._valueSeparator - ) - ) - ); - } - - callback(selected); - }; - - InputData.prototype.select = function (_, data) { - if (!this.options.get('multiple')) { - this.current(function (allData) { - $.map(allData, function (data) { - data.selected = false; - }); - }); - - this.$element.val(data.id); - this.$element.trigger('input').trigger('change'); - } else { - var value = this.$element.val(); - value += this._valueSeparator + data.id; - - this.$element.val(value); - this.$element.trigger('input').trigger('change'); - } - }; - - InputData.prototype.unselect = function (_, data) { - var self = this; - - data.selected = false; - - this.current(function (allData) { - var values = []; - - for (var d = 0; d < allData.length; d++) { - var item = allData[d]; - - if (data.id == item.id) { - continue; - } - - values.push(item.id); - } - - self.$element.val(values.join(self._valueSeparator)); - self.$element.trigger('input').trigger('change'); - }); - }; - - InputData.prototype.query = function (_, params, callback) { - var results = []; - - for (var d = 0; d < this._currentData.length; d++) { - var data = this._currentData[d]; - - var matches = this.matches(params, data); - - if (matches !== null) { - results.push(matches); - } - } - - callback({ - results: results - }); - }; - - InputData.prototype.addOptions = function (_, $options) { - var options = $.map($options, function ($option) { - return Utils.GetData($option[0], 'data'); - }); - - this._currentData.push.apply(this._currentData, options); - }; - - return InputData; -}); - -S2.define('select2/compat/matcher',[ - 'jquery' -], function ($) { - function oldMatcher (matcher) { - function wrappedMatcher (params, data) { - var match = $.extend(true, {}, data); - - if (params.term == null || $.trim(params.term) === '') { - return match; - } - - if (data.children) { - for (var c = data.children.length - 1; c >= 0; c--) { - var child = data.children[c]; - - // Check if the child object matches - // The old matcher returned a boolean true or false - var doesMatch = matcher(params.term, child.text, child); - - // If the child didn't match, pop it off - if (!doesMatch) { - match.children.splice(c, 1); - } - } - - if (match.children.length > 0) { - return match; - } - } - - if (matcher(params.term, data.text, data)) { - return match; - } - - return null; - } - - return wrappedMatcher; - } - - return oldMatcher; -}); - -S2.define('select2/compat/query',[ - -], function () { - function Query (decorated, $element, options) { - if (options.get('debug') && window.console && console.warn) { - console.warn( - 'Select2: The `query` option has been deprecated in favor of a ' + - 'custom data adapter that overrides the `query` method. Support ' + - 'will be removed for the `query` option in future versions of ' + - 'Select2.' - ); - } - - decorated.call(this, $element, options); - } - - Query.prototype.query = function (_, params, callback) { - params.callback = callback; - - var query = this.options.get('query'); - - query.call(null, params); - }; - - return Query; -}); - S2.define('select2/dropdown/attachContainer',[ ], function () { function AttachContainer (decorated, $element, options) { decorated.call(this, $element, options); @@ -6431,12 +6070,12 @@ AttachContainer.prototype.position = function (decorated, $dropdown, $container) { var $dropdownContainer = $container.find('.dropdown-wrapper'); $dropdownContainer.append($dropdown); - $dropdown.addClass('select2-dropdown--below'); - $container.addClass('select2-container--below'); + $dropdown[0].classList.add('select2-dropdown--below'); + $container[0].classList.add('select2-container--below'); }; return AttachContainer; }); @@ -6779,10 +6418,10 @@ ret = instance[options].apply(instance, args); }); // Check if we should be returning `this` - if ($.inArray(options, thisMethods) > -1) { + if (thisMethods.indexOf(options) > -1) { return this; } return ret; } else {