Browse Source

Added compatibility with `<input />` tags

This adds backwards compatibility back into Select2 for `<input />`
tags.  The compatibility modules are only available in the full
version and will trigger a warning if a hidden input is being used.

With the new decorator, Select2 should support the basic operations
that it previously did, with the exception of completely overriding
the internal objects. As we no longer expose `data` as a writable
method, it is no longer possible to completely override the selected
data. The state is still managed internally, but in order to prevent
data corruption issues in the past, it is not exposed to the public.

Some small changes needed to be made to how Select2 was dynamically
generating new `<option>` tags, so now a method is called that can
be overridden. In the case of the new decorator, this method is
intercepted and handled without having to actually place the
`<option>` tags into the DOM.

The decorator is applied after all of the other defaults, as the
defaults are not given the current element.

There has only been limited testing with this decorator, primarily
using the `data` and `placeholder` options.

This closes https://github.com/select2/select2/issues/3022.
Kevin Brown 10 years ago
parent
commit
956ac46dab

+ 1 - 0
Gruntfile.js

@@ -14,6 +14,7 @@ module.exports = function (grunt) {
 
 
     'select2/compat/matcher',
     'select2/compat/matcher',
     'select2/compat/initSelection',
     'select2/compat/initSelection',
+    'select2/compat/inputData',
     'select2/compat/query',
     'select2/compat/query',
 
 
     'select2/dropdown/attachContainer',
     'select2/dropdown/attachContainer',

+ 122 - 4
dist/js/select2.amd.full.js

@@ -2406,7 +2406,6 @@ define('select2/data/select',[
       var val = data.id;
       var val = data.id;
 
 
       this.$element.val(val);
       this.$element.val(val);
-
       this.$element.trigger('change');
       this.$element.trigger('change');
     }
     }
   };
   };
@@ -2492,6 +2491,10 @@ define('select2/data/select',[
     });
     });
   };
   };
 
 
+  SelectAdapter.prototype.addOptions = function ($options) {
+    this.$element.append($options);
+  };
+
   SelectAdapter.prototype.option = function (data) {
   SelectAdapter.prototype.option = function (data) {
     var option;
     var option;
 
 
@@ -2632,7 +2635,7 @@ define('select2/data/array',[
 
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
 
-    $element.append(this.convertToOptions(data));
+    this.addOptions(this.convertToOptions(data));
   }
   }
 
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -2643,7 +2646,7 @@ define('select2/data/array',[
     if ($option.length === 0) {
     if ($option.length === 0) {
       $option = this.option(data);
       $option = this.option(data);
 
 
-      this.$element.append($option);
+      this.addOptions([$option]);
     }
     }
 
 
     ArrayAdapter.__super__.select.call(this, data);
     ArrayAdapter.__super__.select.call(this, data);
@@ -2871,7 +2874,7 @@ define('select2/data/tags',[
         var $option = self.option(tag);
         var $option = self.option(tag);
         $option.attr('data-select2-tag', true);
         $option.attr('data-select2-tag', true);
 
 
-        self.$element.append($option);
+        self.addOptions([$option]);
 
 
         self.insertTag(data, tag);
         self.insertTag(data, tag);
       }
       }
@@ -4057,6 +4060,15 @@ define('select2/options',[
     }
     }
 
 
     this.options = Defaults.apply(this.options);
     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) {
   Options.prototype.fromElement = function ($e) {
@@ -4974,6 +4986,112 @@ define('select2/compat/initSelection',[
   return InitSelection;
   return InitSelection;
 });
 });
 
 
+define('select2/compat/inputData',[
+  'jquery'
+], function ($) {
+  function InputData (decorated, $element, options) {
+    this._currentData = [];
+    this._valueSeparator = options.get('valueSeparator') || ',';
+
+    decorated.call(this, $element, options);
+  }
+
+  InputData.prototype.current = function (_, callback) {
+    function getSelected (data, selectedIds) {
+      var selected = [];
+
+      if (data.selected || $.inArray(selectedIds, data.id) !== -1) {
+        selected.push(data);
+      }
+
+      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;
+        });
+      });
+    }
+
+    data.selected = true;
+
+    this._syncValue();
+  };
+
+  InputData.prototype.unselect = function (_, data) {
+    data.selected = false;
+
+    this._syncValue();
+  };
+
+  InputData.prototype._syncValue = function () {
+    var self = this;
+
+    this.current(function (allData) {
+      self.$element.val(
+        allData.join(
+          self._valueSeparator
+        )
+      );
+      self.$element.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 $.data($option[0], 'data');
+    });
+
+    this._currentData.push.apply(this._currentData, options);
+  };
+
+  return InputData;
+});
+
 define('select2/compat/query',[
 define('select2/compat/query',[
 
 
 ], function () {
 ], function () {

+ 16 - 4
dist/js/select2.amd.js

@@ -2406,7 +2406,6 @@ define('select2/data/select',[
       var val = data.id;
       var val = data.id;
 
 
       this.$element.val(val);
       this.$element.val(val);
-
       this.$element.trigger('change');
       this.$element.trigger('change');
     }
     }
   };
   };
@@ -2492,6 +2491,10 @@ define('select2/data/select',[
     });
     });
   };
   };
 
 
+  SelectAdapter.prototype.addOptions = function ($options) {
+    this.$element.append($options);
+  };
+
   SelectAdapter.prototype.option = function (data) {
   SelectAdapter.prototype.option = function (data) {
     var option;
     var option;
 
 
@@ -2632,7 +2635,7 @@ define('select2/data/array',[
 
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
 
-    $element.append(this.convertToOptions(data));
+    this.addOptions(this.convertToOptions(data));
   }
   }
 
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -2643,7 +2646,7 @@ define('select2/data/array',[
     if ($option.length === 0) {
     if ($option.length === 0) {
       $option = this.option(data);
       $option = this.option(data);
 
 
-      this.$element.append($option);
+      this.addOptions([$option]);
     }
     }
 
 
     ArrayAdapter.__super__.select.call(this, data);
     ArrayAdapter.__super__.select.call(this, data);
@@ -2871,7 +2874,7 @@ define('select2/data/tags',[
         var $option = self.option(tag);
         var $option = self.option(tag);
         $option.attr('data-select2-tag', true);
         $option.attr('data-select2-tag', true);
 
 
-        self.$element.append($option);
+        self.addOptions([$option]);
 
 
         self.insertTag(data, tag);
         self.insertTag(data, tag);
       }
       }
@@ -4057,6 +4060,15 @@ define('select2/options',[
     }
     }
 
 
     this.options = Defaults.apply(this.options);
     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) {
   Options.prototype.fromElement = function ($e) {

+ 122 - 4
dist/js/select2.full.js

@@ -2845,7 +2845,6 @@ define('select2/data/select',[
       var val = data.id;
       var val = data.id;
 
 
       this.$element.val(val);
       this.$element.val(val);
-
       this.$element.trigger('change');
       this.$element.trigger('change');
     }
     }
   };
   };
@@ -2931,6 +2930,10 @@ define('select2/data/select',[
     });
     });
   };
   };
 
 
+  SelectAdapter.prototype.addOptions = function ($options) {
+    this.$element.append($options);
+  };
+
   SelectAdapter.prototype.option = function (data) {
   SelectAdapter.prototype.option = function (data) {
     var option;
     var option;
 
 
@@ -3071,7 +3074,7 @@ define('select2/data/array',[
 
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
 
-    $element.append(this.convertToOptions(data));
+    this.addOptions(this.convertToOptions(data));
   }
   }
 
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -3082,7 +3085,7 @@ define('select2/data/array',[
     if ($option.length === 0) {
     if ($option.length === 0) {
       $option = this.option(data);
       $option = this.option(data);
 
 
-      this.$element.append($option);
+      this.addOptions([$option]);
     }
     }
 
 
     ArrayAdapter.__super__.select.call(this, data);
     ArrayAdapter.__super__.select.call(this, data);
@@ -3310,7 +3313,7 @@ define('select2/data/tags',[
         var $option = self.option(tag);
         var $option = self.option(tag);
         $option.attr('data-select2-tag', true);
         $option.attr('data-select2-tag', true);
 
 
-        self.$element.append($option);
+        self.addOptions([$option]);
 
 
         self.insertTag(data, tag);
         self.insertTag(data, tag);
       }
       }
@@ -4496,6 +4499,15 @@ define('select2/options',[
     }
     }
 
 
     this.options = Defaults.apply(this.options);
     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) {
   Options.prototype.fromElement = function ($e) {
@@ -5413,6 +5425,112 @@ define('select2/compat/initSelection',[
   return InitSelection;
   return InitSelection;
 });
 });
 
 
+define('select2/compat/inputData',[
+  'jquery'
+], function ($) {
+  function InputData (decorated, $element, options) {
+    this._currentData = [];
+    this._valueSeparator = options.get('valueSeparator') || ',';
+
+    decorated.call(this, $element, options);
+  }
+
+  InputData.prototype.current = function (_, callback) {
+    function getSelected (data, selectedIds) {
+      var selected = [];
+
+      if (data.selected || $.inArray(selectedIds, data.id) !== -1) {
+        selected.push(data);
+      }
+
+      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;
+        });
+      });
+    }
+
+    data.selected = true;
+
+    this._syncValue();
+  };
+
+  InputData.prototype.unselect = function (_, data) {
+    data.selected = false;
+
+    this._syncValue();
+  };
+
+  InputData.prototype._syncValue = function () {
+    var self = this;
+
+    this.current(function (allData) {
+      self.$element.val(
+        allData.join(
+          self._valueSeparator
+        )
+      );
+      self.$element.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 $.data($option[0], 'data');
+    });
+
+    this._currentData.push.apply(this._currentData, options);
+  };
+
+  return InputData;
+});
+
 define('select2/compat/query',[
 define('select2/compat/query',[
 
 
 ], function () {
 ], function () {

File diff suppressed because it is too large
+ 0 - 0
dist/js/select2.full.min.js


+ 16 - 4
dist/js/select2.js

@@ -2845,7 +2845,6 @@ define('select2/data/select',[
       var val = data.id;
       var val = data.id;
 
 
       this.$element.val(val);
       this.$element.val(val);
-
       this.$element.trigger('change');
       this.$element.trigger('change');
     }
     }
   };
   };
@@ -2931,6 +2930,10 @@ define('select2/data/select',[
     });
     });
   };
   };
 
 
+  SelectAdapter.prototype.addOptions = function ($options) {
+    this.$element.append($options);
+  };
+
   SelectAdapter.prototype.option = function (data) {
   SelectAdapter.prototype.option = function (data) {
     var option;
     var option;
 
 
@@ -3071,7 +3074,7 @@ define('select2/data/array',[
 
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
 
-    $element.append(this.convertToOptions(data));
+    this.addOptions(this.convertToOptions(data));
   }
   }
 
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -3082,7 +3085,7 @@ define('select2/data/array',[
     if ($option.length === 0) {
     if ($option.length === 0) {
       $option = this.option(data);
       $option = this.option(data);
 
 
-      this.$element.append($option);
+      this.addOptions([$option]);
     }
     }
 
 
     ArrayAdapter.__super__.select.call(this, data);
     ArrayAdapter.__super__.select.call(this, data);
@@ -3310,7 +3313,7 @@ define('select2/data/tags',[
         var $option = self.option(tag);
         var $option = self.option(tag);
         $option.attr('data-select2-tag', true);
         $option.attr('data-select2-tag', true);
 
 
-        self.$element.append($option);
+        self.addOptions([$option]);
 
 
         self.insertTag(data, tag);
         self.insertTag(data, tag);
       }
       }
@@ -4496,6 +4499,15 @@ define('select2/options',[
     }
     }
 
 
     this.options = Defaults.apply(this.options);
     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) {
   Options.prototype.fromElement = function ($e) {

File diff suppressed because it is too large
+ 0 - 0
dist/js/select2.min.js


+ 105 - 0
src/js/select2/compat/inputData.js

@@ -0,0 +1,105 @@
+define([
+  'jquery'
+], function ($) {
+  function InputData (decorated, $element, options) {
+    this._currentData = [];
+    this._valueSeparator = options.get('valueSeparator') || ',';
+
+    decorated.call(this, $element, options);
+  }
+
+  InputData.prototype.current = function (_, callback) {
+    function getSelected (data, selectedIds) {
+      var selected = [];
+
+      if (data.selected || $.inArray(selectedIds, data.id) !== -1) {
+        selected.push(data);
+      }
+
+      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;
+        });
+      });
+    }
+
+    data.selected = true;
+
+    this._syncValue();
+  };
+
+  InputData.prototype.unselect = function (_, data) {
+    data.selected = false;
+
+    this._syncValue();
+  };
+
+  InputData.prototype._syncValue = function () {
+    var self = this;
+
+    this.current(function (allData) {
+      self.$element.val(
+        allData.join(
+          self._valueSeparator
+        )
+      );
+      self.$element.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 $.data($option[0], 'data');
+    });
+
+    this._currentData.push.apply(this._currentData, options);
+  };
+
+  return InputData;
+});

+ 2 - 2
src/js/select2/data/array.js

@@ -8,7 +8,7 @@ define([
 
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
 
-    $element.append(this.convertToOptions(data));
+    this.addOptions(this.convertToOptions(data));
   }
   }
 
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -19,7 +19,7 @@ define([
     if ($option.length === 0) {
     if ($option.length === 0) {
       $option = this.option(data);
       $option = this.option(data);
 
 
-      this.$element.append($option);
+      this.addOptions([$option]);
     }
     }
 
 
     ArrayAdapter.__super__.select.call(this, data);
     ArrayAdapter.__super__.select.call(this, data);

+ 4 - 1
src/js/select2/data/select.js

@@ -61,7 +61,6 @@ define([
       var val = data.id;
       var val = data.id;
 
 
       this.$element.val(val);
       this.$element.val(val);
-
       this.$element.trigger('change');
       this.$element.trigger('change');
     }
     }
   };
   };
@@ -147,6 +146,10 @@ define([
     });
     });
   };
   };
 
 
+  SelectAdapter.prototype.addOptions = function ($options) {
+    this.$element.append($options);
+  };
+
   SelectAdapter.prototype.option = function (data) {
   SelectAdapter.prototype.option = function (data) {
     var option;
     var option;
 
 

+ 1 - 1
src/js/select2/data/tags.js

@@ -71,7 +71,7 @@ define([
         var $option = self.option(tag);
         var $option = self.option(tag);
         $option.attr('data-select2-tag', true);
         $option.attr('data-select2-tag', true);
 
 
-        self.$element.append($option);
+        self.addOptions([$option]);
 
 
         self.insertTag(data, tag);
         self.insertTag(data, tag);
       }
       }

+ 9 - 0
src/js/select2/options.js

@@ -11,6 +11,15 @@ define([
     }
     }
 
 
     this.options = Defaults.apply(this.options);
     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) {
   Options.prototype.fromElement = function ($e) {

Some files were not shown because too many files changed in this diff