Skip to content

Commit

Permalink
Remove support for legacy IE (select2#5834)
Browse files Browse the repository at this point in the history
* Replace usage of $.trim with String.prototype.trim

This officially drops support for Internet Explorer 8, since this
method is not provided by that browser. This is one more step closer
to removing the dependency on jQuery itself.

* Replace $.isArray with Array.isArray

Now that we no longer support IE8, we can rely on this method
actually existing now.

* Remove old getAttribute workaround for IE7

This has long been unsupported, and now that we don't care about
IE8 with quirks mode, we can go back to expecting that `getAttribute`
actually returns the string like it is supposed to.

* Remove jQuery from Utils.copyNonInternalCssClasses

This also simplifies the logic used to use more modern array methods
to calculate the list of classes which need to be copied over to
the destination object.

* Remove unused imports

* Remove use of jQuery.fn.is()

* Replace $.inArray with Array.prototype.indexOf

* Replace addClass, hasClass, removeClass with classList calls

This replaces calls to the jQuery `getClass`, `hasClass`, and
`removeClass` methods with the corresponding calls to `classList`
methods on the elements that are referenced. There is one exception
to this, specifically where results remove a class from any elements
which may contain it, since that cannot be easily translated to use
the `classList` syntax.

* Remove legacy DOM modification tracking

This removes the legacy tracking for `onpropertychange`, which was
used by IE 8, to synchronize attribute changes from the `<select>`
element back to Select2.

This removes the legacy tracking of `DOMNodeInserted`,
`DOMNodeRemoved`, and `DOMAttrModified` which was used by IE 9 and
IE 10 for tracking when `<option>` elements were added and removed,
as well as when attributes on the `<select>` element were
synchronized.

Now only the `MutationObserver` is in use for synchronizing changes
to the `<select>` as well as changes to the `<option>` elements that
it contains.

* Replace $.map with Array.prototype.map
  • Loading branch information
kevin-brown authored Apr 19, 2020
1 parent f999ed8 commit f7cbd2c
Show file tree
Hide file tree
Showing 20 changed files with 118 additions and 174 deletions.
2 changes: 1 addition & 1 deletion src/js/jquery.select2.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ define([
});

// Check if we should be returning `this`
if ($.inArray(options, thisMethods) > -1) {
if (thisMethods.indexOf(options) > -1) {
return this;
}

Expand Down
132 changes: 37 additions & 95 deletions src/js/select2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ define([
});

// 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
Expand Down Expand Up @@ -197,42 +197,15 @@ define([
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 () {
Expand All @@ -256,7 +229,7 @@ define([
});

this.selection.on('*', function (name, params) {
if ($.inArray(name, nonRelayEvents) !== -1) {
if (nonRelayEvents.indexOf(name) !== -1) {
return;
}

Expand Down Expand Up @@ -284,23 +257,23 @@ define([
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) {
Expand Down Expand Up @@ -376,49 +349,30 @@ define([
}
};

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
Expand Down Expand Up @@ -523,11 +477,11 @@ define([
};

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) {
Expand All @@ -536,7 +490,7 @@ define([
return;
}

this.$container.addClass('select2-container--focus');
this.$container[0].classList.add('select2-container--focus');
this.trigger('focus', {});
};

Expand Down Expand Up @@ -590,8 +544,8 @@ define([

var newVal = args[0];

if ($.isArray(newVal)) {
newVal = $.map(newVal, function (obj) {
if (Array.isArray(newVal)) {
newVal = newVal.map(function (obj) {
return obj.toString();
});
}
Expand All @@ -602,21 +556,8 @@ define([
Select2.prototype.destroy = function () {
this.$container.remove();

if (this.$element[0].detachEvent) {
this.$element[0].detachEvent('onpropertychange', this._syncA);
}

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._observer.disconnect();
this._observer = null;

this._syncA = null;
this._syncS = null;
Expand All @@ -625,7 +566,7 @@ define([
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');
Expand Down Expand Up @@ -653,7 +594,8 @@ define([

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);

Expand Down
2 changes: 1 addition & 1 deletion src/js/select2/data/ajax.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ define([

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.'
Expand Down
2 changes: 1 addition & 1 deletion src/js/select2/data/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ define([
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);
Expand Down
32 changes: 20 additions & 12 deletions src/js/select2/data/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ define([
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');
Expand All @@ -51,7 +53,7 @@ define([
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);
}
}
Expand All @@ -76,7 +78,10 @@ define([

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');
Expand All @@ -90,7 +95,7 @@ define([
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);
}
}
Expand Down Expand Up @@ -130,12 +135,15 @@ define([
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);
Expand Down Expand Up @@ -186,15 +194,13 @@ define([
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) {
Expand All @@ -206,15 +212,17 @@ define([
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: [],
Expand Down
8 changes: 6 additions & 2 deletions src/js/select2/data/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ define([

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);
Expand Down Expand Up @@ -94,7 +94,11 @@ define([
};

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;
Expand Down
2 changes: 1 addition & 1 deletion src/js/select2/data/tokenizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ define([
while (i < term.length) {
var termChar = term[i];

if ($.inArray(termChar, separators) === -1) {
if (separators.indexOf(termChar) === -1) {
i++;

continue;
Expand Down
Loading

0 comments on commit f7cbd2c

Please sign in to comment.