diff --git a/src/js/jquery.select2.js b/src/js/jquery.select2.js index a3f8eff4..9136ff78 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 a338bf1e..6e1dee26 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 1445f17b..61fee6ca 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,7 +83,7 @@ define([ // Synchronize any monitored attributes this._syncAttributes(); - $element.data('select2', this); + Utils.StoreData($element[0], 'select2', this); }; Utils.Extend(Select2, Utils.Observable); @@ -573,11 +573,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'); - this.$element.removeData('select2'); + Utils.RemoveData(this.$element[0]); this.dataAdapter.destroy(); this.selection.destroy(); @@ -604,7 +605,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 abad2a05..a897198b 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/dropdown/attachBody.js b/src/js/select2/dropdown/attachBody.js index 70a10d40..2c099963 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 430c0329..8c633356 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 3e48deea..d667c4f1 100644 --- a/src/js/select2/options.js +++ b/src/js/select2/options.js @@ -55,7 +55,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 +64,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 +77,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 +87,9 @@ 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()); + dataset = $.extend(true, {}, $e[0].dataset, Utils.GetData($e[0])); } else { - dataset = $e.data(); + dataset = Utils.GetData($e[0]); } var data = $.extend(true, {}, dataset); diff --git a/src/js/select2/results.js b/src/js/select2/results.js index a16b65fd..f155821c 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 a7bbc2ea..3f7d0826 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'); var previousVal = this.$element.val(); this.$element.val(this.placeholder.id); @@ -101,7 +102,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 cd3aafda..2efb1039 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 45225256..6f106788 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, @@ -97,7 +97,7 @@ define([ $selection.append(formatted); $selection.attr('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 1bc4b096..9ce321be 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 d1a23d29..7905e49b 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 08b806ae..70ead12d 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 UserDefinedType = function (id, text) { var self = this; @@ -247,7 +248,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, '