|
@@ -185,10 +185,11 @@ define([
|
|
|
self.trigger('focus', evt);
|
|
|
});
|
|
|
|
|
|
- this._sync = Utils.bind(this._syncAttributes, this);
|
|
|
+ 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._sync);
|
|
|
+ this.$element[0].attachEvent('onpropertychange', this._syncA);
|
|
|
}
|
|
|
|
|
|
var observer = window.MutationObserver ||
|
|
@@ -198,14 +199,30 @@ define([
|
|
|
|
|
|
if (observer != null) {
|
|
|
this._observer = new observer(function (mutations) {
|
|
|
- $.each(mutations, self._sync);
|
|
|
+ $.each(mutations, self._syncA);
|
|
|
+ $.each(mutations, self._syncS);
|
|
|
});
|
|
|
this._observer.observe(this.$element[0], {
|
|
|
attributes: true,
|
|
|
+ childList: true,
|
|
|
subtree: false
|
|
|
});
|
|
|
} else if (this.$element[0].addEventListener) {
|
|
|
- this.$element[0].addEventListener('DOMAttrModified', self._sync, false);
|
|
|
+ this.$element[0].addEventListener(
|
|
|
+ 'DOMAttrModified',
|
|
|
+ self._syncA,
|
|
|
+ false
|
|
|
+ );
|
|
|
+ this.$element[0].addEventListener(
|
|
|
+ 'DOMNodeInserted',
|
|
|
+ self._syncS,
|
|
|
+ false
|
|
|
+ );
|
|
|
+ this.$element[0].addEventListener(
|
|
|
+ 'DOMNodeRemoved',
|
|
|
+ self._syncS,
|
|
|
+ false
|
|
|
+ );
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -350,6 +367,46 @@ define([
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ Select2.prototype._syncSubtree = function (evt, mutations) {
|
|
|
+ var changed = false;
|
|
|
+ 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) {
|
|
|
+ for (var n = 0; n < mutations.addedNodes.length; n++) {
|
|
|
+ var node = mutations.addedNodes[n];
|
|
|
+
|
|
|
+ if (node.selected) {
|
|
|
+ changed = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (mutations.removedNodes && mutations.removedNodes.length > 0) {
|
|
|
+ changed = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Only re-pull the data if we think there is a change
|
|
|
+ if (changed) {
|
|
|
+ this.dataAdapter.current(function (currentData) {
|
|
|
+ self.trigger('selection:update', {
|
|
|
+ data: currentData
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
/**
|
|
|
* Override the trigger method to automatically trigger pre-events when
|
|
|
* there are events that can be prevented.
|
|
@@ -496,7 +553,7 @@ define([
|
|
|
this.$container.remove();
|
|
|
|
|
|
if (this.$element[0].detachEvent) {
|
|
|
- this.$element[0].detachEvent('onpropertychange', this._sync);
|
|
|
+ this.$element[0].detachEvent('onpropertychange', this._syncA);
|
|
|
}
|
|
|
|
|
|
if (this._observer != null) {
|
|
@@ -504,10 +561,15 @@ define([
|
|
|
this._observer = null;
|
|
|
} else if (this.$element[0].removeEventListener) {
|
|
|
this.$element[0]
|
|
|
- .removeEventListener('DOMAttrModified', this._sync, false);
|
|
|
+ .removeEventListener('DOMAttrModified', this._syncA, false);
|
|
|
+ this.$element[0]
|
|
|
+ .removeEventListener('DOMNodeInserted', this._syncS, false);
|
|
|
+ this.$element[0]
|
|
|
+ .removeEventListener('DOMNodeRemoved', this._syncS, false);
|
|
|
}
|
|
|
|
|
|
- this._sync = null;
|
|
|
+ this._syncA = null;
|
|
|
+ this._syncS = null;
|
|
|
|
|
|
this.$element.off('.select2');
|
|
|
this.$element.attr('tabindex', this.$element.data('old-tabindex'));
|