diff --git a/src/js/jquery.select2.js b/src/js/jquery.select2.js
index a3f8eff412..9136ff784e 100644
--- a/src/js/jquery.select2.js
+++ b/src/js/jquery.select2.js
@@ -3,8 +3,9 @@ define([
'jquery-mousewheel',
'./select2/core',
- './select2/defaults'
-], function ($, _, Select2, Defaults) {
+ './select2/defaults',
+ './select2/utils'
+], function ($, _, Select2, Defaults, Utils) {
if ($.fn.select2 == null) {
// All methods that should return the element
var thisMethods = ['open', 'close', 'destroy'];
@@ -25,7 +26,7 @@ define([
var args = Array.prototype.slice.call(arguments, 1);
this.each(function () {
- var instance = $(this).data('select2');
+ var instance = Utils.GetData(this, 'select2');
if (instance == null && window.console && console.error) {
console.error(
diff --git a/src/js/select2/compat/inputData.js b/src/js/select2/compat/inputData.js
index a338bf1eea..6e1dee2614 100644
--- a/src/js/select2/compat/inputData.js
+++ b/src/js/select2/compat/inputData.js
@@ -1,6 +1,7 @@
define([
- 'jquery'
-], function ($) {
+ 'jquery',
+ '../utils'
+], function ($, Utils) {
function InputData (decorated, $element, options) {
this._currentData = [];
this._valueSeparator = options.get('valueSeparator') || ',';
@@ -117,7 +118,7 @@ define([
InputData.prototype.addOptions = function (_, $options) {
var options = $.map($options, function ($option) {
- return $.data($option[0], 'data');
+ return Utils.GetData($option[0], 'data');
});
this._currentData.push.apply(this._currentData, options);
diff --git a/src/js/select2/core.js b/src/js/select2/core.js
index 4ba06edb3e..4ba2b2f97e 100644
--- a/src/js/select2/core.js
+++ b/src/js/select2/core.js
@@ -5,8 +5,8 @@ define([
'./keys'
], function ($, Options, Utils, KEYS) {
var Select2 = function ($element, options) {
- if ($element.data('select2') != null) {
- $element.data('select2').destroy();
+ if (Utils.GetData($element[0], 'select2') != null) {
+ Utils.GetData($element[0], 'select2').destroy();
}
this.$element = $element;
@@ -22,7 +22,7 @@ define([
// Set up the tabindex
var tabindex = $element.attr('tabindex') || 0;
- $element.data('old-tabindex', tabindex);
+ Utils.StoreData($element[0], 'old-tabindex', tabindex);
$element.attr('tabindex', '-1');
// Set up containers and adapters
@@ -83,6 +83,9 @@ define([
// Synchronize any monitored attributes
this._syncAttributes();
+ Utils.StoreData($element[0], 'select2', this);
+
+ // Ensure backwards compatibility with $element.data('select2').
$element.data('select2', this);
};
@@ -572,10 +575,12 @@ define([
this._syncS = null;
this.$element.off('.select2');
- this.$element.attr('tabindex', this.$element.data('old-tabindex'));
+ this.$element.attr('tabindex',
+ Utils.GetData(this.$element[0], 'old-tabindex'));
this.$element.removeClass('select2-hidden-accessible');
this.$element.attr('aria-hidden', 'false');
+ Utils.RemoveData(this.$element[0]);
this.$element.removeData('select2');
this.dataAdapter.destroy();
@@ -603,7 +608,7 @@ define([
this.$container.addClass('select2-container--' + this.options.get('theme'));
- $container.data('element', this.$element);
+ Utils.StoreData($container[0], 'element', this.$element);
return $container;
};
diff --git a/src/js/select2/data/select.js b/src/js/select2/data/select.js
index 6865a47daf..02cf9bdf08 100644
--- a/src/js/select2/data/select.js
+++ b/src/js/select2/data/select.js
@@ -119,7 +119,7 @@ define([
// Remove anything added to child elements
this.$element.find('*').each(function () {
// Remove any custom data set by Select2
- $.removeData(this, 'data');
+ Utils.RemoveData(this);
});
};
@@ -192,7 +192,7 @@ define([
normalizedData.element = option;
// Override the option's data with the combined data
- $.data(option, 'data', normalizedData);
+ Utils.StoreData(option, 'data', normalizedData);
return $option;
};
@@ -200,7 +200,7 @@ define([
SelectAdapter.prototype.item = function ($option) {
var data = {};
- data = $.data($option[0], 'data');
+ data = Utils.GetData($option[0], 'data');
if (data != null) {
return data;
@@ -238,7 +238,7 @@ define([
data = this._normalizeItem(data);
data.element = $option[0];
- $.data($option[0], 'data', data);
+ Utils.StoreData($option[0], 'data', data);
return data;
};
diff --git a/src/js/select2/defaults.js b/src/js/select2/defaults.js
index ad4d224f84..54aa95bbc5 100644
--- a/src/js/select2/defaults.js
+++ b/src/js/select2/defaults.js
@@ -352,6 +352,14 @@ define([
}
this.defaults = {
+ ajax: null,
+ placeholder: null,
+ tags: false,
+ tokenSeparators: [],
+ multiple: false,
+ dropdownCssClass: null,
+ allowClear: false,
+ containerCssClass: null,
amdBase: './',
amdLanguageBase: './i18n/',
closeOnSelect: true,
diff --git a/src/js/select2/dropdown/attachBody.js b/src/js/select2/dropdown/attachBody.js
index 70a10d40d0..2c09996347 100644
--- a/src/js/select2/dropdown/attachBody.js
+++ b/src/js/select2/dropdown/attachBody.js
@@ -90,14 +90,14 @@ define([
var $watchers = this.$container.parents().filter(Utils.hasScroll);
$watchers.each(function () {
- $(this).data('select2-scroll-position', {
+ Utils.StoreData(this, 'select2-scroll-position', {
x: $(this).scrollLeft(),
y: $(this).scrollTop()
});
});
$watchers.on(scrollEvent, function (ev) {
- var position = $(this).data('select2-scroll-position');
+ var position = Utils.GetData(this, 'select2-scroll-position');
$(this).scrollTop(position.y);
});
diff --git a/src/js/select2/dropdown/selectOnClose.js b/src/js/select2/dropdown/selectOnClose.js
index 430c0329b5..8c633356aa 100644
--- a/src/js/select2/dropdown/selectOnClose.js
+++ b/src/js/select2/dropdown/selectOnClose.js
@@ -1,6 +1,6 @@
define([
-
-], function () {
+ '../utils'
+], function (Utils) {
function SelectOnClose () { }
SelectOnClose.prototype.bind = function (decorated, container, $container) {
@@ -31,7 +31,7 @@ define([
return;
}
- var data = $highlightedResults.data('data');
+ var data = Utils.GetData($highlightedResults[0], 'data');
// Don't re-select already selected resulte
if (
diff --git a/src/js/select2/options.js b/src/js/select2/options.js
index 3e48deea50..a6870da04a 100644
--- a/src/js/select2/options.js
+++ b/src/js/select2/options.js
@@ -6,9 +6,21 @@ define([
], function (require, $, Defaults, Utils) {
function Options (options, $element) {
this.options = options;
+ this.jQuery1x = false;
+ this.elementData = null;
+
+ // Html already attempted to be read from html5-* attribs.
+ this.attemptedHtml5DataItems = {};
if ($element != null) {
+ // Override `options` with html5 data-* attribs if any.
this.fromElement($element);
+
+ // Prefetch properties.
+ for(var propertyName in Defaults.defaults) {
+ this.get(propertyName);
+ }
+
}
this.options = Defaults.apply(this.options);
@@ -55,7 +67,7 @@ define([
$e.prop('disabled', this.options.disabled);
$e.prop('multiple', this.options.multiple);
- if ($e.data('select2Tags')) {
+ if (Utils.GetData($e[0], 'select2Tags')) {
if (this.options.debug && window.console && console.warn) {
console.warn(
'Select2: The `data-select2-tags` attribute has been changed to ' +
@@ -64,11 +76,11 @@ define([
);
}
- $e.data('data', $e.data('select2Tags'));
- $e.data('tags', true);
+ Utils.StoreData($e[0], 'data', Utils.GetData($e[0], 'select2Tags'));
+ Utils.StoreData($e[0], 'tags', true);
}
- if ($e.data('ajaxUrl')) {
+ if (Utils.GetData($e[0], 'ajaxUrl')) {
if (this.options.debug && window.console && console.warn) {
console.warn(
'Select2: The `data-ajax-url` attribute has been changed to ' +
@@ -77,8 +89,9 @@ define([
);
}
- $e.attr('ajax--url', $e.data('ajaxUrl'));
- $e.data('ajax--url', $e.data('ajaxUrl'));
+ $e.attr('ajax--url', Utils.GetData($e[0], 'ajaxUrl'));
+ Utils.StoreData($e[0], 'ajax-Url', Utils.GetData($e[0], 'ajaxUrl'));
+
}
var dataset = {};
@@ -86,9 +99,10 @@ define([
// Prefer the element's `dataset` attribute if it exists
// jQuery 1.x does not correctly handle data attributes with multiple dashes
if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) {
- dataset = $.extend(true, {}, $e[0].dataset, $e.data());
+ this.jQuery1x = true;
+ dataset = $.extend(true, {}, $e[0].dataset, Utils.GetData($e[0]));
} else {
- dataset = $e.data();
+ dataset = Utils.GetData($e[0]);
}
var data = $.extend(true, {}, dataset);
@@ -107,10 +121,36 @@ define([
}
}
+ // Store normalized element data for later retrieval.
+ this.elementData = Utils._convertData($e.data());
+
return this;
};
Options.prototype.get = function (key) {
+
+ if (!this.elementData || this.attemptedHtml5DataItems[key]) {
+ return this.options[key];
+ }
+ // If the option value is stored in html5 data-* attribs,
+ // fetch the value and store it for performance.
+ if (!this.jQuery1x) {
+ // For jQuery 1.x, the html5 data-* attribs have already
+ // been parsed above.
+ // Read all attribs that start with `key`.
+ for (var dataKey in this.elementData) {
+ if (dataKey.indexOf(key) === 0) {
+
+ if ($.isPlainObject(this.options[key])) {
+ $.extend(this.options[key], this.elementData[dataKey]);
+ } else {
+ this.options[key] = this.elementData[dataKey];
+ }
+ }
+ }
+ }
+ // Mark as attempted.
+ this.attemptedHtml5DataItems[key] = true;
return this.options[key];
};
diff --git a/src/js/select2/results.js b/src/js/select2/results.js
index a16b65fd0f..f155821cea 100644
--- a/src/js/select2/results.js
+++ b/src/js/select2/results.js
@@ -130,7 +130,7 @@ define([
$options.each(function () {
var $option = $(this);
- var item = $.data(this, 'data');
+ var item = Utils.GetData(this, 'data');
// id needs to be converted to a string when comparing
var id = '' + item.id;
@@ -235,7 +235,7 @@ define([
this.template(data, option);
}
- $.data(option, 'data', data);
+ Utils.StoreData(option, 'data', data);
return option;
};
@@ -321,7 +321,7 @@ define([
return;
}
- var data = $highlighted.data('data');
+ var data = Utils.GetData($highlighted[0], 'data');
if ($highlighted.attr('aria-selected') == 'true') {
self.trigger('close', {});
@@ -433,7 +433,7 @@ define([
function (evt) {
var $this = $(this);
- var data = $this.data('data');
+ var data = Utils.GetData(this, 'data');
if ($this.attr('aria-selected') === 'true') {
if (self.options.get('multiple')) {
@@ -456,7 +456,7 @@ define([
this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
function (evt) {
- var data = $(this).data('data');
+ var data = Utils.GetData(this, 'data');
self.getHighlightedResults()
.removeClass('select2-results__option--highlighted');
diff --git a/src/js/select2/selection/allowClear.js b/src/js/select2/selection/allowClear.js
index d3502069f5..5c4e1fc8f3 100644
--- a/src/js/select2/selection/allowClear.js
+++ b/src/js/select2/selection/allowClear.js
@@ -1,7 +1,8 @@
define([
'jquery',
- '../keys'
-], function ($, KEYS) {
+ '../keys',
+ '../utils'
+], function ($, KEYS, Utils) {
function AllowClear () { }
AllowClear.prototype.bind = function (decorated, container, $container) {
@@ -43,7 +44,7 @@ define([
evt.stopPropagation();
- var data = $clear.data('data');
+ var data = Utils.GetData($clear[0], 'data');
for (var d = 0; d < data.length; d++) {
var unselectData = {
@@ -88,7 +89,7 @@ define([
'×' +
''
);
- $remove.data('data', data);
+ Utils.StoreData($remove[0], 'data', data);
this.$selection.find('.select2-selection__rendered').prepend($remove);
};
diff --git a/src/js/select2/selection/base.js b/src/js/select2/selection/base.js
index cd3aafdab7..2efb103922 100644
--- a/src/js/select2/selection/base.js
+++ b/src/js/select2/selection/base.js
@@ -21,8 +21,8 @@ define([
this._tabindex = 0;
- if (this.$element.data('old-tabindex') != null) {
- this._tabindex = this.$element.data('old-tabindex');
+ if (Utils.GetData(this.$element[0], 'old-tabindex') != null) {
+ this._tabindex = Utils.GetData(this.$element[0], 'old-tabindex');
} else if (this.$element.attr('tabindex') != null) {
this._tabindex = this.$element.attr('tabindex');
}
@@ -130,7 +130,7 @@ define([
return;
}
- var $element = $this.data('element');
+ var $element = Utils.GetData(this, 'element');
$element.select2('close');
});
diff --git a/src/js/select2/selection/multiple.js b/src/js/select2/selection/multiple.js
index ae0f7cce7e..df0cb08a86 100644
--- a/src/js/select2/selection/multiple.js
+++ b/src/js/select2/selection/multiple.js
@@ -44,7 +44,7 @@ define([
var $remove = $(this);
var $selection = $remove.parent();
- var data = $selection.data('data');
+ var data = Utils.GetData($selection[0], 'data');
self.trigger('unselect', {
originalEvent: evt,
@@ -95,7 +95,7 @@ define([
$selection.append(formatted);
$selection.prop('title', selection.title || selection.text);
- $selection.data('data', selection);
+ Utils.StoreData($selection[0], 'data', selection);
$selections.push($selection);
}
diff --git a/src/js/select2/selection/search.js b/src/js/select2/selection/search.js
index 1bc4b096bb..9ce321beae 100644
--- a/src/js/select2/selection/search.js
+++ b/src/js/select2/selection/search.js
@@ -81,7 +81,7 @@ define([
.prev('.select2-selection__choice');
if ($previousChoice.length > 0) {
- var item = $previousChoice.data('data');
+ var item = Utils.GetData($previousChoice[0], 'data');
self.searchRemoveChoice(item);
diff --git a/src/js/select2/utils.js b/src/js/select2/utils.js
index d1a23d293e..7905e49b0a 100644
--- a/src/js/select2/utils.js
+++ b/src/js/select2/utils.js
@@ -272,5 +272,68 @@ define([
$element.append($nodes);
};
+ // Cache objects in Utils.__cache instead of $.data
+ 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.
+
+ 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();
+ }
+ }
+ return select2Id;
+ };
+
+ Utils.StoreData = function (element, name, value) {
+ // Stores an item in the cache for a specified element.
+ // name is the cache key.
+ var id = Utils.GetUniqueElementId(element);
+ if (!Utils.__cache[id]) {
+ Utils.__cache[id] = {};
+ }
+
+ Utils.__cache[id][name] = value;
+ };
+
+
+ Utils.GetData = function (element, name) {
+ // Retrieves a value from the cache by its key (name)
+ // name is optional. If no name specified, return
+ // all cache items for the specified element.
+ // and for a specified element.
+ var id = Utils.GetUniqueElementId(element);
+ if (name) {
+ if (Utils.__cache[id]) {
+ return Utils.__cache[id][name] != null ?
+ Utils.__cache[id][name]:
+ $(element).data(name); // Fallback to HTML5 data attribs.
+ }
+ return $(element).data(name); // Fallback to HTML5 data attribs.
+ } else {
+ return Utils.__cache[id];
+ }
+ };
+
+ Utils.RemoveData = function (element) {
+ // Removes all cached items for a specified element.
+ var id = Utils.GetUniqueElementId(element);
+ if (Utils.__cache[id] != null) {
+ delete Utils.__cache[id];
+ }
+ };
+
return Utils;
});
diff --git a/tests/data/array-tests.js b/tests/data/array-tests.js
index 65a6e32c3d..39a2c9e24d 100644
--- a/tests/data/array-tests.js
+++ b/tests/data/array-tests.js
@@ -3,6 +3,7 @@ module('Data adapters - Array');
var ArrayData = require('select2/data/array');
var $ = require('jquery');
var Options = require('select2/options');
+var Utils = require('select2/utils');
var arrayOptions = new Options({
data: [
@@ -237,7 +238,7 @@ test('option tags can receive new data', function(assert) {
});
assert.ok(
- $select.find(':selected').data('data').extra,
+ Utils.GetData($select.find(':selected')[0], 'data').extra,
'