You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2020/07/30 13:48:19 UTC

[myfaces-tobago] branch TOBAGO-1999_Select2 created (now da0088d)

This is an automated email from the ASF dual-hosted git repository.

weber pushed a change to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git.


      at da0088d  Tobago-1999: SelectItemIterator iterates only once

This branch includes the following new commits:

     new 23718d4  Tobago-1999
     new a2a9e6e  Tobago-1999: suggest
     new 885574e  Tobago-1999: enable inline rendering of selectOneChoice
     new 8cd2cd2  working on QS-1295
     new b298e06  Tobago-1999: styling
     new d413918  Tobago-1999: SuggestRenderer: escape '"' in json
     new bf33402  Tobago-1999: fix: SuggestRenderer: escape '"' in json
     new 60bab91  Tobago-1999: update select2 to version 4.0.13
     new da0088d  Tobago-1999: SelectItemIterator iterates only once

The 9 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[myfaces-tobago] 08/09: Tobago-1999: update select2 to version 4.0.13

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 60bab91e8143f9c96a2e9d0486d8f617f12cc79c
Author: Volker Weber <v....@inexso.de>
AuthorDate: Thu Apr 23 11:31:03 2020 +0200

    Tobago-1999: update select2 to version 4.0.13
---
 .../src/main/resources/META-INF/tobago-config.xml  |   8 +-
 .../contrib/select2/select2-4.0.8.full.min.js      |   2 -
 .../script/contrib/select2/select2-4.0.8.min.js    |   2 -
 .../{select2-4.0.8.full.js => select2.full.js}     | 459 +++++++++++++++------
 .../script/contrib/select2/select2.full.min.js     |   2 +
 .../select2/{select2-4.0.8.js => select2.js}       | 453 ++++++++++++++------
 .../standard/script/contrib/select2/select2.min.js |   2 +
 .../standard/standard/script/tobago-select2.js     |  70 +++-
 .../style/contrib/select2/select2-4.0.8.min.css    |   1 -
 .../select2/{select2-4.0.8.css => select2.css}     |   3 +-
 .../standard/style/contrib/select2/select2.min.css |   1 +
 11 files changed, 753 insertions(+), 250 deletions(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
index 4bece02..4e875e7 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
@@ -402,11 +402,11 @@
         <script priority="10" name="script/contrib/jquery-3.5.1.js"/>
         <script priority="20" name="script/contrib/jquery-ui-1.10.4.custom.js"/>
         <script priority="30" name="script/contrib/jquery-ui-timepicker-addon-1.4.5.js"/>
-        <script priority="35" name="script/contrib/select2/select2-4.0.8.full.js"/>
+        <script priority="35" name="script/contrib/select2/select2.full.js"/>
         <script priority="40" name="script/tobago.js"/>
         <style priority="10" name="style/contrib/ui-lightness/jquery-ui-1.10.4.custom.css"/>
         <style priority="20" name="style/contrib/jquery-ui-timepicker-addon-1.4.5.css"/>
-        <style priority="25" name="style/contrib/select2/select2-4.0.8.css"/>
+        <style priority="25" name="style/contrib/select2/select2.css"/>
         <style priority="30" name="style/tobago.css"/>
         <!-- backward compatibility -->
         <style priority="40" name="style/style.css"/>
@@ -415,7 +415,7 @@
         <script priority="10" name="script/contrib/jquery-3.5.1.js"/>
         <script priority="20" name="script/contrib/jquery-ui-1.10.4.custom.js"/>
         <script priority="30" name="script/contrib/jquery-ui-timepicker-addon-1.4.5.js"/>
-        <script priority="35" name="script/contrib/select2/select2-4.0.8.full.js"/>
+        <script priority="35" name="script/contrib/select2/select2.full.js"/>
         <script priority="40" name="script/tobago.js"/>
         <script priority="50" name="script/tobago-calendar.js"/>
         <script priority="60" name="script/tobago-console.js"/>
@@ -434,7 +434,7 @@
         <script priority="180" name="script/tobago-logging.js"/>
         <style priority="10" name="style/contrib/ui-lightness/jquery-ui-1.10.4.custom.css"/>
         <style priority="20" name="style/contrib/jquery-ui-timepicker-addon-1.4.5.css"/>
-        <style priority="25" name="style/contrib/select2/select2-4.0.8.css"/>
+        <style priority="25" name="style/contrib/select2/select2.css"/>
         <style priority="30" name="style/tobago.css"/>
         <!-- backward compatibility -->
         <style priority="40" name="style/style.css"/>
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.min.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.min.js
deleted file mode 100644
index 1d0460d..0000000
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
-!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(d){var e=function(){if(d&&d.fn&&d.fn.select2&&d.fn.select2.amd)var e=d.fn.select2.amd;var t,n,i,h,o,s,f,g,m,v,y,_,r,a,w,l;function b(e,t){return r.call(e,t)}function c(e,t){var n,i,r,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["* [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.min.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.min.js
deleted file mode 100644
index 9def5ae..0000000
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
-!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(u){var e=function(){if(u&&u.fn&&u.fn.select2&&u.fn.select2.amd)var e=u.fn.select2.amd;var t,n,r,h,o,s,f,g,m,v,y,_,i,a,w;function b(e,t){return i.call(e,t)}function l(e,t){var n,r,i,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["*"] [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.full.js
similarity index 94%
rename from tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.js
rename to tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.full.js
index efb27b2..358572a 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.js
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.full.js
@@ -1,5 +1,5 @@
 /*!
- * Select2 4.0.8
+ * Select2 4.0.13
  * https://select2.github.io
  *
  * Released under the MIT license
@@ -832,6 +832,8 @@ S2.define('select2/utils',[
     if (Utils.__cache[id] != null) {
       delete Utils.__cache[id];
     }
+
+    element.removeAttribute('data-select2-id');
   };
 
   return Utils;
@@ -853,7 +855,7 @@ S2.define('select2/results',[
 
   Results.prototype.render = function () {
     var $results = $(
-      '<ul class="select2-results__options" role="tree"></ul>'
+      '<ul class="select2-results__options" role="listbox"></ul>'
     );
 
     if (this.options.get('multiple')) {
@@ -876,7 +878,7 @@ S2.define('select2/results',[
     this.hideLoading();
 
     var $message = $(
-      '<li role="treeitem" aria-live="assertive"' +
+      '<li role="alert" aria-live="assertive"' +
       ' class="select2-results__option"></li>'
     );
 
@@ -1010,7 +1012,7 @@ S2.define('select2/results',[
     option.className = 'select2-results__option';
 
     var attrs = {
-      'role': 'treeitem',
+      'role': 'option',
       'aria-selected': 'false'
     };
 
@@ -1430,6 +1432,7 @@ S2.define('select2/selection/base',[
 
     $selection.attr('title', this.$element.attr('title'));
     $selection.attr('tabindex', this._tabindex);
+    $selection.attr('aria-disabled', 'false');
 
     this.$selection = $selection;
 
@@ -1439,7 +1442,6 @@ S2.define('select2/selection/base',[
   BaseSelection.prototype.bind = function (container, $container) {
     var self = this;
 
-    var id = container.id + '-container';
     var resultsId = container.id + '-results';
 
     this.container = container;
@@ -1489,10 +1491,12 @@ S2.define('select2/selection/base',[
 
     container.on('enable', function () {
       self.$selection.attr('tabindex', self._tabindex);
+      self.$selection.attr('aria-disabled', 'false');
     });
 
     container.on('disable', function () {
       self.$selection.attr('tabindex', '-1');
+      self.$selection.attr('aria-disabled', 'true');
     });
   };
 
@@ -1515,7 +1519,6 @@ S2.define('select2/selection/base',[
   };
 
   BaseSelection.prototype._attachCloseHandler = function (container) {
-    var self = this;
 
     $(document.body).on('mousedown.select2.' + container.id, function (e) {
       var $target = $(e.target);
@@ -1525,8 +1528,6 @@ S2.define('select2/selection/base',[
       var $all = $('.select2.select2-container--open');
 
       $all.each(function () {
-        var $this = $(this);
-
         if (this == $select[0]) {
           return;
         }
@@ -1555,6 +1556,27 @@ S2.define('select2/selection/base',[
     throw new Error('The `update` method must be defined in child classes.');
   };
 
+  /**
+   * Helper method to abstract the "enabled" (not "disabled") state of this
+   * object.
+   *
+   * @return {true} if the instance is not disabled.
+   * @return {false} if the instance is disabled.
+   */
+  BaseSelection.prototype.isEnabled = function () {
+    return !this.isDisabled();
+  };
+
+  /**
+   * Helper method to abstract the "disabled" state of this object.
+   *
+   * @return {true} if the disabled option is true.
+   * @return {false} if the disabled option is false.
+   */
+  BaseSelection.prototype.isDisabled = function () {
+    return this.options.get('disabled');
+  };
+
   return BaseSelection;
 });
 
@@ -1653,7 +1675,14 @@ S2.define('select2/selection/single',[
     var formatted = this.display(selection, $rendered);
 
     $rendered.empty().append(formatted);
-    $rendered.attr('title', selection.title || selection.text);
+
+    var title = selection.title || selection.text;
+
+    if (title) {
+      $rendered.attr('title', title);
+    } else {
+      $rendered.removeAttr('title');
+    }
   };
 
   return SingleSelection;
@@ -1698,7 +1727,7 @@ S2.define('select2/selection/multiple',[
       '.select2-selection__choice__remove',
       function (evt) {
         // Ignore the event if it is disabled
-        if (self.options.get('disabled')) {
+        if (self.isDisabled()) {
           return;
         }
 
@@ -1756,7 +1785,12 @@ S2.define('select2/selection/multiple',[
       var formatted = this.display(selection, $selection);
 
       $selection.append(formatted);
-      $selection.attr('title', selection.title || selection.text);
+
+      var title = selection.title || selection.text;
+
+      if (title) {
+        $selection.attr('title', title);
+      }
 
       Utils.StoreData($selection[0], 'data', selection);
 
@@ -1854,7 +1888,7 @@ S2.define('select2/selection/allowClear',[
 
   AllowClear.prototype._handleClear = function (_, evt) {
     // Ignore the event if it is disabled
-    if (this.options.get('disabled')) {
+    if (this.isDisabled()) {
       return;
     }
 
@@ -1897,7 +1931,7 @@ S2.define('select2/selection/allowClear',[
       }
     }
 
-    this.$element.trigger('change');
+    this.$element.trigger('input').trigger('change');
 
     this.trigger('toggle', {});
   };
@@ -1920,7 +1954,7 @@ S2.define('select2/selection/allowClear',[
       return;
     }
 
-    var removeAll = this.options.get('translations').get('removeAllItems');   
+    var removeAll = this.options.get('translations').get('removeAllItems');
 
     var $remove = $(
       '<span class="select2-selection__clear" title="' + removeAll() +'">' +
@@ -1949,7 +1983,7 @@ S2.define('select2/selection/search',[
       '<li class="select2-search select2-search--inline">' +
         '<input class="select2-search__field" type="search" tabindex="-1"' +
         ' autocomplete="off" autocorrect="off" autocapitalize="none"' +
-        ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
+        ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' +
       '</li>'
     );
 
@@ -1966,14 +2000,18 @@ S2.define('select2/selection/search',[
   Search.prototype.bind = function (decorated, container, $container) {
     var self = this;
 
+    var resultsId = container.id + '-results';
+
     decorated.call(this, container, $container);
 
     container.on('open', function () {
+      self.$search.attr('aria-controls', resultsId);
       self.$search.trigger('focus');
     });
 
     container.on('close', function () {
       self.$search.val('');
+      self.$search.removeAttr('aria-controls');
       self.$search.removeAttr('aria-activedescendant');
       self.$search.trigger('focus');
     });
@@ -1993,7 +2031,11 @@ S2.define('select2/selection/search',[
     });
 
     container.on('results:focus', function (params) {
-      self.$search.attr('aria-activedescendant', params.id);
+      if (params.data._resultId) {
+        self.$search.attr('aria-activedescendant', params.data._resultId);
+      } else {
+        self.$search.removeAttr('aria-activedescendant');
+      }
     });
 
     this.$selection.on('focusin', '.select2-search--inline', function (evt) {
@@ -2027,6 +2069,12 @@ S2.define('select2/selection/search',[
       }
     });
 
+    this.$selection.on('click', '.select2-search--inline', function (evt) {
+      if (self.$search.val()) {
+        evt.stopPropagation();
+      }
+    });
+
     // Try to detect the IE version should the `documentMode` property that
     // is stored on the document. This is only implemented in IE and is
     // slightly cleaner than doing a user agent check.
@@ -2145,7 +2193,7 @@ S2.define('select2/selection/search',[
     var width = '';
 
     if (this.$search.attr('placeholder') !== '') {
-      width = this.$selection.find('.select2-selection__rendered').innerWidth();
+      width = this.$selection.find('.select2-selection__rendered').width();
     } else {
       var minimumWidth = this.$search.val().length + 1;
 
@@ -3174,7 +3222,7 @@ S2.define('select2/data/select',[
     if ($(data.element).is('option')) {
       data.element.selected = true;
 
-      this.$element.trigger('change');
+      this.$element.trigger('input').trigger('change');
 
       return;
     }
@@ -3195,13 +3243,13 @@ S2.define('select2/data/select',[
         }
 
         self.$element.val(val);
-        self.$element.trigger('change');
+        self.$element.trigger('input').trigger('change');
       });
     } else {
       var val = data.id;
 
       this.$element.val(val);
-      this.$element.trigger('change');
+      this.$element.trigger('input').trigger('change');
     }
   };
 
@@ -3217,7 +3265,7 @@ S2.define('select2/data/select',[
     if ($(data.element).is('option')) {
       data.element.selected = false;
 
-      this.$element.trigger('change');
+      this.$element.trigger('input').trigger('change');
 
       return;
     }
@@ -3235,7 +3283,7 @@ S2.define('select2/data/select',[
 
       self.$element.val(val);
 
-      self.$element.trigger('change');
+      self.$element.trigger('input').trigger('change');
     });
   };
 
@@ -3428,15 +3476,19 @@ S2.define('select2/data/array',[
   'jquery'
 ], function (SelectAdapter, Utils, $) {
   function ArrayAdapter ($element, options) {
-    var data = options.get('data') || [];
+    this._dataToConvert = options.get('data') || [];
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
-
-    this.addOptions(this.convertToOptions(data));
   }
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
 
+  ArrayAdapter.prototype.bind = function (container, $container) {
+    ArrayAdapter.__super__.bind.call(this, container, $container);
+
+    this.addOptions(this.convertToOptions(this._dataToConvert));
+  };
+
   ArrayAdapter.prototype.select = function (data) {
     var $option = this.$element.find('option').filter(function (i, elm) {
       return elm.value == data.id.toString();
@@ -3726,8 +3778,6 @@ S2.define('select2/data/tags',[
   };
 
   Tags.prototype._removeOldTags = function (_) {
-    var tag = this._lastTag;
-
     var $options = this.$element.find('option[data-select2-tag]');
 
     $options.each(function () {
@@ -3931,10 +3981,30 @@ S2.define('select2/data/maximumSelectionLength',[
     decorated.call(this, $e, options);
   }
 
+  MaximumSelectionLength.prototype.bind =
+    function (decorated, container, $container) {
+      var self = this;
+
+      decorated.call(this, container, $container);
+
+      container.on('select', function () {
+        self._checkIfMaximumSelected();
+      });
+  };
+
   MaximumSelectionLength.prototype.query =
     function (decorated, params, callback) {
       var self = this;
 
+      this._checkIfMaximumSelected(function () {
+        decorated.call(self, params, callback);
+      });
+  };
+
+  MaximumSelectionLength.prototype._checkIfMaximumSelected =
+    function (_, successCallback) {
+      var self = this;
+
       this.current(function (currentData) {
         var count = currentData != null ? currentData.length : 0;
         if (self.maximumSelectionLength > 0 &&
@@ -3947,7 +4017,10 @@ S2.define('select2/data/maximumSelectionLength',[
           });
           return;
         }
-        decorated.call(self, params, callback);
+
+        if (successCallback) {
+          successCallback();
+        }
       });
   };
 
@@ -4010,7 +4083,7 @@ S2.define('select2/dropdown/search',[
       '<span class="select2-search select2-search--dropdown">' +
         '<input class="select2-search__field" type="search" tabindex="-1"' +
         ' autocomplete="off" autocorrect="off" autocapitalize="none"' +
-        ' spellcheck="false" role="textbox" />' +
+        ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' +
       '</span>'
     );
 
@@ -4025,6 +4098,8 @@ S2.define('select2/dropdown/search',[
   Search.prototype.bind = function (decorated, container, $container) {
     var self = this;
 
+    var resultsId = container.id + '-results';
+
     decorated.call(this, container, $container);
 
     this.$search.on('keydown', function (evt) {
@@ -4047,6 +4122,7 @@ S2.define('select2/dropdown/search',[
 
     container.on('open', function () {
       self.$search.attr('tabindex', 0);
+      self.$search.attr('aria-controls', resultsId);
 
       self.$search.trigger('focus');
 
@@ -4057,6 +4133,8 @@ S2.define('select2/dropdown/search',[
 
     container.on('close', function () {
       self.$search.attr('tabindex', -1);
+      self.$search.removeAttr('aria-controls');
+      self.$search.removeAttr('aria-activedescendant');
 
       self.$search.val('');
       self.$search.trigger('blur');
@@ -4079,6 +4157,14 @@ S2.define('select2/dropdown/search',[
         }
       }
     });
+
+    container.on('results:focus', function (params) {
+      if (params.data._resultId) {
+        self.$search.attr('aria-activedescendant', params.data._resultId);
+      } else {
+        self.$search.removeAttr('aria-activedescendant');
+      }
+    });
   };
 
   Search.prototype.handleSearch = function (evt) {
@@ -4223,7 +4309,7 @@ S2.define('select2/dropdown/infiniteScroll',[
     var $option = $(
       '<li ' +
       'class="select2-results__option select2-results__option--load-more"' +
-      'role="treeitem" aria-disabled="true"></li>'
+      'role="option" aria-disabled="true"></li>'
     );
 
     var message = this.options.get('translations').get('loadingMore');
@@ -4241,7 +4327,7 @@ S2.define('select2/dropdown/attachBody',[
   '../utils'
 ], function ($, Utils) {
   function AttachBody (decorated, $element, options) {
-    this.$dropdownParent = options.get('dropdownParent') || $(document.body);
+    this.$dropdownParent = $(options.get('dropdownParent') || document.body);
 
     decorated.call(this, $element, options);
   }
@@ -4249,27 +4335,14 @@ S2.define('select2/dropdown/attachBody',[
   AttachBody.prototype.bind = function (decorated, container, $container) {
     var self = this;
 
-    var setupResultsEvents = false;
-
     decorated.call(this, container, $container);
 
     container.on('open', function () {
       self._showDropdown();
       self._attachPositioningHandler(container);
 
-      if (!setupResultsEvents) {
-        setupResultsEvents = true;
-
-        container.on('results:all', function () {
-          self._positionDropdown();
-          self._resizeDropdown();
-        });
-
-        container.on('results:append', function () {
-          self._positionDropdown();
-          self._resizeDropdown();
-        });
-      }
+      // Must bind after the results handlers to ensure correct sizing
+      self._bindContainerResultHandlers(container);
     });
 
     container.on('close', function () {
@@ -4318,6 +4391,44 @@ S2.define('select2/dropdown/attachBody',[
     this.$dropdownContainer.detach();
   };
 
+  AttachBody.prototype._bindContainerResultHandlers =
+      function (decorated, container) {
+
+    // These should only be bound once
+    if (this._containerResultsHandlersBound) {
+      return;
+    }
+
+    var self = this;
+
+    container.on('results:all', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    container.on('results:append', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    container.on('results:message', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    container.on('select', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    container.on('unselect', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    this._containerResultsHandlersBound = true;
+  };
+
   AttachBody.prototype._attachPositioningHandler =
       function (decorated, container) {
     var self = this;
@@ -4403,7 +4514,17 @@ S2.define('select2/dropdown/attachBody',[
       $offsetParent = $offsetParent.offsetParent();
     }
 
-    var parentOffset = $offsetParent.offset();
+    var parentOffset = {
+      top: 0,
+      left: 0
+    };
+
+    if (
+      $.contains(document.body, $offsetParent[0]) ||
+      $offsetParent[0].isConnected
+      ) {
+      parentOffset = $offsetParent.offset();
+    }
 
     css.top -= parentOffset.top;
     css.left -= parentOffset.left;
@@ -4868,66 +4989,29 @@ S2.define('select2/defaults',[
       );
     }
 
-    if (typeof options.language === 'string') {
-      // Check if the language is specified with a region
-      if (options.language.indexOf('-') > 0) {
-        // Extract the region information if it is included
-        var languageParts = options.language.split('-');
-        var baseLanguage = languageParts[0];
-
-        options.language = [options.language, baseLanguage];
-      } else {
-        options.language = [options.language];
-      }
-    }
-
-    if ($.isArray(options.language)) {
-      var languages = new Translation();
-      options.language.push('en');
+    // If the defaults were not previously applied from an element, it is
+    // possible for the language option to have not been resolved
+    options.language = this._resolveLanguage(options.language);
 
-      var languageNames = options.language;
+    // Always fall back to English since it will always be complete
+    options.language.push('en');
 
-      for (var l = 0; l < languageNames.length; l++) {
-        var name = languageNames[l];
-        var language = {};
-
-        try {
-          // Try to load it with the original name
-          language = Translation.loadPath(name);
-        } catch (e) {
-          try {
-            // If we couldn't load it, check if it wasn't the full path
-            name = this.defaults.amdLanguageBase + name;
-            language = Translation.loadPath(name);
-          } catch (ex) {
-            // The translation could not be loaded at all. Sometimes this is
-            // because of a configuration problem, other times this can be
-            // because of how Select2 helps load all possible translation files.
-            if (options.debug && window.console && console.warn) {
-              console.warn(
-                'Select2: The language file for "' + name + '" could not be ' +
-                'automatically loaded. A fallback will be used instead.'
-              );
-            }
+    var uniqueLanguages = [];
 
-            continue;
-          }
-        }
+    for (var l = 0; l < options.language.length; l++) {
+      var language = options.language[l];
 
-        languages.extend(language);
+      if (uniqueLanguages.indexOf(language) === -1) {
+        uniqueLanguages.push(language);
       }
+    }
 
-      options.translations = languages;
-    } else {
-      var baseTranslation = Translation.loadPath(
-        this.defaults.amdLanguageBase + 'en'
-      );
-      var customTranslation = new Translation(options.language);
-
-      customTranslation.extend(baseTranslation);
+    options.language = uniqueLanguages;
 
-      options.translations = customTranslation;
-    }
+    options.translations = this._processTranslations(
+      options.language,
+      options.debug
+    );
 
     return options;
   };
@@ -4994,7 +5078,7 @@ S2.define('select2/defaults',[
       debug: false,
       dropdownAutoWidth: false,
       escapeMarkup: Utils.escapeMarkup,
-      language: EnglishTranslation,
+      language: {},
       matcher: matcher,
       minimumInputLength: 0,
       maximumInputLength: 0,
@@ -5016,6 +5100,103 @@ S2.define('select2/defaults',[
     };
   };
 
+  Defaults.prototype.applyFromElement = function (options, $element) {
+    var optionLanguage = options.language;
+    var defaultLanguage = this.defaults.language;
+    var elementLanguage = $element.prop('lang');
+    var parentLanguage = $element.closest('[lang]').prop('lang');
+
+    var languages = Array.prototype.concat.call(
+      this._resolveLanguage(elementLanguage),
+      this._resolveLanguage(optionLanguage),
+      this._resolveLanguage(defaultLanguage),
+      this._resolveLanguage(parentLanguage)
+    );
+
+    options.language = languages;
+
+    return options;
+  };
+
+  Defaults.prototype._resolveLanguage = function (language) {
+    if (!language) {
+      return [];
+    }
+
+    if ($.isEmptyObject(language)) {
+      return [];
+    }
+
+    if ($.isPlainObject(language)) {
+      return [language];
+    }
+
+    var languages;
+
+    if (!$.isArray(language)) {
+      languages = [language];
+    } else {
+      languages = language;
+    }
+
+    var resolvedLanguages = [];
+
+    for (var l = 0; l < languages.length; l++) {
+      resolvedLanguages.push(languages[l]);
+
+      if (typeof languages[l] === 'string' && languages[l].indexOf('-') > 0) {
+        // Extract the region information if it is included
+        var languageParts = languages[l].split('-');
+        var baseLanguage = languageParts[0];
+
+        resolvedLanguages.push(baseLanguage);
+      }
+    }
+
+    return resolvedLanguages;
+  };
+
+  Defaults.prototype._processTranslations = function (languages, debug) {
+    var translations = new Translation();
+
+    for (var l = 0; l < languages.length; l++) {
+      var languageData = new Translation();
+
+      var language = languages[l];
+
+      if (typeof language === 'string') {
+        try {
+          // Try to load it with the original name
+          languageData = Translation.loadPath(language);
+        } catch (e) {
+          try {
+            // If we couldn't load it, check if it wasn't the full path
+            language = this.defaults.amdLanguageBase + language;
+            languageData = Translation.loadPath(language);
+          } catch (ex) {
+            // The translation could not be loaded at all. Sometimes this is
+            // because of a configuration problem, other times this can be
+            // because of how Select2 helps load all possible translation files
+            if (debug && window.console && console.warn) {
+              console.warn(
+                'Select2: The language file for "' + language + '" could ' +
+                'not be automatically loaded. A fallback will be used instead.'
+              );
+            }
+          }
+        }
+      } else if ($.isPlainObject(language)) {
+        languageData = new Translation(language);
+      } else {
+        languageData = language;
+      }
+
+      translations.extend(languageData);
+    }
+
+    return translations;
+  };
+
   Defaults.prototype.set = function (key, value) {
     var camelKey = $.camelCase(key);
 
@@ -5045,6 +5226,10 @@ S2.define('select2/options',[
       this.fromElement($element);
     }
 
+    if ($element != null) {
+      this.options = Defaults.applyFromElement(this.options, $element);
+    }
+
     this.options = Defaults.apply(this.options);
 
     if ($element && $element.is('input')) {
@@ -5068,14 +5253,6 @@ S2.define('select2/options',[
       this.options.disabled = $e.prop('disabled');
     }
 
-    if (this.options.language == null) {
-      if ($e.prop('lang')) {
-        this.options.language = $e.prop('lang').toLowerCase();
-      } else if ($e.closest('[lang]').prop('lang')) {
-        this.options.language = $e.closest('[lang]').prop('lang');
-      }
-    }
-
     if (this.options.dir == null) {
       if ($e.prop('dir')) {
         this.options.dir = $e.prop('dir');
@@ -5389,8 +5566,8 @@ S2.define('select2/core',[
 
     if (observer != null) {
       this._observer = new observer(function (mutations) {
-        $.each(mutations, self._syncA);
-        $.each(mutations, self._syncS);
+        self._syncA();
+        self._syncS(null, mutations);
       });
       this._observer.observe(this.$element[0], {
         attributes: true,
@@ -5512,7 +5689,7 @@ S2.define('select2/core',[
       if (self.isOpen()) {
         if (key === KEYS.ESC || key === KEYS.TAB ||
             (key === KEYS.UP && evt.altKey)) {
-          self.close();
+          self.close(evt);
 
           evt.preventDefault();
         } else if (key === KEYS.ENTER) {
@@ -5546,7 +5723,7 @@ S2.define('select2/core',[
   Select2.prototype._syncAttributes = function () {
     this.options.set('disabled', this.$element.prop('disabled'));
 
-    if (this.options.get('disabled')) {
+    if (this.isDisabled()) {
       if (this.isOpen()) {
         this.close();
       }
@@ -5557,7 +5734,7 @@ S2.define('select2/core',[
     }
   };
 
-  Select2.prototype._syncSubtree = function (evt, mutations) {
+  Select2.prototype._isChangeMutation = function (evt, mutations) {
     var changed = false;
     var self = this;
 
@@ -5585,7 +5762,22 @@ S2.define('select2/core',[
       }
     } 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 changed;
+  };
+
+  Select2.prototype._syncSubtree = function (evt, mutations) {
+    var changed = this._isChangeMutation(evt, mutations);
+    var self = this;
 
     // Only re-pull the data if we think there is a change
     if (changed) {
@@ -5636,7 +5828,7 @@ S2.define('select2/core',[
   };
 
   Select2.prototype.toggleDropdown = function () {
-    if (this.options.get('disabled')) {
+    if (this.isDisabled()) {
       return;
     }
 
@@ -5652,15 +5844,40 @@ S2.define('select2/core',[
       return;
     }
 
+    if (this.isDisabled()) {
+      return;
+    }
+
     this.trigger('query', {});
   };
 
-  Select2.prototype.close = function () {
+  Select2.prototype.close = function (evt) {
     if (!this.isOpen()) {
       return;
     }
 
-    this.trigger('close', {});
+    this.trigger('close', { originalEvent : evt });
+  };
+
+  /**
+   * Helper method to abstract the "enabled" (not "disabled") state of this
+   * object.
+   *
+   * @return {true} if the instance is not disabled.
+   * @return {false} if the instance is disabled.
+   */
+  Select2.prototype.isEnabled = function () {
+    return !this.isDisabled();
+  };
+
+  /**
+   * Helper method to abstract the "disabled" state of this object.
+   *
+   * @return {true} if the disabled option is true.
+   * @return {false} if the disabled option is false.
+   */
+  Select2.prototype.isDisabled = function () {
+    return this.options.get('disabled');
   };
 
   Select2.prototype.isOpen = function () {
@@ -5737,7 +5954,7 @@ S2.define('select2/core',[
       });
     }
 
-    this.$element.val(newVal).trigger('change');
+    this.$element.val(newVal).trigger('input').trigger('change');
   };
 
   Select2.prototype.destroy = function () {
@@ -6072,13 +6289,13 @@ S2.define('select2/compat/inputData',[
       });
 
       this.$element.val(data.id);
-      this.$element.trigger('change');
+      this.$element.trigger('input').trigger('change');
     } else {
       var value = this.$element.val();
       value += this._valueSeparator + data.id;
 
       this.$element.val(value);
-      this.$element.trigger('change');
+      this.$element.trigger('input').trigger('change');
     }
   };
 
@@ -6101,7 +6318,7 @@ S2.define('select2/compat/inputData',[
       }
 
       self.$element.val(values.join(self._valueSeparator));
-      self.$element.trigger('change');
+      self.$element.trigger('input').trigger('change');
     });
   };
 
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.full.min.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.full.min.js
new file mode 100644
index 0000000..fa78191
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.full.min.js
@@ -0,0 +1,2 @@
+/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
+!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(d){var e=function(){if(d&&d.fn&&d.fn.select2&&d.fn.select2.amd)var e=d.fn.select2.amd;var t,n,i,h,o,s,f,g,m,v,y,_,r,a,w,l;function b(e,t){return r.call(e,t)}function c(e,t){var n,i,r,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["* [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.js
similarity index 93%
rename from tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.js
rename to tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.js
index d33caac..fcfb5ab 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.js
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.js
@@ -1,5 +1,5 @@
 /*!
- * Select2 4.0.8
+ * Select2 4.0.13
  * https://select2.github.io
  *
  * Released under the MIT license
@@ -832,6 +832,8 @@ S2.define('select2/utils',[
     if (Utils.__cache[id] != null) {
       delete Utils.__cache[id];
     }
+
+    element.removeAttribute('data-select2-id');
   };
 
   return Utils;
@@ -853,7 +855,7 @@ S2.define('select2/results',[
 
   Results.prototype.render = function () {
     var $results = $(
-      '<ul class="select2-results__options" role="tree"></ul>'
+      '<ul class="select2-results__options" role="listbox"></ul>'
     );
 
     if (this.options.get('multiple')) {
@@ -876,7 +878,7 @@ S2.define('select2/results',[
     this.hideLoading();
 
     var $message = $(
-      '<li role="treeitem" aria-live="assertive"' +
+      '<li role="alert" aria-live="assertive"' +
       ' class="select2-results__option"></li>'
     );
 
@@ -1010,7 +1012,7 @@ S2.define('select2/results',[
     option.className = 'select2-results__option';
 
     var attrs = {
-      'role': 'treeitem',
+      'role': 'option',
       'aria-selected': 'false'
     };
 
@@ -1430,6 +1432,7 @@ S2.define('select2/selection/base',[
 
     $selection.attr('title', this.$element.attr('title'));
     $selection.attr('tabindex', this._tabindex);
+    $selection.attr('aria-disabled', 'false');
 
     this.$selection = $selection;
 
@@ -1439,7 +1442,6 @@ S2.define('select2/selection/base',[
   BaseSelection.prototype.bind = function (container, $container) {
     var self = this;
 
-    var id = container.id + '-container';
     var resultsId = container.id + '-results';
 
     this.container = container;
@@ -1489,10 +1491,12 @@ S2.define('select2/selection/base',[
 
     container.on('enable', function () {
       self.$selection.attr('tabindex', self._tabindex);
+      self.$selection.attr('aria-disabled', 'false');
     });
 
     container.on('disable', function () {
       self.$selection.attr('tabindex', '-1');
+      self.$selection.attr('aria-disabled', 'true');
     });
   };
 
@@ -1515,7 +1519,6 @@ S2.define('select2/selection/base',[
   };
 
   BaseSelection.prototype._attachCloseHandler = function (container) {
-    var self = this;
 
     $(document.body).on('mousedown.select2.' + container.id, function (e) {
       var $target = $(e.target);
@@ -1525,8 +1528,6 @@ S2.define('select2/selection/base',[
       var $all = $('.select2.select2-container--open');
 
       $all.each(function () {
-        var $this = $(this);
-
         if (this == $select[0]) {
           return;
         }
@@ -1555,6 +1556,27 @@ S2.define('select2/selection/base',[
     throw new Error('The `update` method must be defined in child classes.');
   };
 
+  /**
+   * Helper method to abstract the "enabled" (not "disabled") state of this
+   * object.
+   *
+   * @return {true} if the instance is not disabled.
+   * @return {false} if the instance is disabled.
+   */
+  BaseSelection.prototype.isEnabled = function () {
+    return !this.isDisabled();
+  };
+
+  /**
+   * Helper method to abstract the "disabled" state of this object.
+   *
+   * @return {true} if the disabled option is true.
+   * @return {false} if the disabled option is false.
+   */
+  BaseSelection.prototype.isDisabled = function () {
+    return this.options.get('disabled');
+  };
+
   return BaseSelection;
 });
 
@@ -1653,7 +1675,14 @@ S2.define('select2/selection/single',[
     var formatted = this.display(selection, $rendered);
 
     $rendered.empty().append(formatted);
-    $rendered.attr('title', selection.title || selection.text);
+
+    var title = selection.title || selection.text;
+
+    if (title) {
+      $rendered.attr('title', title);
+    } else {
+      $rendered.removeAttr('title');
+    }
   };
 
   return SingleSelection;
@@ -1698,7 +1727,7 @@ S2.define('select2/selection/multiple',[
       '.select2-selection__choice__remove',
       function (evt) {
         // Ignore the event if it is disabled
-        if (self.options.get('disabled')) {
+        if (self.isDisabled()) {
           return;
         }
 
@@ -1756,7 +1785,12 @@ S2.define('select2/selection/multiple',[
       var formatted = this.display(selection, $selection);
 
       $selection.append(formatted);
-      $selection.attr('title', selection.title || selection.text);
+
+      var title = selection.title || selection.text;
+
+      if (title) {
+        $selection.attr('title', title);
+      }
 
       Utils.StoreData($selection[0], 'data', selection);
 
@@ -1854,7 +1888,7 @@ S2.define('select2/selection/allowClear',[
 
   AllowClear.prototype._handleClear = function (_, evt) {
     // Ignore the event if it is disabled
-    if (this.options.get('disabled')) {
+    if (this.isDisabled()) {
       return;
     }
 
@@ -1897,7 +1931,7 @@ S2.define('select2/selection/allowClear',[
       }
     }
 
-    this.$element.trigger('change');
+    this.$element.trigger('input').trigger('change');
 
     this.trigger('toggle', {});
   };
@@ -1920,7 +1954,7 @@ S2.define('select2/selection/allowClear',[
       return;
     }
 
-    var removeAll = this.options.get('translations').get('removeAllItems');   
+    var removeAll = this.options.get('translations').get('removeAllItems');
 
     var $remove = $(
       '<span class="select2-selection__clear" title="' + removeAll() +'">' +
@@ -1949,7 +1983,7 @@ S2.define('select2/selection/search',[
       '<li class="select2-search select2-search--inline">' +
         '<input class="select2-search__field" type="search" tabindex="-1"' +
         ' autocomplete="off" autocorrect="off" autocapitalize="none"' +
-        ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
+        ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' +
       '</li>'
     );
 
@@ -1966,14 +2000,18 @@ S2.define('select2/selection/search',[
   Search.prototype.bind = function (decorated, container, $container) {
     var self = this;
 
+    var resultsId = container.id + '-results';
+
     decorated.call(this, container, $container);
 
     container.on('open', function () {
+      self.$search.attr('aria-controls', resultsId);
       self.$search.trigger('focus');
     });
 
     container.on('close', function () {
       self.$search.val('');
+      self.$search.removeAttr('aria-controls');
       self.$search.removeAttr('aria-activedescendant');
       self.$search.trigger('focus');
     });
@@ -1993,7 +2031,11 @@ S2.define('select2/selection/search',[
     });
 
     container.on('results:focus', function (params) {
-      self.$search.attr('aria-activedescendant', params.id);
+      if (params.data._resultId) {
+        self.$search.attr('aria-activedescendant', params.data._resultId);
+      } else {
+        self.$search.removeAttr('aria-activedescendant');
+      }
     });
 
     this.$selection.on('focusin', '.select2-search--inline', function (evt) {
@@ -2027,6 +2069,12 @@ S2.define('select2/selection/search',[
       }
     });
 
+    this.$selection.on('click', '.select2-search--inline', function (evt) {
+      if (self.$search.val()) {
+        evt.stopPropagation();
+      }
+    });
+
     // Try to detect the IE version should the `documentMode` property that
     // is stored on the document. This is only implemented in IE and is
     // slightly cleaner than doing a user agent check.
@@ -2145,7 +2193,7 @@ S2.define('select2/selection/search',[
     var width = '';
 
     if (this.$search.attr('placeholder') !== '') {
-      width = this.$selection.find('.select2-selection__rendered').innerWidth();
+      width = this.$selection.find('.select2-selection__rendered').width();
     } else {
       var minimumWidth = this.$search.val().length + 1;
 
@@ -3174,7 +3222,7 @@ S2.define('select2/data/select',[
     if ($(data.element).is('option')) {
       data.element.selected = true;
 
-      this.$element.trigger('change');
+      this.$element.trigger('input').trigger('change');
 
       return;
     }
@@ -3195,13 +3243,13 @@ S2.define('select2/data/select',[
         }
 
         self.$element.val(val);
-        self.$element.trigger('change');
+        self.$element.trigger('input').trigger('change');
       });
     } else {
       var val = data.id;
 
       this.$element.val(val);
-      this.$element.trigger('change');
+      this.$element.trigger('input').trigger('change');
     }
   };
 
@@ -3217,7 +3265,7 @@ S2.define('select2/data/select',[
     if ($(data.element).is('option')) {
       data.element.selected = false;
 
-      this.$element.trigger('change');
+      this.$element.trigger('input').trigger('change');
 
       return;
     }
@@ -3235,7 +3283,7 @@ S2.define('select2/data/select',[
 
       self.$element.val(val);
 
-      self.$element.trigger('change');
+      self.$element.trigger('input').trigger('change');
     });
   };
 
@@ -3428,15 +3476,19 @@ S2.define('select2/data/array',[
   'jquery'
 ], function (SelectAdapter, Utils, $) {
   function ArrayAdapter ($element, options) {
-    var data = options.get('data') || [];
+    this._dataToConvert = options.get('data') || [];
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
-
-    this.addOptions(this.convertToOptions(data));
   }
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
 
+  ArrayAdapter.prototype.bind = function (container, $container) {
+    ArrayAdapter.__super__.bind.call(this, container, $container);
+
+    this.addOptions(this.convertToOptions(this._dataToConvert));
+  };
+
   ArrayAdapter.prototype.select = function (data) {
     var $option = this.$element.find('option').filter(function (i, elm) {
       return elm.value == data.id.toString();
@@ -3726,8 +3778,6 @@ S2.define('select2/data/tags',[
   };
 
   Tags.prototype._removeOldTags = function (_) {
-    var tag = this._lastTag;
-
     var $options = this.$element.find('option[data-select2-tag]');
 
     $options.each(function () {
@@ -3931,10 +3981,30 @@ S2.define('select2/data/maximumSelectionLength',[
     decorated.call(this, $e, options);
   }
 
+  MaximumSelectionLength.prototype.bind =
+    function (decorated, container, $container) {
+      var self = this;
+
+      decorated.call(this, container, $container);
+
+      container.on('select', function () {
+        self._checkIfMaximumSelected();
+      });
+  };
+
   MaximumSelectionLength.prototype.query =
     function (decorated, params, callback) {
       var self = this;
 
+      this._checkIfMaximumSelected(function () {
+        decorated.call(self, params, callback);
+      });
+  };
+
+  MaximumSelectionLength.prototype._checkIfMaximumSelected =
+    function (_, successCallback) {
+      var self = this;
+
       this.current(function (currentData) {
         var count = currentData != null ? currentData.length : 0;
         if (self.maximumSelectionLength > 0 &&
@@ -3947,7 +4017,10 @@ S2.define('select2/data/maximumSelectionLength',[
           });
           return;
         }
-        decorated.call(self, params, callback);
+
+        if (successCallback) {
+          successCallback();
+        }
       });
   };
 
@@ -4010,7 +4083,7 @@ S2.define('select2/dropdown/search',[
       '<span class="select2-search select2-search--dropdown">' +
         '<input class="select2-search__field" type="search" tabindex="-1"' +
         ' autocomplete="off" autocorrect="off" autocapitalize="none"' +
-        ' spellcheck="false" role="textbox" />' +
+        ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' +
       '</span>'
     );
 
@@ -4025,6 +4098,8 @@ S2.define('select2/dropdown/search',[
   Search.prototype.bind = function (decorated, container, $container) {
     var self = this;
 
+    var resultsId = container.id + '-results';
+
     decorated.call(this, container, $container);
 
     this.$search.on('keydown', function (evt) {
@@ -4047,6 +4122,7 @@ S2.define('select2/dropdown/search',[
 
     container.on('open', function () {
       self.$search.attr('tabindex', 0);
+      self.$search.attr('aria-controls', resultsId);
 
       self.$search.trigger('focus');
 
@@ -4057,6 +4133,8 @@ S2.define('select2/dropdown/search',[
 
     container.on('close', function () {
       self.$search.attr('tabindex', -1);
+      self.$search.removeAttr('aria-controls');
+      self.$search.removeAttr('aria-activedescendant');
 
       self.$search.val('');
       self.$search.trigger('blur');
@@ -4079,6 +4157,14 @@ S2.define('select2/dropdown/search',[
         }
       }
     });
+
+    container.on('results:focus', function (params) {
+      if (params.data._resultId) {
+        self.$search.attr('aria-activedescendant', params.data._resultId);
+      } else {
+        self.$search.removeAttr('aria-activedescendant');
+      }
+    });
   };
 
   Search.prototype.handleSearch = function (evt) {
@@ -4223,7 +4309,7 @@ S2.define('select2/dropdown/infiniteScroll',[
     var $option = $(
       '<li ' +
       'class="select2-results__option select2-results__option--load-more"' +
-      'role="treeitem" aria-disabled="true"></li>'
+      'role="option" aria-disabled="true"></li>'
     );
 
     var message = this.options.get('translations').get('loadingMore');
@@ -4241,7 +4327,7 @@ S2.define('select2/dropdown/attachBody',[
   '../utils'
 ], function ($, Utils) {
   function AttachBody (decorated, $element, options) {
-    this.$dropdownParent = options.get('dropdownParent') || $(document.body);
+    this.$dropdownParent = $(options.get('dropdownParent') || document.body);
 
     decorated.call(this, $element, options);
   }
@@ -4249,27 +4335,14 @@ S2.define('select2/dropdown/attachBody',[
   AttachBody.prototype.bind = function (decorated, container, $container) {
     var self = this;
 
-    var setupResultsEvents = false;
-
     decorated.call(this, container, $container);
 
     container.on('open', function () {
       self._showDropdown();
       self._attachPositioningHandler(container);
 
-      if (!setupResultsEvents) {
-        setupResultsEvents = true;
-
-        container.on('results:all', function () {
-          self._positionDropdown();
-          self._resizeDropdown();
-        });
-
-        container.on('results:append', function () {
-          self._positionDropdown();
-          self._resizeDropdown();
-        });
-      }
+      // Must bind after the results handlers to ensure correct sizing
+      self._bindContainerResultHandlers(container);
     });
 
     container.on('close', function () {
@@ -4318,6 +4391,44 @@ S2.define('select2/dropdown/attachBody',[
     this.$dropdownContainer.detach();
   };
 
+  AttachBody.prototype._bindContainerResultHandlers =
+      function (decorated, container) {
+
+    // These should only be bound once
+    if (this._containerResultsHandlersBound) {
+      return;
+    }
+
+    var self = this;
+
+    container.on('results:all', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    container.on('results:append', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    container.on('results:message', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    container.on('select', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    container.on('unselect', function () {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+
+    this._containerResultsHandlersBound = true;
+  };
+
   AttachBody.prototype._attachPositioningHandler =
       function (decorated, container) {
     var self = this;
@@ -4403,7 +4514,17 @@ S2.define('select2/dropdown/attachBody',[
       $offsetParent = $offsetParent.offsetParent();
     }
 
-    var parentOffset = $offsetParent.offset();
+    var parentOffset = {
+      top: 0,
+      left: 0
+    };
+
+    if (
+      $.contains(document.body, $offsetParent[0]) ||
+      $offsetParent[0].isConnected
+      ) {
+      parentOffset = $offsetParent.offset();
+    }
 
     css.top -= parentOffset.top;
     css.left -= parentOffset.left;
@@ -4868,66 +4989,29 @@ S2.define('select2/defaults',[
       );
     }
 
-    if (typeof options.language === 'string') {
-      // Check if the language is specified with a region
-      if (options.language.indexOf('-') > 0) {
-        // Extract the region information if it is included
-        var languageParts = options.language.split('-');
-        var baseLanguage = languageParts[0];
-
-        options.language = [options.language, baseLanguage];
-      } else {
-        options.language = [options.language];
-      }
-    }
-
-    if ($.isArray(options.language)) {
-      var languages = new Translation();
-      options.language.push('en');
+    // If the defaults were not previously applied from an element, it is
+    // possible for the language option to have not been resolved
+    options.language = this._resolveLanguage(options.language);
 
-      var languageNames = options.language;
+    // Always fall back to English since it will always be complete
+    options.language.push('en');
 
-      for (var l = 0; l < languageNames.length; l++) {
-        var name = languageNames[l];
-        var language = {};
-
-        try {
-          // Try to load it with the original name
-          language = Translation.loadPath(name);
-        } catch (e) {
-          try {
-            // If we couldn't load it, check if it wasn't the full path
-            name = this.defaults.amdLanguageBase + name;
-            language = Translation.loadPath(name);
-          } catch (ex) {
-            // The translation could not be loaded at all. Sometimes this is
-            // because of a configuration problem, other times this can be
-            // because of how Select2 helps load all possible translation files.
-            if (options.debug && window.console && console.warn) {
-              console.warn(
-                'Select2: The language file for "' + name + '" could not be ' +
-                'automatically loaded. A fallback will be used instead.'
-              );
-            }
+    var uniqueLanguages = [];
 
-            continue;
-          }
-        }
+    for (var l = 0; l < options.language.length; l++) {
+      var language = options.language[l];
 
-        languages.extend(language);
+      if (uniqueLanguages.indexOf(language) === -1) {
+        uniqueLanguages.push(language);
       }
+    }
 
-      options.translations = languages;
-    } else {
-      var baseTranslation = Translation.loadPath(
-        this.defaults.amdLanguageBase + 'en'
-      );
-      var customTranslation = new Translation(options.language);
-
-      customTranslation.extend(baseTranslation);
+    options.language = uniqueLanguages;
 
-      options.translations = customTranslation;
-    }
+    options.translations = this._processTranslations(
+      options.language,
+      options.debug
+    );
 
     return options;
   };
@@ -4994,7 +5078,7 @@ S2.define('select2/defaults',[
       debug: false,
       dropdownAutoWidth: false,
       escapeMarkup: Utils.escapeMarkup,
-      language: EnglishTranslation,
+      language: {},
       matcher: matcher,
       minimumInputLength: 0,
       maximumInputLength: 0,
@@ -5016,6 +5100,103 @@ S2.define('select2/defaults',[
     };
   };
 
+  Defaults.prototype.applyFromElement = function (options, $element) {
+    var optionLanguage = options.language;
+    var defaultLanguage = this.defaults.language;
+    var elementLanguage = $element.prop('lang');
+    var parentLanguage = $element.closest('[lang]').prop('lang');
+
+    var languages = Array.prototype.concat.call(
+      this._resolveLanguage(elementLanguage),
+      this._resolveLanguage(optionLanguage),
+      this._resolveLanguage(defaultLanguage),
+      this._resolveLanguage(parentLanguage)
+    );
+
+    options.language = languages;
+
+    return options;
+  };
+
+  Defaults.prototype._resolveLanguage = function (language) {
+    if (!language) {
+      return [];
+    }
+
+    if ($.isEmptyObject(language)) {
+      return [];
+    }
+
+    if ($.isPlainObject(language)) {
+      return [language];
+    }
+
+    var languages;
+
+    if (!$.isArray(language)) {
+      languages = [language];
+    } else {
+      languages = language;
+    }
+
+    var resolvedLanguages = [];
+
+    for (var l = 0; l < languages.length; l++) {
+      resolvedLanguages.push(languages[l]);
+
+      if (typeof languages[l] === 'string' && languages[l].indexOf('-') > 0) {
+        // Extract the region information if it is included
+        var languageParts = languages[l].split('-');
+        var baseLanguage = languageParts[0];
+
+        resolvedLanguages.push(baseLanguage);
+      }
+    }
+
+    return resolvedLanguages;
+  };
+
+  Defaults.prototype._processTranslations = function (languages, debug) {
+    var translations = new Translation();
+
+    for (var l = 0; l < languages.length; l++) {
+      var languageData = new Translation();
+
+      var language = languages[l];
+
+      if (typeof language === 'string') {
+        try {
+          // Try to load it with the original name
+          languageData = Translation.loadPath(language);
+        } catch (e) {
+          try {
+            // If we couldn't load it, check if it wasn't the full path
+            language = this.defaults.amdLanguageBase + language;
+            languageData = Translation.loadPath(language);
+          } catch (ex) {
+            // The translation could not be loaded at all. Sometimes this is
+            // because of a configuration problem, other times this can be
+            // because of how Select2 helps load all possible translation files
+            if (debug && window.console && console.warn) {
+              console.warn(
+                'Select2: The language file for "' + language + '" could ' +
+                'not be automatically loaded. A fallback will be used instead.'
+              );
+            }
+          }
+        }
+      } else if ($.isPlainObject(language)) {
+        languageData = new Translation(language);
+      } else {
+        languageData = language;
+      }
+
+      translations.extend(languageData);
+    }
+
+    return translations;
+  };
+
   Defaults.prototype.set = function (key, value) {
     var camelKey = $.camelCase(key);
 
@@ -5045,6 +5226,10 @@ S2.define('select2/options',[
       this.fromElement($element);
     }
 
+    if ($element != null) {
+      this.options = Defaults.applyFromElement(this.options, $element);
+    }
+
     this.options = Defaults.apply(this.options);
 
     if ($element && $element.is('input')) {
@@ -5068,14 +5253,6 @@ S2.define('select2/options',[
       this.options.disabled = $e.prop('disabled');
     }
 
-    if (this.options.language == null) {
-      if ($e.prop('lang')) {
-        this.options.language = $e.prop('lang').toLowerCase();
-      } else if ($e.closest('[lang]').prop('lang')) {
-        this.options.language = $e.closest('[lang]').prop('lang');
-      }
-    }
-
     if (this.options.dir == null) {
       if ($e.prop('dir')) {
         this.options.dir = $e.prop('dir');
@@ -5389,8 +5566,8 @@ S2.define('select2/core',[
 
     if (observer != null) {
       this._observer = new observer(function (mutations) {
-        $.each(mutations, self._syncA);
-        $.each(mutations, self._syncS);
+        self._syncA();
+        self._syncS(null, mutations);
       });
       this._observer.observe(this.$element[0], {
         attributes: true,
@@ -5512,7 +5689,7 @@ S2.define('select2/core',[
       if (self.isOpen()) {
         if (key === KEYS.ESC || key === KEYS.TAB ||
             (key === KEYS.UP && evt.altKey)) {
-          self.close();
+          self.close(evt);
 
           evt.preventDefault();
         } else if (key === KEYS.ENTER) {
@@ -5546,7 +5723,7 @@ S2.define('select2/core',[
   Select2.prototype._syncAttributes = function () {
     this.options.set('disabled', this.$element.prop('disabled'));
 
-    if (this.options.get('disabled')) {
+    if (this.isDisabled()) {
       if (this.isOpen()) {
         this.close();
       }
@@ -5557,7 +5734,7 @@ S2.define('select2/core',[
     }
   };
 
-  Select2.prototype._syncSubtree = function (evt, mutations) {
+  Select2.prototype._isChangeMutation = function (evt, mutations) {
     var changed = false;
     var self = this;
 
@@ -5585,7 +5762,22 @@ S2.define('select2/core',[
       }
     } 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 changed;
+  };
+
+  Select2.prototype._syncSubtree = function (evt, mutations) {
+    var changed = this._isChangeMutation(evt, mutations);
+    var self = this;
 
     // Only re-pull the data if we think there is a change
     if (changed) {
@@ -5636,7 +5828,7 @@ S2.define('select2/core',[
   };
 
   Select2.prototype.toggleDropdown = function () {
-    if (this.options.get('disabled')) {
+    if (this.isDisabled()) {
       return;
     }
 
@@ -5652,15 +5844,40 @@ S2.define('select2/core',[
       return;
     }
 
+    if (this.isDisabled()) {
+      return;
+    }
+
     this.trigger('query', {});
   };
 
-  Select2.prototype.close = function () {
+  Select2.prototype.close = function (evt) {
     if (!this.isOpen()) {
       return;
     }
 
-    this.trigger('close', {});
+    this.trigger('close', { originalEvent : evt });
+  };
+
+  /**
+   * Helper method to abstract the "enabled" (not "disabled") state of this
+   * object.
+   *
+   * @return {true} if the instance is not disabled.
+   * @return {false} if the instance is disabled.
+   */
+  Select2.prototype.isEnabled = function () {
+    return !this.isDisabled();
+  };
+
+  /**
+   * Helper method to abstract the "disabled" state of this object.
+   *
+   * @return {true} if the disabled option is true.
+   * @return {false} if the disabled option is false.
+   */
+  Select2.prototype.isDisabled = function () {
+    return this.options.get('disabled');
   };
 
   Select2.prototype.isOpen = function () {
@@ -5737,7 +5954,7 @@ S2.define('select2/core',[
       });
     }
 
-    this.$element.val(newVal).trigger('change');
+    this.$element.val(newVal).trigger('input').trigger('change');
   };
 
   Select2.prototype.destroy = function () {
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.min.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.min.js
new file mode 100644
index 0000000..e421426
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2.min.js
@@ -0,0 +1,2 @@
+/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
+!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(u){var e=function(){if(u&&u.fn&&u.fn.select2&&u.fn.select2.amd)var e=u.fn.select2.amd;var t,n,r,h,o,s,f,g,m,v,y,_,i,a,b;function w(e,t){return i.call(e,t)}function l(e,t){var n,r,i,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["*"] [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
index 86b7c95..07d0dce 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
@@ -274,9 +274,77 @@ Tobago.Select2.register('suppressMessages', (function() {
 
   var Utils = jQuery.fn.select2.amd.require('select2/utils');
   var resultAdapter = jQuery.fn.select2.amd.require('select2/results');
-  return Utils.Decorate(resultAdapter, suppressMessages);
+  var selectOnClose = jQuery.fn.select2.amd.require('select2/dropdown/selectOnClose');
+  var adapter = Utils.Decorate(resultAdapter, selectOnClose);
+  return Utils.Decorate(adapter, suppressMessages);
 
 })());
 
+
+Tobago.Select2.register('testDataAdapter', (function() {
+
+  var testDataAdapter = (function () {
+
+    function TestDataAdapter (decorated, $element, options) {
+      decorated.call(this, $element, options);
+    }
+
+    TestDataAdapter.prototype.query = function(decorated, params, callback) {
+      var select2Instance = this;
+
+      if (!select2Instance.$element.data("tttxt")) {
+        select2Instance.container.on("open", function (params) {
+          console.warn("TestDataAdapter.container.open");
+          if (!select2Instance.$search.val()) {
+            select2Instance.container.$results.empty();
+          }
+        });
+        select2Instance.$element.data("tttxt", true);
+      }
+
+      console.warn("TestDataAdapter.prototype.query");
+      callback({results: []});
+      function clearDuplicatesCallback(results) {
+        callback(results);
+
+        var values = [];
+        select2Instance.$element.find("option[data-select2-tag='true']").each(function () {
+           var option  = jQuery(this);
+           if (values.includes(option.val())) {
+             option.detach();
+           } else {
+             values.push(option.val());
+           }
+        });
+      }
+      decorated.call(this, params, clearDuplicatesCallback);
+    };
+
+    return TestDataAdapter;
+  })();
+
+  var Utils = jQuery.fn.select2.amd.require('select2/utils');
+
+
+  var adapter = jQuery.fn.select2.amd.require('select2/data/ajax');
+
+
+  /*
+    {"tags":true,"tokenSeparators":[","],"language":"de","minimumInputLength":2,"placeholder":"Select countries"}'
+   */
+
+  adapter = Utils.Decorate(adapter, testDataAdapter);
+
+
+  adapter = Utils.Decorate(adapter, jQuery.fn.select2.amd.require('select2/data/minimumInputLength'));
+  adapter = Utils.Decorate(adapter, jQuery.fn.select2.amd.require('select2/data/tags'));
+  return Utils.Decorate(adapter, jQuery.fn.select2.amd.require('select2/data/tokenizer'));
+
+})());
+
+
+
+
+
 Tobago.registerListener(Tobago.Select2.init, Tobago.Phase.DOCUMENT_READY);
 Tobago.registerListener(Tobago.Select2.init, Tobago.Phase.AFTER_UPDATE);
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2-4.0.8.min.css b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2-4.0.8.min.css
deleted file mode 100644
index dc2315a..0000000
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2-4.0.8.min.css
+++ /dev/null
@@ -1 +0,0 @@
-.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single . [...]
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2-4.0.8.css b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2.css
similarity index 99%
rename from tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2-4.0.8.css
rename to tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2.css
index cd45ce8..750b320 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2-4.0.8.css
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2.css
@@ -193,7 +193,8 @@
     float: right;
     font-weight: bold;
     margin-top: 5px;
-    margin-right: 10px; }
+    margin-right: 10px;
+    padding: 1px; }
   .select2-container--default .select2-selection--multiple .select2-selection__choice {
     background-color: #e4e4e4;
     border: 1px solid #aaa;
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2.min.css b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2.min.css
new file mode 100644
index 0000000..7c18ad5
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/style/contrib/select2/select2.min.css
@@ -0,0 +1 @@
+.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single . [...]


[myfaces-tobago] 09/09: Tobago-1999: SelectItemIterator iterates only once

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit da0088d82104c10b8f5a955aa092c8d0d2103f31
Author: Volker Weber <v....@inexso.de>
AuthorDate: Tue Apr 28 14:22:41 2020 +0200

    Tobago-1999: SelectItemIterator iterates only once
---
 .../tobago/internal/util/UISelect2ComponentUtil.java | 20 +++++---------------
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
index 758e05a..de43bd7 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
@@ -140,8 +140,10 @@ public class UISelect2ComponentUtil {
       return items;
     }
     Converter converter = component.getConverter();
+    List<javax.faces.model.SelectItem> itemsToRender = new ArrayList<javax.faces.model.SelectItem>();
     Map<String, javax.faces.model.SelectItem> optionValues = new HashMap<String, javax.faces.model.SelectItem>();
     for (javax.faces.model.SelectItem item : items) {
+      itemsToRender.add(item);
       if (converter != null) {
         optionValues.put(converter.getAsString(facesContext, component, item.getValue()), item);
       } else {
@@ -149,24 +151,12 @@ public class UISelect2ComponentUtil {
       }
     }
 
-
-    List<javax.faces.model.SelectItem> itemsToRender = new ArrayList<javax.faces.model.SelectItem>();
     for (String submittedValue : submittedValues) {
-      if (optionValues.keySet().contains(submittedValue)) {
-        optionValues.remove(submittedValue);
-      } else {
+      if (!optionValues.keySet().contains(submittedValue)) {
         itemsToRender.add(new SubmittedItem(submittedValue));
       }
     }
-    if (itemsToRender.isEmpty()) {
-      return items;
-    } else {
-      for (javax.faces.model.SelectItem item : items) {
-        if (optionValues.values().contains(item)) {
-          itemsToRender.add(item);
-        }
-      }
-      return itemsToRender;
-    }
+
+    return itemsToRender;
   }
 }


[myfaces-tobago] 07/09: Tobago-1999: fix: SuggestRenderer: escape '"' in json

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit bf334026b48930359105c77bbd3a485b87bf1aee
Author: Volker Weber <v....@inexso.de>
AuthorDate: Tue Apr 21 14:04:57 2020 +0200

    Tobago-1999: fix: SuggestRenderer: escape '"' in json
---
 .../tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
index 6cd6519..b588db1 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
@@ -170,7 +170,7 @@ public class SuggestRenderer extends InputRendererBase {
   }
 
   private String encode(String value) {
-    return value.replace("\"", "\\\"").replace("\\", "\\\\");
+    return value.replace("\\", "\\\\").replace("\"", "\\\"");
   }
 
 }


[myfaces-tobago] 06/09: Tobago-1999: SuggestRenderer: escape '"' in json

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit d413918fee650e3165835bdc9f3fd19bb8784c65
Author: Volker Weber <v....@inexso.de>
AuthorDate: Tue Apr 21 12:50:14 2020 +0200

    Tobago-1999: SuggestRenderer: escape '"' in json
---
 .../renderkit/html/standard/standard/tag/SuggestRenderer.java    | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
index 230655f..6cd6519 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
@@ -35,6 +35,7 @@ import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
 import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
+import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
 
@@ -154,8 +155,8 @@ public class SuggestRenderer extends InputRendererBase {
     for (final AutoSuggestItem item : items.getItems()) {
       builder.append("{");
 
-      builder.append("\"id\":\"").append(item.getValue()).append("\",");
-      builder.append("\"text\":\"").append(item.getLabel()).append("\"");
+      builder.append("\"id\":\"").append(encode(item.getValue())).append("\",");
+      builder.append("\"text\":\"").append(encode(item.getLabel())).append("\"");
 
       builder.append("},");
     }
@@ -168,4 +169,8 @@ public class SuggestRenderer extends InputRendererBase {
     writer.endElement(HtmlElements.INPUT);
   }
 
+  private String encode(String value) {
+    return value.replace("\"", "\\\"").replace("\\", "\\\\");
+  }
+
 }


[myfaces-tobago] 01/09: Tobago-1999

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 23718d42b71e8bf5b02e7bb19b25bd8c9364f264
Author: Volker Weber <vo...@weber-oldenburg.de>
AuthorDate: Mon Aug 19 09:04:17 2019 +0200

    Tobago-1999
---
 pom.xml                                            |    2 +-
 tobago-assembly/pom.xml                            |    2 +-
 tobago-assembly/src/main/resources/LICENSE.txt     |   28 +
 tobago-core/pom.xml                                |    4 +-
 .../myfaces/tobago/component/RendererTypes.java    |    1 +
 .../myfaces/tobago/facelets/SelectManyBoxRule.java |   49 +
 .../tobago/facelets/TobagoComponentHandler.java    |    4 +
 .../extension/SelectManyBoxExtensionHandler.java}  |   26 +-
 .../component/AbstractUISelectManyBox.java         |  186 +
 .../component/AbstractUISelectOneBase.java         |    1 +
 .../component/AbstractUISelectOneChoice.java       |  128 +
 .../component/SelectManyBoxTagDeclaration.java     |  100 +
 .../component/SelectOneChoiceTagDeclaration.java   |   30 +-
 .../internal/taglib/declaration/Select2.java       |   63 +
 .../extension/SelectManyBoxExtensionTag.java       |  419 ++
 tobago-example/pom.xml                             |    2 +-
 .../tobago-example-addressbook-cdi/pom.xml         |    2 +-
 tobago-example/tobago-example-addressbook/pom.xml  |    2 +-
 tobago-example/tobago-example-assembly/pom.xml     |    2 +-
 tobago-example/tobago-example-blank/pom.xml        |    2 +-
 tobago-example/tobago-example-data/pom.xml         |    2 +-
 tobago-example/tobago-example-demo/pom.xml         |    2 +-
 .../tobago/example/demo/Select2Controller.java     |   27 +
 .../src/main/webapp/WEB-INF/web.xml                |    4 +-
 .../content/25-select/00-select2/select2.xhtml     |  207 +
 .../src/main/webapp/script/demo.js                 |   99 +
 tobago-example/tobago-example-portlet/pom.xml      |    2 +-
 tobago-example/tobago-example-sandbox/pom.xml      |    2 +-
 tobago-example/tobago-example-security/pom.xml     |    2 +-
 tobago-example/tobago-example-test/pom.xml         |    4 +-
 tobago-extension/pom.xml                           |    2 +-
 tobago-extension/tobago-deprecation/pom.xml        |    2 +-
 tobago-extension/tobago-fileupload/pom.xml         |    2 +-
 tobago-extension/tobago-sandbox/pom.xml            |    2 +-
 tobago-extension/tobago-security/pom.xml           |    2 +-
 tobago-theme/pom.xml                               |    2 +-
 tobago-theme/tobago-theme-charlotteville/pom.xml   |    2 +-
 tobago-theme/tobago-theme-example/pom.xml          |    2 +-
 tobago-theme/tobago-theme-richmond/pom.xml         |    2 +-
 tobago-theme/tobago-theme-scarborough/pom.xml      |    2 +-
 tobago-theme/tobago-theme-speyside/pom.xml         |    2 +-
 .../html/speyside/standard/style/tobago.less       |   27 +
 .../property/tobago-theme-config.properties        |    3 +
 tobago-theme/tobago-theme-standard/pom.xml         |    5 +-
 .../src/main/appended-resources/META-INF/LICENSE   |   30 +
 .../myfaces/tobago/renderkit/html/JsonUtils.java   |    2 +-
 .../tobago/renderkit/html/Select2Options.java      |  314 +
 ...iceRenderer.java => SelectManyBoxRenderer.java} |   69 +-
 .../standard/tag/SelectOneChoiceRenderer.java      |   39 +-
 .../src/main/resources/META-INF/tobago-config.xml  |    5 +
 .../standard/script/contrib/select2/i18n/af.js     |    3 +
 .../standard/script/contrib/select2/i18n/ar.js     |    3 +
 .../standard/script/contrib/select2/i18n/az.js     |    3 +
 .../standard/script/contrib/select2/i18n/bg.js     |    3 +
 .../standard/script/contrib/select2/i18n/bn.js     |    3 +
 .../standard/script/contrib/select2/i18n/bs.js     |    3 +
 .../standard/script/contrib/select2/i18n/ca.js     |    3 +
 .../standard/script/contrib/select2/i18n/cs.js     |    3 +
 .../standard/script/contrib/select2/i18n/da.js     |    3 +
 .../standard/script/contrib/select2/i18n/de.js     |    3 +
 .../standard/script/contrib/select2/i18n/dsb.js    |    3 +
 .../standard/script/contrib/select2/i18n/el.js     |    3 +
 .../standard/script/contrib/select2/i18n/en.js     |    3 +
 .../standard/script/contrib/select2/i18n/es.js     |    3 +
 .../standard/script/contrib/select2/i18n/et.js     |    3 +
 .../standard/script/contrib/select2/i18n/eu.js     |    3 +
 .../standard/script/contrib/select2/i18n/fa.js     |    3 +
 .../standard/script/contrib/select2/i18n/fi.js     |    3 +
 .../standard/script/contrib/select2/i18n/fr.js     |    3 +
 .../standard/script/contrib/select2/i18n/gl.js     |    3 +
 .../standard/script/contrib/select2/i18n/he.js     |    3 +
 .../standard/script/contrib/select2/i18n/hi.js     |    3 +
 .../standard/script/contrib/select2/i18n/hr.js     |    3 +
 .../standard/script/contrib/select2/i18n/hsb.js    |    3 +
 .../standard/script/contrib/select2/i18n/hu.js     |    3 +
 .../standard/script/contrib/select2/i18n/hy.js     |    3 +
 .../standard/script/contrib/select2/i18n/id.js     |    3 +
 .../standard/script/contrib/select2/i18n/is.js     |    3 +
 .../standard/script/contrib/select2/i18n/it.js     |    3 +
 .../standard/script/contrib/select2/i18n/ja.js     |    3 +
 .../standard/script/contrib/select2/i18n/ka.js     |    3 +
 .../standard/script/contrib/select2/i18n/km.js     |    3 +
 .../standard/script/contrib/select2/i18n/ko.js     |    3 +
 .../standard/script/contrib/select2/i18n/lt.js     |    3 +
 .../standard/script/contrib/select2/i18n/lv.js     |    3 +
 .../standard/script/contrib/select2/i18n/mk.js     |    3 +
 .../standard/script/contrib/select2/i18n/ms.js     |    3 +
 .../standard/script/contrib/select2/i18n/nb.js     |    3 +
 .../standard/script/contrib/select2/i18n/ne.js     |    3 +
 .../standard/script/contrib/select2/i18n/nl.js     |    3 +
 .../standard/script/contrib/select2/i18n/pl.js     |    3 +
 .../standard/script/contrib/select2/i18n/ps.js     |    3 +
 .../standard/script/contrib/select2/i18n/pt-BR.js  |    3 +
 .../standard/script/contrib/select2/i18n/pt.js     |    3 +
 .../standard/script/contrib/select2/i18n/ro.js     |    3 +
 .../standard/script/contrib/select2/i18n/ru.js     |    3 +
 .../standard/script/contrib/select2/i18n/sk.js     |    3 +
 .../standard/script/contrib/select2/i18n/sl.js     |    3 +
 .../standard/script/contrib/select2/i18n/sq.js     |    3 +
 .../script/contrib/select2/i18n/sr-Cyrl.js         |    3 +
 .../standard/script/contrib/select2/i18n/sr.js     |    3 +
 .../standard/script/contrib/select2/i18n/sv.js     |    3 +
 .../standard/script/contrib/select2/i18n/th.js     |    3 +
 .../standard/script/contrib/select2/i18n/tk.js     |    3 +
 .../standard/script/contrib/select2/i18n/tr.js     |    3 +
 .../standard/script/contrib/select2/i18n/uk.js     |    3 +
 .../standard/script/contrib/select2/i18n/vi.js     |    3 +
 .../standard/script/contrib/select2/i18n/zh-CN.js  |    3 +
 .../standard/script/contrib/select2/i18n/zh-TW.js  |    3 +
 .../script/contrib/select2/select2-4.0.8.full.js   | 6603 ++++++++++++++++++++
 .../contrib/select2/select2-4.0.8.full.min.js      |    2 +
 .../script/contrib/select2/select2-4.0.8.js        | 5891 +++++++++++++++++
 .../script/contrib/select2/select2-4.0.8.min.js    |    2 +
 .../standard/standard/script/tobago-select2.js     |  145 +
 .../style/contrib/select2/select2-4.0.8.css        |  480 ++
 .../style/contrib/select2/select2-4.0.8.min.css    |    1 +
 .../html/standard/standard/style/tobago.css        |    7 +
 tobago-tool/pom.xml                                |    2 +-
 tobago-tool/tobago-theme-plugin/pom.xml            |    2 +-
 tobago-tool/tobago-tool-annotation/pom.xml         |    2 +-
 tobago-tool/tobago-tool-apt/pom.xml                |    2 +-
 121 files changed, 15174 insertions(+), 62 deletions(-)

diff --git a/pom.xml b/pom.xml
index 66ec3aa..f1a50ce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
   <artifactId>tobago</artifactId>
   <packaging>pom</packaging>
   <name>Apache Tobago</name>
-  <version>2.4.3-SNAPSHOT</version>
+  <version>2.5.0-SNAPSHOT</version>
   <description>The goal of Tobago is to provide the community with a well designed set of user interface components based on JSF and run on MyFaces.</description>
   <prerequisites>
     <maven>3.0.4</maven>
diff --git a/tobago-assembly/pom.xml b/tobago-assembly/pom.xml
index babff51..3eacbdd 100644
--- a/tobago-assembly/pom.xml
+++ b/tobago-assembly/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
 
   <dependencies>
diff --git a/tobago-assembly/src/main/resources/LICENSE.txt b/tobago-assembly/src/main/resources/LICENSE.txt
index 4ad7a26..fa9a97f 100644
--- a/tobago-assembly/src/main/resources/LICENSE.txt
+++ b/tobago-assembly/src/main/resources/LICENSE.txt
@@ -325,4 +325,32 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
+------------------------------------------------------------------------------
+ For https://select2.org/:
+------------------------------------------------------------------------------
+
+ Select2 is licensed under MIT
+
+ The MIT License (MIT)
+
+ Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
 
diff --git a/tobago-core/pom.xml b/tobago-core/pom.xml
index 16abafe..6193b72 100644
--- a/tobago-core/pom.xml
+++ b/tobago-core/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-core</artifactId>
   <packaging>jar</packaging>
@@ -180,7 +180,7 @@
       <groupId>commons-codec</groupId>
       <artifactId>commons-codec</artifactId>
     </dependency>
-    
+
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-utils</artifactId>
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/RendererTypes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/RendererTypes.java
index dde8696..bc0ff1f6 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/RendererTypes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/RendererTypes.java
@@ -58,6 +58,7 @@ public final class RendererTypes {
   public static final String PROGRESS = "Progress";
   public static final String SCRIPT = "Script";
   public static final String SELECT_BOOLEAN_CHECKBOX = "SelectBooleanCheckbox";
+  public static final String SELECT_MANY_BOX = "SelectManyBox";
   public static final String SELECT_MANY_CHECKBOX = "SelectManyCheckbox";
   public static final String SELECT_MANY_LISTBOX = "SelectManyListbox";
   public static final String SELECT_MANY_SHUTTLE = "SelectManyShuttle";;
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/SelectManyBoxRule.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/SelectManyBoxRule.java
new file mode 100644
index 0000000..0e413a3
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/SelectManyBoxRule.java
@@ -0,0 +1,49 @@
+package org.apache.myfaces.tobago.facelets;
+
+import org.apache.myfaces.tobago.component.Attributes;
+import org.apache.myfaces.tobago.component.SupportsRenderedPartially;
+import org.apache.myfaces.tobago.internal.component.AbstractUISelectManyBox;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+
+import javax.el.ValueExpression;
+import javax.faces.component.UIComponent;
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.MetaRule;
+import javax.faces.view.facelets.Metadata;
+import javax.faces.view.facelets.MetadataTarget;
+import javax.faces.view.facelets.TagAttribute;
+
+public class SelectManyBoxRule extends MetaRule {
+
+  public static final String TOKEN_SEPARATORS = "tokenSeparators";
+
+  public static final SelectManyBoxRule INSTANCE = new SelectManyBoxRule();
+
+  public Metadata applyRule(String name, TagAttribute attribute, MetadataTarget metadataTarget) {
+    if (metadataTarget.isTargetInstanceOf(AbstractUISelectManyBox.class)) {
+      if (TOKEN_SEPARATORS.equals(name)) {
+        return new SelectManyBoxRule.SelectManyBoxMapper(attribute);
+      }
+    }
+    return null;
+  }
+
+  static final class SelectManyBoxMapper extends Metadata {
+
+    private final TagAttribute attribute;
+
+    public SelectManyBoxMapper(TagAttribute attribute) {
+      this.attribute = attribute;
+    }
+
+    public void applyMetadata(FaceletContext faceletContext, Object instance) {
+      if (attribute.isLiteral()) {
+        final String[] components = AbstractUISelectManyBox.parseTokenSeparators(attribute.getValue());
+        ((AbstractUISelectManyBox) instance).setTokenSeparators(components);
+      } else {
+        final ValueExpression expression = attribute.getValueExpression(faceletContext, Object.class);
+        ((UIComponent) instance).setValueExpression(TOKEN_SEPARATORS, expression);
+      }
+    }
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/TobagoComponentHandler.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/TobagoComponentHandler.java
index 741501c..0848243 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/TobagoComponentHandler.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/TobagoComponentHandler.java
@@ -32,6 +32,7 @@ import org.apache.myfaces.tobago.event.TabChangeSource;
 import org.apache.myfaces.tobago.internal.component.AbstractUIFlowLayout;
 import org.apache.myfaces.tobago.internal.component.AbstractUIGridLayout;
 import org.apache.myfaces.tobago.internal.component.AbstractUIPopup;
+import org.apache.myfaces.tobago.internal.component.AbstractUISelectManyBox;
 import org.apache.myfaces.tobago.internal.config.TobagoConfigImpl;
 
 import javax.faces.component.EditableValueHolder;
@@ -85,6 +86,9 @@ public class TobagoComponentHandler extends ComponentHandler {
     if (SupportsRenderedPartially.class.isAssignableFrom(aClass)) {
       metaRuleset.addRule(SupportsRenderedPartiallyRule.INSTANCE);
     }
+    if (AbstractUISelectManyBox.class.isAssignableFrom(aClass)) {
+      metaRuleset.addRule(SelectManyBoxRule.INSTANCE);
+    }
     return metaRuleset;
   }
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/extension/SelectManyBoxExtensionHandler.java
similarity index 53%
copy from tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
copy to tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/extension/SelectManyBoxExtensionHandler.java
index 206f3a8..3338b95 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/extension/SelectManyBoxExtensionHandler.java
@@ -17,7 +17,29 @@
  * under the License.
  */
 
-package org.apache.myfaces.tobago.internal.component;
+package org.apache.myfaces.tobago.facelets.extension;
 
-public abstract class AbstractUISelectOneChoice extends AbstractUISelectOneBase {
+import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.component.UISelectManyBox;
+import org.apache.myfaces.tobago.component.UISelectManyListbox;
+
+import javax.faces.view.facelets.ComponentConfig;
+
+public class SelectManyBoxExtensionHandler extends TobagoLabelExtensionHandler {
+
+  public SelectManyBoxExtensionHandler(final ComponentConfig config) {
+    super(config);
+  }
+
+  protected String getSubComponentType() {
+    return UISelectManyBox.COMPONENT_TYPE;
+  }
+
+  protected String getSubRendererType() {
+    return RendererTypes.SELECT_MANY_BOX;
+  }
+
+  protected String getRows() {
+    return "1*";
+  }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java
new file mode 100644
index 0000000..56c657d
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java
@@ -0,0 +1,186 @@
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+package org.apache.myfaces.tobago.internal.component;
+
+import org.apache.myfaces.tobago.internal.component.AbstractUISelectOneChoice.Select2Keys;
+
+import javax.faces.context.FacesContext;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class AbstractUISelectManyBox extends AbstractUISelectMany {
+
+  @Override
+  protected void validateValue(FacesContext context, Object convertedValue) {
+    if (!isAllowCustom()) {
+      super.validateValue(context, convertedValue);
+    }
+  }
+
+  public boolean isAllowClear() {
+    Boolean allowClear = (Boolean) getStateHelper().eval(Select2Keys.allowClear);
+    if (allowClear != null) {
+      return allowClear;
+    }
+    return false;
+  }
+  public boolean isAllowClearSet() {
+    return getStateHelper().eval(Select2Keys.allowClear) != null;
+  }
+
+  public void setAllowClear(boolean allowClear) {
+    getStateHelper().put(Select2Keys.allowClear, allowClear);
+  }
+
+
+  public boolean isAllowCustom() {
+    Boolean allowCustom = (Boolean) getStateHelper().eval(Select2Keys.allowCustom);
+    if (allowCustom != null) {
+      return allowCustom;
+    }
+    return false;
+  }
+  public boolean isAllowCustomSet() {
+    return getStateHelper().eval(Select2Keys.allowCustom) != null;
+  }
+
+  public void setAllowCustom(boolean allowCustom) {
+    getStateHelper().put(Select2Keys.allowCustom, allowCustom);
+  }
+
+  public boolean isHideDropdown() {
+    Boolean hideDropdown = (Boolean) getStateHelper().eval(Select2Keys.hideDropdown);
+    if (hideDropdown != null) {
+      return hideDropdown;
+    }
+    return false;
+  }
+
+  public void setHideDropdown(boolean hideDropdown) {
+    getStateHelper().put(Select2Keys.hideDropdown, hideDropdown);
+  }
+
+  public String getMatcher() {
+    String matcher = (String) getStateHelper().eval(Select2Keys.matcher);
+    if (matcher != null) {
+      return matcher;
+    }
+    return null;
+  }
+  public boolean isMatcherSet() {
+    return getStateHelper().eval(Select2Keys.matcher) != null;
+  }
+
+  public void setMatcher(String matcher) {
+    getStateHelper().put(Select2Keys.matcher, matcher);
+  }
+
+  public int getMaximumInputLength() {
+    Integer maximumInputLength = (Integer) getStateHelper().eval(Select2Keys.maximumInputLength);
+    if (maximumInputLength != null) {
+      return maximumInputLength;
+    }
+    return 0;
+  }
+  public boolean isMaximumInputLengthSet() {
+    return getStateHelper().eval(Select2Keys.maximumInputLength) != null;
+  }
+
+  public void setMaximumInputLength(int minimumInputLength) {
+    getStateHelper().put(Select2Keys.maximumInputLength, minimumInputLength);
+  }
+
+  public int getMinimumInputLength() {
+    Integer minimumInputLength = (Integer) getStateHelper().eval(Select2Keys.minimumInputLength);
+    if (minimumInputLength != null) {
+      return minimumInputLength;
+    }
+    return 0;
+  }
+  public boolean isMinimumInputLengthSet() {
+    return getStateHelper().eval(Select2Keys.minimumInputLength) != null;
+  }
+
+  public void setMinimumInputLength(int minimumInputLength) {
+    getStateHelper().put(Select2Keys.minimumInputLength, minimumInputLength);
+  }
+
+  public int getMaximumSelectionLength() {
+    Integer maximumSelectionLength = (Integer) getStateHelper().eval(Select2Keys.maximumSelectionLength);
+    if (maximumSelectionLength != null) {
+      return maximumSelectionLength;
+    }
+    return 0;
+  }
+  public boolean isMaximumSelectionLengthSet() {
+    return getStateHelper().eval(Select2Keys.maximumSelectionLength) != null;
+  }
+
+  public void setMaximumSelectionLength(int maximumSelectionLength) {
+    getStateHelper().put(Select2Keys.maximumSelectionLength, maximumSelectionLength);
+  }
+
+  public void setMinimumResultsForSearch(int minimumResultsForSearch) {
+    getStateHelper().put(Select2Keys.minimumResultsForSearch, minimumResultsForSearch);
+  }
+
+  public int getMinimumResultsForSearch() {
+    Integer minimumResultsForSearch = (Integer) getStateHelper().eval(Select2Keys.minimumResultsForSearch);
+    if (minimumResultsForSearch != null) {
+      return minimumResultsForSearch;
+    }
+    return 20;
+  }
+
+  public boolean isMinimumResultsForSearchSet() {
+    return getStateHelper().eval(Select2Keys.minimumResultsForSearch) != null;
+  }
+
+
+  public String getTokenizer() {
+    String tokenizer = (String) getStateHelper().eval(Select2Keys.tokenizer);
+    if (tokenizer != null) {
+      return tokenizer;
+    }
+    return null;
+  }
+
+  public boolean isTokenizerSet() {
+    return getStateHelper().eval(Select2Keys.tokenizer) != null;
+  }
+
+  public void setTokenizer(String tokenizer) {
+    getStateHelper().put(Select2Keys.tokenizer, tokenizer);
+  }
+
+  public String[] getTokenSeparators() {
+    Object tokenSeparators = getStateHelper().eval(Select2Keys.tokenSeparators);
+    if (tokenSeparators instanceof String[]) {
+      return  (String[]) tokenSeparators;
+    } else if (tokenSeparators instanceof String) {
+      return parseTokenSeparators((String) tokenSeparators);
+    }
+    return null;
+  }
+
+  public static String[] parseTokenSeparators(String tokenSeparators) {
+    Set<String> tokens = new HashSet<String>();
+    for (int i = 0; i < tokenSeparators.length(); i++) {
+      tokens.add(tokenSeparators.substring(i, i + 1));
+    }
+    return tokens.toArray(new String[0]);
+  }
+
+  public boolean isTokenSeparatorsSet() {
+    return getStateHelper().eval(Select2Keys.tokenSeparators) != null;
+  }
+
+  public void setTokenSeparators(String[] tokenSeparators) {
+    getStateHelper().put(Select2Keys.tokenSeparators, tokenSeparators);
+  }
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneBase.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneBase.java
index 921a70d..e2eae91 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneBase.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneBase.java
@@ -50,5 +50,6 @@ public abstract class AbstractUISelectOneBase extends javax.faces.component.UISe
     super.validate(facesContext);
   }
 
+
   public abstract boolean isReadonly();
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
index 206f3a8..8555b95 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
@@ -20,4 +20,132 @@
 package org.apache.myfaces.tobago.internal.component;
 
 public abstract class AbstractUISelectOneChoice extends AbstractUISelectOneBase {
+
+  enum Select2Keys {
+    allowClear,
+    allowCustom,
+    hideDropdown,
+    isSelect2,
+    language,
+    matcher,
+    maximumInputLength,
+    minimumInputLength,
+    maximumSelectionLength,
+    minimumResultsForSearch,
+    tokenizer,
+    tokenSeparators
+  }
+
+
+
+
+
+  public boolean isAllowClear() {
+    Boolean allowClear = (Boolean) getStateHelper().eval(Select2Keys.allowClear);
+    if (allowClear != null) {
+      return allowClear;
+    }
+    return false;
+  }
+  public boolean isAllowClearSet() {
+    return getStateHelper().eval(Select2Keys.allowClear) != null;
+  }
+
+  public void setAllowClear(boolean allowClear) {
+    getStateHelper().put(Select2Keys.allowClear, allowClear);
+  }
+
+
+  public boolean isAllowCustom() {
+    Boolean allowCustom = (Boolean) getStateHelper().eval(Select2Keys.allowCustom);
+    if (allowCustom != null) {
+      return allowCustom;
+    }
+    return false;
+  }
+  public boolean isAllowCustomSet() {
+    return getStateHelper().eval(Select2Keys.allowCustom) != null;
+  }
+
+  public void setAllowCustom(boolean allowCustom) {
+    getStateHelper().put(Select2Keys.allowCustom, allowCustom);
+  }
+
+  public String getMatcher() {
+    String matcher = (String) getStateHelper().eval(Select2Keys.matcher);
+    if (matcher != null) {
+      return matcher;
+    }
+    return null;
+  }
+
+  public boolean isMatcherSet() {
+    return getStateHelper().eval(Select2Keys.matcher) != null;
+  }
+
+  public void setMatcher(String matcher) {
+    getStateHelper().put(Select2Keys.matcher, matcher);
+  }
+
+  public int getMaximumInputLength() {
+    Integer maximumInputLength = (Integer) getStateHelper().eval(Select2Keys.maximumInputLength);
+    if (maximumInputLength != null) {
+      return maximumInputLength;
+    }
+    return 0;
+  }
+  public boolean isMaximumInputLengthSet() {
+    return getStateHelper().eval(Select2Keys.maximumInputLength) != null;
+  }
+
+  public void setMaximumInputLength(int minimumInputLength) {
+    getStateHelper().put(Select2Keys.maximumInputLength, minimumInputLength);
+  }
+
+  public int getMinimumInputLength() {
+    Integer minimumInputLength = (Integer) getStateHelper().eval(Select2Keys.minimumInputLength);
+    if (minimumInputLength != null) {
+      return minimumInputLength;
+    }
+    return 0;
+  }
+  public boolean isMinimumInputLengthSet() {
+    return getStateHelper().eval(Select2Keys.minimumInputLength) != null;
+  }
+
+  public void setMinimumInputLength(int minimumInputLength) {
+    getStateHelper().put(Select2Keys.minimumInputLength, minimumInputLength);
+  }
+
+  public int getMaximumSelectionLength() {
+    Integer maximumSelectionLength = (Integer) getStateHelper().eval(Select2Keys.maximumSelectionLength);
+    if (maximumSelectionLength != null) {
+      return maximumSelectionLength;
+    }
+    return 0;
+  }
+  public boolean isMaximumSelectionLengthSet() {
+    return getStateHelper().eval(Select2Keys.maximumSelectionLength) != null;
+  }
+
+  public void setMaximumSelectionLength(int maximumSelectionLength) {
+    getStateHelper().put(Select2Keys.maximumSelectionLength, maximumSelectionLength);
+  }
+
+  public void setMinimumResultsForSearch(int minimumResultsForSearch) {
+    getStateHelper().put(Select2Keys.minimumResultsForSearch, minimumResultsForSearch);
+  }
+
+  public int getMinimumResultsForSearch() {
+    Integer minimumResultsForSearch = (Integer) getStateHelper().eval(Select2Keys.minimumResultsForSearch);
+    if (minimumResultsForSearch != null) {
+      return minimumResultsForSearch;
+    }
+    return 20;
+  }
+
+  public boolean isMinimumResultsForSearchSet() {
+    return getStateHelper().eval(Select2Keys.minimumResultsForSearch) != null;
+  }
+
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectManyBoxTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectManyBoxTagDeclaration.java
new file mode 100644
index 0000000..4d8aef5
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectManyBoxTagDeclaration.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.taglib.component;
+
+import org.apache.myfaces.tobago.apt.annotation.Tag;
+import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasBinding;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasConverter;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasConverterMessage;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasCurrentMarkup;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasId;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasLabel;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasMarkup;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasOnchange;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasPlaceholder;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasRequiredMessageForSelect;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasTabIndex;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasTip;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasValidator;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasValidatorMessage;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasValueChangeListener;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsDisabled;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsFocus;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsGridLayoutComponent;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsReadonly;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsRendered;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsRequiredForSelect;
+import org.apache.myfaces.tobago.internal.taglib.declaration.Select2;
+
+import javax.faces.component.UISelectMany;
+
+/**
+ * Render a multi selection option listbox.
+ */
+@Tag(name = "selectManyBox")
+@UIComponentTag(
+    uiComponent = "org.apache.myfaces.tobago.component.UISelectManyBox",
+    uiComponentBaseClass = "org.apache.myfaces.tobago.internal.component.AbstractUISelectManyBox",
+    uiComponentFacesClass = "javax.faces.component.UISelectMany",
+    componentFamily = UISelectMany.COMPONENT_FAMILY,
+    rendererType = RendererTypes.SELECT_MANY_BOX,
+    allowedChildComponenents = {"javax.faces.SelectItem", "javax.faces.SelectItems"})
+
+public interface SelectManyBoxTagDeclaration
+    extends HasId, IsDisabled, IsRendered, HasBinding, HasTip,
+    IsReadonly, HasConverter, IsRequiredForSelect, HasMarkup, HasCurrentMarkup,
+    HasLabel, HasValidator, HasOnchange, HasValueChangeListener,
+    HasValidatorMessage, HasConverterMessage, HasRequiredMessageForSelect, HasTabIndex, IsFocus, IsGridLayoutComponent,
+    Select2, HasPlaceholder {
+
+  /**
+   * The value of the multi select.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = {"java.lang.Object[]", "java.util.List"})
+  void setValue(String value);
+
+
+
+  /**
+   * A javascript callback that handles automatic tokenization of free-text entry.
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(generate = false)
+  void setTokenizer(String tokenizer);
+
+  /**
+   * The list of characters that should be used as token separators.
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "java.lang.String[]", generate = false)
+  void setTokenSeparators(String tokenSeparators);
+
+  /**
+   * Hide the dropdown, this is only useful with allowCustom=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false", generate = false)
+  void setHideDropdown(String hideDropdown);
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectOneChoiceTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectOneChoiceTagDeclaration.java
index 5bc7abf..b3ff946 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectOneChoiceTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectOneChoiceTagDeclaration.java
@@ -35,6 +35,7 @@ import org.apache.myfaces.tobago.internal.taglib.declaration.HasTip;
 import org.apache.myfaces.tobago.internal.taglib.declaration.IsDisabled;
 import org.apache.myfaces.tobago.internal.taglib.declaration.IsReadonly;
 import org.apache.myfaces.tobago.internal.taglib.declaration.IsRendered;
+import org.apache.myfaces.tobago.internal.taglib.declaration.Select2;
 
 import javax.faces.component.UISelectOne;
 
@@ -65,7 +66,7 @@ import javax.faces.component.UISelectOne;
         })
 public interface SelectOneChoiceTagDeclaration
     extends SelectOneTagDeclaration, HasId, IsDisabled,
-    IsReadonly, HasLabel, IsRendered, HasConverter, HasBinding, HasTip {
+            IsReadonly, HasLabel, IsRendered, HasConverter, HasBinding, HasTip, Select2 {
 
   /**
    * Flag indicating that selecting an Item representing a value is required.
@@ -75,4 +76,31 @@ public interface SelectOneChoiceTagDeclaration
   @TagAttribute()
   @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
   void setRequired(String required);
+
+  /**
+   * Flag indicating that this element is rendered as select2.
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
+  void setSelect2(String disabled);
+
+  /**
+   * The minimum number of results required to display the search box.
+   *
+   * This is a select2 feature and will force select2=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "int", defaultValue = "20", generate = false)
+  void setMinimumResultsForSearch(String disabled);
+
+  /**
+   * Displays a short text in the input field, that describes the meaning of this field.
+   * This is part of HTML 5, the theme should emulate the behaviour, when the browser doesn't support it.
+   *
+   * This is a select2 feature and will force select2=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute
+  void setPlaceholder(String allowed);
+
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java
new file mode 100644
index 0000000..b5506f6
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java
@@ -0,0 +1,63 @@
+package org.apache.myfaces.tobago.internal.taglib.declaration;
+
+import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+
+public interface Select2 {
+
+  /**
+   * Flag indicating that this select provides support for clearable selections.
+   *
+   * This is a select2 feature and will force select2=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false", generate = false)
+  void setAllowClear(String allowed);
+
+  /**
+   * Flag indicating that this select enables free text responses.
+   *
+   * This is a select2 feature and will force select2=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false", generate = false)
+  void setAllowCustom(String allowed);
+
+  /**
+   * Javascript callback to handle custom search matching
+   *
+   * This is a select2 feature and will force select2=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(generate = false)
+  void setMatcher(String matcher);
+
+  /**
+   * Maximum number of characters that may be provided for a search term.
+   *
+   * This is a select2 feature and will force select2=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "int", defaultValue = "0", generate = false)
+  void setMaximumInputLength(String allowed);
+
+  /**
+   * Minimum number of characters required to start a search.
+   *
+   * This is a select2 feature and will force select2=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "int", defaultValue = "0", generate = false)
+  void setMinimumInputLength(String allowed);
+
+  /**
+   * The maximum number of items that may be selected in a multi-select control.
+   * If the value of this option is less than 1, the number of selected items will not be limited.
+   *
+   * This is a select2 feature and will force select2=true
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "int", defaultValue = "0", generate = false)
+  void setMaximumSelectionLength(String allowed);
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/extension/SelectManyBoxExtensionTag.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/extension/SelectManyBoxExtensionTag.java
new file mode 100644
index 0000000..d23448f
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/extension/SelectManyBoxExtensionTag.java
@@ -0,0 +1,419 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.taglib.extension;
+
+import org.apache.myfaces.tobago.apt.annotation.DynamicExpression;
+import org.apache.myfaces.tobago.apt.annotation.ExtensionTag;
+import org.apache.myfaces.tobago.apt.annotation.Tag;
+import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+import org.apache.myfaces.tobago.internal.taglib.SelectManyListboxTag;
+
+import javax.el.MethodExpression;
+import javax.el.ValueExpression;
+import javax.servlet.jsp.JspException;
+
+/**
+ * Renders a multi selection option listbox with a label.
+ */
+@Tag(name = "selectManyBox")
+@ExtensionTag(
+    baseClassName = "org.apache.myfaces.tobago.internal.taglib.SelectManyBoxTag",
+    faceletHandler = "org.apache.myfaces.tobago.facelets.extension.SelectManyBoxExtensionHandler")
+public class SelectManyBoxExtensionTag extends TobagoExtensionBodyTagSupport {
+
+  private ValueExpression required;
+  private ValueExpression value;
+  private MethodExpression valueChangeListener;
+  private ValueExpression disabled;
+  private ValueExpression readonly;
+  private ValueExpression onchange;
+  private ValueExpression label;
+  private ValueExpression accessKey;
+  private ValueExpression rendered;
+  private ValueExpression binding;
+  private ValueExpression tip;
+  private ValueExpression converter;
+  private MethodExpression validator;
+  private ValueExpression labelWidth;
+  private ValueExpression markup;
+  private ValueExpression tabIndex;
+  private ValueExpression focus;
+  private ValueExpression validatorMessage;
+  private ValueExpression converterMessage;
+  private ValueExpression requiredMessage;
+  private String fieldId;
+
+  private LabelExtensionTag labelTag;
+  private SelectManyListboxTag selectManyListboxTag;
+
+  @Override
+  public int doStartTag() throws JspException {
+
+    labelTag = new LabelExtensionTag();
+    labelTag.setPageContext(pageContext);
+    labelTag.setRows("*");
+    if (id != null) {
+      labelTag.setId(id);
+    }
+    if (label != null) {
+      labelTag.setValue(label);
+    }
+    if (accessKey != null) {
+      labelTag.setAccessKey(accessKey);
+    }
+    if (tip != null) {
+      labelTag.setTip(tip);
+    }
+    if (rendered != null) {
+      labelTag.setRendered(rendered);
+    }
+    if (labelWidth != null) {
+      labelTag.setColumns(createStringValueExpression(labelWidth.getExpressionString() + ";*"));
+    }
+    if (markup != null) {
+      labelTag.setMarkup(markup);
+    }
+    labelTag.setParent(getParent());
+    labelTag.setJspId(nextJspId());
+    labelTag.doStartTag();
+
+    selectManyListboxTag = new SelectManyListboxTag();
+    selectManyListboxTag.setPageContext(pageContext);
+    if (value != null) {
+      selectManyListboxTag.setValue(value);
+    }
+    if (valueChangeListener != null) {
+      selectManyListboxTag.setValueChangeListener(valueChangeListener);
+    }
+    if (binding != null) {
+      selectManyListboxTag.setBinding(binding);
+    }
+    if (onchange != null) {
+      selectManyListboxTag.setOnchange(onchange);
+    }
+    if (validator != null) {
+      selectManyListboxTag.setValidator(validator);
+    }
+    if (converter != null) {
+      selectManyListboxTag.setConverter(converter);
+    }
+    if (disabled != null) {
+      selectManyListboxTag.setDisabled(disabled);
+    }
+    if (focus != null) {
+      selectManyListboxTag.setFocus(focus);
+    }
+    if (fieldId != null) {
+      selectManyListboxTag.setId(fieldId);
+    }
+    if (label != null) {
+      selectManyListboxTag.setLabel(label);
+    }
+    if (readonly != null) {
+      selectManyListboxTag.setReadonly(readonly);
+    }
+    if (required != null) {
+      selectManyListboxTag.setRequired(required);
+    }
+    if (markup != null) {
+      selectManyListboxTag.setMarkup(markup);
+    }
+    if (tabIndex != null) {
+      selectManyListboxTag.setTabIndex(tabIndex);
+    }
+    if (validatorMessage != null) {
+      selectManyListboxTag.setValidatorMessage(validatorMessage);
+    }
+    if (converterMessage != null) {
+      selectManyListboxTag.setConverterMessage(converterMessage);
+    }
+    if (requiredMessage != null) {
+      selectManyListboxTag.setRequiredMessage(requiredMessage);
+    }
+    selectManyListboxTag.setParent(labelTag);
+    selectManyListboxTag.setJspId(nextJspId());
+    selectManyListboxTag.doStartTag();
+
+    return super.doStartTag();
+  }
+
+  @Override
+  public int doEndTag() throws JspException {
+    selectManyListboxTag.doEndTag();
+    labelTag.doEndTag();
+    return super.doEndTag();
+  }
+
+  @Override
+  public void release() {
+    super.release();
+    binding = null;
+    onchange = null;
+    disabled = null;
+    label = null;
+    accessKey = null;
+    labelWidth = null;
+    readonly = null;
+    rendered = null;
+    converter = null;
+    validator = null;
+    required = null;
+    tip = null;
+    value = null;
+    valueChangeListener = null;
+    markup = null;
+    tabIndex = null;
+    selectManyListboxTag = null;
+    labelTag = null;
+    focus = null;
+    validatorMessage = null;
+    converterMessage = null;
+    requiredMessage = null;
+    fieldId = null;
+  }
+
+  /**
+   * Flag indicating that a value is required.
+   * If the value is an empty string a
+   * ValidationError occurs and a Error Message is rendered.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
+  public void setRequired(final ValueExpression required) {
+    this.required = required;
+  }
+
+  /**
+   * The current value of this component.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "java.lang.Object")
+  public void setValue(final ValueExpression value) {
+    this.value = value;
+  }
+
+  /**
+   * MethodBinding representing a value change listener method
+   * that will be notified when a new value has been set for this input component.
+   * The expression must evaluate to a public method that takes a ValueChangeEvent
+   * parameter, with a return type of void.
+   *
+   * @param valueChangeListener
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(
+          type = {},
+          expression = DynamicExpression.METHOD_EXPRESSION_REQUIRED,
+          methodSignature = "javax.faces.event.ValueChangeEvent")
+  public void setValueChangeListener(final MethodExpression valueChangeListener) {
+    this.valueChangeListener = valueChangeListener;
+  }
+
+  /**
+   * Flag indicating that this element is disabled.
+   */
+  @TagAttribute()
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
+  public void setDisabled(final ValueExpression disabled) {
+    this.disabled = disabled;
+  }
+
+  /**
+   * Flag indicating that this component will prohibit changes by the user.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
+  public void setReadonly(final ValueExpression readonly) {
+    this.readonly = readonly;
+  }
+
+  /**
+   * Clientside script function to add to this component's onchange handler.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute()
+  public void setOnchange(final ValueExpression onchange) {
+    this.onchange = onchange;
+  }
+
+  /**
+   * Text value to display as label.
+   * If text contains an underscore the next character is used as accesskey.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute()
+  public void setLabel(final ValueExpression label) {
+    this.label = label;
+  }
+
+  /**
+   * The accessKey of this component.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "java.lang.Character")
+  public void setAccessKey(final ValueExpression accessKey) {
+    this.accessKey = accessKey;
+  }
+
+  /**
+   * A method binding EL expression,
+   * accepting FacesContext, UIComponent,
+   * and Object parameters, and returning void, that validates
+   * the component's local value.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = {},
+      expression = DynamicExpression.METHOD_EXPRESSION,
+      methodSignature = { "javax.faces.context.FacesContext", "javax.faces.component.UIComponent", "java.lang.Object" })
+  public void setValidator(final MethodExpression validator) {
+    this.validator = validator;
+  }
+
+  /**
+   * An expression that specifies the Converter for this component.
+   * If the value binding expression is a String,
+   * the String is used as an ID to look up a Converter.
+   * If the value binding expression is a Converter,
+   * uses that instance as the converter.
+   * The value can either be a static value (ID case only)
+   * or an EL expression.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "javax.faces.convert.Converter",
+      expression = DynamicExpression.VALUE_EXPRESSION)
+  public void setConverter(final ValueExpression converter) {
+    this.converter = converter;
+  }
+
+  /**
+   * Flag indicating whether or not this component should be rendered
+   * (during Render Response Phase), or processed on any subsequent form submit.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "true")
+  public void setRendered(final ValueExpression rendered) {
+    this.rendered = rendered;
+  }
+
+  /**
+   * The value binding expression linking this
+   * component to a property in a backing bean.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "javax.faces.component.UIComponent")
+  public void setBinding(final ValueExpression binding) {
+    this.binding = binding;
+  }
+
+  /**
+   * Text value to display as tooltip.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute()
+  public void setTip(final ValueExpression tip) {
+    this.tip = tip;
+  }
+
+  /**
+   * The width for the label component. Default: 'auto'.
+   * This value is used in the gridLayouts columns attribute.
+   * See gridLayout tag for valid values.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute()
+  public void setLabelWidth(final ValueExpression labelWidth) {
+    this.labelWidth = labelWidth;
+  }
+
+  /**
+   * Indicate markup of this component.
+   * Possible value is 'none'. But this can be overridden in the theme.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(defaultValue = "none", type = "java.lang.String[]")
+  public void setMarkup(final ValueExpression markup) {
+    this.markup = markup;
+  }
+
+  @TagAttribute
+  @UIComponentTagAttribute(type = "java.lang.Integer")
+  public void setTabIndex(final ValueExpression tabIndex) {
+    this.tabIndex = tabIndex;
+  }
+
+  /**
+   * Flag indicating this component should receive the focus.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
+  public void setFocus(final ValueExpression focus) {
+    this.focus = focus;
+  }
+
+  /**
+   * An expression that specifies the validator message
+   */
+  @TagAttribute
+  @UIComponentTagAttribute()
+  public void setValidatorMessage(final ValueExpression validatorMessage) {
+    this.validatorMessage = validatorMessage;
+  }
+
+  /**
+   * An expression that specifies the converter message
+   */
+  @TagAttribute
+  @UIComponentTagAttribute()
+  public void setConverterMessage(final ValueExpression converterMessage) {
+    this.converterMessage = converterMessage;
+  }
+
+  /**
+   * An expression that specifies the required message
+   */
+  @TagAttribute
+  @UIComponentTagAttribute()
+  public void setRequiredMessage(final ValueExpression requiredMessage) {
+    this.requiredMessage = requiredMessage;
+  }
+  
+  /**
+   * The component identifier for the input field component inside of the container.
+   * This value must be unique within the closest parent component that is a naming container.
+   */
+  @TagAttribute(rtexprvalue = true)
+  @UIComponentTagAttribute
+  public void setFieldId(final String fieldId) {
+    this.fieldId = fieldId;
+  }
+
+  /**
+   * The component identifier for this component.
+   * This value must be unique within the closest parent component that is a naming container.
+   * For tx components the id will be set to the container (e. g. the panel).
+   * To set the id of the input field, you have to use the attribute "fieldId".
+   */
+  @TagAttribute(rtexprvalue = true)
+  @UIComponentTagAttribute
+  public void setId(final String id) {
+    super.setId(id);
+  }
+}
diff --git a/tobago-example/pom.xml b/tobago-example/pom.xml
index c830df9..c0bcbb9 100644
--- a/tobago-example/pom.xml
+++ b/tobago-example/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <packaging>pom</packaging>
   <name>Tobago Examples</name>
diff --git a/tobago-example/tobago-example-addressbook-cdi/pom.xml b/tobago-example/tobago-example-addressbook-cdi/pom.xml
index df6a529..4ebed0f 100644
--- a/tobago-example/tobago-example-addressbook-cdi/pom.xml
+++ b/tobago-example/tobago-example-addressbook-cdi/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-addressbook-cdi</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-addressbook/pom.xml b/tobago-example/tobago-example-addressbook/pom.xml
index 23210a1..cc81c36 100644
--- a/tobago-example/tobago-example-addressbook/pom.xml
+++ b/tobago-example/tobago-example-addressbook/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-addressbook</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-assembly/pom.xml b/tobago-example/tobago-example-assembly/pom.xml
index bafe213..ed30338 100644
--- a/tobago-example/tobago-example-assembly/pom.xml
+++ b/tobago-example/tobago-example-assembly/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
 
   <profiles>
diff --git a/tobago-example/tobago-example-blank/pom.xml b/tobago-example/tobago-example-blank/pom.xml
index 935ba4f..6de6e92 100644
--- a/tobago-example/tobago-example-blank/pom.xml
+++ b/tobago-example/tobago-example-blank/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-blank</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-data/pom.xml b/tobago-example/tobago-example-data/pom.xml
index 9539fe2..afe28ec 100644
--- a/tobago-example/tobago-example-data/pom.xml
+++ b/tobago-example/tobago-example-data/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-data</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-example/tobago-example-demo/pom.xml b/tobago-example/tobago-example-demo/pom.xml
index 2071312..dff56ed 100644
--- a/tobago-example/tobago-example-demo/pom.xml
+++ b/tobago-example/tobago-example-demo/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-demo</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
new file mode 100644
index 0000000..1e81080
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
@@ -0,0 +1,27 @@
+package org.apache.myfaces.tobago.example.demo;
+
+import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
+
+import javax.enterprise.context.SessionScoped;
+import javax.inject.Named;
+import java.io.Serializable;
+
+@SessionScoped
+@Named
+public class Select2Controller implements Serializable {
+
+  private String one2;
+
+  public String getOne2() {
+    return one2;
+  }
+
+  public void setOne2(String one2) {
+    this.one2 = one2;
+  }
+
+  public String getCaseSensitiveMatcher() {
+    return "{\"matcher\": \"Tobago.Select2.caseSensitiveMatcher\"}";
+//    JsonUtils.encode()
+  }
+}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
index 1676138..b21f264 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
@@ -27,8 +27,8 @@
 
   <context-param>
     <param-name>javax.faces.PROJECT_STAGE</param-name>
-    <!--<param-value>Development</param-value>-->
-    <param-value>Production</param-value>
+    <param-value>Development</param-value>
+    <!--<param-value>Production</param-value>-->
   </context-param>
 
   <context-param>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
new file mode 100644
index 0000000..4e1edeb
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<ui:composition template="/WEB-INF/tags/layout/overview.xhtml"
+                xmlns:tc="http://myfaces.apache.org/tobago/component"
+                xmlns:tx="http://myfaces.apache.org/tobago/extension"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core">
+  <ui:param name="title" value="Select2 Controls"/>
+  <tc:panel>
+    <f:facet name="layout">
+      <tc:gridLayout rows="auto;auto;auto;*;auto"/>
+    </f:facet>
+
+    <tc:panel>
+      <tc:gridLayoutConstraint height="75px"/>
+      <f:facet name="layout">
+        <tc:flowLayout/>
+      </f:facet>
+      <tc:out value="Tobago integration of "/>
+      <tc:link label="https://select2.org" link="https://select2.org"/>
+      <tc:out value=" component features"/>
+
+
+    </tc:panel>
+
+    <tc:panel>
+      <f:facet name="layout">
+        <tc:gridLayout rows="auto;auto"/>
+      </f:facet>
+
+      <tc:separator label="SelectOneChoice"/>
+
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*"/>
+        </f:facet>
+
+        <tc:label value="No further options set" for="one_1"/>
+        <tc:selectOneChoice id="one_1"
+                            value=""
+                            select2="true">
+
+          <tc:selectItem itemLabel="Letter" itemValue="letter"/>
+          <tc:selectItem itemLabel="Phone" itemValue="phone"/>
+          <tc:selectItem itemLabel="eMail" itemValue="eMail"/>
+          <tc:selectItem itemLabel="Fax" itemValue="fax"/>
+        </tc:selectOneChoice>
+
+        <tc:label value="Suppressed filter input with placeholder" for="one_2"/>
+        <tc:selectOneChoice id="one_2"
+                            minimumResultsForSearch="10"
+                            placeholder="Please select message type"
+                            value="#{select2Controller.one2}">
+
+          <tc:selectItem itemLabel="Letter" itemValue="letter"/>
+          <tc:selectItem itemLabel="Phone" itemValue="phone"/>
+          <tc:selectItem itemLabel="eMail" itemValue="eMail"/>
+          <tc:selectItem itemLabel="Fax" itemValue="fax"/>
+        </tc:selectOneChoice>
+
+        <tc:label value="Custom input allowed and clearabel" for="one_3"/>
+        <tc:selectOneChoice id="one_3"
+                            allowCustom="true"
+                            placeholder="Custom input allowed"
+                            allowClear="true"
+                            value="">
+
+          <tc:selectItem itemLabel="Letter" itemValue="letter"/>
+          <tc:selectItem itemLabel="Phone" itemValue="phone"/>
+          <tc:selectItem itemLabel="eMail" itemValue="eMail"/>
+          <tc:selectItem itemLabel="Fax" itemValue="fax"/>
+        </tc:selectOneChoice>
+
+      </tc:panel>
+    </tc:panel>
+
+
+    <tc:panel>
+      <f:facet name="layout">
+        <tc:gridLayout rows="auto;auto;auto"/>
+      </f:facet>
+
+      <tc:separator label="SelectManyBox"/>
+
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="No further options set" for="many_1"/>
+        <tc:selectManyBox id="many_1">
+          <f:selectItem itemValue="Phone" itemLabel="Phone"/>
+          <f:selectItem itemValue="eMail" itemLabel="eMail"/>
+          <f:selectItem itemValue="Mobile" itemLabel="Mobile"/>
+          <f:selectItem itemValue="Fax" itemLabel="Faxscimile"/>
+        </tc:selectManyBox>
+
+      </tc:panel>
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="Custom input allowed" for="many_2"/>
+        <tc:selectManyBox id="many_2"
+                          placeholder="Custom input allowed"
+                          allowCustom="true">
+          <f:selectItem itemValue="Phone" itemLabel="Phone"/>
+          <f:selectItem itemValue="eMail" itemLabel="eMail"/>
+          <f:selectItem itemValue="Mobile" itemLabel="Mobile"/>
+          <f:selectItem itemValue="Fax" itemLabel="Faxscimile"/>
+        </tc:selectManyBox>
+
+      </tc:panel>
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="TokenSeparators ',', ' ', '-'" for="many_3"/>
+        <tc:selectManyBox id="many_3"
+                          placeholder="Custom input allowed"
+                          tokenSeparators=", -"
+                          allowCustom="true">
+        </tc:selectManyBox>
+
+      </tc:panel>
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="Hiden dropdown" for="many_4"/>
+        <tc:selectManyBox id="many_4"
+                          placeholder="Enter your values!"
+                          tokenSeparators=", -"
+                          allowCustom="true"
+                          hideDropdown="true">
+          <tc:dataAttribute name="tobago-select2-extend" value='{"matcher" : "caseSensitiveMatcher"}'/>
+        </tc:selectManyBox>
+
+      </tc:panel>
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="Case sensitive" for="many_6"/>
+        <tc:selectManyBox id="many_6"
+                          placeholder="Enter your case senditive values!"
+                          tokenSeparators=", -"
+                          allowCustom="true"
+                          hideDropdown="true">
+          <tc:dataAttribute name="tobago-select2-extend" value='{"matcher" : "caseSensitiveMatcher"}'/>
+        </tc:selectManyBox>
+
+      </tc:panel>
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="Tokenizer" for="many_5"/>
+        <tc:selectManyBox id="many_5"
+                          placeholder="tokenizer test"
+                          tokenSeparators=", -"
+                          allowCustom="true">
+          <!--tokenizer="TBG_DEMO.Select2.Tokenizer"-->
+          <f:facet name="select2:select">
+            <tc:command onclick="TBG_DEMO.Select2.doOnSelect;"/>
+          </f:facet>
+        </tc:selectManyBox>
+
+      </tc:panel>
+    </tc:panel>
+
+
+    <tc:panel/>
+
+    <tc:panel>
+      <f:facet name="layout">
+        <tc:gridLayout columns="*;auto"/>
+      </f:facet>
+      <tc:panel/>
+      <tc:button label="Submit"/>
+    </tc:panel>
+
+  </tc:panel>
+
+</ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/script/demo.js b/tobago-example/tobago-example-demo/src/main/webapp/script/demo.js
index bfa6653..02a8993 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/script/demo.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/script/demo.js
@@ -34,3 +34,102 @@ var initAlert = function () {
 
 Tobago.registerListener(initAlert, Tobago.Phase.DOCUMENT_READY);
 Tobago.registerListener(initAlert, Tobago.Phase.AFTER_UPDATE);
+
+jQuery.fn.select2.amd.define("CustomTokenizerAdapter", [
+      "select2/utils",
+      "select2/data/tokenizer"
+    ],
+    function(Utils, Tokenizer) {
+      var emptyFunc = function (params) {
+
+      };
+      emptyFunc.tokenizer = function (params) {
+        var result = Tokenizer.prototype.tokenizer.call(this, params);
+        console.info("tokenizer result: " + result);
+        return result;
+      };
+     return function(params) {emptyFunc.tokenizer(params)};
+    });
+
+jQuery.fn.select2.amd.define("HideDropdown", [
+      "select2/utils",
+      "select2/dropdown",
+      "select2/dropdown/attachBody",
+      "select2/dropdown/attachContainer",
+      "select2/dropdown/search",
+      "select2/dropdown/minimumResultsForSearch"
+    ],
+    function(Utils, Dropdown, AttachBody, AttachContainer, Search, MinimumResultsForSearch) {
+
+      // Decorate the dropdown+search with necessary containers
+      var adapter = Utils.Decorate(Dropdown, AttachBody);
+      adapter.prototype.render = function() {
+        Dropdown.prototype.render.call(this);
+        return jQuery("<span/>");
+      };
+      return adapter;
+    });
+
+
+jQuery.fn.select2.amd.define("CustomDropdownAdapter", [
+      "select2/utils",
+      "select2/dropdown",
+      "select2/dropdown/attachBody",
+      "select2/dropdown/attachContainer",
+      "select2/dropdown/search",
+      "select2/dropdown/minimumResultsForSearch"
+    ],
+    function(Utils, Dropdown, AttachBody, AttachContainer, Search, MinimumResultsForSearch) {
+
+      // Decorate Dropdown with Search functionalities
+      var dropdownWithSearch = Utils.Decorate(Dropdown, Search);
+      dropdownWithSearch.prototype.render = function() {
+        // Copy and modify default search render method
+        var $rendered = Dropdown.prototype.render.call(this);
+        // Add ability for a placeholder in the search box
+        var placeholder = this.options.get("placeholderForSearch") || "";
+        var $search = $(
+            '<span class="select2-search select2-search--dropdown">' +
+            '<input class="select2-search__field" placeholder="' + placeholder + '" type="search"' +
+            ' tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off"' +
+            ' spellcheck="false" role="textbox" />' +
+            '</span>'
+        );
+
+        this.$searchContainer = $search;
+        this.$search = $search.find('input');
+
+        // $rendered.prepend($search);
+        return $rendered;
+      };
+
+      // Decorate the dropdown+search with necessary containers
+      var adapter = Utils.Decorate(dropdownWithSearch, AttachContainer);
+      adapter = Utils.Decorate(adapter, AttachBody);
+
+      return adapter;
+    });
+
+
+var TBG_DEMO = {
+  Select2: {
+    Tokenizer: function (params) {
+      console.info("params: " + params);
+      var tokenizer = jQuery.fn.select2.amd.require("Tokenizer");
+      var results = tokenizer.call(this, params);
+      console.info("results: " + results);
+      return results;
+    },
+
+    doOnSelect: function (event) {
+      var element = jQuery(this);
+      var data = element.select2("data");
+      var newData = event.params.data;
+      console.info("doOnSelect id     : " +  element.attr("id"));
+      console.info("doOnSelect tagName: " +  element.prop("tagName"));
+      console.info("doOnSelect newData: " +  newData);
+      console.info("doOnSelect data   : " +  data);
+      // console.info("doOnSelect : " +  element);
+    }
+  }
+};
diff --git a/tobago-example/tobago-example-portlet/pom.xml b/tobago-example/tobago-example-portlet/pom.xml
index 1656448..f302460 100644
--- a/tobago-example/tobago-example-portlet/pom.xml
+++ b/tobago-example/tobago-example-portlet/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-portlet</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-sandbox/pom.xml b/tobago-example/tobago-example-sandbox/pom.xml
index 710e6ae..ffb3237 100644
--- a/tobago-example/tobago-example-sandbox/pom.xml
+++ b/tobago-example/tobago-example-sandbox/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-sandbox</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-security/pom.xml b/tobago-example/tobago-example-security/pom.xml
index 14b2c15..84be018 100644
--- a/tobago-example/tobago-example-security/pom.xml
+++ b/tobago-example/tobago-example-security/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-security</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-test/pom.xml b/tobago-example/tobago-example-test/pom.xml
index af2d5a4..a399338 100644
--- a/tobago-example/tobago-example-test/pom.xml
+++ b/tobago-example/tobago-example-test/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-test</artifactId>
   <packaging>war</packaging>
@@ -178,6 +178,7 @@
       <version>${log4j.version}</version>
       <scope>provided</scope>
     </dependency>
+<!--
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-slf4j-impl</artifactId>
@@ -187,6 +188,7 @@
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
+-->
     <dependency>
       <groupId>oro</groupId>
       <artifactId>oro</artifactId>
diff --git a/tobago-extension/pom.xml b/tobago-extension/pom.xml
index 0d18425..93429ac 100644
--- a/tobago-extension/pom.xml
+++ b/tobago-extension/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <packaging>pom</packaging>
   <name>Tobago Extensions</name>
diff --git a/tobago-extension/tobago-deprecation/pom.xml b/tobago-extension/tobago-deprecation/pom.xml
index cf5264d..0a13fe4 100644
--- a/tobago-extension/tobago-deprecation/pom.xml
+++ b/tobago-extension/tobago-deprecation/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-extension</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-deprecation</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-extension/tobago-fileupload/pom.xml b/tobago-extension/tobago-fileupload/pom.xml
index d5bcc17..2c42caa 100644
--- a/tobago-extension/tobago-fileupload/pom.xml
+++ b/tobago-extension/tobago-fileupload/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-extension</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <packaging>jar</packaging>
   <name>Tobago Fileupload</name>
diff --git a/tobago-extension/tobago-sandbox/pom.xml b/tobago-extension/tobago-sandbox/pom.xml
index ff1a160..8e52b8c 100644
--- a/tobago-extension/tobago-sandbox/pom.xml
+++ b/tobago-extension/tobago-sandbox/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-extension</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-sandbox</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-extension/tobago-security/pom.xml b/tobago-extension/tobago-security/pom.xml
index 3ead11a..02e01bd 100644
--- a/tobago-extension/tobago-security/pom.xml
+++ b/tobago-extension/tobago-security/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-extension</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <packaging>jar</packaging>
   <name>Tobago Security</name>
diff --git a/tobago-theme/pom.xml b/tobago-theme/pom.xml
index 5548d29..f0fa41b 100644
--- a/tobago-theme/pom.xml
+++ b/tobago-theme/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <packaging>pom</packaging>
   <name>Tobago Themes</name>
diff --git a/tobago-theme/tobago-theme-charlotteville/pom.xml b/tobago-theme/tobago-theme-charlotteville/pom.xml
index 92f71dd..4d9f80f 100644
--- a/tobago-theme/tobago-theme-charlotteville/pom.xml
+++ b/tobago-theme/tobago-theme-charlotteville/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-charlotteville</artifactId>
   <name>Tobago Theme Charlotteville</name>
diff --git a/tobago-theme/tobago-theme-example/pom.xml b/tobago-theme/tobago-theme-example/pom.xml
index 7f0713a..355958e 100644
--- a/tobago-theme/tobago-theme-example/pom.xml
+++ b/tobago-theme/tobago-theme-example/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-example</artifactId>
   <name>Tobago Theme Example</name>
diff --git a/tobago-theme/tobago-theme-richmond/pom.xml b/tobago-theme/tobago-theme-richmond/pom.xml
index d01c4b1..939cc5b 100644
--- a/tobago-theme/tobago-theme-richmond/pom.xml
+++ b/tobago-theme/tobago-theme-richmond/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-richmond</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-theme/tobago-theme-scarborough/pom.xml b/tobago-theme/tobago-theme-scarborough/pom.xml
index 5245c24..71b21b2 100644
--- a/tobago-theme/tobago-theme-scarborough/pom.xml
+++ b/tobago-theme/tobago-theme-scarborough/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-scarborough</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-theme/tobago-theme-speyside/pom.xml b/tobago-theme/tobago-theme-speyside/pom.xml
index 3b88f3e..43c5149 100644
--- a/tobago-theme/tobago-theme-speyside/pom.xml
+++ b/tobago-theme/tobago-theme-speyside/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-speyside</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-theme/tobago-theme-speyside/src/main/less/org/apache/myfaces/tobago/renderkit/html/speyside/standard/style/tobago.less b/tobago-theme/tobago-theme-speyside/src/main/less/org/apache/myfaces/tobago/renderkit/html/speyside/standard/style/tobago.less
index 8366ff4..04ce858 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/less/org/apache/myfaces/tobago/renderkit/html/speyside/standard/style/tobago.less
+++ b/tobago-theme/tobago-theme-speyside/src/main/less/org/apache/myfaces/tobago/renderkit/html/speyside/standard/style/tobago.less
@@ -1040,3 +1040,30 @@ input.tobago-time-markup-error, input.tobago-time-markup-fatal {
 .tobago-treeMenuNode-markup-disabled {
   background-color: transparent;
 }
+
+/* select2 */
+
+.select2-container--default .select2-selection--single,
+.select2-container--default .select2-selection--multiple {
+    border: 1px solid darken(@borderColor, 10%);
+    font: @font;
+    .border-radius;
+}
+.select2-container--default .select2-selection--single {
+  height: 20px;
+}
+
+.select2-container--default .select2-selection--single .select2-selection__rendered {
+  color: @textColor;
+  line-height: 18px;
+  padding-left: 3px;
+}
+
+.select2-container--default .select2-selection--single .select2-selection__arrow {
+  height: 18px;
+}
+
+.select2-container--default .select2-selection--single .select2-selection__arrow b {
+  border-color: @textColor transparent transparent transparent;
+}
+
diff --git a/tobago-theme/tobago-theme-speyside/src/main/resources/org/apache/myfaces/tobago/renderkit/html/speyside/standard/property/tobago-theme-config.properties b/tobago-theme/tobago-theme-speyside/src/main/resources/org/apache/myfaces/tobago/renderkit/html/speyside/standard/property/tobago-theme-config.properties
index 7fb35fa..98e3339 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/resources/org/apache/myfaces/tobago/renderkit/html/speyside/standard/property/tobago-theme-config.properties
+++ b/tobago-theme/tobago-theme-speyside/src/main/resources/org/apache/myfaces/tobago/renderkit/html/speyside/standard/property/tobago-theme-config.properties
@@ -88,6 +88,9 @@ Progress.css.border-bottom-width=1
 
 SelectBooleanCheckbox.height=19
 
+SelectManyBox.minimumHeight=20
+SelectManyBox.preferredHeight=20
+
 SelectManyListbox.minimumHeight=33
 SelectManyListbox.preferredHeight=75
 
diff --git a/tobago-theme/tobago-theme-standard/pom.xml b/tobago-theme/tobago-theme-standard/pom.xml
index 7a1998f..299a3ed 100644
--- a/tobago-theme/tobago-theme-standard/pom.xml
+++ b/tobago-theme/tobago-theme-standard/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.4.3-SNAPSHOT</version>
+    <version>2.5.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-standard</artifactId>
   <packaging>jar</packaging>
@@ -40,7 +40,8 @@
             <configuration>
               <target>
                 <concat destfile="${project.build.directory}/javascript-min/standard/script/tobago.min.js" force="no">
-                  <filelist dir="${basedir}/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script" files="tobago.js,tobago-calendar.js,tobago-converter.js,tobago-in.js,tobago-menu.js,tobago-overlay.js,tobago-popup.js,tobago-sheet.js,tobago-suggest.js,tobago-tab.js,tobago-tree.js,tobago-utils.js,tobago-file.js" />
+                  <filelist dir="${basedir}/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script"
+                            files="tobago.js,tobago-calendar.js,tobago-converter.js,tobago-in.js,tobago-menu.js,tobago-overlay.js,tobago-popup.js,tobago-select2.js,tobago-sheet.js,tobago-suggest.js,tobago-tab.js,tobago-tree.js,tobago-utils.js,tobago-file.js" />
                 </concat>
                 <concat destfile="${project.build.directory}/javascript-min/msie_6_0/script/tobago.min.js" force="no">
                   <filelist dir="${basedir}/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/msie_6_0/script" files="tobago.js" />
diff --git a/tobago-theme/tobago-theme-standard/src/main/appended-resources/META-INF/LICENSE b/tobago-theme/tobago-theme-standard/src/main/appended-resources/META-INF/LICENSE
index d166be3..50ecaf9 100644
--- a/tobago-theme/tobago-theme-standard/src/main/appended-resources/META-INF/LICENSE
+++ b/tobago-theme/tobago-theme-standard/src/main/appended-resources/META-INF/LICENSE
@@ -95,3 +95,33 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+------------------------------------------------------------------------------
+ For https://select2.org/:
+------------------------------------------------------------------------------
+
+ Select2 is licensed under MIT
+
+ The MIT License (MIT)
+
+ Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/JsonUtils.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/JsonUtils.java
index 031d611..7bbe028 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/JsonUtils.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/JsonUtils.java
@@ -28,7 +28,7 @@ public class JsonUtils {
   private JsonUtils() {
   }
 
-  private static void encode(final StringBuilder builder, final String name, final String[] value) {
+  public static void encode(final StringBuilder builder, final String name, final String[] value) {
     builder.append("\"");
     builder.append(name);
     builder.append("\":");
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/Select2Options.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/Select2Options.java
new file mode 100644
index 0000000..09e5c18
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/Select2Options.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.renderkit.html;
+
+
+import org.apache.myfaces.tobago.component.UISelectManyBox;
+import org.apache.myfaces.tobago.component.UISelectOneChoice;
+
+import javax.faces.context.FacesContext;
+
+public class Select2Options {
+
+  private Boolean allowClear;
+  private Boolean dropdownAutoWidth;
+  private Boolean hideDropdown;
+  private String language;
+  private String matcher;
+  private Integer maximumInputLength;
+  private Integer minimumInputLength;
+  private Integer maximumSelectionLength;
+  private Integer minimumResultsForSearch;
+  private boolean minimumResultsForSearchSet;
+  private String placeholder;
+  private boolean renderSelect2;
+  private Boolean tags;
+  private String tokenizer;
+  private String[] tokenSeparators;
+
+  public static Select2Options of(UISelectOneChoice select) {
+    Select2Options options = new Select2Options();
+    options.renderSelect2 = select.isSelect2();
+    options.dropdownAutoWidth = true;
+
+    if (select.isMinimumResultsForSearchSet()) {
+      options.setMinimumResultsForSearch(select.getMinimumResultsForSearch());
+    }
+
+    if (select.isAllowCustomSet()) {
+      options.setTags(select.isAllowCustom());
+    }
+
+    if (select.isAllowClearSet()) {
+      options.setAllowClear(select.isAllowClear());
+    }
+
+    FacesContext facesContext = FacesContext.getCurrentInstance();
+    if (facesContext != null) {
+      options.setLanguage(facesContext.getViewRoot().getLocale().getLanguage());
+    }
+
+    if (select.isMatcherSet()) {
+      options.setMatcher(select.getMatcher());
+    }
+
+    if (select.isMaximumInputLengthSet()) {
+      options.setMaximumInputLength(select.getMaximumInputLength());
+    }
+
+    if (select.isMinimumInputLengthSet()) {
+      options.setMinimumInputLength(select.getMinimumInputLength());
+    }
+
+    if (select.isMaximumSelectionLengthSet()) {
+      options.setMaximumSelectionLength(select.getMaximumSelectionLength());
+    }
+
+    String placeholder = select.getPlaceholder();
+    if (placeholder != null && placeholder.length() > 0) {
+      options.setPlaceholder(placeholder);
+    }
+
+    return options;
+  }
+
+  public static Select2Options of(UISelectManyBox select) {
+    Select2Options options = new Select2Options();
+
+    if (select.isMinimumResultsForSearchSet()) {
+      options.setMinimumResultsForSearch(select.getMinimumResultsForSearch());
+    }
+
+    if (select.isAllowCustomSet()) {
+      options.setTags(select.isAllowCustom());
+    }
+
+    if (select.isAllowClearSet()) {
+      options.setAllowClear(select.isAllowClear());
+    }
+
+    if (select.isHideDropdown()) {
+      options.setHideDropdown(true);
+    }
+
+    FacesContext facesContext = FacesContext.getCurrentInstance();
+    if (facesContext != null) {
+      options.setLanguage(facesContext.getViewRoot().getLocale().getLanguage());
+    }
+
+    if (select.isMatcherSet()) {
+      options.setMatcher(select.getMatcher());
+    }
+
+    if (select.isMaximumInputLengthSet()) {
+      options.setMaximumInputLength(select.getMaximumInputLength());
+    }
+
+    if (select.isMinimumInputLengthSet()) {
+      options.setMinimumInputLength(select.getMinimumInputLength());
+    }
+
+    if (select.isMaximumSelectionLengthSet()) {
+      options.setMaximumSelectionLength(select.getMaximumSelectionLength());
+    }
+
+    if (select.isTokenizerSet()) {
+      options.setTokenizer(select.getTokenizer());
+    }
+
+    if (select.isTokenSeparatorsSet()) {
+      options.setTokenSeparators(select.getTokenSeparators());
+    }
+
+    String placeholder = select.getPlaceholder();
+    if (placeholder != null && placeholder.length() > 0) {
+      options.setPlaceholder(placeholder);
+    }
+
+    return options;
+  }
+
+  public boolean hasAnyOption() {
+    return renderSelect2
+        || allowClear != null
+        || matcher != null
+        || maximumInputLength != null
+        || minimumInputLength != null
+        || maximumSelectionLength != null
+        || minimumResultsForSearchSet
+        || placeholder != null
+        || tags != null
+        || tokenizer != null
+        || tokenSeparators != null;
+  }
+
+  public Boolean isTags() {
+    return tags;
+  }
+
+  public void setTags(Boolean tags) {
+    this.tags = tags;
+  }
+
+  public String getTokenizer() {
+    return tokenizer;
+  }
+
+  public void setTokenizer(String tokenizer) {
+    this.tokenizer = tokenizer;
+  }
+
+  public String[] getTokenSeparators() {
+    return tokenSeparators;
+  }
+
+  public void setTokenSeparators(String[] tokenSeparators) {
+    this.tokenSeparators = tokenSeparators;
+  }
+
+  public Boolean isAllowClear() {
+    return allowClear;
+  }
+
+  public void setAllowClear(Boolean allowClear) {
+    this.allowClear = allowClear;
+  }
+
+  public Boolean getHideDropdown() {
+    return hideDropdown;
+  }
+
+  public void setHideDropdown(Boolean hideDropdown) {
+    this.hideDropdown = hideDropdown;
+  }
+
+  public String getLanguage() {
+    return language;
+  }
+
+  public void setLanguage(String language) {
+    this.language = language;
+  }
+
+  public String getMatcher() {
+    return matcher;
+  }
+
+  public void setMatcher(String matcher) {
+    this.matcher = matcher;
+  }
+
+  public Integer getMaximumInputLength() {
+    return maximumInputLength;
+  }
+
+  public void setMaximumInputLength(Integer maximumInputLength) {
+    this.maximumInputLength = maximumInputLength;
+  }
+
+  public Integer getMinimumInputLength() {
+    return minimumInputLength;
+  }
+
+  public void setMinimumInputLength(Integer minimumInputLength) {
+    this.minimumInputLength = minimumInputLength;
+  }
+
+  public Integer getMaximumSelectionLength() {
+    return maximumSelectionLength;
+  }
+
+  public void setMaximumSelectionLength(Integer maximumSelectionLength) {
+    this.maximumSelectionLength = maximumSelectionLength;
+  }
+
+  public Integer getMinimumResultsForSearch() {
+    return minimumResultsForSearch;
+  }
+
+  public void setMinimumResultsForSearch(Integer minimumResultsForSearch) {
+    this.minimumResultsForSearch = minimumResultsForSearch;
+    minimumResultsForSearchSet = true;
+  }
+
+  public String getPlaceholder() {
+    return placeholder;
+  }
+
+  public void setPlaceholder(String placeholder) {
+    this.placeholder = placeholder;
+  }
+
+  public String toJson() {
+    final StringBuilder builder = new StringBuilder();
+    builder.append("{");
+
+    if (tags != null) {
+      JsonUtils.encode(builder, "tags", tags);
+    }
+    if (tokenizer != null) {
+      JsonUtils.encode(builder, "tokenizer", tokenizer);
+    }
+    if (tokenSeparators != null) {
+      JsonUtils.encode(builder, "tokenSeparators", tokenSeparators);
+    }
+    if (dropdownAutoWidth != null) {
+      JsonUtils.encode(builder, "dropdownAutoWidth", dropdownAutoWidth);
+    }
+    if (allowClear != null) {
+      JsonUtils.encode(builder, "allowClear", allowClear);
+    }
+
+    if (hideDropdown != null && hideDropdown) {
+      JsonUtils.encode(builder, "dropdownCssClass", "tobago-select2-hide-dropdown");
+    }
+
+    if (language != null) {
+      JsonUtils.encode(builder, "language", language);
+    }
+    if (matcher != null) {
+      JsonUtils.encode(builder, "matcher", matcher);
+    }
+    if (maximumInputLength != null) {
+      JsonUtils.encode(builder, "maximumInputLength", maximumInputLength);
+    }
+    if (minimumInputLength != null) {
+      JsonUtils.encode(builder, "minimumInputLength", minimumInputLength);
+    }
+    if (maximumSelectionLength != null) {
+      JsonUtils.encode(builder, "maximumSelectionLength", maximumSelectionLength);
+    }
+    if (minimumResultsForSearch != null) {
+      JsonUtils.encode(builder, "minimumResultsForSearch", minimumResultsForSearch);
+    }
+    if (placeholder != null) {
+      JsonUtils.encode(builder, "placeholder", placeholder);
+    }
+
+
+
+    if (builder.length() > 0 && builder.charAt(builder.length() - 1) == ',') {
+      builder.deleteCharAt(builder.length() - 1);
+    }
+    builder.append("}");
+    return builder.toString();
+  }
+
+}
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
similarity index 59%
copy from tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
copy to tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
index c44b34f..4010fa6 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
@@ -19,74 +19,99 @@
 
 package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
 
-import org.apache.myfaces.tobago.component.UISelectOneChoice;
-import org.apache.myfaces.tobago.renderkit.HtmlUtils;
-import org.apache.myfaces.tobago.renderkit.SelectOneRendererBase;
+import org.apache.myfaces.tobago.component.UISelectManyBox;
+import org.apache.myfaces.tobago.component.UISelectManyListbox;
+import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
+import org.apache.myfaces.tobago.layout.Measure;
+import org.apache.myfaces.tobago.renderkit.SelectManyRendererBase;
 import org.apache.myfaces.tobago.renderkit.css.Classes;
 import org.apache.myfaces.tobago.renderkit.css.Style;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.renderkit.html.Select2Options;
 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.renderkit.util.SelectItemUtils;
 import org.apache.myfaces.tobago.util.ComponentUtils;
 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 import javax.faces.model.SelectItem;
 import java.io.IOException;
 
-public class SelectOneChoiceRenderer extends SelectOneRendererBase {
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SelectManyBoxRenderer extends SelectManyRendererBase {
 
-  private static final Logger LOG = LoggerFactory.getLogger(SelectOneChoiceRenderer.class);
+  private static final Logger LOG = LoggerFactory.getLogger(SelectManyBoxRenderer.class);
 
   public boolean getRendersChildren() {
     return true;
   }
 
+  @Override
+  public void prepareRender(FacesContext facesContext, UIComponent component) throws IOException {
+    super.prepareRender(facesContext, component);
+    addSelect2LanguageJs(facesContext);
+  }
+
+  public static void addSelect2LanguageJs(FacesContext facesContext) {
+    String file = "script/contrib/select2/i18n/" + facesContext.getViewRoot().getLocale().getLanguage() + ".js";
+    FacesContextUtils.addScriptFile(facesContext, file);
+  }
+
   public void encodeEnd(final FacesContext facesContext, final UIComponent component) throws IOException {
-    if (!(component instanceof UISelectOneChoice)) {
-      LOG.error("Wrong type: Need " + UISelectOneChoice.class.getName()
+    if (!(component instanceof UISelectManyBox)) {
+      LOG.error("Wrong type: Need " + UISelectManyBox.class.getName()
           + ", but was " + component.getClass().getName());
       return;
     }
 
-    final UISelectOneChoice select = (UISelectOneChoice) component;
+    final UISelectManyBox select = (UISelectManyBox) component;
     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
 
     final String id = select.getClientId(facesContext);
     final Iterable<SelectItem> items = SelectItemUtils.getItemIterator(facesContext, select);
+    final boolean readonly = select.isReadonly();
+    final boolean disabled = (!items.iterator().hasNext() && !select.isAllowCustom())
+        || select.isDisabled() || readonly;
+    final Style style = new Style(facesContext, select);
+
+    ComponentUtils.putDataAttribute(select, "tobago-select2", Select2Options.of(select).toJson());
+
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, select);
-    final boolean disabled = !items.iterator().hasNext() || select.isDisabled() || select.isReadonly();
+    writer.startElement(HtmlElements.DIV, select);
+    writer.writeStyleAttribute(style);
+    style.setTop(Measure.ZERO);
+    style.setLeft(Measure.ZERO);
 
     writer.startElement(HtmlElements.SELECT, select);
     writer.writeNameAttribute(id);
     writer.writeIdAttribute(id);
     HtmlRendererUtils.writeDataAttributes(facesContext, writer, select);
     writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
+    writer.writeAttribute(HtmlAttributes.READONLY, readonly);
+    writer.writeAttribute(HtmlAttributes.REQUIRED, select.isRequired());
+    HtmlRendererUtils.renderFocus(id, select.isFocus(), ComponentUtils.isError(select), facesContext, writer);
     final Integer tabIndex = select.getTabIndex();
     if (tabIndex != null) {
       writer.writeAttribute(HtmlAttributes.TABINDEX, tabIndex);
     }
-    final Style style = new Style(facesContext, select);
     writer.writeStyleAttribute(style);
     writer.writeClassAttribute(Classes.create(select));
+    writer.writeAttribute(HtmlAttributes.MULTIPLE, HtmlAttributes.MULTIPLE, false);
     if (title != null) {
       writer.writeAttribute(HtmlAttributes.TITLE, title, true);
     }
-    final String onchange = HtmlUtils.generateOnchange(select, facesContext);
-    if (onchange != null) {
-      writer.writeAttribute(HtmlAttributes.ONCHANGE, onchange, true);
-    }
-    HtmlRendererUtils.renderCommandFacet(select, facesContext , writer);
-    HtmlRendererUtils.renderFocus(id, select.isFocus(), ComponentUtils.isError(select), facesContext, writer);
-    
-    HtmlRendererUtils.renderSelectItems(select, items, select.getValue(), (String) select.getSubmittedValue(), writer,
-        facesContext);
+    HtmlRendererUtils.renderCommandFacet(select, facesContext, writer);
+    final Object[] values = select.getSelectedValues();
+    final String[] submittedValues = getSubmittedValues(select);
+    HtmlRendererUtils.renderSelectItems(select, items, values, submittedValues, writer, facesContext);
 
     writer.endElement(HtmlElements.SELECT);
-    super.encodeEnd(facesContext, select);
+    writer.endElement(HtmlElements.DIV);
   }
+
 }
+
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
index c44b34f..8c4d5d8 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
@@ -20,12 +20,16 @@
 package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
 
 import org.apache.myfaces.tobago.component.UISelectOneChoice;
+import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
+import org.apache.myfaces.tobago.layout.Measure;
+import org.apache.myfaces.tobago.renderkit.html.Select2Options;
 import org.apache.myfaces.tobago.renderkit.HtmlUtils;
 import org.apache.myfaces.tobago.renderkit.SelectOneRendererBase;
 import org.apache.myfaces.tobago.renderkit.css.Classes;
 import org.apache.myfaces.tobago.renderkit.css.Style;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.renderkit.util.SelectItemUtils;
 import org.apache.myfaces.tobago.util.ComponentUtils;
@@ -46,6 +50,15 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
     return true;
   }
 
+  @Override
+  public void prepareRender(FacesContext facesContext, UIComponent component) throws IOException {
+    super.prepareRender(facesContext, component);
+
+    if (Select2Options.of((UISelectOneChoice) component).hasAnyOption()) {
+      SelectManyBoxRenderer.addSelect2LanguageJs(facesContext);
+    }
+  }
+
   public void encodeEnd(final FacesContext facesContext, final UIComponent component) throws IOException {
     if (!(component instanceof UISelectOneChoice)) {
       LOG.error("Wrong type: Need " + UISelectOneChoice.class.getName()
@@ -60,6 +73,21 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
     final Iterable<SelectItem> items = SelectItemUtils.getItemIterator(facesContext, select);
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, select);
     final boolean disabled = !items.iterator().hasNext() || select.isDisabled() || select.isReadonly();
+    final Style style = new Style(facesContext, select);
+    final Select2Options select2Options = Select2Options.of(select);
+    final boolean renderAsSelect2 = select2Options.hasAnyOption();
+
+    if (renderAsSelect2) {
+      String json = select2Options.toJson();
+      LOG.trace("Select2 json = \"{}\"", json);
+      ComponentUtils.putDataAttribute(select, "tobago-select2", json);
+
+      writer.startElement(HtmlElements.DIV, select);
+      writer.writeStyleAttribute(style);
+      style.setTop(Measure.ZERO);
+      style.setLeft(Measure.ZERO);
+    }
+
 
     writer.startElement(HtmlElements.SELECT, select);
     writer.writeNameAttribute(id);
@@ -70,7 +98,6 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
     if (tabIndex != null) {
       writer.writeAttribute(HtmlAttributes.TABINDEX, tabIndex);
     }
-    final Style style = new Style(facesContext, select);
     writer.writeStyleAttribute(style);
     writer.writeClassAttribute(Classes.create(select));
     if (title != null) {
@@ -82,11 +109,19 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
     }
     HtmlRendererUtils.renderCommandFacet(select, facesContext , writer);
     HtmlRendererUtils.renderFocus(id, select.isFocus(), ComponentUtils.isError(select), facesContext, writer);
-    
+    if (renderAsSelect2 && select.getPlaceholder() != null && select.getPlaceholder().length() > 0) {
+     writer.startElement(HtmlElements.OPTION, null);
+     writer.endElement(HtmlElements.OPTION);
+    }
+
     HtmlRendererUtils.renderSelectItems(select, items, select.getValue(), (String) select.getSubmittedValue(), writer,
         facesContext);
 
     writer.endElement(HtmlElements.SELECT);
+    if (renderAsSelect2) {
+      writer.endElement(HtmlElements.DIV);
+    }
+
     super.encodeEnd(facesContext, select);
   }
 }
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
index 128048b..7d00ddb 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
@@ -387,9 +387,11 @@
         <script priority="10" name="script/contrib/jquery-3.5.1.js"/>
         <script priority="20" name="script/contrib/jquery-ui-1.10.4.custom.js"/>
         <script priority="30" name="script/contrib/jquery-ui-timepicker-addon-1.4.5.js"/>
+        <script priority="35" name="script/contrib/select2/select2-4.0.8.full.js"/>
         <script priority="40" name="script/tobago.js"/>
         <style priority="10" name="style/contrib/ui-lightness/jquery-ui-1.10.4.custom.css"/>
         <style priority="20" name="style/contrib/jquery-ui-timepicker-addon-1.4.5.css"/>
+        <style priority="25" name="style/contrib/select2/select2-4.0.8.css"/>
         <style priority="30" name="style/tobago.css"/>
         <!-- backward compatibility -->
         <style priority="40" name="style/style.css"/>
@@ -398,6 +400,7 @@
         <script priority="10" name="script/contrib/jquery-3.5.1.js"/>
         <script priority="20" name="script/contrib/jquery-ui-1.10.4.custom.js"/>
         <script priority="30" name="script/contrib/jquery-ui-timepicker-addon-1.4.5.js"/>
+        <script priority="35" name="script/contrib/select2/select2-4.0.8.full.js"/>
         <script priority="40" name="script/tobago.js"/>
         <script priority="50" name="script/tobago-calendar.js"/>
         <script priority="60" name="script/tobago-console.js"/>
@@ -407,6 +410,7 @@
         <script priority="100" name="script/tobago-menu.js"/>
         <script priority="110" name="script/tobago-overlay.js"/>
         <script priority="120" name="script/tobago-popup.js"/>
+        <script priority="125" name="script/tobago-select2.js"/>
         <script priority="130" name="script/tobago-sheet.js"/>
         <script priority="140" name="script/tobago-suggest.js"/>
         <script priority="150" name="script/tobago-tab.js"/>
@@ -415,6 +419,7 @@
         <script priority="180" name="script/tobago-logging.js"/>
         <style priority="10" name="style/contrib/ui-lightness/jquery-ui-1.10.4.custom.css"/>
         <style priority="20" name="style/contrib/jquery-ui-timepicker-addon-1.4.5.css"/>
+        <style priority="25" name="style/contrib/select2/select2-4.0.8.css"/>
         <style priority="30" name="style/tobago.css"/>
         <!-- backward compatibility -->
         <style priority="40" name="style/style.css"/>
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/af.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/af.js
new file mode 100644
index 0000000..68f7a90
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/af.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/af",[],function(){return{errorLoading:function(){return"Die resultate kon nie gelaai word nie."},inputTooLong:function(e){var n=e.input.length-e.maximum,r="Verwyders asseblief "+n+" character";return 1!=n&&(r+="s"),r},inputTooShort:function(e){return"Voer asseblief "+(e.minimum-e.input.length)+" of meer karakters"},loadingMore:function(){return"Meer resultate word [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ar.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ar.js
new file mode 100644
index 0000000..9398f78
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ar.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/ar",[],function(){return{errorLoading:function(){return"لا يمكن تحميل النتائج"},inputTooLong:function(n){return"الرجاء حذف "+(n.input.length-n.maximum)+" عناصر"},inputTooShort:function(n){return"الرجاء إضافة "+(n.minimum-n.input.length)+" عناصر"},loadingMore:function(){return"جاري تحميل نتائج إضافية..."},maximumSelected:function(n){return"تستطيع إختيار "+n.maximum [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/az.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/az.js
new file mode 100644
index 0000000..2d570e0
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/az.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/az",[],function(){return{inputTooLong:function(n){return n.input.length-n.maximum+" simvol silin"},inputTooShort:function(n){return n.minimum-n.input.length+" simvol daxil edin"},loadingMore:function(){return"Daha çox nəticə yüklənir…"},maximumSelected:function(n){return"Sadəcə "+n.maximum+" element seçə bilərsiniz"},noResults:function(){return"Nəticə tapılmadı"}, [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bg.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bg.js
new file mode 100644
index 0000000..6222c77
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bg.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/bg",[],function(){return{inputTooLong:function(n){var e=n.input.length-n.maximum,u="Моля въведете с "+e+" по-малко символ";return e>1&&(u+="a"),u},inputTooShort:function(n){var e=n.minimum-n.input.length,u="Моля въведете още "+e+" символ";return e>1&&(u+="a"),u},loadingMore:function(){return"Зареждат се още…"},maximumSelected:function(n){var e="Можете да направите [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bn.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bn.js
new file mode 100644
index 0000000..d624d32
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bn.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/bn",[],function(){return{errorLoading:function(){return"ফলাফলগুলি লোড করা যায়নি।"},inputTooLong:function(n){var e=n.input.length-n.maximum,u="অনুগ্রহ করে "+e+" টি অক্ষর মুছে দিন।";return 1!=e&&(u="অনুগ্রহ করে "+e+" টি অক্ষর মুছে দিন।"),u},inputTooShort:function(n){return n.minimum-n.input.length+" টি অক্ষর অথবা অধিক অক্ষর লিখুন।"},loadingMore:function(){return"আর [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bs.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bs.js
new file mode 100644
index 0000000..bcaca12
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/bs.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/bs",[],function(){function e(e,n,r,t){return e%10==1&&e%100!=11?n:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?r:t}return{errorLoading:function(){return"Preuzimanje nije uspijelo."},inputTooLong:function(n){var r=n.input.length-n.maximum,t="Obrišite "+r+" simbol";return t+=e(r,"","a","a")},inputTooShort:function(n){var r=n.minimum-n.input.length,t="Ukucajte bar još "+r+ [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ca.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ca.js
new file mode 100644
index 0000000..ddf9505
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ca.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/ca",[],function(){return{errorLoading:function(){return"La càrrega ha fallat"},inputTooLong:function(e){var n=e.input.length-e.maximum,r="Si us plau, elimina "+n+" car";return r+=1==n?"àcter":"àcters"},inputTooShort:function(e){var n=e.minimum-e.input.length,r="Si us plau, introdueix "+n+" car";return r+=1==n?"àcter":"àcters"},loadingMore:function(){return"Carrega [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/cs.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/cs.js
new file mode 100644
index 0000000..8435d75
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/cs.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/cs",[],function(){function e(e,n){switch(e){case 2:return n?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(n){var t=n.input.length-n.maximum;return 1==t?"Prosím, zadejte o jeden znak méně.":t<=4?"Prosím, zadejte o "+e(t,!0)+" znaky méně.":"Prosím, zadejte o "+ [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/da.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/da.js
new file mode 100644
index 0000000..7d90ee3
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/da.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/da",[],function(){return{errorLoading:function(){return"Resultaterne kunne ikke indlæses."},inputTooLong:function(e){return"Angiv venligst "+(e.input.length-e.maximum)+" tegn mindre"},inputTooShort:function(e){return"Angiv venligst "+(e.minimum-e.input.length)+" tegn mere"},loadingMore:function(){return"Indlæser flere resultater…"},maximumSelected:function(e){var  [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/de.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/de.js
new file mode 100644
index 0000000..953381b
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/de.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/de",[],function(){return{errorLoading:function(){return"Die Ergebnisse konnten nicht geladen werden."},inputTooLong:function(e){return"Bitte "+(e.input.length-e.maximum)+" Zeichen weniger eingeben"},inputTooShort:function(e){return"Bitte "+(e.minimum-e.input.length)+" Zeichen mehr eingeben"},loadingMore:function(){return"Lade mehr Ergebnisse…"},maximumSelected:fun [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/dsb.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/dsb.js
new file mode 100644
index 0000000..b0613b1
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/dsb.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/dsb",[],function(){var n=["znamuško","znamušce","znamuška","znamuškow"],e=["zapisk","zapiska","zapiski","zapiskow"],u=function(n,e){return 1===n?e[0]:2===n?e[1]:n>2&&n<=4?e[2]:n>=5?e[3]:void 0};return{errorLoading:function(){return"Wuslědki njejsu se dali zacytaś."},inputTooLong:function(e){var a=e.input.length-e.maximum;return"Pšosym lašuj "+a+" "+u(a,n)},inputTo [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/el.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/el.js
new file mode 100644
index 0000000..5da0c09
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/el.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/el",[],function(){return{errorLoading:function(){return"Τα αποτελέσματα δεν μπόρεσαν να φορτώσουν."},inputTooLong:function(n){var e=n.input.length-n.maximum,u="Παρακαλώ διαγράψτε "+e+" χαρακτήρ";return 1==e&&(u+="α"),1!=e&&(u+="ες"),u},inputTooShort:function(n){return"Παρακαλώ συμπληρώστε "+(n.minimum-n.input.length)+" ή περισσότερους χαρακτήρες"},loadingMore:func [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/en.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/en.js
new file mode 100644
index 0000000..099de39
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/en.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var n=e.input.length-e.maximum,r="Please delete "+n+" character";return 1!=n&&(r+="s"),r},inputTooShort:function(e){return"Please enter "+(e.minimum-e.input.length)+" or more characters"},loadingMore:function(){return"Loading more results…"},maximumS [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/es.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/es.js
new file mode 100644
index 0000000..d531e9c
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/es.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/es",[],function(){return{errorLoading:function(){return"No se pudieron cargar los resultados"},inputTooLong:function(e){var n=e.input.length-e.maximum,r="Por favor, elimine "+n+" car";return r+=1==n?"ácter":"acteres"},inputTooShort:function(e){var n=e.minimum-e.input.length,r="Por favor, introduzca "+n+" car";return r+=1==n?"ácter":"acteres"},loadingMore:function( [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/et.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/et.js
new file mode 100644
index 0000000..197bef6
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/et.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/et",[],function(){return{inputTooLong:function(e){var n=e.input.length-e.maximum,t="Sisesta "+n+" täht";return 1!=n&&(t+="e"),t+=" vähem"},inputTooShort:function(e){var n=e.minimum-e.input.length,t="Sisesta "+n+" täht";return 1!=n&&(t+="e"),t+=" rohkem"},loadingMore:function(){return"Laen tulemusi…"},maximumSelected:function(e){var n="Saad vaid "+e.maximum+" tulem [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/eu.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/eu.js
new file mode 100644
index 0000000..557a164
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/eu.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/eu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Idatzi ";return n+=1==t?"karaktere bat":t+" karaktere",n+=" gutxiago"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Idatzi ";return n+=1==t?"karaktere bat":t+" karaktere",n+=" gehiago"},loadingMore:function(){return"Emaitza gehiago kargatzen…"},maximumSelected:functi [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fa.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fa.js
new file mode 100644
index 0000000..febffc8
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fa.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/fa",[],function(){return{errorLoading:function(){return"امکان بارگذاری نتایج وجود ندارد."},inputTooLong:function(n){return"لطفاً "+(n.input.length-n.maximum)+" کاراکتر را حذف نمایید"},inputTooShort:function(n){return"لطفاً تعداد "+(n.minimum-n.input.length)+" کاراکتر یا بیشتر وارد نمایید"},loadingMore:function(){return"در حال بارگذاری نتایج بیشتر..."},maximumSelec [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fi.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fi.js
new file mode 100644
index 0000000..f60a308
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fi.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/fi",[],function(){return{errorLoading:function(){return"Tuloksia ei saatu ladattua."},inputTooLong:function(n){return"Ole hyvä ja anna "+(n.input.length-n.maximum)+" merkkiä vähemmän"},inputTooShort:function(n){return"Ole hyvä ja anna "+(n.minimum-n.input.length)+" merkkiä lisää"},loadingMore:function(){return"Ladataan lisää tuloksia…"},maximumSelected:function(n) [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fr.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fr.js
new file mode 100644
index 0000000..5f3cc90
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/fr.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/fr",[],function(){return{errorLoading:function(){return"Les résultats ne peuvent pas être chargés."},inputTooLong:function(e){var n=e.input.length-e.maximum;return"Supprimez "+n+" caractère"+(n>1?"s":"")},inputTooShort:function(e){var n=e.minimum-e.input.length;return"Saisissez au moins "+n+" caractère"+(n>1?"s":"")},loadingMore:function(){return"Chargement de rés [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/gl.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/gl.js
new file mode 100644
index 0000000..959e1cc
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/gl.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/gl",[],function(){return{errorLoading:function(){return"Non foi posíbel cargar os resultados."},inputTooLong:function(e){var n=e.input.length-e.maximum;return 1===n?"Elimine un carácter":"Elimine "+n+" caracteres"},inputTooShort:function(e){var n=e.minimum-e.input.length;return 1===n?"Engada un carácter":"Engada "+n+" caracteres"},loadingMore:function(){return"Car [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/he.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/he.js
new file mode 100644
index 0000000..c96b592
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/he.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/he",[],function(){return{errorLoading:function(){return"שגיאה בטעינת התוצאות"},inputTooLong:function(n){var e=n.input.length-n.maximum,r="נא למחוק ";return r+=1===e?"תו אחד":e+" תווים"},inputTooShort:function(n){var e=n.minimum-n.input.length,r="נא להכניס ";return r+=1===e?"תו אחד":e+" תווים",r+=" או יותר"},loadingMore:function(){return"טוען תוצאות נוספות…"},maxim [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hi.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hi.js
new file mode 100644
index 0000000..6de2e25
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hi.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/hi",[],function(){return{errorLoading:function(){return"परिणामों को लोड नहीं किया जा सका।"},inputTooLong:function(n){var e=n.input.length-n.maximum,r=e+" अक्षर को हटा दें";return e>1&&(r=e+" अक्षरों को हटा दें "),r},inputTooShort:function(n){return"कृपया "+(n.minimum-n.input.length)+" या अधिक अक्षर दर्ज करें"},loadingMore:function(){return"अधिक परिणाम लोड हो रहे ह [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hr.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hr.js
new file mode 100644
index 0000000..832bc12
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hr.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/hr",[],function(){function n(n){var e=" "+n+" znak";return n%10<5&&n%10>0&&(n%100<5||n%100>19)?n%10>1&&(e+="a"):e+="ova",e}return{errorLoading:function(){return"Preuzimanje nije uspjelo."},inputTooLong:function(e){return"Unesite "+n(e.input.length-e.maximum)},inputTooShort:function(e){return"Unesite još "+n(e.minimum-e.input.length)},loadingMore:function(){return" [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hsb.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hsb.js
new file mode 100644
index 0000000..edbf187
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hsb.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/hsb",[],function(){var n=["znamješko","znamješce","znamješka","znamješkow"],e=["zapisk","zapiskaj","zapiski","zapiskow"],u=function(n,e){return 1===n?e[0]:2===n?e[1]:n>2&&n<=4?e[2]:n>=5?e[3]:void 0};return{errorLoading:function(){return"Wuslědki njedachu so začitać."},inputTooLong:function(e){var a=e.input.length-e.maximum;return"Prošu zhašej "+a+" "+u(a,n)},input [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hu.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hu.js
new file mode 100644
index 0000000..162b756
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hu.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/hu",[],function(){return{errorLoading:function(){return"Az eredmények betöltése nem sikerült."},inputTooLong:function(e){return"Túl hosszú. "+(e.input.length-e.maximum)+" karakterrel több, mint kellene."},inputTooShort:function(e){return"Túl rövid. Még "+(e.minimum-e.input.length)+" karakter hiányzik."},loadingMore:function(){return"Töltés…"},maximumSelected:funct [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hy.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hy.js
new file mode 100644
index 0000000..030be02
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/hy.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/hy",[],function(){return{errorLoading:function(){return"Արդյունքները հնարավոր չէ բեռնել։"},inputTooLong:function(n){return"Խնդրում ենք հեռացնել "+(n.input.length-n.maximum)+" նշան"},inputTooShort:function(n){return"Խնդրում ենք մուտքագրել "+(n.minimum-n.input.length)+" կամ ավել նշաններ"},loadingMore:function(){return"Բեռնվում են նոր արդյունքներ․․․"},maximumSelected [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/id.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/id.js
new file mode 100644
index 0000000..6725863
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/id.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/id",[],function(){return{errorLoading:function(){return"Data tidak boleh diambil."},inputTooLong:function(n){return"Hapuskan "+(n.input.length-n.maximum)+" huruf"},inputTooShort:function(n){return"Masukkan "+(n.minimum-n.input.length)+" huruf lagi"},loadingMore:function(){return"Mengambil data…"},maximumSelected:function(n){return"Anda hanya dapat memilih "+n.maxi [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/is.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/is.js
new file mode 100644
index 0000000..316c82e
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/is.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/is",[],function(){return{inputTooLong:function(n){var t=n.input.length-n.maximum,e="Vinsamlegast styttið texta um "+t+" staf";return t<=1?e:e+"i"},inputTooShort:function(n){var t=n.minimum-n.input.length,e="Vinsamlegast skrifið "+t+" staf";return t>1&&(e+="i"),e+=" í viðbót"},loadingMore:function(){return"Sæki fleiri niðurstöður…"},maximumSelected:function(n){retu [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/it.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/it.js
new file mode 100644
index 0000000..e0dcada
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/it.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/it",[],function(){return{errorLoading:function(){return"I risultati non possono essere caricati."},inputTooLong:function(e){var n=e.input.length-e.maximum,t="Per favore cancella "+n+" caratter";return t+=1!==n?"i":"e"},inputTooShort:function(e){return"Per favore inserisci "+(e.minimum-e.input.length)+" o più caratteri"},loadingMore:function(){return"Caricando più  [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ja.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ja.js
new file mode 100644
index 0000000..87d3799
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ja.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/ja",[],function(){return{errorLoading:function(){return"結果が読み込まれませんでした"},inputTooLong:function(n){return n.input.length-n.maximum+" 文字を削除してください"},inputTooShort:function(n){return"少なくとも "+(n.minimum-n.input.length)+" 文字を入力してください"},loadingMore:function(){return"読み込み中…"},maximumSelected:function(n){return n.maximum+" 件しか選択できません"},noResults:function(){return"対象が見つかりませ [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ka.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ka.js
new file mode 100644
index 0000000..e189d8c
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ka.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/ka",[],function(){return{errorLoading:function(){return"მონაცემების ჩატვირთვა შეუძლებელია."},inputTooLong:function(n){return"გთხოვთ აკრიფეთ "+(n.input.length-n.maximum)+" სიმბოლოთი ნაკლები"},inputTooShort:function(n){return"გთხოვთ აკრიფეთ "+(n.minimum-n.input.length)+" სიმბოლო ან მეტი"},loadingMore:function(){return"მონაცემების ჩატვირთვა…"},maximumSelected:functio [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/km.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/km.js
new file mode 100644
index 0000000..2fe4462
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/km.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/km",[],function(){return{errorLoading:function(){return"មិនអាចទាញយកទិន្នន័យ"},inputTooLong:function(n){return"សូមលុបចេញ  "+(n.input.length-n.maximum)+" អក្សរ"},inputTooShort:function(n){return"សូមបញ្ចូល"+(n.minimum-n.input.length)+" អក្សរ រឺ ច្រើនជាងនេះ"},loadingMore:function(){return"កំពុងទាញយកទិន្នន័យបន្ថែម..."},maximumSelected:function(n){return"អ្នកអាចជ្រើសរើស [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ko.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ko.js
new file mode 100644
index 0000000..2112ce1
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ko.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/ko",[],function(){return{errorLoading:function(){return"결과를 불러올 수 없습니다."},inputTooLong:function(n){return"너무 깁니다. "+(n.input.length-n.maximum)+" 글자 지워주세요."},inputTooShort:function(n){return"너무 짧습니다. "+(n.minimum-n.input.length)+" 글자 더 입력해주세요."},loadingMore:function(){return"불러오는 중…"},maximumSelected:function(n){return"최대 "+n.maximum+"개까지만 선택 가능합니다."},noResults:fun [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/lt.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/lt.js
new file mode 100644
index 0000000..c43da93
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/lt.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/lt",[],function(){function n(n,e,i,t){return n%10==1&&(n%100<11||n%100>19)?e:n%10>=2&&n%10<=9&&(n%100<11||n%100>19)?i:t}return{inputTooLong:function(e){var i=e.input.length-e.maximum,t="Pašalinkite "+i+" simbol";return t+=n(i,"į","ius","ių")},inputTooShort:function(e){var i=e.minimum-e.input.length,t="Įrašykite dar "+i+" simbol";return t+=n(i,"į","ius","ių")},load [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/lv.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/lv.js
new file mode 100644
index 0000000..b0bc306
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/lv.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/lv",[],function(){function e(e,n,u,i){return 11===e?n:e%10==1?u:i}return{inputTooLong:function(n){var u=n.input.length-n.maximum,i="Lūdzu ievadiet par  "+u;return(i+=" simbol"+e(u,"iem","u","iem"))+" mazāk"},inputTooShort:function(n){var u=n.minimum-n.input.length,i="Lūdzu ievadiet vēl "+u;return i+=" simbol"+e(u,"us","u","us")},loadingMore:function(){return"Datu  [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/mk.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/mk.js
new file mode 100644
index 0000000..c38a7f5
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/mk.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/mk",[],function(){return{inputTooLong:function(n){var e=(n.input.length,n.maximum,"Ве молиме внесете "+n.maximum+" помалку карактер");return 1!==n.maximum&&(e+="и"),e},inputTooShort:function(n){var e=(n.minimum,n.input.length,"Ве молиме внесете уште "+n.maximum+" карактер");return 1!==n.maximum&&(e+="и"),e},loadingMore:function(){return"Вчитување резултати…"},maxi [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ms.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ms.js
new file mode 100644
index 0000000..d014f2d
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ms.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/ms",[],function(){return{errorLoading:function(){return"Keputusan tidak berjaya dimuatkan."},inputTooLong:function(n){return"Sila hapuskan "+(n.input.length-n.maximum)+" aksara"},inputTooShort:function(n){return"Sila masukkan "+(n.minimum-n.input.length)+" atau lebih aksara"},loadingMore:function(){return"Sedang memuatkan keputusan…"},maximumSelected:function(n){r [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/nb.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/nb.js
new file mode 100644
index 0000000..919ed8e
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/nb.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/nb",[],function(){return{errorLoading:function(){return"Kunne ikke hente resultater."},inputTooLong:function(e){return"Vennligst fjern "+(e.input.length-e.maximum)+" tegn"},inputTooShort:function(e){return"Vennligst skriv inn "+(e.minimum-e.input.length)+" tegn til"},loadingMore:function(){return"Laster flere resultater…"},maximumSelected:function(e){return"Du kan [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ne.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ne.js
new file mode 100644
index 0000000..686d1dc
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ne.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/ne",[],function(){return{errorLoading:function(){return"नतिजाहरु देखाउन सकिएन।"},inputTooLong:function(n){var e=n.input.length-n.maximum,u="कृपया "+e+" अक्षर मेटाउनुहोस्।";return 1!=e&&(u+="कृपया "+e+" अक्षरहरु मेटाउनुहोस्।"),u},inputTooShort:function(n){return"कृपया बाँकी रहेका "+(n.minimum-n.input.length)+" वा अरु धेरै अक्षरहरु भर्नुहोस्।"},loadingMore:function( [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/nl.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/nl.js
new file mode 100644
index 0000000..4e02459
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/nl.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/nl",[],function(){return{errorLoading:function(){return"De resultaten konden niet worden geladen."},inputTooLong:function(e){return"Gelieve "+(e.input.length-e.maximum)+" karakters te verwijderen"},inputTooShort:function(e){return"Gelieve "+(e.minimum-e.input.length)+" of meer karakters in te voeren"},loadingMore:function(){return"Meer resultaten laden…"},maximumS [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pl.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pl.js
new file mode 100644
index 0000000..9754a95
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pl.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/pl",[],function(){var n=["znak","znaki","znaków"],e=["element","elementy","elementów"],r=function(n,e){return 1===n?e[0]:n>1&&n<=4?e[1]:n>=5?e[2]:void 0};return{errorLoading:function(){return"Nie można załadować wyników."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Usuń "+t+" "+r(t,n)},inputTooShort:function(e){var t=e.minimum-e.input.length;re [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ps.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ps.js
new file mode 100644
index 0000000..9d55d88
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ps.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/ps",[],function(){return{errorLoading:function(){return"پايلي نه سي ترلاسه کېدای"},inputTooLong:function(n){var e=n.input.length-n.maximum,r="د مهربانۍ لمخي "+e+" توری ړنګ کړئ";return 1!=e&&(r=r.replace("توری","توري")),r},inputTooShort:function(n){return"لږ تر لږه "+(n.minimum-n.input.length)+" يا ډېر توري وليکئ"},loadingMore:function(){return"نوري پايلي ترلاسه کي [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pt-BR.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pt-BR.js
new file mode 100644
index 0000000..5d0af0e
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pt-BR.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/pt-BR",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var n=e.input.length-e.maximum,r="Apague "+n+" caracter";return 1!=n&&(r+="es"),r},inputTooShort:function(e){return"Digite "+(e.minimum-e.input.length)+" ou mais caracteres"},loadingMore:function(){return"Carregando mais resultados…"},max [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pt.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pt.js
new file mode 100644
index 0000000..657ec1f
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/pt.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/pt",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var r=e.input.length-e.maximum,n="Por favor apague "+r+" ";return n+=1!=r?"caracteres":"caractere"},inputTooShort:function(e){return"Introduza "+(e.minimum-e.input.length)+" ou mais caracteres"},loadingMore:function(){return"A carregar mais  [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ro.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ro.js
new file mode 100644
index 0000000..57afde1
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ro.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/ro",[],function(){return{errorLoading:function(){return"Rezultatele nu au putut fi incărcate."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vă rugăm să ștergeți"+t+" caracter";return 1!==t&&(n+="e"),n},inputTooShort:function(e){return"Vă rugăm să introduceți "+(e.minimum-e.input.length)+" sau mai multe caractere"},loadingMore:function(){return"Se în [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ru.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ru.js
new file mode 100644
index 0000000..9786983
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/ru.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/ru",[],function(){function n(n,e,r,u){return n%10<5&&n%10>0&&n%100<5||n%100>20?n%10>1?r:e:u}return{errorLoading:function(){return"Невозможно загрузить результаты"},inputTooLong:function(e){var r=e.input.length-e.maximum,u="Пожалуйста, введите на "+r+" символ";return u+=n(r,"","a","ов"),u+=" меньше"},inputTooShort:function(e){var r=e.minimum-e.input.length,u="Пожал [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sk.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sk.js
new file mode 100644
index 0000000..3dd418d
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sk.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/sk",[],function(){var e={2:function(e){return e?"dva":"dve"},3:function(){return"tri"},4:function(){return"štyri"}};return{errorLoading:function(){return"Výsledky sa nepodarilo načítať."},inputTooLong:function(n){var t=n.input.length-n.maximum;return 1==t?"Prosím, zadajte o jeden znak menej":t>=2&&t<=4?"Prosím, zadajte o "+e[t](!0)+" znaky menej":"Prosím, zadajte  [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sl.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sl.js
new file mode 100644
index 0000000..2400d42
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sl.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/sl",[],function(){return{errorLoading:function(){return"Zadetkov iskanja ni bilo mogoče naložiti."},inputTooLong:function(e){var n=e.input.length-e.maximum,t="Prosim zbrišite "+n+" znak";return 2==n?t+="a":1!=n&&(t+="e"),t},inputTooShort:function(e){var n=e.minimum-e.input.length,t="Prosim vpišite še "+n+" znak";return 2==n?t+="a":1!=n&&(t+="e"),t},loadingMore:fun [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sq.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sq.js
new file mode 100644
index 0000000..631bdea
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sq.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/sq",[],function(){return{errorLoading:function(){return"Rezultatet nuk mund të ngarkoheshin."},inputTooLong:function(e){var n=e.input.length-e.maximum,t="Të lutem fshi "+n+" karakter";return 1!=n&&(t+="e"),t},inputTooShort:function(e){return"Të lutem shkruaj "+(e.minimum-e.input.length)+" ose më shumë karaktere"},loadingMore:function(){return"Duke ngarkuar më shum [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sr-Cyrl.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sr-Cyrl.js
new file mode 100644
index 0000000..32020c0
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sr-Cyrl.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/sr-Cyrl",[],function(){function n(n,e,r,u){return n%10==1&&n%100!=11?e:n%10>=2&&n%10<=4&&(n%100<12||n%100>14)?r:u}return{errorLoading:function(){return"Преузимање није успело."},inputTooLong:function(e){var r=e.input.length-e.maximum,u="Обришите "+r+" симбол";return u+=n(r,"","а","а")},inputTooShort:function(e){var r=e.minimum-e.input.length,u="Укуцајте бар још "+ [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sr.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sr.js
new file mode 100644
index 0000000..3c2a4ab
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sr.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/sr",[],function(){function n(n,e,r,t){return n%10==1&&n%100!=11?e:n%10>=2&&n%10<=4&&(n%100<12||n%100>14)?r:t}return{errorLoading:function(){return"Preuzimanje nije uspelo."},inputTooLong:function(e){var r=e.input.length-e.maximum,t="Obrišite "+r+" simbol";return t+=n(r,"","a","a")},inputTooShort:function(e){var r=e.minimum-e.input.length,t="Ukucajte bar još "+r+"  [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sv.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sv.js
new file mode 100644
index 0000000..19b2d92
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/sv.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/sv",[],function(){return{errorLoading:function(){return"Resultat kunde inte laddas."},inputTooLong:function(n){return"Vänligen sudda ut "+(n.input.length-n.maximum)+" tecken"},inputTooShort:function(n){return"Vänligen skriv in "+(n.minimum-n.input.length)+" eller fler tecken"},loadingMore:function(){return"Laddar fler resultat…"},maximumSelected:function(n){return [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/th.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/th.js
new file mode 100644
index 0000000..a68ae9c
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/th.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/th",[],function(){return{errorLoading:function(){return"ไม่สามารถค้นข้อมูลได้"},inputTooLong:function(n){return"โปรดลบออก "+(n.input.length-n.maximum)+" ตัวอักษร"},inputTooShort:function(n){return"โปรดพิมพ์เพิ่มอีก "+(n.minimum-n.input.length)+" ตัวอักษร"},loadingMore:function(){return"กำลังค้นข้อมูลเพิ่ม…"},maximumSelected:function(n){return"คุณสามารถเลือกได้ไม่เ [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/tk.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/tk.js
new file mode 100644
index 0000000..8886be3
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/tk.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;e.define("select2/i18n/tk",[],function(){return{errorLoading:function(){return"Netije ýüklenmedi."},inputTooLong:function(e){return e.input.length-e.maximum+" harp bozuň."},inputTooShort:function(e){return"Ýene-de iň az "+(e.minimum-e.input.length)+" harp ýazyň."},loadingMore:function(){return"Köpräk netije görkezilýär…"},maximumSelected:function(e){return"Diňe "+e.maximum+" sanysyny sa [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/tr.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/tr.js
new file mode 100644
index 0000000..6e29377
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/tr.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/tr",[],function(){return{errorLoading:function(){return"Sonuç yüklenemedi"},inputTooLong:function(n){return n.input.length-n.maximum+" karakter daha girmelisiniz"},inputTooShort:function(n){return"En az "+(n.minimum-n.input.length)+" karakter daha girmelisiniz"},loadingMore:function(){return"Daha fazla…"},maximumSelected:function(n){return"Sadece "+n.maximum+" seç [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/uk.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/uk.js
new file mode 100644
index 0000000..b0a1dcb
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/uk.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/uk",[],function(){function n(n,e,u,r){return n%100>10&&n%100<15?r:n%10==1?e:n%10>1&&n%10<5?u:r}return{errorLoading:function(){return"Неможливо завантажити результати"},inputTooLong:function(e){return"Будь ласка, видаліть "+(e.input.length-e.maximum)+" "+n(e.maximum,"літеру","літери","літер")},inputTooShort:function(n){return"Будь ласка, введіть "+(n.minimum-n.inpu [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/vi.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/vi.js
new file mode 100644
index 0000000..474edba
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/vi.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/vi",[],function(){return{inputTooLong:function(n){return"Vui lòng xóa bớt "+(n.input.length-n.maximum)+" ký tự"},inputTooShort:function(n){return"Vui lòng nhập thêm từ "+(n.minimum-n.input.length)+" ký tự trở lên"},loadingMore:function(){return"Đang lấy thêm kết quả…"},maximumSelected:function(n){return"Chỉ có thể chọn được "+n.maximum+" lựa chọn"},noResults:funct [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/zh-CN.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/zh-CN.js
new file mode 100644
index 0000000..061f6df
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/zh-CN.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(n){return"请删除"+(n.input.length-n.maximum)+"个字符"},inputTooShort:function(n){return"请再输入至少"+(n.minimum-n.input.length)+"个字符"},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(n){return"最多只能选择"+n.maximum+"个项目"},noResults:function(){return"未找到结果"},searching:funct [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/zh-TW.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/zh-TW.js
new file mode 100644
index 0000000..6346cce
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/i18n/zh-TW.js
@@ -0,0 +1,3 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/zh-TW",[],function(){return{inputTooLong:function(n){return"請刪掉"+(n.input.length-n.maximum)+"個字元"},inputTooShort:function(n){return"請再輸入"+(n.minimum-n.input.length)+"個字元"},loadingMore:function(){return"載入中…"},maximumSelected:function(n){return"你只能選擇最多"+n.maximum+"項"},noResults:function(){return"沒有找到相符的項目"},searching:function(){return"搜尋中…"},removeAllItems:function [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.js
new file mode 100644
index 0000000..efb27b2
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.js
@@ -0,0 +1,6603 @@
+/*!
+ * Select2 4.0.8
+ * https://select2.github.io
+ *
+ * Released under the MIT license
+ * https://github.com/select2/select2/blob/master/LICENSE.md
+ */
+;(function (factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module.
+    define(['jquery'], factory);
+  } else if (typeof module === 'object' && module.exports) {
+    // Node/CommonJS
+    module.exports = function (root, jQuery) {
+      if (jQuery === undefined) {
+        // require('jQuery') returns a factory that requires window to
+        // build a jQuery instance, we normalize how we use modules
+        // that require this pattern but the window provided is a noop
+        // if it's defined (how jquery works)
+        if (typeof window !== 'undefined') {
+          jQuery = require('jquery');
+        }
+        else {
+          jQuery = require('jquery')(root);
+        }
+      }
+      factory(jQuery);
+      return jQuery;
+    };
+  } else {
+    // Browser globals
+    factory(jQuery);
+  }
+} (function (jQuery) {
+  // This is needed so we can catch the AMD loader configuration and use it
+  // The inner file should be wrapped (by `banner.start.js`) in a function that
+  // returns the AMD loader references.
+  var S2 =(function () {
+  // Restore the Select2 AMD loader so it can be used
+  // Needed mostly in the language files, where the loader is not inserted
+  if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
+    var S2 = jQuery.fn.select2.amd;
+  }
+var S2;(function () { if (!S2 || !S2.requirejs) {
+if (!S2) { S2 = {}; } else { require = S2; }
+/**
+ * @license almond 0.3.3 Copyright jQuery Foundation and other contributors.
+ * Released under MIT license, http://github.com/requirejs/almond/LICENSE
+ */
+//Going sloppy to avoid 'use strict' string cost, but strict practices should
+//be followed.
+/*global setTimeout: false */
+
+var requirejs, require, define;
+(function (undef) {
+    var main, req, makeMap, handlers,
+        defined = {},
+        waiting = {},
+        config = {},
+        defining = {},
+        hasOwn = Object.prototype.hasOwnProperty,
+        aps = [].slice,
+        jsSuffixRegExp = /\.js$/;
+
+    function hasProp(obj, prop) {
+        return hasOwn.call(obj, prop);
+    }
+
+    /**
+     * Given a relative module name, like ./something, normalize it to
+     * a real name that can be mapped to a path.
+     * @param {String} name the relative name
+     * @param {String} baseName a real name that the name arg is relative
+     * to.
+     * @returns {String} normalized name
+     */
+    function normalize(name, baseName) {
+        var nameParts, nameSegment, mapValue, foundMap, lastIndex,
+            foundI, foundStarMap, starI, i, j, part, normalizedBaseParts,
+            baseParts = baseName && baseName.split("/"),
+            map = config.map,
+            starMap = (map && map['*']) || {};
+
+        //Adjust any relative paths.
+        if (name) {
+            name = name.split('/');
+            lastIndex = name.length - 1;
+
+            // If wanting node ID compatibility, strip .js from end
+            // of IDs. Have to do this here, and not in nameToUrl
+            // because node allows either .js or non .js to map
+            // to same file.
+            if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
+                name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
+            }
+
+            // Starts with a '.' so need the baseName
+            if (name[0].charAt(0) === '.' && baseParts) {
+                //Convert baseName to array, and lop off the last part,
+                //so that . matches that 'directory' and not name of the baseName's
+                //module. For instance, baseName of 'one/two/three', maps to
+                //'one/two/three.js', but we want the directory, 'one/two' for
+                //this normalization.
+                normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
+                name = normalizedBaseParts.concat(name);
+            }
+
+            //start trimDots
+            for (i = 0; i < name.length; i++) {
+                part = name[i];
+                if (part === '.') {
+                    name.splice(i, 1);
+                    i -= 1;
+                } else if (part === '..') {
+                    // If at the start, or previous value is still ..,
+                    // keep them so that when converted to a path it may
+                    // still work when converted to a path, even though
+                    // as an ID it is less than ideal. In larger point
+                    // releases, may be better to just kick out an error.
+                    if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') {
+                        continue;
+                    } else if (i > 0) {
+                        name.splice(i - 1, 2);
+                        i -= 2;
+                    }
+                }
+            }
+            //end trimDots
+
+            name = name.join('/');
+        }
+
+        //Apply map config if available.
+        if ((baseParts || starMap) && map) {
+            nameParts = name.split('/');
+
+            for (i = nameParts.length; i > 0; i -= 1) {
+                nameSegment = nameParts.slice(0, i).join("/");
+
+                if (baseParts) {
+                    //Find the longest baseName segment match in the config.
+                    //So, do joins on the biggest to smallest lengths of baseParts.
+                    for (j = baseParts.length; j > 0; j -= 1) {
+                        mapValue = map[baseParts.slice(0, j).join('/')];
+
+                        //baseName segment has  config, find if it has one for
+                        //this name.
+                        if (mapValue) {
+                            mapValue = mapValue[nameSegment];
+                            if (mapValue) {
+                                //Match, update name to the new value.
+                                foundMap = mapValue;
+                                foundI = i;
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                if (foundMap) {
+                    break;
+                }
+
+                //Check for a star map match, but just hold on to it,
+                //if there is a shorter segment match later in a matching
+                //config, then favor over this star map.
+                if (!foundStarMap && starMap && starMap[nameSegment]) {
+                    foundStarMap = starMap[nameSegment];
+                    starI = i;
+                }
+            }
+
+            if (!foundMap && foundStarMap) {
+                foundMap = foundStarMap;
+                foundI = starI;
+            }
+
+            if (foundMap) {
+                nameParts.splice(0, foundI, foundMap);
+                name = nameParts.join('/');
+            }
+        }
+
+        return name;
+    }
+
+    function makeRequire(relName, forceSync) {
+        return function () {
+            //A version of a require function that passes a moduleName
+            //value for items that may need to
+            //look up paths relative to the moduleName
+            var args = aps.call(arguments, 0);
+
+            //If first arg is not require('string'), and there is only
+            //one arg, it is the array form without a callback. Insert
+            //a null so that the following concat is correct.
+            if (typeof args[0] !== 'string' && args.length === 1) {
+                args.push(null);
+            }
+            return req.apply(undef, args.concat([relName, forceSync]));
+        };
+    }
+
+    function makeNormalize(relName) {
+        return function (name) {
+            return normalize(name, relName);
+        };
+    }
+
+    function makeLoad(depName) {
+        return function (value) {
+            defined[depName] = value;
+        };
+    }
+
+    function callDep(name) {
+        if (hasProp(waiting, name)) {
+            var args = waiting[name];
+            delete waiting[name];
+            defining[name] = true;
+            main.apply(undef, args);
+        }
+
+        if (!hasProp(defined, name) && !hasProp(defining, name)) {
+            throw new Error('No ' + name);
+        }
+        return defined[name];
+    }
+
+    //Turns a plugin!resource to [plugin, resource]
+    //with the plugin being undefined if the name
+    //did not have a plugin prefix.
+    function splitPrefix(name) {
+        var prefix,
+            index = name ? name.indexOf('!') : -1;
+        if (index > -1) {
+            prefix = name.substring(0, index);
+            name = name.substring(index + 1, name.length);
+        }
+        return [prefix, name];
+    }
+
+    //Creates a parts array for a relName where first part is plugin ID,
+    //second part is resource ID. Assumes relName has already been normalized.
+    function makeRelParts(relName) {
+        return relName ? splitPrefix(relName) : [];
+    }
+
+    /**
+     * Makes a name map, normalizing the name, and using a plugin
+     * for normalization if necessary. Grabs a ref to plugin
+     * too, as an optimization.
+     */
+    makeMap = function (name, relParts) {
+        var plugin,
+            parts = splitPrefix(name),
+            prefix = parts[0],
+            relResourceName = relParts[1];
+
+        name = parts[1];
+
+        if (prefix) {
+            prefix = normalize(prefix, relResourceName);
+            plugin = callDep(prefix);
+        }
+
+        //Normalize according
+        if (prefix) {
+            if (plugin && plugin.normalize) {
+                name = plugin.normalize(name, makeNormalize(relResourceName));
+            } else {
+                name = normalize(name, relResourceName);
+            }
+        } else {
+            name = normalize(name, relResourceName);
+            parts = splitPrefix(name);
+            prefix = parts[0];
+            name = parts[1];
+            if (prefix) {
+                plugin = callDep(prefix);
+            }
+        }
+
+        //Using ridiculous property names for space reasons
+        return {
+            f: prefix ? prefix + '!' + name : name, //fullName
+            n: name,
+            pr: prefix,
+            p: plugin
+        };
+    };
+
+    function makeConfig(name) {
+        return function () {
+            return (config && config.config && config.config[name]) || {};
+        };
+    }
+
+    handlers = {
+        require: function (name) {
+            return makeRequire(name);
+        },
+        exports: function (name) {
+            var e = defined[name];
+            if (typeof e !== 'undefined') {
+                return e;
+            } else {
+                return (defined[name] = {});
+            }
+        },
+        module: function (name) {
+            return {
+                id: name,
+                uri: '',
+                exports: defined[name],
+                config: makeConfig(name)
+            };
+        }
+    };
+
+    main = function (name, deps, callback, relName) {
+        var cjsModule, depName, ret, map, i, relParts,
+            args = [],
+            callbackType = typeof callback,
+            usingExports;
+
+        //Use name if no relName
+        relName = relName || name;
+        relParts = makeRelParts(relName);
+
+        //Call the callback to define the module, if necessary.
+        if (callbackType === 'undefined' || callbackType === 'function') {
+            //Pull out the defined dependencies and pass the ordered
+            //values to the callback.
+            //Default to [require, exports, module] if no deps
+            deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
+            for (i = 0; i < deps.length; i += 1) {
+                map = makeMap(deps[i], relParts);
+                depName = map.f;
+
+                //Fast path CommonJS standard dependencies.
+                if (depName === "require") {
+                    args[i] = handlers.require(name);
+                } else if (depName === "exports") {
+                    //CommonJS module spec 1.1
+                    args[i] = handlers.exports(name);
+                    usingExports = true;
+                } else if (depName === "module") {
+                    //CommonJS module spec 1.1
+                    cjsModule = args[i] = handlers.module(name);
+                } else if (hasProp(defined, depName) ||
+                           hasProp(waiting, depName) ||
+                           hasProp(defining, depName)) {
+                    args[i] = callDep(depName);
+                } else if (map.p) {
+                    map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
+                    args[i] = defined[depName];
+                } else {
+                    throw new Error(name + ' missing ' + depName);
+                }
+            }
+
+            ret = callback ? callback.apply(defined[name], args) : undefined;
+
+            if (name) {
+                //If setting exports via "module" is in play,
+                //favor that over return value and exports. After that,
+                //favor a non-undefined return value over exports use.
+                if (cjsModule && cjsModule.exports !== undef &&
+                        cjsModule.exports !== defined[name]) {
+                    defined[name] = cjsModule.exports;
+                } else if (ret !== undef || !usingExports) {
+                    //Use the return value from the function.
+                    defined[name] = ret;
+                }
+            }
+        } else if (name) {
+            //May just be an object definition for the module. Only
+            //worry about defining if have a module name.
+            defined[name] = callback;
+        }
+    };
+
+    requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
+        if (typeof deps === "string") {
+            if (handlers[deps]) {
+                //callback in this case is really relName
+                return handlers[deps](callback);
+            }
+            //Just return the module wanted. In this scenario, the
+            //deps arg is the module name, and second arg (if passed)
+            //is just the relName.
+            //Normalize module name, if it contains . or ..
+            return callDep(makeMap(deps, makeRelParts(callback)).f);
+        } else if (!deps.splice) {
+            //deps is a config object, not an array.
+            config = deps;
+            if (config.deps) {
+                req(config.deps, config.callback);
+            }
+            if (!callback) {
+                return;
+            }
+
+            if (callback.splice) {
+                //callback is an array, which means it is a dependency list.
+                //Adjust args if there are dependencies
+                deps = callback;
+                callback = relName;
+                relName = null;
+            } else {
+                deps = undef;
+            }
+        }
+
+        //Support require(['a'])
+        callback = callback || function () {};
+
+        //If relName is a function, it is an errback handler,
+        //so remove it.
+        if (typeof relName === 'function') {
+            relName = forceSync;
+            forceSync = alt;
+        }
+
+        //Simulate async callback;
+        if (forceSync) {
+            main(undef, deps, callback, relName);
+        } else {
+            //Using a non-zero value because of concern for what old browsers
+            //do, and latest browsers "upgrade" to 4 if lower value is used:
+            //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
+            //If want a value immediately, use require('id') instead -- something
+            //that works in almond on the global level, but not guaranteed and
+            //unlikely to work in other AMD implementations.
+            setTimeout(function () {
+                main(undef, deps, callback, relName);
+            }, 4);
+        }
+
+        return req;
+    };
+
+    /**
+     * Just drops the config on the floor, but returns req in case
+     * the config return value is used.
+     */
+    req.config = function (cfg) {
+        return req(cfg);
+    };
+
+    /**
+     * Expose module registry for debugging and tooling
+     */
+    requirejs._defined = defined;
+
+    define = function (name, deps, callback) {
+        if (typeof name !== 'string') {
+            throw new Error('See almond README: incorrect module build, no module name');
+        }
+
+        //This module may not have dependencies
+        if (!deps.splice) {
+            //deps is not an array, so probably means
+            //an object literal or factory function for
+            //the value. Adjust args.
+            callback = deps;
+            deps = [];
+        }
+
+        if (!hasProp(defined, name) && !hasProp(waiting, name)) {
+            waiting[name] = [name, deps, callback];
+        }
+    };
+
+    define.amd = {
+        jQuery: true
+    };
+}());
+
+S2.requirejs = requirejs;S2.require = require;S2.define = define;
+}
+}());
+S2.define("almond", function(){});
+
+/* global jQuery:false, $:false */
+S2.define('jquery',[],function () {
+  var _$ = jQuery || $;
+
+  if (_$ == null && console && console.error) {
+    console.error(
+      'Select2: An instance of jQuery or a jQuery-compatible library was not ' +
+      'found. Make sure that you are including jQuery before Select2 on your ' +
+      'web page.'
+    );
+  }
+
+  return _$;
+});
+
+S2.define('select2/utils',[
+  'jquery'
+], function ($) {
+  var Utils = {};
+
+  Utils.Extend = function (ChildClass, SuperClass) {
+    var __hasProp = {}.hasOwnProperty;
+
+    function BaseConstructor () {
+      this.constructor = ChildClass;
+    }
+
+    for (var key in SuperClass) {
+      if (__hasProp.call(SuperClass, key)) {
+        ChildClass[key] = SuperClass[key];
+      }
+    }
+
+    BaseConstructor.prototype = SuperClass.prototype;
+    ChildClass.prototype = new BaseConstructor();
+    ChildClass.__super__ = SuperClass.prototype;
+
+    return ChildClass;
+  };
+
+  function getMethods (theClass) {
+    var proto = theClass.prototype;
+
+    var methods = [];
+
+    for (var methodName in proto) {
+      var m = proto[methodName];
+
+      if (typeof m !== 'function') {
+        continue;
+      }
+
+      if (methodName === 'constructor') {
+        continue;
+      }
+
+      methods.push(methodName);
+    }
+
+    return methods;
+  }
+
+  Utils.Decorate = function (SuperClass, DecoratorClass) {
+    var decoratedMethods = getMethods(DecoratorClass);
+    var superMethods = getMethods(SuperClass);
+
+    function DecoratedClass () {
+      var unshift = Array.prototype.unshift;
+
+      var argCount = DecoratorClass.prototype.constructor.length;
+
+      var calledConstructor = SuperClass.prototype.constructor;
+
+      if (argCount > 0) {
+        unshift.call(arguments, SuperClass.prototype.constructor);
+
+        calledConstructor = DecoratorClass.prototype.constructor;
+      }
+
+      calledConstructor.apply(this, arguments);
+    }
+
+    DecoratorClass.displayName = SuperClass.displayName;
+
+    function ctr () {
+      this.constructor = DecoratedClass;
+    }
+
+    DecoratedClass.prototype = new ctr();
+
+    for (var m = 0; m < superMethods.length; m++) {
+      var superMethod = superMethods[m];
+
+      DecoratedClass.prototype[superMethod] =
+        SuperClass.prototype[superMethod];
+    }
+
+    var calledMethod = function (methodName) {
+      // Stub out the original method if it's not decorating an actual method
+      var originalMethod = function () {};
+
+      if (methodName in DecoratedClass.prototype) {
+        originalMethod = DecoratedClass.prototype[methodName];
+      }
+
+      var decoratedMethod = DecoratorClass.prototype[methodName];
+
+      return function () {
+        var unshift = Array.prototype.unshift;
+
+        unshift.call(arguments, originalMethod);
+
+        return decoratedMethod.apply(this, arguments);
+      };
+    };
+
+    for (var d = 0; d < decoratedMethods.length; d++) {
+      var decoratedMethod = decoratedMethods[d];
+
+      DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
+    }
+
+    return DecoratedClass;
+  };
+
+  var Observable = function () {
+    this.listeners = {};
+  };
+
+  Observable.prototype.on = function (event, callback) {
+    this.listeners = this.listeners || {};
+
+    if (event in this.listeners) {
+      this.listeners[event].push(callback);
+    } else {
+      this.listeners[event] = [callback];
+    }
+  };
+
+  Observable.prototype.trigger = function (event) {
+    var slice = Array.prototype.slice;
+    var params = slice.call(arguments, 1);
+
+    this.listeners = this.listeners || {};
+
+    // Params should always come in as an array
+    if (params == null) {
+      params = [];
+    }
+
+    // If there are no arguments to the event, use a temporary object
+    if (params.length === 0) {
+      params.push({});
+    }
+
+    // Set the `_type` of the first object to the event
+    params[0]._type = event;
+
+    if (event in this.listeners) {
+      this.invoke(this.listeners[event], slice.call(arguments, 1));
+    }
+
+    if ('*' in this.listeners) {
+      this.invoke(this.listeners['*'], arguments);
+    }
+  };
+
+  Observable.prototype.invoke = function (listeners, params) {
+    for (var i = 0, len = listeners.length; i < len; i++) {
+      listeners[i].apply(this, params);
+    }
+  };
+
+  Utils.Observable = Observable;
+
+  Utils.generateChars = function (length) {
+    var chars = '';
+
+    for (var i = 0; i < length; i++) {
+      var randomChar = Math.floor(Math.random() * 36);
+      chars += randomChar.toString(36);
+    }
+
+    return chars;
+  };
+
+  Utils.bind = function (func, context) {
+    return function () {
+      func.apply(context, arguments);
+    };
+  };
+
+  Utils._convertData = function (data) {
+    for (var originalKey in data) {
+      var keys = originalKey.split('-');
+
+      var dataLevel = data;
+
+      if (keys.length === 1) {
+        continue;
+      }
+
+      for (var k = 0; k < keys.length; k++) {
+        var key = keys[k];
+
+        // Lowercase the first letter
+        // By default, dash-separated becomes camelCase
+        key = key.substring(0, 1).toLowerCase() + key.substring(1);
+
+        if (!(key in dataLevel)) {
+          dataLevel[key] = {};
+        }
+
+        if (k == keys.length - 1) {
+          dataLevel[key] = data[originalKey];
+        }
+
+        dataLevel = dataLevel[key];
+      }
+
+      delete data[originalKey];
+    }
+
+    return data;
+  };
+
+  Utils.hasScroll = function (index, el) {
+    // Adapted from the function created by @ShadowScripter
+    // and adapted by @BillBarry on the Stack Exchange Code Review website.
+    // The original code can be found at
+    // http://codereview.stackexchange.com/q/13338
+    // and was designed to be used with the Sizzle selector engine.
+
+    var $el = $(el);
+    var overflowX = el.style.overflowX;
+    var overflowY = el.style.overflowY;
+
+    //Check both x and y declarations
+    if (overflowX === overflowY &&
+        (overflowY === 'hidden' || overflowY === 'visible')) {
+      return false;
+    }
+
+    if (overflowX === 'scroll' || overflowY === 'scroll') {
+      return true;
+    }
+
+    return ($el.innerHeight() < el.scrollHeight ||
+      $el.innerWidth() < el.scrollWidth);
+  };
+
+  Utils.escapeMarkup = function (markup) {
+    var replaceMap = {
+      '\\': '&#92;',
+      '&': '&amp;',
+      '<': '&lt;',
+      '>': '&gt;',
+      '"': '&quot;',
+      '\'': '&#39;',
+      '/': '&#47;'
+    };
+
+    // Do not try to escape the markup if it's not a string
+    if (typeof markup !== 'string') {
+      return markup;
+    }
+
+    return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
+      return replaceMap[match];
+    });
+  };
+
+  // Append an array of jQuery nodes to a given element.
+  Utils.appendMany = function ($element, $nodes) {
+    // jQuery 1.7.x does not support $.fn.append() with an array
+    // Fall back to a jQuery object collection using $.fn.add()
+    if ($.fn.jquery.substr(0, 3) === '1.7') {
+      var $jqNodes = $();
+
+      $.map($nodes, function (node) {
+        $jqNodes = $jqNodes.add(node);
+      });
+
+      $nodes = $jqNodes;
+    }
+
+    $element.append($nodes);
+  };
+
+  // Cache objects in Utils.__cache instead of $.data (see #4346)
+  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]) {
+        if (Utils.__cache[id][name] != null) {
+          return Utils.__cache[id][name];
+        }
+        return $(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;
+});
+
+S2.define('select2/results',[
+  'jquery',
+  './utils'
+], function ($, Utils) {
+  function Results ($element, options, dataAdapter) {
+    this.$element = $element;
+    this.data = dataAdapter;
+    this.options = options;
+
+    Results.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(Results, Utils.Observable);
+
+  Results.prototype.render = function () {
+    var $results = $(
+      '<ul class="select2-results__options" role="tree"></ul>'
+    );
+
+    if (this.options.get('multiple')) {
+      $results.attr('aria-multiselectable', 'true');
+    }
+
+    this.$results = $results;
+
+    return $results;
+  };
+
+  Results.prototype.clear = function () {
+    this.$results.empty();
+  };
+
+  Results.prototype.displayMessage = function (params) {
+    var escapeMarkup = this.options.get('escapeMarkup');
+
+    this.clear();
+    this.hideLoading();
+
+    var $message = $(
+      '<li role="treeitem" aria-live="assertive"' +
+      ' class="select2-results__option"></li>'
+    );
+
+    var message = this.options.get('translations').get(params.message);
+
+    $message.append(
+      escapeMarkup(
+        message(params.args)
+      )
+    );
+
+    $message[0].className += ' select2-results__message';
+
+    this.$results.append($message);
+  };
+
+  Results.prototype.hideMessages = function () {
+    this.$results.find('.select2-results__message').remove();
+  };
+
+  Results.prototype.append = function (data) {
+    this.hideLoading();
+
+    var $options = [];
+
+    if (data.results == null || data.results.length === 0) {
+      if (this.$results.children().length === 0) {
+        this.trigger('results:message', {
+          message: 'noResults'
+        });
+      }
+
+      return;
+    }
+
+    data.results = this.sort(data.results);
+
+    for (var d = 0; d < data.results.length; d++) {
+      var item = data.results[d];
+
+      var $option = this.option(item);
+
+      $options.push($option);
+    }
+
+    this.$results.append($options);
+  };
+
+  Results.prototype.position = function ($results, $dropdown) {
+    var $resultsContainer = $dropdown.find('.select2-results');
+    $resultsContainer.append($results);
+  };
+
+  Results.prototype.sort = function (data) {
+    var sorter = this.options.get('sorter');
+
+    return sorter(data);
+  };
+
+  Results.prototype.highlightFirstItem = function () {
+    var $options = this.$results
+      .find('.select2-results__option[aria-selected]');
+
+    var $selected = $options.filter('[aria-selected=true]');
+
+    // Check if there are any selected options
+    if ($selected.length > 0) {
+      // If there are selected options, highlight the first
+      $selected.first().trigger('mouseenter');
+    } else {
+      // If there are no selected options, highlight the first option
+      // in the dropdown
+      $options.first().trigger('mouseenter');
+    }
+
+    this.ensureHighlightVisible();
+  };
+
+  Results.prototype.setClasses = function () {
+    var self = this;
+
+    this.data.current(function (selected) {
+      var selectedIds = $.map(selected, function (s) {
+        return s.id.toString();
+      });
+
+      var $options = self.$results
+        .find('.select2-results__option[aria-selected]');
+
+      $options.each(function () {
+        var $option = $(this);
+
+        var item = Utils.GetData(this, 'data');
+
+        // id needs to be converted to a string when comparing
+        var id = '' + item.id;
+
+        if ((item.element != null && item.element.selected) ||
+            (item.element == null && $.inArray(id, selectedIds) > -1)) {
+          $option.attr('aria-selected', 'true');
+        } else {
+          $option.attr('aria-selected', 'false');
+        }
+      });
+
+    });
+  };
+
+  Results.prototype.showLoading = function (params) {
+    this.hideLoading();
+
+    var loadingMore = this.options.get('translations').get('searching');
+
+    var loading = {
+      disabled: true,
+      loading: true,
+      text: loadingMore(params)
+    };
+    var $loading = this.option(loading);
+    $loading.className += ' loading-results';
+
+    this.$results.prepend($loading);
+  };
+
+  Results.prototype.hideLoading = function () {
+    this.$results.find('.loading-results').remove();
+  };
+
+  Results.prototype.option = function (data) {
+    var option = document.createElement('li');
+    option.className = 'select2-results__option';
+
+    var attrs = {
+      'role': 'treeitem',
+      'aria-selected': 'false'
+    };
+
+    var matches = window.Element.prototype.matches ||
+      window.Element.prototype.msMatchesSelector ||
+      window.Element.prototype.webkitMatchesSelector;
+
+    if ((data.element != null && matches.call(data.element, ':disabled')) ||
+        (data.element == null && data.disabled)) {
+      delete attrs['aria-selected'];
+      attrs['aria-disabled'] = 'true';
+    }
+
+    if (data.id == null) {
+      delete attrs['aria-selected'];
+    }
+
+    if (data._resultId != null) {
+      option.id = data._resultId;
+    }
+
+    if (data.title) {
+      option.title = data.title;
+    }
+
+    if (data.children) {
+      attrs.role = 'group';
+      attrs['aria-label'] = data.text;
+      delete attrs['aria-selected'];
+    }
+
+    for (var attr in attrs) {
+      var val = attrs[attr];
+
+      option.setAttribute(attr, val);
+    }
+
+    if (data.children) {
+      var $option = $(option);
+
+      var label = document.createElement('strong');
+      label.className = 'select2-results__group';
+
+      var $label = $(label);
+      this.template(data, label);
+
+      var $children = [];
+
+      for (var c = 0; c < data.children.length; c++) {
+        var child = data.children[c];
+
+        var $child = this.option(child);
+
+        $children.push($child);
+      }
+
+      var $childrenContainer = $('<ul></ul>', {
+        'class': 'select2-results__options select2-results__options--nested'
+      });
+
+      $childrenContainer.append($children);
+
+      $option.append(label);
+      $option.append($childrenContainer);
+    } else {
+      this.template(data, option);
+    }
+
+    Utils.StoreData(option, 'data', data);
+
+    return option;
+  };
+
+  Results.prototype.bind = function (container, $container) {
+    var self = this;
+
+    var id = container.id + '-results';
+
+    this.$results.attr('id', id);
+
+    container.on('results:all', function (params) {
+      self.clear();
+      self.append(params.data);
+
+      if (container.isOpen()) {
+        self.setClasses();
+        self.highlightFirstItem();
+      }
+    });
+
+    container.on('results:append', function (params) {
+      self.append(params.data);
+
+      if (container.isOpen()) {
+        self.setClasses();
+      }
+    });
+
+    container.on('query', function (params) {
+      self.hideMessages();
+      self.showLoading(params);
+    });
+
+    container.on('select', function () {
+      if (!container.isOpen()) {
+        return;
+      }
+
+      self.setClasses();
+
+      if (self.options.get('scrollAfterSelect')) {
+        self.highlightFirstItem();
+      }
+    });
+
+    container.on('unselect', function () {
+      if (!container.isOpen()) {
+        return;
+      }
+
+      self.setClasses();
+
+      if (self.options.get('scrollAfterSelect')) {
+        self.highlightFirstItem();
+      }
+    });
+
+    container.on('open', function () {
+      // When the dropdown is open, aria-expended="true"
+      self.$results.attr('aria-expanded', 'true');
+      self.$results.attr('aria-hidden', 'false');
+
+      self.setClasses();
+      self.ensureHighlightVisible();
+    });
+
+    container.on('close', function () {
+      // When the dropdown is closed, aria-expended="false"
+      self.$results.attr('aria-expanded', 'false');
+      self.$results.attr('aria-hidden', 'true');
+      self.$results.removeAttr('aria-activedescendant');
+    });
+
+    container.on('results:toggle', function () {
+      var $highlighted = self.getHighlightedResults();
+
+      if ($highlighted.length === 0) {
+        return;
+      }
+
+      $highlighted.trigger('mouseup');
+    });
+
+    container.on('results:select', function () {
+      var $highlighted = self.getHighlightedResults();
+
+      if ($highlighted.length === 0) {
+        return;
+      }
+
+      var data = Utils.GetData($highlighted[0], 'data');
+
+      if ($highlighted.attr('aria-selected') == 'true') {
+        self.trigger('close', {});
+      } else {
+        self.trigger('select', {
+          data: data
+        });
+      }
+    });
+
+    container.on('results:previous', function () {
+      var $highlighted = self.getHighlightedResults();
+
+      var $options = self.$results.find('[aria-selected]');
+
+      var currentIndex = $options.index($highlighted);
+
+      // If we are already at the top, don't move further
+      // If no options, currentIndex will be -1
+      if (currentIndex <= 0) {
+        return;
+      }
+
+      var nextIndex = currentIndex - 1;
+
+      // If none are highlighted, highlight the first
+      if ($highlighted.length === 0) {
+        nextIndex = 0;
+      }
+
+      var $next = $options.eq(nextIndex);
+
+      $next.trigger('mouseenter');
+
+      var currentOffset = self.$results.offset().top;
+      var nextTop = $next.offset().top;
+      var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);
+
+      if (nextIndex === 0) {
+        self.$results.scrollTop(0);
+      } else if (nextTop - currentOffset < 0) {
+        self.$results.scrollTop(nextOffset);
+      }
+    });
+
+    container.on('results:next', function () {
+      var $highlighted = self.getHighlightedResults();
+
+      var $options = self.$results.find('[aria-selected]');
+
+      var currentIndex = $options.index($highlighted);
+
+      var nextIndex = currentIndex + 1;
+
+      // If we are at the last option, stay there
+      if (nextIndex >= $options.length) {
+        return;
+      }
+
+      var $next = $options.eq(nextIndex);
+
+      $next.trigger('mouseenter');
+
+      var currentOffset = self.$results.offset().top +
+        self.$results.outerHeight(false);
+      var nextBottom = $next.offset().top + $next.outerHeight(false);
+      var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;
+
+      if (nextIndex === 0) {
+        self.$results.scrollTop(0);
+      } else if (nextBottom > currentOffset) {
+        self.$results.scrollTop(nextOffset);
+      }
+    });
+
+    container.on('results:focus', function (params) {
+      params.element.addClass('select2-results__option--highlighted');
+    });
+
+    container.on('results:message', function (params) {
+      self.displayMessage(params);
+    });
+
+    if ($.fn.mousewheel) {
+      this.$results.on('mousewheel', function (e) {
+        var top = self.$results.scrollTop();
+
+        var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
+
+        var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
+        var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
+
+        if (isAtTop) {
+          self.$results.scrollTop(0);
+
+          e.preventDefault();
+          e.stopPropagation();
+        } else if (isAtBottom) {
+          self.$results.scrollTop(
+            self.$results.get(0).scrollHeight - self.$results.height()
+          );
+
+          e.preventDefault();
+          e.stopPropagation();
+        }
+      });
+    }
+
+    this.$results.on('mouseup', '.select2-results__option[aria-selected]',
+      function (evt) {
+      var $this = $(this);
+
+      var data = Utils.GetData(this, 'data');
+
+      if ($this.attr('aria-selected') === 'true') {
+        if (self.options.get('multiple')) {
+          self.trigger('unselect', {
+            originalEvent: evt,
+            data: data
+          });
+        } else {
+          self.trigger('close', {});
+        }
+
+        return;
+      }
+
+      self.trigger('select', {
+        originalEvent: evt,
+        data: data
+      });
+    });
+
+    this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
+      function (evt) {
+      var data = Utils.GetData(this, 'data');
+
+      self.getHighlightedResults()
+          .removeClass('select2-results__option--highlighted');
+
+      self.trigger('results:focus', {
+        data: data,
+        element: $(this)
+      });
+    });
+  };
+
+  Results.prototype.getHighlightedResults = function () {
+    var $highlighted = this.$results
+    .find('.select2-results__option--highlighted');
+
+    return $highlighted;
+  };
+
+  Results.prototype.destroy = function () {
+    this.$results.remove();
+  };
+
+  Results.prototype.ensureHighlightVisible = function () {
+    var $highlighted = this.getHighlightedResults();
+
+    if ($highlighted.length === 0) {
+      return;
+    }
+
+    var $options = this.$results.find('[aria-selected]');
+
+    var currentIndex = $options.index($highlighted);
+
+    var currentOffset = this.$results.offset().top;
+    var nextTop = $highlighted.offset().top;
+    var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);
+
+    var offsetDelta = nextTop - currentOffset;
+    nextOffset -= $highlighted.outerHeight(false) * 2;
+
+    if (currentIndex <= 2) {
+      this.$results.scrollTop(0);
+    } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
+      this.$results.scrollTop(nextOffset);
+    }
+  };
+
+  Results.prototype.template = function (result, container) {
+    var template = this.options.get('templateResult');
+    var escapeMarkup = this.options.get('escapeMarkup');
+
+    var content = template(result, container);
+
+    if (content == null) {
+      container.style.display = 'none';
+    } else if (typeof content === 'string') {
+      container.innerHTML = escapeMarkup(content);
+    } else {
+      $(container).append(content);
+    }
+  };
+
+  return Results;
+});
+
+S2.define('select2/keys',[
+
+], function () {
+  var KEYS = {
+    BACKSPACE: 8,
+    TAB: 9,
+    ENTER: 13,
+    SHIFT: 16,
+    CTRL: 17,
+    ALT: 18,
+    ESC: 27,
+    SPACE: 32,
+    PAGE_UP: 33,
+    PAGE_DOWN: 34,
+    END: 35,
+    HOME: 36,
+    LEFT: 37,
+    UP: 38,
+    RIGHT: 39,
+    DOWN: 40,
+    DELETE: 46
+  };
+
+  return KEYS;
+});
+
+S2.define('select2/selection/base',[
+  'jquery',
+  '../utils',
+  '../keys'
+], function ($, Utils, KEYS) {
+  function BaseSelection ($element, options) {
+    this.$element = $element;
+    this.options = options;
+
+    BaseSelection.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(BaseSelection, Utils.Observable);
+
+  BaseSelection.prototype.render = function () {
+    var $selection = $(
+      '<span class="select2-selection" role="combobox" ' +
+      ' aria-haspopup="true" aria-expanded="false">' +
+      '</span>'
+    );
+
+    this._tabindex = 0;
+
+    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');
+    }
+
+    $selection.attr('title', this.$element.attr('title'));
+    $selection.attr('tabindex', this._tabindex);
+
+    this.$selection = $selection;
+
+    return $selection;
+  };
+
+  BaseSelection.prototype.bind = function (container, $container) {
+    var self = this;
+
+    var id = container.id + '-container';
+    var resultsId = container.id + '-results';
+
+    this.container = container;
+
+    this.$selection.on('focus', function (evt) {
+      self.trigger('focus', evt);
+    });
+
+    this.$selection.on('blur', function (evt) {
+      self._handleBlur(evt);
+    });
+
+    this.$selection.on('keydown', function (evt) {
+      self.trigger('keypress', evt);
+
+      if (evt.which === KEYS.SPACE) {
+        evt.preventDefault();
+      }
+    });
+
+    container.on('results:focus', function (params) {
+      self.$selection.attr('aria-activedescendant', params.data._resultId);
+    });
+
+    container.on('selection:update', function (params) {
+      self.update(params.data);
+    });
+
+    container.on('open', function () {
+      // When the dropdown is open, aria-expanded="true"
+      self.$selection.attr('aria-expanded', 'true');
+      self.$selection.attr('aria-owns', resultsId);
+
+      self._attachCloseHandler(container);
+    });
+
+    container.on('close', function () {
+      // When the dropdown is closed, aria-expanded="false"
+      self.$selection.attr('aria-expanded', 'false');
+      self.$selection.removeAttr('aria-activedescendant');
+      self.$selection.removeAttr('aria-owns');
+
+      self.$selection.trigger('focus');
+
+      self._detachCloseHandler(container);
+    });
+
+    container.on('enable', function () {
+      self.$selection.attr('tabindex', self._tabindex);
+    });
+
+    container.on('disable', function () {
+      self.$selection.attr('tabindex', '-1');
+    });
+  };
+
+  BaseSelection.prototype._handleBlur = function (evt) {
+    var self = this;
+
+    // This needs to be delayed as the active element is the body when the tab
+    // key is pressed, possibly along with others.
+    window.setTimeout(function () {
+      // Don't trigger `blur` if the focus is still in the selection
+      if (
+        (document.activeElement == self.$selection[0]) ||
+        ($.contains(self.$selection[0], document.activeElement))
+      ) {
+        return;
+      }
+
+      self.trigger('blur', evt);
+    }, 1);
+  };
+
+  BaseSelection.prototype._attachCloseHandler = function (container) {
+    var self = this;
+
+    $(document.body).on('mousedown.select2.' + container.id, function (e) {
+      var $target = $(e.target);
+
+      var $select = $target.closest('.select2');
+
+      var $all = $('.select2.select2-container--open');
+
+      $all.each(function () {
+        var $this = $(this);
+
+        if (this == $select[0]) {
+          return;
+        }
+
+        var $element = Utils.GetData(this, 'element');
+
+        $element.select2('close');
+      });
+    });
+  };
+
+  BaseSelection.prototype._detachCloseHandler = function (container) {
+    $(document.body).off('mousedown.select2.' + container.id);
+  };
+
+  BaseSelection.prototype.position = function ($selection, $container) {
+    var $selectionContainer = $container.find('.selection');
+    $selectionContainer.append($selection);
+  };
+
+  BaseSelection.prototype.destroy = function () {
+    this._detachCloseHandler(this.container);
+  };
+
+  BaseSelection.prototype.update = function (data) {
+    throw new Error('The `update` method must be defined in child classes.');
+  };
+
+  return BaseSelection;
+});
+
+S2.define('select2/selection/single',[
+  'jquery',
+  './base',
+  '../utils',
+  '../keys'
+], function ($, BaseSelection, Utils, KEYS) {
+  function SingleSelection () {
+    SingleSelection.__super__.constructor.apply(this, arguments);
+  }
+
+  Utils.Extend(SingleSelection, BaseSelection);
+
+  SingleSelection.prototype.render = function () {
+    var $selection = SingleSelection.__super__.render.call(this);
+
+    $selection.addClass('select2-selection--single');
+
+    $selection.html(
+      '<span class="select2-selection__rendered"></span>' +
+      '<span class="select2-selection__arrow" role="presentation">' +
+        '<b role="presentation"></b>' +
+      '</span>'
+    );
+
+    return $selection;
+  };
+
+  SingleSelection.prototype.bind = function (container, $container) {
+    var self = this;
+
+    SingleSelection.__super__.bind.apply(this, arguments);
+
+    var id = container.id + '-container';
+
+    this.$selection.find('.select2-selection__rendered')
+      .attr('id', id)
+      .attr('role', 'textbox')
+      .attr('aria-readonly', 'true');
+    this.$selection.attr('aria-labelledby', id);
+
+    this.$selection.on('mousedown', function (evt) {
+      // Only respond to left clicks
+      if (evt.which !== 1) {
+        return;
+      }
+
+      self.trigger('toggle', {
+        originalEvent: evt
+      });
+    });
+
+    this.$selection.on('focus', function (evt) {
+      // User focuses on the container
+    });
+
+    this.$selection.on('blur', function (evt) {
+      // User exits the container
+    });
+
+    container.on('focus', function (evt) {
+      if (!container.isOpen()) {
+        self.$selection.trigger('focus');
+      }
+    });
+  };
+
+  SingleSelection.prototype.clear = function () {
+    var $rendered = this.$selection.find('.select2-selection__rendered');
+    $rendered.empty();
+    $rendered.removeAttr('title'); // clear tooltip on empty
+  };
+
+  SingleSelection.prototype.display = function (data, container) {
+    var template = this.options.get('templateSelection');
+    var escapeMarkup = this.options.get('escapeMarkup');
+
+    return escapeMarkup(template(data, container));
+  };
+
+  SingleSelection.prototype.selectionContainer = function () {
+    return $('<span></span>');
+  };
+
+  SingleSelection.prototype.update = function (data) {
+    if (data.length === 0) {
+      this.clear();
+      return;
+    }
+
+    var selection = data[0];
+
+    var $rendered = this.$selection.find('.select2-selection__rendered');
+    var formatted = this.display(selection, $rendered);
+
+    $rendered.empty().append(formatted);
+    $rendered.attr('title', selection.title || selection.text);
+  };
+
+  return SingleSelection;
+});
+
+S2.define('select2/selection/multiple',[
+  'jquery',
+  './base',
+  '../utils'
+], function ($, BaseSelection, Utils) {
+  function MultipleSelection ($element, options) {
+    MultipleSelection.__super__.constructor.apply(this, arguments);
+  }
+
+  Utils.Extend(MultipleSelection, BaseSelection);
+
+  MultipleSelection.prototype.render = function () {
+    var $selection = MultipleSelection.__super__.render.call(this);
+
+    $selection.addClass('select2-selection--multiple');
+
+    $selection.html(
+      '<ul class="select2-selection__rendered"></ul>'
+    );
+
+    return $selection;
+  };
+
+  MultipleSelection.prototype.bind = function (container, $container) {
+    var self = this;
+
+    MultipleSelection.__super__.bind.apply(this, arguments);
+
+    this.$selection.on('click', function (evt) {
+      self.trigger('toggle', {
+        originalEvent: evt
+      });
+    });
+
+    this.$selection.on(
+      'click',
+      '.select2-selection__choice__remove',
+      function (evt) {
+        // Ignore the event if it is disabled
+        if (self.options.get('disabled')) {
+          return;
+        }
+
+        var $remove = $(this);
+        var $selection = $remove.parent();
+
+        var data = Utils.GetData($selection[0], 'data');
+
+        self.trigger('unselect', {
+          originalEvent: evt,
+          data: data
+        });
+      }
+    );
+  };
+
+  MultipleSelection.prototype.clear = function () {
+    var $rendered = this.$selection.find('.select2-selection__rendered');
+    $rendered.empty();
+    $rendered.removeAttr('title');
+  };
+
+  MultipleSelection.prototype.display = function (data, container) {
+    var template = this.options.get('templateSelection');
+    var escapeMarkup = this.options.get('escapeMarkup');
+
+    return escapeMarkup(template(data, container));
+  };
+
+  MultipleSelection.prototype.selectionContainer = function () {
+    var $container = $(
+      '<li class="select2-selection__choice">' +
+        '<span class="select2-selection__choice__remove" role="presentation">' +
+          '&times;' +
+        '</span>' +
+      '</li>'
+    );
+
+    return $container;
+  };
+
+  MultipleSelection.prototype.update = function (data) {
+    this.clear();
+
+    if (data.length === 0) {
+      return;
+    }
+
+    var $selections = [];
+
+    for (var d = 0; d < data.length; d++) {
+      var selection = data[d];
+
+      var $selection = this.selectionContainer();
+      var formatted = this.display(selection, $selection);
+
+      $selection.append(formatted);
+      $selection.attr('title', selection.title || selection.text);
+
+      Utils.StoreData($selection[0], 'data', selection);
+
+      $selections.push($selection);
+    }
+
+    var $rendered = this.$selection.find('.select2-selection__rendered');
+
+    Utils.appendMany($rendered, $selections);
+  };
+
+  return MultipleSelection;
+});
+
+S2.define('select2/selection/placeholder',[
+  '../utils'
+], function (Utils) {
+  function Placeholder (decorated, $element, options) {
+    this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
+
+    decorated.call(this, $element, options);
+  }
+
+  Placeholder.prototype.normalizePlaceholder = function (_, placeholder) {
+    if (typeof placeholder === 'string') {
+      placeholder = {
+        id: '',
+        text: placeholder
+      };
+    }
+
+    return placeholder;
+  };
+
+  Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
+    var $placeholder = this.selectionContainer();
+
+    $placeholder.html(this.display(placeholder));
+    $placeholder.addClass('select2-selection__placeholder')
+                .removeClass('select2-selection__choice');
+
+    return $placeholder;
+  };
+
+  Placeholder.prototype.update = function (decorated, data) {
+    var singlePlaceholder = (
+      data.length == 1 && data[0].id != this.placeholder.id
+    );
+    var multipleSelections = data.length > 1;
+
+    if (multipleSelections || singlePlaceholder) {
+      return decorated.call(this, data);
+    }
+
+    this.clear();
+
+    var $placeholder = this.createPlaceholder(this.placeholder);
+
+    this.$selection.find('.select2-selection__rendered').append($placeholder);
+  };
+
+  return Placeholder;
+});
+
+S2.define('select2/selection/allowClear',[
+  'jquery',
+  '../keys',
+  '../utils'
+], function ($, KEYS, Utils) {
+  function AllowClear () { }
+
+  AllowClear.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    if (this.placeholder == null) {
+      if (this.options.get('debug') && window.console && console.error) {
+        console.error(
+          'Select2: The `allowClear` option should be used in combination ' +
+          'with the `placeholder` option.'
+        );
+      }
+    }
+
+    this.$selection.on('mousedown', '.select2-selection__clear',
+      function (evt) {
+        self._handleClear(evt);
+    });
+
+    container.on('keypress', function (evt) {
+      self._handleKeyboardClear(evt, container);
+    });
+  };
+
+  AllowClear.prototype._handleClear = function (_, evt) {
+    // Ignore the event if it is disabled
+    if (this.options.get('disabled')) {
+      return;
+    }
+
+    var $clear = this.$selection.find('.select2-selection__clear');
+
+    // Ignore the event if nothing has been selected
+    if ($clear.length === 0) {
+      return;
+    }
+
+    evt.stopPropagation();
+
+    var data = Utils.GetData($clear[0], 'data');
+
+    var previousVal = this.$element.val();
+    this.$element.val(this.placeholder.id);
+
+    var unselectData = {
+      data: data
+    };
+    this.trigger('clear', unselectData);
+    if (unselectData.prevented) {
+      this.$element.val(previousVal);
+      return;
+    }
+
+    for (var d = 0; d < data.length; d++) {
+      unselectData = {
+        data: data[d]
+      };
+
+      // Trigger the `unselect` event, so people can prevent it from being
+      // cleared.
+      this.trigger('unselect', unselectData);
+
+      // If the event was prevented, don't clear it out.
+      if (unselectData.prevented) {
+        this.$element.val(previousVal);
+        return;
+      }
+    }
+
+    this.$element.trigger('change');
+
+    this.trigger('toggle', {});
+  };
+
+  AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
+    if (container.isOpen()) {
+      return;
+    }
+
+    if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
+      this._handleClear(evt);
+    }
+  };
+
+  AllowClear.prototype.update = function (decorated, data) {
+    decorated.call(this, data);
+
+    if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
+        data.length === 0) {
+      return;
+    }
+
+    var removeAll = this.options.get('translations').get('removeAllItems');   
+
+    var $remove = $(
+      '<span class="select2-selection__clear" title="' + removeAll() +'">' +
+        '&times;' +
+      '</span>'
+    );
+    Utils.StoreData($remove[0], 'data', data);
+
+    this.$selection.find('.select2-selection__rendered').prepend($remove);
+  };
+
+  return AllowClear;
+});
+
+S2.define('select2/selection/search',[
+  'jquery',
+  '../utils',
+  '../keys'
+], function ($, Utils, KEYS) {
+  function Search (decorated, $element, options) {
+    decorated.call(this, $element, options);
+  }
+
+  Search.prototype.render = function (decorated) {
+    var $search = $(
+      '<li class="select2-search select2-search--inline">' +
+        '<input class="select2-search__field" type="search" tabindex="-1"' +
+        ' autocomplete="off" autocorrect="off" autocapitalize="none"' +
+        ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
+      '</li>'
+    );
+
+    this.$searchContainer = $search;
+    this.$search = $search.find('input');
+
+    var $rendered = decorated.call(this);
+
+    this._transferTabIndex();
+
+    return $rendered;
+  };
+
+  Search.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    container.on('open', function () {
+      self.$search.trigger('focus');
+    });
+
+    container.on('close', function () {
+      self.$search.val('');
+      self.$search.removeAttr('aria-activedescendant');
+      self.$search.trigger('focus');
+    });
+
+    container.on('enable', function () {
+      self.$search.prop('disabled', false);
+
+      self._transferTabIndex();
+    });
+
+    container.on('disable', function () {
+      self.$search.prop('disabled', true);
+    });
+
+    container.on('focus', function (evt) {
+      self.$search.trigger('focus');
+    });
+
+    container.on('results:focus', function (params) {
+      self.$search.attr('aria-activedescendant', params.id);
+    });
+
+    this.$selection.on('focusin', '.select2-search--inline', function (evt) {
+      self.trigger('focus', evt);
+    });
+
+    this.$selection.on('focusout', '.select2-search--inline', function (evt) {
+      self._handleBlur(evt);
+    });
+
+    this.$selection.on('keydown', '.select2-search--inline', function (evt) {
+      evt.stopPropagation();
+
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+
+      var key = evt.which;
+
+      if (key === KEYS.BACKSPACE && self.$search.val() === '') {
+        var $previousChoice = self.$searchContainer
+          .prev('.select2-selection__choice');
+
+        if ($previousChoice.length > 0) {
+          var item = Utils.GetData($previousChoice[0], 'data');
+
+          self.searchRemoveChoice(item);
+
+          evt.preventDefault();
+        }
+      }
+    });
+
+    // Try to detect the IE version should the `documentMode` property that
+    // is stored on the document. This is only implemented in IE and is
+    // slightly cleaner than doing a user agent check.
+    // This property is not available in Edge, but Edge also doesn't have
+    // this bug.
+    var msie = document.documentMode;
+    var disableInputEvents = msie && msie <= 11;
+
+    // Workaround for browsers which do not support the `input` event
+    // This will prevent double-triggering of events for browsers which support
+    // both the `keyup` and `input` events.
+    this.$selection.on(
+      'input.searchcheck',
+      '.select2-search--inline',
+      function (evt) {
+        // IE will trigger the `input` event when a placeholder is used on a
+        // search box. To get around this issue, we are forced to ignore all
+        // `input` events in IE and keep using `keyup`.
+        if (disableInputEvents) {
+          self.$selection.off('input.search input.searchcheck');
+          return;
+        }
+
+        // Unbind the duplicated `keyup` event
+        self.$selection.off('keyup.search');
+      }
+    );
+
+    this.$selection.on(
+      'keyup.search input.search',
+      '.select2-search--inline',
+      function (evt) {
+        // IE will trigger the `input` event when a placeholder is used on a
+        // search box. To get around this issue, we are forced to ignore all
+        // `input` events in IE and keep using `keyup`.
+        if (disableInputEvents && evt.type === 'input') {
+          self.$selection.off('input.search input.searchcheck');
+          return;
+        }
+
+        var key = evt.which;
+
+        // We can freely ignore events from modifier keys
+        if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
+          return;
+        }
+
+        // Tabbing will be handled during the `keydown` phase
+        if (key == KEYS.TAB) {
+          return;
+        }
+
+        self.handleSearch(evt);
+      }
+    );
+  };
+
+  /**
+   * This method will transfer the tabindex attribute from the rendered
+   * selection to the search box. This allows for the search box to be used as
+   * the primary focus instead of the selection container.
+   *
+   * @private
+   */
+  Search.prototype._transferTabIndex = function (decorated) {
+    this.$search.attr('tabindex', this.$selection.attr('tabindex'));
+    this.$selection.attr('tabindex', '-1');
+  };
+
+  Search.prototype.createPlaceholder = function (decorated, placeholder) {
+    this.$search.attr('placeholder', placeholder.text);
+  };
+
+  Search.prototype.update = function (decorated, data) {
+    var searchHadFocus = this.$search[0] == document.activeElement;
+
+    this.$search.attr('placeholder', '');
+
+    decorated.call(this, data);
+
+    this.$selection.find('.select2-selection__rendered')
+                   .append(this.$searchContainer);
+
+    this.resizeSearch();
+    if (searchHadFocus) {
+      this.$search.trigger('focus');
+    }
+  };
+
+  Search.prototype.handleSearch = function () {
+    this.resizeSearch();
+
+    if (!this._keyUpPrevented) {
+      var input = this.$search.val();
+
+      this.trigger('query', {
+        term: input
+      });
+    }
+
+    this._keyUpPrevented = false;
+  };
+
+  Search.prototype.searchRemoveChoice = function (decorated, item) {
+    this.trigger('unselect', {
+      data: item
+    });
+
+    this.$search.val(item.text);
+    this.handleSearch();
+  };
+
+  Search.prototype.resizeSearch = function () {
+    this.$search.css('width', '25px');
+
+    var width = '';
+
+    if (this.$search.attr('placeholder') !== '') {
+      width = this.$selection.find('.select2-selection__rendered').innerWidth();
+    } else {
+      var minimumWidth = this.$search.val().length + 1;
+
+      width = (minimumWidth * 0.75) + 'em';
+    }
+
+    this.$search.css('width', width);
+  };
+
+  return Search;
+});
+
+S2.define('select2/selection/eventRelay',[
+  'jquery'
+], function ($) {
+  function EventRelay () { }
+
+  EventRelay.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+    var relayEvents = [
+      'open', 'opening',
+      'close', 'closing',
+      'select', 'selecting',
+      'unselect', 'unselecting',
+      'clear', 'clearing'
+    ];
+
+    var preventableEvents = [
+      'opening', 'closing', 'selecting', 'unselecting', 'clearing'
+    ];
+
+    decorated.call(this, container, $container);
+
+    container.on('*', function (name, params) {
+      // Ignore events that should not be relayed
+      if ($.inArray(name, relayEvents) === -1) {
+        return;
+      }
+
+      // The parameters should always be an object
+      params = params || {};
+
+      // Generate the jQuery event for the Select2 event
+      var evt = $.Event('select2:' + name, {
+        params: params
+      });
+
+      self.$element.trigger(evt);
+
+      // Only handle preventable events if it was one
+      if ($.inArray(name, preventableEvents) === -1) {
+        return;
+      }
+
+      params.prevented = evt.isDefaultPrevented();
+    });
+  };
+
+  return EventRelay;
+});
+
+S2.define('select2/translation',[
+  'jquery',
+  'require'
+], function ($, require) {
+  function Translation (dict) {
+    this.dict = dict || {};
+  }
+
+  Translation.prototype.all = function () {
+    return this.dict;
+  };
+
+  Translation.prototype.get = function (key) {
+    return this.dict[key];
+  };
+
+  Translation.prototype.extend = function (translation) {
+    this.dict = $.extend({}, translation.all(), this.dict);
+  };
+
+  // Static functions
+
+  Translation._cache = {};
+
+  Translation.loadPath = function (path) {
+    if (!(path in Translation._cache)) {
+      var translations = require(path);
+
+      Translation._cache[path] = translations;
+    }
+
+    return new Translation(Translation._cache[path]);
+  };
+
+  return Translation;
+});
+
+S2.define('select2/diacritics',[
+
+], function () {
+  var diacritics = {
+    '\u24B6': 'A',
+    '\uFF21': 'A',
+    '\u00C0': 'A',
+    '\u00C1': 'A',
+    '\u00C2': 'A',
+    '\u1EA6': 'A',
+    '\u1EA4': 'A',
+    '\u1EAA': 'A',
+    '\u1EA8': 'A',
+    '\u00C3': 'A',
+    '\u0100': 'A',
+    '\u0102': 'A',
+    '\u1EB0': 'A',
+    '\u1EAE': 'A',
+    '\u1EB4': 'A',
+    '\u1EB2': 'A',
+    '\u0226': 'A',
+    '\u01E0': 'A',
+    '\u00C4': 'A',
+    '\u01DE': 'A',
+    '\u1EA2': 'A',
+    '\u00C5': 'A',
+    '\u01FA': 'A',
+    '\u01CD': 'A',
+    '\u0200': 'A',
+    '\u0202': 'A',
+    '\u1EA0': 'A',
+    '\u1EAC': 'A',
+    '\u1EB6': 'A',
+    '\u1E00': 'A',
+    '\u0104': 'A',
+    '\u023A': 'A',
+    '\u2C6F': 'A',
+    '\uA732': 'AA',
+    '\u00C6': 'AE',
+    '\u01FC': 'AE',
+    '\u01E2': 'AE',
+    '\uA734': 'AO',
+    '\uA736': 'AU',
+    '\uA738': 'AV',
+    '\uA73A': 'AV',
+    '\uA73C': 'AY',
+    '\u24B7': 'B',
+    '\uFF22': 'B',
+    '\u1E02': 'B',
+    '\u1E04': 'B',
+    '\u1E06': 'B',
+    '\u0243': 'B',
+    '\u0182': 'B',
+    '\u0181': 'B',
+    '\u24B8': 'C',
+    '\uFF23': 'C',
+    '\u0106': 'C',
+    '\u0108': 'C',
+    '\u010A': 'C',
+    '\u010C': 'C',
+    '\u00C7': 'C',
+    '\u1E08': 'C',
+    '\u0187': 'C',
+    '\u023B': 'C',
+    '\uA73E': 'C',
+    '\u24B9': 'D',
+    '\uFF24': 'D',
+    '\u1E0A': 'D',
+    '\u010E': 'D',
+    '\u1E0C': 'D',
+    '\u1E10': 'D',
+    '\u1E12': 'D',
+    '\u1E0E': 'D',
+    '\u0110': 'D',
+    '\u018B': 'D',
+    '\u018A': 'D',
+    '\u0189': 'D',
+    '\uA779': 'D',
+    '\u01F1': 'DZ',
+    '\u01C4': 'DZ',
+    '\u01F2': 'Dz',
+    '\u01C5': 'Dz',
+    '\u24BA': 'E',
+    '\uFF25': 'E',
+    '\u00C8': 'E',
+    '\u00C9': 'E',
+    '\u00CA': 'E',
+    '\u1EC0': 'E',
+    '\u1EBE': 'E',
+    '\u1EC4': 'E',
+    '\u1EC2': 'E',
+    '\u1EBC': 'E',
+    '\u0112': 'E',
+    '\u1E14': 'E',
+    '\u1E16': 'E',
+    '\u0114': 'E',
+    '\u0116': 'E',
+    '\u00CB': 'E',
+    '\u1EBA': 'E',
+    '\u011A': 'E',
+    '\u0204': 'E',
+    '\u0206': 'E',
+    '\u1EB8': 'E',
+    '\u1EC6': 'E',
+    '\u0228': 'E',
+    '\u1E1C': 'E',
+    '\u0118': 'E',
+    '\u1E18': 'E',
+    '\u1E1A': 'E',
+    '\u0190': 'E',
+    '\u018E': 'E',
+    '\u24BB': 'F',
+    '\uFF26': 'F',
+    '\u1E1E': 'F',
+    '\u0191': 'F',
+    '\uA77B': 'F',
+    '\u24BC': 'G',
+    '\uFF27': 'G',
+    '\u01F4': 'G',
+    '\u011C': 'G',
+    '\u1E20': 'G',
+    '\u011E': 'G',
+    '\u0120': 'G',
+    '\u01E6': 'G',
+    '\u0122': 'G',
+    '\u01E4': 'G',
+    '\u0193': 'G',
+    '\uA7A0': 'G',
+    '\uA77D': 'G',
+    '\uA77E': 'G',
+    '\u24BD': 'H',
+    '\uFF28': 'H',
+    '\u0124': 'H',
+    '\u1E22': 'H',
+    '\u1E26': 'H',
+    '\u021E': 'H',
+    '\u1E24': 'H',
+    '\u1E28': 'H',
+    '\u1E2A': 'H',
+    '\u0126': 'H',
+    '\u2C67': 'H',
+    '\u2C75': 'H',
+    '\uA78D': 'H',
+    '\u24BE': 'I',
+    '\uFF29': 'I',
+    '\u00CC': 'I',
+    '\u00CD': 'I',
+    '\u00CE': 'I',
+    '\u0128': 'I',
+    '\u012A': 'I',
+    '\u012C': 'I',
+    '\u0130': 'I',
+    '\u00CF': 'I',
+    '\u1E2E': 'I',
+    '\u1EC8': 'I',
+    '\u01CF': 'I',
+    '\u0208': 'I',
+    '\u020A': 'I',
+    '\u1ECA': 'I',
+    '\u012E': 'I',
+    '\u1E2C': 'I',
+    '\u0197': 'I',
+    '\u24BF': 'J',
+    '\uFF2A': 'J',
+    '\u0134': 'J',
+    '\u0248': 'J',
+    '\u24C0': 'K',
+    '\uFF2B': 'K',
+    '\u1E30': 'K',
+    '\u01E8': 'K',
+    '\u1E32': 'K',
+    '\u0136': 'K',
+    '\u1E34': 'K',
+    '\u0198': 'K',
+    '\u2C69': 'K',
+    '\uA740': 'K',
+    '\uA742': 'K',
+    '\uA744': 'K',
+    '\uA7A2': 'K',
+    '\u24C1': 'L',
+    '\uFF2C': 'L',
+    '\u013F': 'L',
+    '\u0139': 'L',
+    '\u013D': 'L',
+    '\u1E36': 'L',
+    '\u1E38': 'L',
+    '\u013B': 'L',
+    '\u1E3C': 'L',
+    '\u1E3A': 'L',
+    '\u0141': 'L',
+    '\u023D': 'L',
+    '\u2C62': 'L',
+    '\u2C60': 'L',
+    '\uA748': 'L',
+    '\uA746': 'L',
+    '\uA780': 'L',
+    '\u01C7': 'LJ',
+    '\u01C8': 'Lj',
+    '\u24C2': 'M',
+    '\uFF2D': 'M',
+    '\u1E3E': 'M',
+    '\u1E40': 'M',
+    '\u1E42': 'M',
+    '\u2C6E': 'M',
+    '\u019C': 'M',
+    '\u24C3': 'N',
+    '\uFF2E': 'N',
+    '\u01F8': 'N',
+    '\u0143': 'N',
+    '\u00D1': 'N',
+    '\u1E44': 'N',
+    '\u0147': 'N',
+    '\u1E46': 'N',
+    '\u0145': 'N',
+    '\u1E4A': 'N',
+    '\u1E48': 'N',
+    '\u0220': 'N',
+    '\u019D': 'N',
+    '\uA790': 'N',
+    '\uA7A4': 'N',
+    '\u01CA': 'NJ',
+    '\u01CB': 'Nj',
+    '\u24C4': 'O',
+    '\uFF2F': 'O',
+    '\u00D2': 'O',
+    '\u00D3': 'O',
+    '\u00D4': 'O',
+    '\u1ED2': 'O',
+    '\u1ED0': 'O',
+    '\u1ED6': 'O',
+    '\u1ED4': 'O',
+    '\u00D5': 'O',
+    '\u1E4C': 'O',
+    '\u022C': 'O',
+    '\u1E4E': 'O',
+    '\u014C': 'O',
+    '\u1E50': 'O',
+    '\u1E52': 'O',
+    '\u014E': 'O',
+    '\u022E': 'O',
+    '\u0230': 'O',
+    '\u00D6': 'O',
+    '\u022A': 'O',
+    '\u1ECE': 'O',
+    '\u0150': 'O',
+    '\u01D1': 'O',
+    '\u020C': 'O',
+    '\u020E': 'O',
+    '\u01A0': 'O',
+    '\u1EDC': 'O',
+    '\u1EDA': 'O',
+    '\u1EE0': 'O',
+    '\u1EDE': 'O',
+    '\u1EE2': 'O',
+    '\u1ECC': 'O',
+    '\u1ED8': 'O',
+    '\u01EA': 'O',
+    '\u01EC': 'O',
+    '\u00D8': 'O',
+    '\u01FE': 'O',
+    '\u0186': 'O',
+    '\u019F': 'O',
+    '\uA74A': 'O',
+    '\uA74C': 'O',
+    '\u0152': 'OE',
+    '\u01A2': 'OI',
+    '\uA74E': 'OO',
+    '\u0222': 'OU',
+    '\u24C5': 'P',
+    '\uFF30': 'P',
+    '\u1E54': 'P',
+    '\u1E56': 'P',
+    '\u01A4': 'P',
+    '\u2C63': 'P',
+    '\uA750': 'P',
+    '\uA752': 'P',
+    '\uA754': 'P',
+    '\u24C6': 'Q',
+    '\uFF31': 'Q',
+    '\uA756': 'Q',
+    '\uA758': 'Q',
+    '\u024A': 'Q',
+    '\u24C7': 'R',
+    '\uFF32': 'R',
+    '\u0154': 'R',
+    '\u1E58': 'R',
+    '\u0158': 'R',
+    '\u0210': 'R',
+    '\u0212': 'R',
+    '\u1E5A': 'R',
+    '\u1E5C': 'R',
+    '\u0156': 'R',
+    '\u1E5E': 'R',
+    '\u024C': 'R',
+    '\u2C64': 'R',
+    '\uA75A': 'R',
+    '\uA7A6': 'R',
+    '\uA782': 'R',
+    '\u24C8': 'S',
+    '\uFF33': 'S',
+    '\u1E9E': 'S',
+    '\u015A': 'S',
+    '\u1E64': 'S',
+    '\u015C': 'S',
+    '\u1E60': 'S',
+    '\u0160': 'S',
+    '\u1E66': 'S',
+    '\u1E62': 'S',
+    '\u1E68': 'S',
+    '\u0218': 'S',
+    '\u015E': 'S',
+    '\u2C7E': 'S',
+    '\uA7A8': 'S',
+    '\uA784': 'S',
+    '\u24C9': 'T',
+    '\uFF34': 'T',
+    '\u1E6A': 'T',
+    '\u0164': 'T',
+    '\u1E6C': 'T',
+    '\u021A': 'T',
+    '\u0162': 'T',
+    '\u1E70': 'T',
+    '\u1E6E': 'T',
+    '\u0166': 'T',
+    '\u01AC': 'T',
+    '\u01AE': 'T',
+    '\u023E': 'T',
+    '\uA786': 'T',
+    '\uA728': 'TZ',
+    '\u24CA': 'U',
+    '\uFF35': 'U',
+    '\u00D9': 'U',
+    '\u00DA': 'U',
+    '\u00DB': 'U',
+    '\u0168': 'U',
+    '\u1E78': 'U',
+    '\u016A': 'U',
+    '\u1E7A': 'U',
+    '\u016C': 'U',
+    '\u00DC': 'U',
+    '\u01DB': 'U',
+    '\u01D7': 'U',
+    '\u01D5': 'U',
+    '\u01D9': 'U',
+    '\u1EE6': 'U',
+    '\u016E': 'U',
+    '\u0170': 'U',
+    '\u01D3': 'U',
+    '\u0214': 'U',
+    '\u0216': 'U',
+    '\u01AF': 'U',
+    '\u1EEA': 'U',
+    '\u1EE8': 'U',
+    '\u1EEE': 'U',
+    '\u1EEC': 'U',
+    '\u1EF0': 'U',
+    '\u1EE4': 'U',
+    '\u1E72': 'U',
+    '\u0172': 'U',
+    '\u1E76': 'U',
+    '\u1E74': 'U',
+    '\u0244': 'U',
+    '\u24CB': 'V',
+    '\uFF36': 'V',
+    '\u1E7C': 'V',
+    '\u1E7E': 'V',
+    '\u01B2': 'V',
+    '\uA75E': 'V',
+    '\u0245': 'V',
+    '\uA760': 'VY',
+    '\u24CC': 'W',
+    '\uFF37': 'W',
+    '\u1E80': 'W',
+    '\u1E82': 'W',
+    '\u0174': 'W',
+    '\u1E86': 'W',
+    '\u1E84': 'W',
+    '\u1E88': 'W',
+    '\u2C72': 'W',
+    '\u24CD': 'X',
+    '\uFF38': 'X',
+    '\u1E8A': 'X',
+    '\u1E8C': 'X',
+    '\u24CE': 'Y',
+    '\uFF39': 'Y',
+    '\u1EF2': 'Y',
+    '\u00DD': 'Y',
+    '\u0176': 'Y',
+    '\u1EF8': 'Y',
+    '\u0232': 'Y',
+    '\u1E8E': 'Y',
+    '\u0178': 'Y',
+    '\u1EF6': 'Y',
+    '\u1EF4': 'Y',
+    '\u01B3': 'Y',
+    '\u024E': 'Y',
+    '\u1EFE': 'Y',
+    '\u24CF': 'Z',
+    '\uFF3A': 'Z',
+    '\u0179': 'Z',
+    '\u1E90': 'Z',
+    '\u017B': 'Z',
+    '\u017D': 'Z',
+    '\u1E92': 'Z',
+    '\u1E94': 'Z',
+    '\u01B5': 'Z',
+    '\u0224': 'Z',
+    '\u2C7F': 'Z',
+    '\u2C6B': 'Z',
+    '\uA762': 'Z',
+    '\u24D0': 'a',
+    '\uFF41': 'a',
+    '\u1E9A': 'a',
+    '\u00E0': 'a',
+    '\u00E1': 'a',
+    '\u00E2': 'a',
+    '\u1EA7': 'a',
+    '\u1EA5': 'a',
+    '\u1EAB': 'a',
+    '\u1EA9': 'a',
+    '\u00E3': 'a',
+    '\u0101': 'a',
+    '\u0103': 'a',
+    '\u1EB1': 'a',
+    '\u1EAF': 'a',
+    '\u1EB5': 'a',
+    '\u1EB3': 'a',
+    '\u0227': 'a',
+    '\u01E1': 'a',
+    '\u00E4': 'a',
+    '\u01DF': 'a',
+    '\u1EA3': 'a',
+    '\u00E5': 'a',
+    '\u01FB': 'a',
+    '\u01CE': 'a',
+    '\u0201': 'a',
+    '\u0203': 'a',
+    '\u1EA1': 'a',
+    '\u1EAD': 'a',
+    '\u1EB7': 'a',
+    '\u1E01': 'a',
+    '\u0105': 'a',
+    '\u2C65': 'a',
+    '\u0250': 'a',
+    '\uA733': 'aa',
+    '\u00E6': 'ae',
+    '\u01FD': 'ae',
+    '\u01E3': 'ae',
+    '\uA735': 'ao',
+    '\uA737': 'au',
+    '\uA739': 'av',
+    '\uA73B': 'av',
+    '\uA73D': 'ay',
+    '\u24D1': 'b',
+    '\uFF42': 'b',
+    '\u1E03': 'b',
+    '\u1E05': 'b',
+    '\u1E07': 'b',
+    '\u0180': 'b',
+    '\u0183': 'b',
+    '\u0253': 'b',
+    '\u24D2': 'c',
+    '\uFF43': 'c',
+    '\u0107': 'c',
+    '\u0109': 'c',
+    '\u010B': 'c',
+    '\u010D': 'c',
+    '\u00E7': 'c',
+    '\u1E09': 'c',
+    '\u0188': 'c',
+    '\u023C': 'c',
+    '\uA73F': 'c',
+    '\u2184': 'c',
+    '\u24D3': 'd',
+    '\uFF44': 'd',
+    '\u1E0B': 'd',
+    '\u010F': 'd',
+    '\u1E0D': 'd',
+    '\u1E11': 'd',
+    '\u1E13': 'd',
+    '\u1E0F': 'd',
+    '\u0111': 'd',
+    '\u018C': 'd',
+    '\u0256': 'd',
+    '\u0257': 'd',
+    '\uA77A': 'd',
+    '\u01F3': 'dz',
+    '\u01C6': 'dz',
+    '\u24D4': 'e',
+    '\uFF45': 'e',
+    '\u00E8': 'e',
+    '\u00E9': 'e',
+    '\u00EA': 'e',
+    '\u1EC1': 'e',
+    '\u1EBF': 'e',
+    '\u1EC5': 'e',
+    '\u1EC3': 'e',
+    '\u1EBD': 'e',
+    '\u0113': 'e',
+    '\u1E15': 'e',
+    '\u1E17': 'e',
+    '\u0115': 'e',
+    '\u0117': 'e',
+    '\u00EB': 'e',
+    '\u1EBB': 'e',
+    '\u011B': 'e',
+    '\u0205': 'e',
+    '\u0207': 'e',
+    '\u1EB9': 'e',
+    '\u1EC7': 'e',
+    '\u0229': 'e',
+    '\u1E1D': 'e',
+    '\u0119': 'e',
+    '\u1E19': 'e',
+    '\u1E1B': 'e',
+    '\u0247': 'e',
+    '\u025B': 'e',
+    '\u01DD': 'e',
+    '\u24D5': 'f',
+    '\uFF46': 'f',
+    '\u1E1F': 'f',
+    '\u0192': 'f',
+    '\uA77C': 'f',
+    '\u24D6': 'g',
+    '\uFF47': 'g',
+    '\u01F5': 'g',
+    '\u011D': 'g',
+    '\u1E21': 'g',
+    '\u011F': 'g',
+    '\u0121': 'g',
+    '\u01E7': 'g',
+    '\u0123': 'g',
+    '\u01E5': 'g',
+    '\u0260': 'g',
+    '\uA7A1': 'g',
+    '\u1D79': 'g',
+    '\uA77F': 'g',
+    '\u24D7': 'h',
+    '\uFF48': 'h',
+    '\u0125': 'h',
+    '\u1E23': 'h',
+    '\u1E27': 'h',
+    '\u021F': 'h',
+    '\u1E25': 'h',
+    '\u1E29': 'h',
+    '\u1E2B': 'h',
+    '\u1E96': 'h',
+    '\u0127': 'h',
+    '\u2C68': 'h',
+    '\u2C76': 'h',
+    '\u0265': 'h',
+    '\u0195': 'hv',
+    '\u24D8': 'i',
+    '\uFF49': 'i',
+    '\u00EC': 'i',
+    '\u00ED': 'i',
+    '\u00EE': 'i',
+    '\u0129': 'i',
+    '\u012B': 'i',
+    '\u012D': 'i',
+    '\u00EF': 'i',
+    '\u1E2F': 'i',
+    '\u1EC9': 'i',
+    '\u01D0': 'i',
+    '\u0209': 'i',
+    '\u020B': 'i',
+    '\u1ECB': 'i',
+    '\u012F': 'i',
+    '\u1E2D': 'i',
+    '\u0268': 'i',
+    '\u0131': 'i',
+    '\u24D9': 'j',
+    '\uFF4A': 'j',
+    '\u0135': 'j',
+    '\u01F0': 'j',
+    '\u0249': 'j',
+    '\u24DA': 'k',
+    '\uFF4B': 'k',
+    '\u1E31': 'k',
+    '\u01E9': 'k',
+    '\u1E33': 'k',
+    '\u0137': 'k',
+    '\u1E35': 'k',
+    '\u0199': 'k',
+    '\u2C6A': 'k',
+    '\uA741': 'k',
+    '\uA743': 'k',
+    '\uA745': 'k',
+    '\uA7A3': 'k',
+    '\u24DB': 'l',
+    '\uFF4C': 'l',
+    '\u0140': 'l',
+    '\u013A': 'l',
+    '\u013E': 'l',
+    '\u1E37': 'l',
+    '\u1E39': 'l',
+    '\u013C': 'l',
+    '\u1E3D': 'l',
+    '\u1E3B': 'l',
+    '\u017F': 'l',
+    '\u0142': 'l',
+    '\u019A': 'l',
+    '\u026B': 'l',
+    '\u2C61': 'l',
+    '\uA749': 'l',
+    '\uA781': 'l',
+    '\uA747': 'l',
+    '\u01C9': 'lj',
+    '\u24DC': 'm',
+    '\uFF4D': 'm',
+    '\u1E3F': 'm',
+    '\u1E41': 'm',
+    '\u1E43': 'm',
+    '\u0271': 'm',
+    '\u026F': 'm',
+    '\u24DD': 'n',
+    '\uFF4E': 'n',
+    '\u01F9': 'n',
+    '\u0144': 'n',
+    '\u00F1': 'n',
+    '\u1E45': 'n',
+    '\u0148': 'n',
+    '\u1E47': 'n',
+    '\u0146': 'n',
+    '\u1E4B': 'n',
+    '\u1E49': 'n',
+    '\u019E': 'n',
+    '\u0272': 'n',
+    '\u0149': 'n',
+    '\uA791': 'n',
+    '\uA7A5': 'n',
+    '\u01CC': 'nj',
+    '\u24DE': 'o',
+    '\uFF4F': 'o',
+    '\u00F2': 'o',
+    '\u00F3': 'o',
+    '\u00F4': 'o',
+    '\u1ED3': 'o',
+    '\u1ED1': 'o',
+    '\u1ED7': 'o',
+    '\u1ED5': 'o',
+    '\u00F5': 'o',
+    '\u1E4D': 'o',
+    '\u022D': 'o',
+    '\u1E4F': 'o',
+    '\u014D': 'o',
+    '\u1E51': 'o',
+    '\u1E53': 'o',
+    '\u014F': 'o',
+    '\u022F': 'o',
+    '\u0231': 'o',
+    '\u00F6': 'o',
+    '\u022B': 'o',
+    '\u1ECF': 'o',
+    '\u0151': 'o',
+    '\u01D2': 'o',
+    '\u020D': 'o',
+    '\u020F': 'o',
+    '\u01A1': 'o',
+    '\u1EDD': 'o',
+    '\u1EDB': 'o',
+    '\u1EE1': 'o',
+    '\u1EDF': 'o',
+    '\u1EE3': 'o',
+    '\u1ECD': 'o',
+    '\u1ED9': 'o',
+    '\u01EB': 'o',
+    '\u01ED': 'o',
+    '\u00F8': 'o',
+    '\u01FF': 'o',
+    '\u0254': 'o',
+    '\uA74B': 'o',
+    '\uA74D': 'o',
+    '\u0275': 'o',
+    '\u0153': 'oe',
+    '\u01A3': 'oi',
+    '\u0223': 'ou',
+    '\uA74F': 'oo',
+    '\u24DF': 'p',
+    '\uFF50': 'p',
+    '\u1E55': 'p',
+    '\u1E57': 'p',
+    '\u01A5': 'p',
+    '\u1D7D': 'p',
+    '\uA751': 'p',
+    '\uA753': 'p',
+    '\uA755': 'p',
+    '\u24E0': 'q',
+    '\uFF51': 'q',
+    '\u024B': 'q',
+    '\uA757': 'q',
+    '\uA759': 'q',
+    '\u24E1': 'r',
+    '\uFF52': 'r',
+    '\u0155': 'r',
+    '\u1E59': 'r',
+    '\u0159': 'r',
+    '\u0211': 'r',
+    '\u0213': 'r',
+    '\u1E5B': 'r',
+    '\u1E5D': 'r',
+    '\u0157': 'r',
+    '\u1E5F': 'r',
+    '\u024D': 'r',
+    '\u027D': 'r',
+    '\uA75B': 'r',
+    '\uA7A7': 'r',
+    '\uA783': 'r',
+    '\u24E2': 's',
+    '\uFF53': 's',
+    '\u00DF': 's',
+    '\u015B': 's',
+    '\u1E65': 's',
+    '\u015D': 's',
+    '\u1E61': 's',
+    '\u0161': 's',
+    '\u1E67': 's',
+    '\u1E63': 's',
+    '\u1E69': 's',
+    '\u0219': 's',
+    '\u015F': 's',
+    '\u023F': 's',
+    '\uA7A9': 's',
+    '\uA785': 's',
+    '\u1E9B': 's',
+    '\u24E3': 't',
+    '\uFF54': 't',
+    '\u1E6B': 't',
+    '\u1E97': 't',
+    '\u0165': 't',
+    '\u1E6D': 't',
+    '\u021B': 't',
+    '\u0163': 't',
+    '\u1E71': 't',
+    '\u1E6F': 't',
+    '\u0167': 't',
+    '\u01AD': 't',
+    '\u0288': 't',
+    '\u2C66': 't',
+    '\uA787': 't',
+    '\uA729': 'tz',
+    '\u24E4': 'u',
+    '\uFF55': 'u',
+    '\u00F9': 'u',
+    '\u00FA': 'u',
+    '\u00FB': 'u',
+    '\u0169': 'u',
+    '\u1E79': 'u',
+    '\u016B': 'u',
+    '\u1E7B': 'u',
+    '\u016D': 'u',
+    '\u00FC': 'u',
+    '\u01DC': 'u',
+    '\u01D8': 'u',
+    '\u01D6': 'u',
+    '\u01DA': 'u',
+    '\u1EE7': 'u',
+    '\u016F': 'u',
+    '\u0171': 'u',
+    '\u01D4': 'u',
+    '\u0215': 'u',
+    '\u0217': 'u',
+    '\u01B0': 'u',
+    '\u1EEB': 'u',
+    '\u1EE9': 'u',
+    '\u1EEF': 'u',
+    '\u1EED': 'u',
+    '\u1EF1': 'u',
+    '\u1EE5': 'u',
+    '\u1E73': 'u',
+    '\u0173': 'u',
+    '\u1E77': 'u',
+    '\u1E75': 'u',
+    '\u0289': 'u',
+    '\u24E5': 'v',
+    '\uFF56': 'v',
+    '\u1E7D': 'v',
+    '\u1E7F': 'v',
+    '\u028B': 'v',
+    '\uA75F': 'v',
+    '\u028C': 'v',
+    '\uA761': 'vy',
+    '\u24E6': 'w',
+    '\uFF57': 'w',
+    '\u1E81': 'w',
+    '\u1E83': 'w',
+    '\u0175': 'w',
+    '\u1E87': 'w',
+    '\u1E85': 'w',
+    '\u1E98': 'w',
+    '\u1E89': 'w',
+    '\u2C73': 'w',
+    '\u24E7': 'x',
+    '\uFF58': 'x',
+    '\u1E8B': 'x',
+    '\u1E8D': 'x',
+    '\u24E8': 'y',
+    '\uFF59': 'y',
+    '\u1EF3': 'y',
+    '\u00FD': 'y',
+    '\u0177': 'y',
+    '\u1EF9': 'y',
+    '\u0233': 'y',
+    '\u1E8F': 'y',
+    '\u00FF': 'y',
+    '\u1EF7': 'y',
+    '\u1E99': 'y',
+    '\u1EF5': 'y',
+    '\u01B4': 'y',
+    '\u024F': 'y',
+    '\u1EFF': 'y',
+    '\u24E9': 'z',
+    '\uFF5A': 'z',
+    '\u017A': 'z',
+    '\u1E91': 'z',
+    '\u017C': 'z',
+    '\u017E': 'z',
+    '\u1E93': 'z',
+    '\u1E95': 'z',
+    '\u01B6': 'z',
+    '\u0225': 'z',
+    '\u0240': 'z',
+    '\u2C6C': 'z',
+    '\uA763': 'z',
+    '\u0386': '\u0391',
+    '\u0388': '\u0395',
+    '\u0389': '\u0397',
+    '\u038A': '\u0399',
+    '\u03AA': '\u0399',
+    '\u038C': '\u039F',
+    '\u038E': '\u03A5',
+    '\u03AB': '\u03A5',
+    '\u038F': '\u03A9',
+    '\u03AC': '\u03B1',
+    '\u03AD': '\u03B5',
+    '\u03AE': '\u03B7',
+    '\u03AF': '\u03B9',
+    '\u03CA': '\u03B9',
+    '\u0390': '\u03B9',
+    '\u03CC': '\u03BF',
+    '\u03CD': '\u03C5',
+    '\u03CB': '\u03C5',
+    '\u03B0': '\u03C5',
+    '\u03CE': '\u03C9',
+    '\u03C2': '\u03C3',
+    '\u2019': '\''
+  };
+
+  return diacritics;
+});
+
+S2.define('select2/data/base',[
+  '../utils'
+], function (Utils) {
+  function BaseAdapter ($element, options) {
+    BaseAdapter.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(BaseAdapter, Utils.Observable);
+
+  BaseAdapter.prototype.current = function (callback) {
+    throw new Error('The `current` method must be defined in child classes.');
+  };
+
+  BaseAdapter.prototype.query = function (params, callback) {
+    throw new Error('The `query` method must be defined in child classes.');
+  };
+
+  BaseAdapter.prototype.bind = function (container, $container) {
+    // Can be implemented in subclasses
+  };
+
+  BaseAdapter.prototype.destroy = function () {
+    // Can be implemented in subclasses
+  };
+
+  BaseAdapter.prototype.generateResultId = function (container, data) {
+    var id = container.id + '-result-';
+
+    id += Utils.generateChars(4);
+
+    if (data.id != null) {
+      id += '-' + data.id.toString();
+    } else {
+      id += '-' + Utils.generateChars(4);
+    }
+    return id;
+  };
+
+  return BaseAdapter;
+});
+
+S2.define('select2/data/select',[
+  './base',
+  '../utils',
+  'jquery'
+], function (BaseAdapter, Utils, $) {
+  function SelectAdapter ($element, options) {
+    this.$element = $element;
+    this.options = options;
+
+    SelectAdapter.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(SelectAdapter, BaseAdapter);
+
+  SelectAdapter.prototype.current = function (callback) {
+    var data = [];
+    var self = this;
+
+    this.$element.find(':selected').each(function () {
+      var $option = $(this);
+
+      var option = self.item($option);
+
+      data.push(option);
+    });
+
+    callback(data);
+  };
+
+  SelectAdapter.prototype.select = function (data) {
+    var self = this;
+
+    data.selected = true;
+
+    // If data.element is a DOM node, use it instead
+    if ($(data.element).is('option')) {
+      data.element.selected = true;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
+    if (this.$element.prop('multiple')) {
+      this.current(function (currentData) {
+        var val = [];
+
+        data = [data];
+        data.push.apply(data, currentData);
+
+        for (var d = 0; d < data.length; d++) {
+          var id = data[d].id;
+
+          if ($.inArray(id, val) === -1) {
+            val.push(id);
+          }
+        }
+
+        self.$element.val(val);
+        self.$element.trigger('change');
+      });
+    } else {
+      var val = data.id;
+
+      this.$element.val(val);
+      this.$element.trigger('change');
+    }
+  };
+
+  SelectAdapter.prototype.unselect = function (data) {
+    var self = this;
+
+    if (!this.$element.prop('multiple')) {
+      return;
+    }
+
+    data.selected = false;
+
+    if ($(data.element).is('option')) {
+      data.element.selected = false;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
+    this.current(function (currentData) {
+      var val = [];
+
+      for (var d = 0; d < currentData.length; d++) {
+        var id = currentData[d].id;
+
+        if (id !== data.id && $.inArray(id, val) === -1) {
+          val.push(id);
+        }
+      }
+
+      self.$element.val(val);
+
+      self.$element.trigger('change');
+    });
+  };
+
+  SelectAdapter.prototype.bind = function (container, $container) {
+    var self = this;
+
+    this.container = container;
+
+    container.on('select', function (params) {
+      self.select(params.data);
+    });
+
+    container.on('unselect', function (params) {
+      self.unselect(params.data);
+    });
+  };
+
+  SelectAdapter.prototype.destroy = function () {
+    // Remove anything added to child elements
+    this.$element.find('*').each(function () {
+      // Remove any custom data set by Select2
+      Utils.RemoveData(this);
+    });
+  };
+
+  SelectAdapter.prototype.query = function (params, callback) {
+    var data = [];
+    var self = this;
+
+    var $options = this.$element.children();
+
+    $options.each(function () {
+      var $option = $(this);
+
+      if (!$option.is('option') && !$option.is('optgroup')) {
+        return;
+      }
+
+      var option = self.item($option);
+
+      var matches = self.matches(params, option);
+
+      if (matches !== null) {
+        data.push(matches);
+      }
+    });
+
+    callback({
+      results: data
+    });
+  };
+
+  SelectAdapter.prototype.addOptions = function ($options) {
+    Utils.appendMany(this.$element, $options);
+  };
+
+  SelectAdapter.prototype.option = function (data) {
+    var option;
+
+    if (data.children) {
+      option = document.createElement('optgroup');
+      option.label = data.text;
+    } else {
+      option = document.createElement('option');
+
+      if (option.textContent !== undefined) {
+        option.textContent = data.text;
+      } else {
+        option.innerText = data.text;
+      }
+    }
+
+    if (data.id !== undefined) {
+      option.value = data.id;
+    }
+
+    if (data.disabled) {
+      option.disabled = true;
+    }
+
+    if (data.selected) {
+      option.selected = true;
+    }
+
+    if (data.title) {
+      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;
+  };
+
+  SelectAdapter.prototype.item = function ($option) {
+    var data = {};
+
+    data = Utils.GetData($option[0], 'data');
+
+    if (data != null) {
+      return data;
+    }
+
+    if ($option.is('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')) {
+      data = {
+        text: $option.prop('label'),
+        children: [],
+        title: $option.prop('title')
+      };
+
+      var $children = $option.children('option');
+      var children = [];
+
+      for (var c = 0; c < $children.length; c++) {
+        var $child = $($children[c]);
+
+        var child = this.item($child);
+
+        children.push(child);
+      }
+
+      data.children = children;
+    }
+
+    data = this._normalizeItem(data);
+    data.element = $option[0];
+
+    Utils.StoreData($option[0], 'data', data);
+
+    return data;
+  };
+
+  SelectAdapter.prototype._normalizeItem = function (item) {
+    if (item !== Object(item)) {
+      item = {
+        id: item,
+        text: item
+      };
+    }
+
+    item = $.extend({}, {
+      text: ''
+    }, item);
+
+    var defaults = {
+      selected: false,
+      disabled: false
+    };
+
+    if (item.id != null) {
+      item.id = item.id.toString();
+    }
+
+    if (item.text != null) {
+      item.text = item.text.toString();
+    }
+
+    if (item._resultId == null && item.id && this.container != null) {
+      item._resultId = this.generateResultId(this.container, item);
+    }
+
+    return $.extend({}, defaults, item);
+  };
+
+  SelectAdapter.prototype.matches = function (params, data) {
+    var matcher = this.options.get('matcher');
+
+    return matcher(params, data);
+  };
+
+  return SelectAdapter;
+});
+
+S2.define('select2/data/array',[
+  './select',
+  '../utils',
+  'jquery'
+], function (SelectAdapter, Utils, $) {
+  function ArrayAdapter ($element, options) {
+    var data = options.get('data') || [];
+
+    ArrayAdapter.__super__.constructor.call(this, $element, options);
+
+    this.addOptions(this.convertToOptions(data));
+  }
+
+  Utils.Extend(ArrayAdapter, SelectAdapter);
+
+  ArrayAdapter.prototype.select = function (data) {
+    var $option = this.$element.find('option').filter(function (i, elm) {
+      return elm.value == data.id.toString();
+    });
+
+    if ($option.length === 0) {
+      $option = this.option(data);
+
+      this.addOptions($option);
+    }
+
+    ArrayAdapter.__super__.select.call(this, data);
+  };
+
+  ArrayAdapter.prototype.convertToOptions = function (data) {
+    var self = this;
+
+    var $existing = this.$element.find('option');
+    var existingIds = $existing.map(function () {
+      return self.item($(this)).id;
+    }).get();
+
+    var $options = [];
+
+    // Filter out all items except for the one passed in the argument
+    function onlyItem (item) {
+      return function () {
+        return $(this).val() == item.id;
+      };
+    }
+
+    for (var d = 0; d < data.length; d++) {
+      var item = this._normalizeItem(data[d]);
+
+      // Skip items which were pre-loaded, only merge the data
+      if ($.inArray(item.id, existingIds) >= 0) {
+        var $existingOption = $existing.filter(onlyItem(item));
+
+        var existingData = this.item($existingOption);
+        var newData = $.extend(true, {}, item, existingData);
+
+        var $newOption = this.option(newData);
+
+        $existingOption.replaceWith($newOption);
+
+        continue;
+      }
+
+      var $option = this.option(item);
+
+      if (item.children) {
+        var $children = this.convertToOptions(item.children);
+
+        Utils.appendMany($option, $children);
+      }
+
+      $options.push($option);
+    }
+
+    return $options;
+  };
+
+  return ArrayAdapter;
+});
+
+S2.define('select2/data/ajax',[
+  './array',
+  '../utils',
+  'jquery'
+], function (ArrayAdapter, Utils, $) {
+  function AjaxAdapter ($element, options) {
+    this.ajaxOptions = this._applyDefaults(options.get('ajax'));
+
+    if (this.ajaxOptions.processResults != null) {
+      this.processResults = this.ajaxOptions.processResults;
+    }
+
+    AjaxAdapter.__super__.constructor.call(this, $element, options);
+  }
+
+  Utils.Extend(AjaxAdapter, ArrayAdapter);
+
+  AjaxAdapter.prototype._applyDefaults = function (options) {
+    var defaults = {
+      data: function (params) {
+        return $.extend({}, params, {
+          q: params.term
+        });
+      },
+      transport: function (params, success, failure) {
+        var $request = $.ajax(params);
+
+        $request.then(success);
+        $request.fail(failure);
+
+        return $request;
+      }
+    };
+
+    return $.extend({}, defaults, options, true);
+  };
+
+  AjaxAdapter.prototype.processResults = function (results) {
+    return results;
+  };
+
+  AjaxAdapter.prototype.query = function (params, callback) {
+    var matches = [];
+    var self = this;
+
+    if (this._request != null) {
+      // JSONP requests cannot always be aborted
+      if ($.isFunction(this._request.abort)) {
+        this._request.abort();
+      }
+
+      this._request = null;
+    }
+
+    var options = $.extend({
+      type: 'GET'
+    }, this.ajaxOptions);
+
+    if (typeof options.url === 'function') {
+      options.url = options.url.call(this.$element, params);
+    }
+
+    if (typeof options.data === 'function') {
+      options.data = options.data.call(this.$element, params);
+    }
+
+    function request () {
+      var $request = options.transport(options, function (data) {
+        var results = self.processResults(data, params);
+
+        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)) {
+            console.error(
+              'Select2: The AJAX results did not return an array in the ' +
+              '`results` key of the response.'
+            );
+          }
+        }
+
+        callback(results);
+      }, function () {
+        // Attempt to detect if a request was aborted
+        // Only works if the transport exposes a status property
+        if ('status' in $request &&
+            ($request.status === 0 || $request.status === '0')) {
+          return;
+        }
+
+        self.trigger('results:message', {
+          message: 'errorLoading'
+        });
+      });
+
+      self._request = $request;
+    }
+
+    if (this.ajaxOptions.delay && params.term != null) {
+      if (this._queryTimeout) {
+        window.clearTimeout(this._queryTimeout);
+      }
+
+      this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay);
+    } else {
+      request();
+    }
+  };
+
+  return AjaxAdapter;
+});
+
+S2.define('select2/data/tags',[
+  'jquery'
+], function ($) {
+  function Tags (decorated, $element, options) {
+    var tags = options.get('tags');
+
+    var createTag = options.get('createTag');
+
+    if (createTag !== undefined) {
+      this.createTag = createTag;
+    }
+
+    var insertTag = options.get('insertTag');
+
+    if (insertTag !== undefined) {
+        this.insertTag = insertTag;
+    }
+
+    decorated.call(this, $element, options);
+
+    if ($.isArray(tags)) {
+      for (var t = 0; t < tags.length; t++) {
+        var tag = tags[t];
+        var item = this._normalizeItem(tag);
+
+        var $option = this.option(item);
+
+        this.$element.append($option);
+      }
+    }
+  }
+
+  Tags.prototype.query = function (decorated, params, callback) {
+    var self = this;
+
+    this._removeOldTags();
+
+    if (params.term == null || params.page != null) {
+      decorated.call(this, params, callback);
+      return;
+    }
+
+    function wrapper (obj, child) {
+      var data = obj.results;
+
+      for (var i = 0; i < data.length; i++) {
+        var option = data[i];
+
+        var checkChildren = (
+          option.children != null &&
+          !wrapper({
+            results: option.children
+          }, true)
+        );
+
+        var optionText = (option.text || '').toUpperCase();
+        var paramsTerm = (params.term || '').toUpperCase();
+
+        var checkText = optionText === paramsTerm;
+
+        if (checkText || checkChildren) {
+          if (child) {
+            return false;
+          }
+
+          obj.data = data;
+          callback(obj);
+
+          return;
+        }
+      }
+
+      if (child) {
+        return true;
+      }
+
+      var tag = self.createTag(params);
+
+      if (tag != null) {
+        var $option = self.option(tag);
+        $option.attr('data-select2-tag', true);
+
+        self.addOptions([$option]);
+
+        self.insertTag(data, tag);
+      }
+
+      obj.results = data;
+
+      callback(obj);
+    }
+
+    decorated.call(this, params, wrapper);
+  };
+
+  Tags.prototype.createTag = function (decorated, params) {
+    var term = $.trim(params.term);
+
+    if (term === '') {
+      return null;
+    }
+
+    return {
+      id: term,
+      text: term
+    };
+  };
+
+  Tags.prototype.insertTag = function (_, data, tag) {
+    data.unshift(tag);
+  };
+
+  Tags.prototype._removeOldTags = function (_) {
+    var tag = this._lastTag;
+
+    var $options = this.$element.find('option[data-select2-tag]');
+
+    $options.each(function () {
+      if (this.selected) {
+        return;
+      }
+
+      $(this).remove();
+    });
+  };
+
+  return Tags;
+});
+
+S2.define('select2/data/tokenizer',[
+  'jquery'
+], function ($) {
+  function Tokenizer (decorated, $element, options) {
+    var tokenizer = options.get('tokenizer');
+
+    if (tokenizer !== undefined) {
+      this.tokenizer = tokenizer;
+    }
+
+    decorated.call(this, $element, options);
+  }
+
+  Tokenizer.prototype.bind = function (decorated, container, $container) {
+    decorated.call(this, container, $container);
+
+    this.$search =  container.dropdown.$search || container.selection.$search ||
+      $container.find('.select2-search__field');
+  };
+
+  Tokenizer.prototype.query = function (decorated, params, callback) {
+    var self = this;
+
+    function createAndSelect (data) {
+      // Normalize the data object so we can use it for checks
+      var item = self._normalizeItem(data);
+
+      // Check if the data object already exists as a tag
+      // Select it if it doesn't
+      var $existingOptions = self.$element.find('option').filter(function () {
+        return $(this).val() === item.id;
+      });
+
+      // If an existing option wasn't found for it, create the option
+      if (!$existingOptions.length) {
+        var $option = self.option(item);
+        $option.attr('data-select2-tag', true);
+
+        self._removeOldTags();
+        self.addOptions([$option]);
+      }
+
+      // Select the item, now that we know there is an option for it
+      select(item);
+    }
+
+    function select (data) {
+      self.trigger('select', {
+        data: data
+      });
+    }
+
+    params.term = params.term || '';
+
+    var tokenData = this.tokenizer(params, this.options, createAndSelect);
+
+    if (tokenData.term !== params.term) {
+      // Replace the search term if we have the search box
+      if (this.$search.length) {
+        this.$search.val(tokenData.term);
+        this.$search.trigger('focus');
+      }
+
+      params.term = tokenData.term;
+    }
+
+    decorated.call(this, params, callback);
+  };
+
+  Tokenizer.prototype.tokenizer = function (_, params, options, callback) {
+    var separators = options.get('tokenSeparators') || [];
+    var term = params.term;
+    var i = 0;
+
+    var createTag = this.createTag || function (params) {
+      return {
+        id: params.term,
+        text: params.term
+      };
+    };
+
+    while (i < term.length) {
+      var termChar = term[i];
+
+      if ($.inArray(termChar, separators) === -1) {
+        i++;
+
+        continue;
+      }
+
+      var part = term.substr(0, i);
+      var partParams = $.extend({}, params, {
+        term: part
+      });
+
+      var data = createTag(partParams);
+
+      if (data == null) {
+        i++;
+        continue;
+      }
+
+      callback(data);
+
+      // Reset the term to not include the tokenized portion
+      term = term.substr(i + 1) || '';
+      i = 0;
+    }
+
+    return {
+      term: term
+    };
+  };
+
+  return Tokenizer;
+});
+
+S2.define('select2/data/minimumInputLength',[
+
+], function () {
+  function MinimumInputLength (decorated, $e, options) {
+    this.minimumInputLength = options.get('minimumInputLength');
+
+    decorated.call(this, $e, options);
+  }
+
+  MinimumInputLength.prototype.query = function (decorated, params, callback) {
+    params.term = params.term || '';
+
+    if (params.term.length < this.minimumInputLength) {
+      this.trigger('results:message', {
+        message: 'inputTooShort',
+        args: {
+          minimum: this.minimumInputLength,
+          input: params.term,
+          params: params
+        }
+      });
+
+      return;
+    }
+
+    decorated.call(this, params, callback);
+  };
+
+  return MinimumInputLength;
+});
+
+S2.define('select2/data/maximumInputLength',[
+
+], function () {
+  function MaximumInputLength (decorated, $e, options) {
+    this.maximumInputLength = options.get('maximumInputLength');
+
+    decorated.call(this, $e, options);
+  }
+
+  MaximumInputLength.prototype.query = function (decorated, params, callback) {
+    params.term = params.term || '';
+
+    if (this.maximumInputLength > 0 &&
+        params.term.length > this.maximumInputLength) {
+      this.trigger('results:message', {
+        message: 'inputTooLong',
+        args: {
+          maximum: this.maximumInputLength,
+          input: params.term,
+          params: params
+        }
+      });
+
+      return;
+    }
+
+    decorated.call(this, params, callback);
+  };
+
+  return MaximumInputLength;
+});
+
+S2.define('select2/data/maximumSelectionLength',[
+
+], function (){
+  function MaximumSelectionLength (decorated, $e, options) {
+    this.maximumSelectionLength = options.get('maximumSelectionLength');
+
+    decorated.call(this, $e, options);
+  }
+
+  MaximumSelectionLength.prototype.query =
+    function (decorated, params, callback) {
+      var self = this;
+
+      this.current(function (currentData) {
+        var count = currentData != null ? currentData.length : 0;
+        if (self.maximumSelectionLength > 0 &&
+          count >= self.maximumSelectionLength) {
+          self.trigger('results:message', {
+            message: 'maximumSelected',
+            args: {
+              maximum: self.maximumSelectionLength
+            }
+          });
+          return;
+        }
+        decorated.call(self, params, callback);
+      });
+  };
+
+  return MaximumSelectionLength;
+});
+
+S2.define('select2/dropdown',[
+  'jquery',
+  './utils'
+], function ($, Utils) {
+  function Dropdown ($element, options) {
+    this.$element = $element;
+    this.options = options;
+
+    Dropdown.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(Dropdown, Utils.Observable);
+
+  Dropdown.prototype.render = function () {
+    var $dropdown = $(
+      '<span class="select2-dropdown">' +
+        '<span class="select2-results"></span>' +
+      '</span>'
+    );
+
+    $dropdown.attr('dir', this.options.get('dir'));
+
+    this.$dropdown = $dropdown;
+
+    return $dropdown;
+  };
+
+  Dropdown.prototype.bind = function () {
+    // Should be implemented in subclasses
+  };
+
+  Dropdown.prototype.position = function ($dropdown, $container) {
+    // Should be implemented in subclasses
+  };
+
+  Dropdown.prototype.destroy = function () {
+    // Remove the dropdown from the DOM
+    this.$dropdown.remove();
+  };
+
+  return Dropdown;
+});
+
+S2.define('select2/dropdown/search',[
+  'jquery',
+  '../utils'
+], function ($, Utils) {
+  function Search () { }
+
+  Search.prototype.render = function (decorated) {
+    var $rendered = decorated.call(this);
+
+    var $search = $(
+      '<span class="select2-search select2-search--dropdown">' +
+        '<input class="select2-search__field" type="search" tabindex="-1"' +
+        ' autocomplete="off" autocorrect="off" autocapitalize="none"' +
+        ' spellcheck="false" role="textbox" />' +
+      '</span>'
+    );
+
+    this.$searchContainer = $search;
+    this.$search = $search.find('input');
+
+    $rendered.prepend($search);
+
+    return $rendered;
+  };
+
+  Search.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    this.$search.on('keydown', function (evt) {
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+
+    // Workaround for browsers which do not support the `input` event
+    // This will prevent double-triggering of events for browsers which support
+    // both the `keyup` and `input` events.
+    this.$search.on('input', function (evt) {
+      // Unbind the duplicated `keyup` event
+      $(this).off('keyup');
+    });
+
+    this.$search.on('keyup input', function (evt) {
+      self.handleSearch(evt);
+    });
+
+    container.on('open', function () {
+      self.$search.attr('tabindex', 0);
+
+      self.$search.trigger('focus');
+
+      window.setTimeout(function () {
+        self.$search.trigger('focus');
+      }, 0);
+    });
+
+    container.on('close', function () {
+      self.$search.attr('tabindex', -1);
+
+      self.$search.val('');
+      self.$search.trigger('blur');
+    });
+
+    container.on('focus', function () {
+      if (!container.isOpen()) {
+        self.$search.trigger('focus');
+      }
+    });
+
+    container.on('results:all', function (params) {
+      if (params.query.term == null || params.query.term === '') {
+        var showSearch = self.showSearch(params);
+
+        if (showSearch) {
+          self.$searchContainer.removeClass('select2-search--hide');
+        } else {
+          self.$searchContainer.addClass('select2-search--hide');
+        }
+      }
+    });
+  };
+
+  Search.prototype.handleSearch = function (evt) {
+    if (!this._keyUpPrevented) {
+      var input = this.$search.val();
+
+      this.trigger('query', {
+        term: input
+      });
+    }
+
+    this._keyUpPrevented = false;
+  };
+
+  Search.prototype.showSearch = function (_, params) {
+    return true;
+  };
+
+  return Search;
+});
+
+S2.define('select2/dropdown/hidePlaceholder',[
+
+], function () {
+  function HidePlaceholder (decorated, $element, options, dataAdapter) {
+    this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
+
+    decorated.call(this, $element, options, dataAdapter);
+  }
+
+  HidePlaceholder.prototype.append = function (decorated, data) {
+    data.results = this.removePlaceholder(data.results);
+
+    decorated.call(this, data);
+  };
+
+  HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) {
+    if (typeof placeholder === 'string') {
+      placeholder = {
+        id: '',
+        text: placeholder
+      };
+    }
+
+    return placeholder;
+  };
+
+  HidePlaceholder.prototype.removePlaceholder = function (_, data) {
+    var modifiedData = data.slice(0);
+
+    for (var d = data.length - 1; d >= 0; d--) {
+      var item = data[d];
+
+      if (this.placeholder.id === item.id) {
+        modifiedData.splice(d, 1);
+      }
+    }
+
+    return modifiedData;
+  };
+
+  return HidePlaceholder;
+});
+
+S2.define('select2/dropdown/infiniteScroll',[
+  'jquery'
+], function ($) {
+  function InfiniteScroll (decorated, $element, options, dataAdapter) {
+    this.lastParams = {};
+
+    decorated.call(this, $element, options, dataAdapter);
+
+    this.$loadingMore = this.createLoadingMore();
+    this.loading = false;
+  }
+
+  InfiniteScroll.prototype.append = function (decorated, data) {
+    this.$loadingMore.remove();
+    this.loading = false;
+
+    decorated.call(this, data);
+
+    if (this.showLoadingMore(data)) {
+      this.$results.append(this.$loadingMore);
+      this.loadMoreIfNeeded();
+    }
+  };
+
+  InfiniteScroll.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    container.on('query', function (params) {
+      self.lastParams = params;
+      self.loading = true;
+    });
+
+    container.on('query:append', function (params) {
+      self.lastParams = params;
+      self.loading = true;
+    });
+
+    this.$results.on('scroll', this.loadMoreIfNeeded.bind(this));
+  };
+
+  InfiniteScroll.prototype.loadMoreIfNeeded = function () {
+    var isLoadMoreVisible = $.contains(
+      document.documentElement,
+      this.$loadingMore[0]
+    );
+
+    if (this.loading || !isLoadMoreVisible) {
+      return;
+    }
+
+    var currentOffset = this.$results.offset().top +
+      this.$results.outerHeight(false);
+    var loadingMoreOffset = this.$loadingMore.offset().top +
+      this.$loadingMore.outerHeight(false);
+
+    if (currentOffset + 50 >= loadingMoreOffset) {
+      this.loadMore();
+    }
+  };
+
+  InfiniteScroll.prototype.loadMore = function () {
+    this.loading = true;
+
+    var params = $.extend({}, {page: 1}, this.lastParams);
+
+    params.page++;
+
+    this.trigger('query:append', params);
+  };
+
+  InfiniteScroll.prototype.showLoadingMore = function (_, data) {
+    return data.pagination && data.pagination.more;
+  };
+
+  InfiniteScroll.prototype.createLoadingMore = function () {
+    var $option = $(
+      '<li ' +
+      'class="select2-results__option select2-results__option--load-more"' +
+      'role="treeitem" aria-disabled="true"></li>'
+    );
+
+    var message = this.options.get('translations').get('loadingMore');
+
+    $option.html(message(this.lastParams));
+
+    return $option;
+  };
+
+  return InfiniteScroll;
+});
+
+S2.define('select2/dropdown/attachBody',[
+  'jquery',
+  '../utils'
+], function ($, Utils) {
+  function AttachBody (decorated, $element, options) {
+    this.$dropdownParent = options.get('dropdownParent') || $(document.body);
+
+    decorated.call(this, $element, options);
+  }
+
+  AttachBody.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    var setupResultsEvents = false;
+
+    decorated.call(this, container, $container);
+
+    container.on('open', function () {
+      self._showDropdown();
+      self._attachPositioningHandler(container);
+
+      if (!setupResultsEvents) {
+        setupResultsEvents = true;
+
+        container.on('results:all', function () {
+          self._positionDropdown();
+          self._resizeDropdown();
+        });
+
+        container.on('results:append', function () {
+          self._positionDropdown();
+          self._resizeDropdown();
+        });
+      }
+    });
+
+    container.on('close', function () {
+      self._hideDropdown();
+      self._detachPositioningHandler(container);
+    });
+
+    this.$dropdownContainer.on('mousedown', function (evt) {
+      evt.stopPropagation();
+    });
+  };
+
+  AttachBody.prototype.destroy = function (decorated) {
+    decorated.call(this);
+
+    this.$dropdownContainer.remove();
+  };
+
+  AttachBody.prototype.position = function (decorated, $dropdown, $container) {
+    // Clone all of the container classes
+    $dropdown.attr('class', $container.attr('class'));
+
+    $dropdown.removeClass('select2');
+    $dropdown.addClass('select2-container--open');
+
+    $dropdown.css({
+      position: 'absolute',
+      top: -999999
+    });
+
+    this.$container = $container;
+  };
+
+  AttachBody.prototype.render = function (decorated) {
+    var $container = $('<span></span>');
+
+    var $dropdown = decorated.call(this);
+    $container.append($dropdown);
+
+    this.$dropdownContainer = $container;
+
+    return $container;
+  };
+
+  AttachBody.prototype._hideDropdown = function (decorated) {
+    this.$dropdownContainer.detach();
+  };
+
+  AttachBody.prototype._attachPositioningHandler =
+      function (decorated, container) {
+    var self = this;
+
+    var scrollEvent = 'scroll.select2.' + container.id;
+    var resizeEvent = 'resize.select2.' + container.id;
+    var orientationEvent = 'orientationchange.select2.' + container.id;
+
+    var $watchers = this.$container.parents().filter(Utils.hasScroll);
+    $watchers.each(function () {
+      Utils.StoreData(this, 'select2-scroll-position', {
+        x: $(this).scrollLeft(),
+        y: $(this).scrollTop()
+      });
+    });
+
+    $watchers.on(scrollEvent, function (ev) {
+      var position = Utils.GetData(this, 'select2-scroll-position');
+      $(this).scrollTop(position.y);
+    });
+
+    $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
+      function (e) {
+      self._positionDropdown();
+      self._resizeDropdown();
+    });
+  };
+
+  AttachBody.prototype._detachPositioningHandler =
+      function (decorated, container) {
+    var scrollEvent = 'scroll.select2.' + container.id;
+    var resizeEvent = 'resize.select2.' + container.id;
+    var orientationEvent = 'orientationchange.select2.' + container.id;
+
+    var $watchers = this.$container.parents().filter(Utils.hasScroll);
+    $watchers.off(scrollEvent);
+
+    $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
+  };
+
+  AttachBody.prototype._positionDropdown = function () {
+    var $window = $(window);
+
+    var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
+    var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
+
+    var newDirection = null;
+
+    var offset = this.$container.offset();
+
+    offset.bottom = offset.top + this.$container.outerHeight(false);
+
+    var container = {
+      height: this.$container.outerHeight(false)
+    };
+
+    container.top = offset.top;
+    container.bottom = offset.top + container.height;
+
+    var dropdown = {
+      height: this.$dropdown.outerHeight(false)
+    };
+
+    var viewport = {
+      top: $window.scrollTop(),
+      bottom: $window.scrollTop() + $window.height()
+    };
+
+    var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
+    var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
+
+    var css = {
+      left: offset.left,
+      top: container.bottom
+    };
+
+    // Determine what the parent element is to use for calculating the offset
+    var $offsetParent = this.$dropdownParent;
+
+    // For statically positioned elements, we need to get the element
+    // that is determining the offset
+    if ($offsetParent.css('position') === 'static') {
+      $offsetParent = $offsetParent.offsetParent();
+    }
+
+    var parentOffset = $offsetParent.offset();
+
+    css.top -= parentOffset.top;
+    css.left -= parentOffset.left;
+
+    if (!isCurrentlyAbove && !isCurrentlyBelow) {
+      newDirection = 'below';
+    }
+
+    if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
+      newDirection = 'above';
+    } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
+      newDirection = 'below';
+    }
+
+    if (newDirection == 'above' ||
+      (isCurrentlyAbove && newDirection !== 'below')) {
+      css.top = container.top - parentOffset.top - dropdown.height;
+    }
+
+    if (newDirection != null) {
+      this.$dropdown
+        .removeClass('select2-dropdown--below select2-dropdown--above')
+        .addClass('select2-dropdown--' + newDirection);
+      this.$container
+        .removeClass('select2-container--below select2-container--above')
+        .addClass('select2-container--' + newDirection);
+    }
+
+    this.$dropdownContainer.css(css);
+  };
+
+  AttachBody.prototype._resizeDropdown = function () {
+    var css = {
+      width: this.$container.outerWidth(false) + 'px'
+    };
+
+    if (this.options.get('dropdownAutoWidth')) {
+      css.minWidth = css.width;
+      css.position = 'relative';
+      css.width = 'auto';
+    }
+
+    this.$dropdown.css(css);
+  };
+
+  AttachBody.prototype._showDropdown = function (decorated) {
+    this.$dropdownContainer.appendTo(this.$dropdownParent);
+
+    this._positionDropdown();
+    this._resizeDropdown();
+  };
+
+  return AttachBody;
+});
+
+S2.define('select2/dropdown/minimumResultsForSearch',[
+
+], function () {
+  function countResults (data) {
+    var count = 0;
+
+    for (var d = 0; d < data.length; d++) {
+      var item = data[d];
+
+      if (item.children) {
+        count += countResults(item.children);
+      } else {
+        count++;
+      }
+    }
+
+    return count;
+  }
+
+  function MinimumResultsForSearch (decorated, $element, options, dataAdapter) {
+    this.minimumResultsForSearch = options.get('minimumResultsForSearch');
+
+    if (this.minimumResultsForSearch < 0) {
+      this.minimumResultsForSearch = Infinity;
+    }
+
+    decorated.call(this, $element, options, dataAdapter);
+  }
+
+  MinimumResultsForSearch.prototype.showSearch = function (decorated, params) {
+    if (countResults(params.data.results) < this.minimumResultsForSearch) {
+      return false;
+    }
+
+    return decorated.call(this, params);
+  };
+
+  return MinimumResultsForSearch;
+});
+
+S2.define('select2/dropdown/selectOnClose',[
+  '../utils'
+], function (Utils) {
+  function SelectOnClose () { }
+
+  SelectOnClose.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    container.on('close', function (params) {
+      self._handleSelectOnClose(params);
+    });
+  };
+
+  SelectOnClose.prototype._handleSelectOnClose = function (_, params) {
+    if (params && params.originalSelect2Event != null) {
+      var event = params.originalSelect2Event;
+
+      // Don't select an item if the close event was triggered from a select or
+      // unselect event
+      if (event._type === 'select' || event._type === 'unselect') {
+        return;
+      }
+    }
+
+    var $highlightedResults = this.getHighlightedResults();
+
+    // Only select highlighted results
+    if ($highlightedResults.length < 1) {
+      return;
+    }
+
+    var data = Utils.GetData($highlightedResults[0], 'data');
+
+    // Don't re-select already selected resulte
+    if (
+      (data.element != null && data.element.selected) ||
+      (data.element == null && data.selected)
+    ) {
+      return;
+    }
+
+    this.trigger('select', {
+        data: data
+    });
+  };
+
+  return SelectOnClose;
+});
+
+S2.define('select2/dropdown/closeOnSelect',[
+
+], function () {
+  function CloseOnSelect () { }
+
+  CloseOnSelect.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    container.on('select', function (evt) {
+      self._selectTriggered(evt);
+    });
+
+    container.on('unselect', function (evt) {
+      self._selectTriggered(evt);
+    });
+  };
+
+  CloseOnSelect.prototype._selectTriggered = function (_, evt) {
+    var originalEvent = evt.originalEvent;
+
+    // Don't close if the control key is being held
+    if (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey)) {
+      return;
+    }
+
+    this.trigger('close', {
+      originalEvent: originalEvent,
+      originalSelect2Event: evt
+    });
+  };
+
+  return CloseOnSelect;
+});
+
+S2.define('select2/i18n/en',[],function () {
+  // English
+  return {
+    errorLoading: function () {
+      return 'The results could not be loaded.';
+    },
+    inputTooLong: function (args) {
+      var overChars = args.input.length - args.maximum;
+
+      var message = 'Please delete ' + overChars + ' character';
+
+      if (overChars != 1) {
+        message += 's';
+      }
+
+      return message;
+    },
+    inputTooShort: function (args) {
+      var remainingChars = args.minimum - args.input.length;
+
+      var message = 'Please enter ' + remainingChars + ' or more characters';
+
+      return message;
+    },
+    loadingMore: function () {
+      return 'Loading more results…';
+    },
+    maximumSelected: function (args) {
+      var message = 'You can only select ' + args.maximum + ' item';
+
+      if (args.maximum != 1) {
+        message += 's';
+      }
+
+      return message;
+    },
+    noResults: function () {
+      return 'No results found';
+    },
+    searching: function () {
+      return 'Searching…';
+    },
+    removeAllItems: function () {
+      return 'Remove all items';
+    }
+  };
+});
+
+S2.define('select2/defaults',[
+  'jquery',
+  'require',
+
+  './results',
+
+  './selection/single',
+  './selection/multiple',
+  './selection/placeholder',
+  './selection/allowClear',
+  './selection/search',
+  './selection/eventRelay',
+
+  './utils',
+  './translation',
+  './diacritics',
+
+  './data/select',
+  './data/array',
+  './data/ajax',
+  './data/tags',
+  './data/tokenizer',
+  './data/minimumInputLength',
+  './data/maximumInputLength',
+  './data/maximumSelectionLength',
+
+  './dropdown',
+  './dropdown/search',
+  './dropdown/hidePlaceholder',
+  './dropdown/infiniteScroll',
+  './dropdown/attachBody',
+  './dropdown/minimumResultsForSearch',
+  './dropdown/selectOnClose',
+  './dropdown/closeOnSelect',
+
+  './i18n/en'
+], function ($, require,
+
+             ResultsList,
+
+             SingleSelection, MultipleSelection, Placeholder, AllowClear,
+             SelectionSearch, EventRelay,
+
+             Utils, Translation, DIACRITICS,
+
+             SelectData, ArrayData, AjaxData, Tags, Tokenizer,
+             MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
+
+             Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
+             AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
+
+             EnglishTranslation) {
+  function Defaults () {
+    this.reset();
+  }
+
+  Defaults.prototype.apply = function (options) {
+    options = $.extend(true, {}, this.defaults, options);
+
+    if (options.dataAdapter == null) {
+      if (options.ajax != null) {
+        options.dataAdapter = AjaxData;
+      } else if (options.data != null) {
+        options.dataAdapter = ArrayData;
+      } else {
+        options.dataAdapter = SelectData;
+      }
+
+      if (options.minimumInputLength > 0) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          MinimumInputLength
+        );
+      }
+
+      if (options.maximumInputLength > 0) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          MaximumInputLength
+        );
+      }
+
+      if (options.maximumSelectionLength > 0) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          MaximumSelectionLength
+        );
+      }
+
+      if (options.tags) {
+        options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
+      }
+
+      if (options.tokenSeparators != null || options.tokenizer != null) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          Tokenizer
+        );
+      }
+
+      if (options.query != null) {
+        var Query = require(options.amdBase + 'compat/query');
+
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          Query
+        );
+      }
+
+      if (options.initSelection != null) {
+        var InitSelection = require(options.amdBase + 'compat/initSelection');
+
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          InitSelection
+        );
+      }
+    }
+
+    if (options.resultsAdapter == null) {
+      options.resultsAdapter = ResultsList;
+
+      if (options.ajax != null) {
+        options.resultsAdapter = Utils.Decorate(
+          options.resultsAdapter,
+          InfiniteScroll
+        );
+      }
+
+      if (options.placeholder != null) {
+        options.resultsAdapter = Utils.Decorate(
+          options.resultsAdapter,
+          HidePlaceholder
+        );
+      }
+
+      if (options.selectOnClose) {
+        options.resultsAdapter = Utils.Decorate(
+          options.resultsAdapter,
+          SelectOnClose
+        );
+      }
+    }
+
+    if (options.dropdownAdapter == null) {
+      if (options.multiple) {
+        options.dropdownAdapter = Dropdown;
+      } else {
+        var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
+
+        options.dropdownAdapter = SearchableDropdown;
+      }
+
+      if (options.minimumResultsForSearch !== 0) {
+        options.dropdownAdapter = Utils.Decorate(
+          options.dropdownAdapter,
+          MinimumResultsForSearch
+        );
+      }
+
+      if (options.closeOnSelect) {
+        options.dropdownAdapter = Utils.Decorate(
+          options.dropdownAdapter,
+          CloseOnSelect
+        );
+      }
+
+      if (
+        options.dropdownCssClass != null ||
+        options.dropdownCss != null ||
+        options.adaptDropdownCssClass != null
+      ) {
+        var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
+
+        options.dropdownAdapter = Utils.Decorate(
+          options.dropdownAdapter,
+          DropdownCSS
+        );
+      }
+
+      options.dropdownAdapter = Utils.Decorate(
+        options.dropdownAdapter,
+        AttachBody
+      );
+    }
+
+    if (options.selectionAdapter == null) {
+      if (options.multiple) {
+        options.selectionAdapter = MultipleSelection;
+      } else {
+        options.selectionAdapter = SingleSelection;
+      }
+
+      // Add the placeholder mixin if a placeholder was specified
+      if (options.placeholder != null) {
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          Placeholder
+        );
+      }
+
+      if (options.allowClear) {
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          AllowClear
+        );
+      }
+
+      if (options.multiple) {
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          SelectionSearch
+        );
+      }
+
+      if (
+        options.containerCssClass != null ||
+        options.containerCss != null ||
+        options.adaptContainerCssClass != null
+      ) {
+        var ContainerCSS = require(options.amdBase + 'compat/containerCss');
+
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          ContainerCSS
+        );
+      }
+
+      options.selectionAdapter = Utils.Decorate(
+        options.selectionAdapter,
+        EventRelay
+      );
+    }
+
+    if (typeof options.language === 'string') {
+      // Check if the language is specified with a region
+      if (options.language.indexOf('-') > 0) {
+        // Extract the region information if it is included
+        var languageParts = options.language.split('-');
+        var baseLanguage = languageParts[0];
+
+        options.language = [options.language, baseLanguage];
+      } else {
+        options.language = [options.language];
+      }
+    }
+
+    if ($.isArray(options.language)) {
+      var languages = new Translation();
+      options.language.push('en');
+
+      var languageNames = options.language;
+
+      for (var l = 0; l < languageNames.length; l++) {
+        var name = languageNames[l];
+        var language = {};
+
+        try {
+          // Try to load it with the original name
+          language = Translation.loadPath(name);
+        } catch (e) {
+          try {
+            // If we couldn't load it, check if it wasn't the full path
+            name = this.defaults.amdLanguageBase + name;
+            language = Translation.loadPath(name);
+          } catch (ex) {
+            // The translation could not be loaded at all. Sometimes this is
+            // because of a configuration problem, other times this can be
+            // because of how Select2 helps load all possible translation files.
+            if (options.debug && window.console && console.warn) {
+              console.warn(
+                'Select2: The language file for "' + name + '" could not be ' +
+                'automatically loaded. A fallback will be used instead.'
+              );
+            }
+
+            continue;
+          }
+        }
+
+        languages.extend(language);
+      }
+
+      options.translations = languages;
+    } else {
+      var baseTranslation = Translation.loadPath(
+        this.defaults.amdLanguageBase + 'en'
+      );
+      var customTranslation = new Translation(options.language);
+
+      customTranslation.extend(baseTranslation);
+
+      options.translations = customTranslation;
+    }
+
+    return options;
+  };
+
+  Defaults.prototype.reset = function () {
+    function stripDiacritics (text) {
+      // Used 'uni range + named function' from http://jsperf.com/diacritics/18
+      function match(a) {
+        return DIACRITICS[a] || a;
+      }
+
+      return text.replace(/[^\u0000-\u007E]/g, match);
+    }
+
+    function matcher (params, data) {
+      // Always return the object if there is nothing to compare
+      if ($.trim(params.term) === '') {
+        return data;
+      }
+
+      // Do a recursive check for options with children
+      if (data.children && data.children.length > 0) {
+        // Clone the data object if there are children
+        // This is required as we modify the object to remove any non-matches
+        var match = $.extend(true, {}, data);
+
+        // Check each child of the option
+        for (var c = data.children.length - 1; c >= 0; c--) {
+          var child = data.children[c];
+
+          var matches = matcher(params, child);
+
+          // If there wasn't a match, remove the object in the array
+          if (matches == null) {
+            match.children.splice(c, 1);
+          }
+        }
+
+        // If any children matched, return the new object
+        if (match.children.length > 0) {
+          return match;
+        }
+
+        // If there were no matching children, check just the plain object
+        return matcher(params, match);
+      }
+
+      var original = stripDiacritics(data.text).toUpperCase();
+      var term = stripDiacritics(params.term).toUpperCase();
+
+      // Check if the text contains the term
+      if (original.indexOf(term) > -1) {
+        return data;
+      }
+
+      // If it doesn't contain the term, don't return anything
+      return null;
+    }
+
+    this.defaults = {
+      amdBase: './',
+      amdLanguageBase: './i18n/',
+      closeOnSelect: true,
+      debug: false,
+      dropdownAutoWidth: false,
+      escapeMarkup: Utils.escapeMarkup,
+      language: EnglishTranslation,
+      matcher: matcher,
+      minimumInputLength: 0,
+      maximumInputLength: 0,
+      maximumSelectionLength: 0,
+      minimumResultsForSearch: 0,
+      selectOnClose: false,
+      scrollAfterSelect: false,
+      sorter: function (data) {
+        return data;
+      },
+      templateResult: function (result) {
+        return result.text;
+      },
+      templateSelection: function (selection) {
+        return selection.text;
+      },
+      theme: 'default',
+      width: 'resolve'
+    };
+  };
+
+  Defaults.prototype.set = function (key, value) {
+    var camelKey = $.camelCase(key);
+
+    var data = {};
+    data[camelKey] = value;
+
+    var convertedData = Utils._convertData(data);
+
+    $.extend(true, this.defaults, convertedData);
+  };
+
+  var defaults = new Defaults();
+
+  return defaults;
+});
+
+S2.define('select2/options',[
+  'require',
+  'jquery',
+  './defaults',
+  './utils'
+], function (require, $, Defaults, Utils) {
+  function Options (options, $element) {
+    this.options = options;
+
+    if ($element != null) {
+      this.fromElement($element);
+    }
+
+    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) {
+    var excludedData = ['select2'];
+
+    if (this.options.multiple == null) {
+      this.options.multiple = $e.prop('multiple');
+    }
+
+    if (this.options.disabled == null) {
+      this.options.disabled = $e.prop('disabled');
+    }
+
+    if (this.options.language == null) {
+      if ($e.prop('lang')) {
+        this.options.language = $e.prop('lang').toLowerCase();
+      } else if ($e.closest('[lang]').prop('lang')) {
+        this.options.language = $e.closest('[lang]').prop('lang');
+      }
+    }
+
+    if (this.options.dir == null) {
+      if ($e.prop('dir')) {
+        this.options.dir = $e.prop('dir');
+      } else if ($e.closest('[dir]').prop('dir')) {
+        this.options.dir = $e.closest('[dir]').prop('dir');
+      } else {
+        this.options.dir = 'ltr';
+      }
+    }
+
+    $e.prop('disabled', this.options.disabled);
+    $e.prop('multiple', this.options.multiple);
+
+    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 ' +
+          'use the `data-data` and `data-tags="true"` attributes and will be ' +
+          'removed in future versions of Select2.'
+        );
+      }
+
+      Utils.StoreData($e[0], 'data', Utils.GetData($e[0], 'select2Tags'));
+      Utils.StoreData($e[0], 'tags', true);
+    }
+
+    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 ' +
+          '`data-ajax--url` and support for the old attribute will be removed' +
+          ' in future versions of Select2.'
+        );
+      }
+
+      $e.attr('ajax--url', Utils.GetData($e[0], 'ajaxUrl'));
+      Utils.StoreData($e[0], 'ajax-Url', Utils.GetData($e[0], 'ajaxUrl'));
+    }
+
+    var dataset = {};
+
+    function upperCaseLetter(_, letter) {
+      return letter.toUpperCase();
+    }
+
+    // Pre-load all of the attributes which are prefixed with `data-`
+    for (var attr = 0; attr < $e[0].attributes.length; attr++) {
+      var attributeName = $e[0].attributes[attr].name;
+      var prefix = 'data-';
+
+      if (attributeName.substr(0, prefix.length) == prefix) {
+        // Get the contents of the attribute after `data-`
+        var dataName = attributeName.substring(prefix.length);
+
+        // Get the data contents from the consistent source
+        // This is more than likely the jQuery data helper
+        var dataValue = Utils.GetData($e[0], dataName);
+
+        // camelCase the attribute name to match the spec
+        var camelDataName = dataName.replace(/-([a-z])/g, upperCaseLetter);
+
+        // Store the data attribute contents into the dataset since
+        dataset[camelDataName] = dataValue;
+      }
+    }
+
+    // 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, dataset);
+    }
+
+    // Prefer our internal data cache if it exists
+    var data = $.extend(true, {}, Utils.GetData($e[0]), dataset);
+
+    data = Utils._convertData(data);
+
+    for (var key in data) {
+      if ($.inArray(key, excludedData) > -1) {
+        continue;
+      }
+
+      if ($.isPlainObject(this.options[key])) {
+        $.extend(this.options[key], data[key]);
+      } else {
+        this.options[key] = data[key];
+      }
+    }
+
+    return this;
+  };
+
+  Options.prototype.get = function (key) {
+    return this.options[key];
+  };
+
+  Options.prototype.set = function (key, val) {
+    this.options[key] = val;
+  };
+
+  return Options;
+});
+
+S2.define('select2/core',[
+  'jquery',
+  './options',
+  './utils',
+  './keys'
+], function ($, Options, Utils, KEYS) {
+  var Select2 = function ($element, options) {
+    if (Utils.GetData($element[0], 'select2') != null) {
+      Utils.GetData($element[0], 'select2').destroy();
+    }
+
+    this.$element = $element;
+
+    this.id = this._generateId($element);
+
+    options = options || {};
+
+    this.options = new Options(options, $element);
+
+    Select2.__super__.constructor.call(this);
+
+    // Set up the tabindex
+
+    var tabindex = $element.attr('tabindex') || 0;
+    Utils.StoreData($element[0], 'old-tabindex', tabindex);
+    $element.attr('tabindex', '-1');
+
+    // Set up containers and adapters
+
+    var DataAdapter = this.options.get('dataAdapter');
+    this.dataAdapter = new DataAdapter($element, this.options);
+
+    var $container = this.render();
+
+    this._placeContainer($container);
+
+    var SelectionAdapter = this.options.get('selectionAdapter');
+    this.selection = new SelectionAdapter($element, this.options);
+    this.$selection = this.selection.render();
+
+    this.selection.position(this.$selection, $container);
+
+    var DropdownAdapter = this.options.get('dropdownAdapter');
+    this.dropdown = new DropdownAdapter($element, this.options);
+    this.$dropdown = this.dropdown.render();
+
+    this.dropdown.position(this.$dropdown, $container);
+
+    var ResultsAdapter = this.options.get('resultsAdapter');
+    this.results = new ResultsAdapter($element, this.options, this.dataAdapter);
+    this.$results = this.results.render();
+
+    this.results.position(this.$results, this.$dropdown);
+
+    // Bind events
+
+    var self = this;
+
+    // Bind the container to all of the adapters
+    this._bindAdapters();
+
+    // Register any DOM event handlers
+    this._registerDomEvents();
+
+    // Register any internal event handlers
+    this._registerDataEvents();
+    this._registerSelectionEvents();
+    this._registerDropdownEvents();
+    this._registerResultsEvents();
+    this._registerEvents();
+
+    // Set the initial state
+    this.dataAdapter.current(function (initialData) {
+      self.trigger('selection:update', {
+        data: initialData
+      });
+    });
+
+    // Hide the original select
+    $element.addClass('select2-hidden-accessible');
+    $element.attr('aria-hidden', 'true');
+
+    // Synchronize any monitored attributes
+    this._syncAttributes();
+
+    Utils.StoreData($element[0], 'select2', this);
+
+    // Ensure backwards compatibility with $element.data('select2').
+    $element.data('select2', this);
+  };
+
+  Utils.Extend(Select2, Utils.Observable);
+
+  Select2.prototype._generateId = function ($element) {
+    var id = '';
+
+    if ($element.attr('id') != null) {
+      id = $element.attr('id');
+    } else if ($element.attr('name') != null) {
+      id = $element.attr('name') + '-' + Utils.generateChars(2);
+    } else {
+      id = Utils.generateChars(4);
+    }
+
+    id = id.replace(/(:|\.|\[|\]|,)/g, '');
+    id = 'select2-' + id;
+
+    return id;
+  };
+
+  Select2.prototype._placeContainer = function ($container) {
+    $container.insertAfter(this.$element);
+
+    var width = this._resolveWidth(this.$element, this.options.get('width'));
+
+    if (width != null) {
+      $container.css('width', width);
+    }
+  };
+
+  Select2.prototype._resolveWidth = function ($element, method) {
+    var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;
+
+    if (method == 'resolve') {
+      var styleWidth = this._resolveWidth($element, 'style');
+
+      if (styleWidth != null) {
+        return styleWidth;
+      }
+
+      return this._resolveWidth($element, 'element');
+    }
+
+    if (method == 'element') {
+      var elementWidth = $element.outerWidth(false);
+
+      if (elementWidth <= 0) {
+        return 'auto';
+      }
+
+      return elementWidth + 'px';
+    }
+
+    if (method == 'style') {
+      var style = $element.attr('style');
+
+      if (typeof(style) !== 'string') {
+        return null;
+      }
+
+      var attrs = style.split(';');
+
+      for (var i = 0, l = attrs.length; i < l; i = i + 1) {
+        var attr = attrs[i].replace(/\s/g, '');
+        var matches = attr.match(WIDTH);
+
+        if (matches !== null && matches.length >= 1) {
+          return matches[1];
+        }
+      }
+
+      return null;
+    }
+
+    if (method == 'computedstyle') {
+      var computedStyle = window.getComputedStyle($element[0]);
+
+      return computedStyle.width;
+    }
+
+    return method;
+  };
+
+  Select2.prototype._bindAdapters = function () {
+    this.dataAdapter.bind(this, this.$container);
+    this.selection.bind(this, this.$container);
+
+    this.dropdown.bind(this, this.$container);
+    this.results.bind(this, this.$container);
+  };
+
+  Select2.prototype._registerDomEvents = function () {
+    var self = this;
+
+    this.$element.on('change.select2', function () {
+      self.dataAdapter.current(function (data) {
+        self.trigger('selection:update', {
+          data: data
+        });
+      });
+    });
+
+    this.$element.on('focus.select2', function (evt) {
+      self.trigger('focus', evt);
+    });
+
+    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) {
+        $.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._syncA,
+        false
+      );
+      this.$element[0].addEventListener(
+        'DOMNodeInserted',
+        self._syncS,
+        false
+      );
+      this.$element[0].addEventListener(
+        'DOMNodeRemoved',
+        self._syncS,
+        false
+      );
+    }
+  };
+
+  Select2.prototype._registerDataEvents = function () {
+    var self = this;
+
+    this.dataAdapter.on('*', function (name, params) {
+      self.trigger(name, params);
+    });
+  };
+
+  Select2.prototype._registerSelectionEvents = function () {
+    var self = this;
+    var nonRelayEvents = ['toggle', 'focus'];
+
+    this.selection.on('toggle', function () {
+      self.toggleDropdown();
+    });
+
+    this.selection.on('focus', function (params) {
+      self.focus(params);
+    });
+
+    this.selection.on('*', function (name, params) {
+      if ($.inArray(name, nonRelayEvents) !== -1) {
+        return;
+      }
+
+      self.trigger(name, params);
+    });
+  };
+
+  Select2.prototype._registerDropdownEvents = function () {
+    var self = this;
+
+    this.dropdown.on('*', function (name, params) {
+      self.trigger(name, params);
+    });
+  };
+
+  Select2.prototype._registerResultsEvents = function () {
+    var self = this;
+
+    this.results.on('*', function (name, params) {
+      self.trigger(name, params);
+    });
+  };
+
+  Select2.prototype._registerEvents = function () {
+    var self = this;
+
+    this.on('open', function () {
+      self.$container.addClass('select2-container--open');
+    });
+
+    this.on('close', function () {
+      self.$container.removeClass('select2-container--open');
+    });
+
+    this.on('enable', function () {
+      self.$container.removeClass('select2-container--disabled');
+    });
+
+    this.on('disable', function () {
+      self.$container.addClass('select2-container--disabled');
+    });
+
+    this.on('blur', function () {
+      self.$container.removeClass('select2-container--focus');
+    });
+
+    this.on('query', function (params) {
+      if (!self.isOpen()) {
+        self.trigger('open', {});
+      }
+
+      this.dataAdapter.query(params, function (data) {
+        self.trigger('results:all', {
+          data: data,
+          query: params
+        });
+      });
+    });
+
+    this.on('query:append', function (params) {
+      this.dataAdapter.query(params, function (data) {
+        self.trigger('results:append', {
+          data: data,
+          query: params
+        });
+      });
+    });
+
+    this.on('keypress', function (evt) {
+      var key = evt.which;
+
+      if (self.isOpen()) {
+        if (key === KEYS.ESC || key === KEYS.TAB ||
+            (key === KEYS.UP && evt.altKey)) {
+          self.close();
+
+          evt.preventDefault();
+        } else if (key === KEYS.ENTER) {
+          self.trigger('results:select', {});
+
+          evt.preventDefault();
+        } else if ((key === KEYS.SPACE && evt.ctrlKey)) {
+          self.trigger('results:toggle', {});
+
+          evt.preventDefault();
+        } else if (key === KEYS.UP) {
+          self.trigger('results:previous', {});
+
+          evt.preventDefault();
+        } else if (key === KEYS.DOWN) {
+          self.trigger('results:next', {});
+
+          evt.preventDefault();
+        }
+      } else {
+        if (key === KEYS.ENTER || key === KEYS.SPACE ||
+            (key === KEYS.DOWN && evt.altKey)) {
+          self.open();
+
+          evt.preventDefault();
+        }
+      }
+    });
+  };
+
+  Select2.prototype._syncAttributes = function () {
+    this.options.set('disabled', this.$element.prop('disabled'));
+
+    if (this.options.get('disabled')) {
+      if (this.isOpen()) {
+        this.close();
+      }
+
+      this.trigger('disable', {});
+    } else {
+      this.trigger('enable', {});
+    }
+  };
+
+  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.
+   */
+  Select2.prototype.trigger = function (name, args) {
+    var actualTrigger = Select2.__super__.trigger;
+    var preTriggerMap = {
+      'open': 'opening',
+      'close': 'closing',
+      'select': 'selecting',
+      'unselect': 'unselecting',
+      'clear': 'clearing'
+    };
+
+    if (args === undefined) {
+      args = {};
+    }
+
+    if (name in preTriggerMap) {
+      var preTriggerName = preTriggerMap[name];
+      var preTriggerArgs = {
+        prevented: false,
+        name: name,
+        args: args
+      };
+
+      actualTrigger.call(this, preTriggerName, preTriggerArgs);
+
+      if (preTriggerArgs.prevented) {
+        args.prevented = true;
+
+        return;
+      }
+    }
+
+    actualTrigger.call(this, name, args);
+  };
+
+  Select2.prototype.toggleDropdown = function () {
+    if (this.options.get('disabled')) {
+      return;
+    }
+
+    if (this.isOpen()) {
+      this.close();
+    } else {
+      this.open();
+    }
+  };
+
+  Select2.prototype.open = function () {
+    if (this.isOpen()) {
+      return;
+    }
+
+    this.trigger('query', {});
+  };
+
+  Select2.prototype.close = function () {
+    if (!this.isOpen()) {
+      return;
+    }
+
+    this.trigger('close', {});
+  };
+
+  Select2.prototype.isOpen = function () {
+    return this.$container.hasClass('select2-container--open');
+  };
+
+  Select2.prototype.hasFocus = function () {
+    return this.$container.hasClass('select2-container--focus');
+  };
+
+  Select2.prototype.focus = function (data) {
+    // No need to re-trigger focus events if we are already focused
+    if (this.hasFocus()) {
+      return;
+    }
+
+    this.$container.addClass('select2-container--focus');
+    this.trigger('focus', {});
+  };
+
+  Select2.prototype.enable = function (args) {
+    if (this.options.get('debug') && window.console && console.warn) {
+      console.warn(
+        'Select2: The `select2("enable")` method has been deprecated and will' +
+        ' be removed in later Select2 versions. Use $element.prop("disabled")' +
+        ' instead.'
+      );
+    }
+
+    if (args == null || args.length === 0) {
+      args = [true];
+    }
+
+    var disabled = !args[0];
+
+    this.$element.prop('disabled', disabled);
+  };
+
+  Select2.prototype.data = function () {
+    if (this.options.get('debug') &&
+        arguments.length > 0 && window.console && console.warn) {
+      console.warn(
+        'Select2: Data can no longer be set using `select2("data")`. You ' +
+        'should consider setting the value instead using `$element.val()`.'
+      );
+    }
+
+    var data = [];
+
+    this.dataAdapter.current(function (currentData) {
+      data = currentData;
+    });
+
+    return data;
+  };
+
+  Select2.prototype.val = function (args) {
+    if (this.options.get('debug') && window.console && console.warn) {
+      console.warn(
+        'Select2: The `select2("val")` method has been deprecated and will be' +
+        ' removed in later Select2 versions. Use $element.val() instead.'
+      );
+    }
+
+    if (args == null || args.length === 0) {
+      return this.$element.val();
+    }
+
+    var newVal = args[0];
+
+    if ($.isArray(newVal)) {
+      newVal = $.map(newVal, function (obj) {
+        return obj.toString();
+      });
+    }
+
+    this.$element.val(newVal).trigger('change');
+  };
+
+  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._syncA = null;
+    this._syncS = null;
+
+    this.$element.off('.select2');
+    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();
+    this.selection.destroy();
+    this.dropdown.destroy();
+    this.results.destroy();
+
+    this.dataAdapter = null;
+    this.selection = null;
+    this.dropdown = null;
+    this.results = null;
+  };
+
+  Select2.prototype.render = function () {
+    var $container = $(
+      '<span class="select2 select2-container">' +
+        '<span class="selection"></span>' +
+        '<span class="dropdown-wrapper" aria-hidden="true"></span>' +
+      '</span>'
+    );
+
+    $container.attr('dir', this.options.get('dir'));
+
+    this.$container = $container;
+
+    this.$container.addClass('select2-container--' + this.options.get('theme'));
+
+    Utils.StoreData($container[0], 'element', this.$element);
+
+    return $container;
+  };
+
+  return Select2;
+});
+
+S2.define('select2/compat/utils',[
+  'jquery'
+], function ($) {
+  function syncCssClasses ($dest, $src, adapter) {
+    var classes, replacements = [], adapted;
+
+    classes = $.trim($dest.attr('class'));
+
+    if (classes) {
+      classes = '' + classes; // for IE which returns object
+
+      $(classes.split(/\s+/)).each(function () {
+        // Save all Select2 classes
+        if (this.indexOf('select2-') === 0) {
+          replacements.push(this);
+        }
+      });
+    }
+
+    classes = $.trim($src.attr('class'));
+
+    if (classes) {
+      classes = '' + classes; // for IE which returns object
+
+      $(classes.split(/\s+/)).each(function () {
+        // Only adapt non-Select2 classes
+        if (this.indexOf('select2-') !== 0) {
+          adapted = adapter(this);
+
+          if (adapted != null) {
+            replacements.push(adapted);
+          }
+        }
+      });
+    }
+
+    $dest.attr('class', replacements.join(' '));
+  }
+
+  return {
+    syncCssClasses: syncCssClasses
+  };
+});
+
+S2.define('select2/compat/containerCss',[
+  'jquery',
+  './utils'
+], function ($, CompatUtils) {
+  // No-op CSS adapter that discards all classes by default
+  function _containerAdapter (clazz) {
+    return null;
+  }
+
+  function ContainerCSS () { }
+
+  ContainerCSS.prototype.render = function (decorated) {
+    var $container = decorated.call(this);
+
+    var containerCssClass = this.options.get('containerCssClass') || '';
+
+    if ($.isFunction(containerCssClass)) {
+      containerCssClass = containerCssClass(this.$element);
+    }
+
+    var containerCssAdapter = this.options.get('adaptContainerCssClass');
+    containerCssAdapter = containerCssAdapter || _containerAdapter;
+
+    if (containerCssClass.indexOf(':all:') !== -1) {
+      containerCssClass = containerCssClass.replace(':all:', '');
+
+      var _cssAdapter = containerCssAdapter;
+
+      containerCssAdapter = function (clazz) {
+        var adapted = _cssAdapter(clazz);
+
+        if (adapted != null) {
+          // Append the old one along with the adapted one
+          return adapted + ' ' + clazz;
+        }
+
+        return clazz;
+      };
+    }
+
+    var containerCss = this.options.get('containerCss') || {};
+
+    if ($.isFunction(containerCss)) {
+      containerCss = containerCss(this.$element);
+    }
+
+    CompatUtils.syncCssClasses($container, this.$element, containerCssAdapter);
+
+    $container.css(containerCss);
+    $container.addClass(containerCssClass);
+
+    return $container;
+  };
+
+  return ContainerCSS;
+});
+
+S2.define('select2/compat/dropdownCss',[
+  'jquery',
+  './utils'
+], function ($, CompatUtils) {
+  // No-op CSS adapter that discards all classes by default
+  function _dropdownAdapter (clazz) {
+    return null;
+  }
+
+  function DropdownCSS () { }
+
+  DropdownCSS.prototype.render = function (decorated) {
+    var $dropdown = decorated.call(this);
+
+    var dropdownCssClass = this.options.get('dropdownCssClass') || '';
+
+    if ($.isFunction(dropdownCssClass)) {
+      dropdownCssClass = dropdownCssClass(this.$element);
+    }
+
+    var dropdownCssAdapter = this.options.get('adaptDropdownCssClass');
+    dropdownCssAdapter = dropdownCssAdapter || _dropdownAdapter;
+
+    if (dropdownCssClass.indexOf(':all:') !== -1) {
+      dropdownCssClass = dropdownCssClass.replace(':all:', '');
+
+      var _cssAdapter = dropdownCssAdapter;
+
+      dropdownCssAdapter = function (clazz) {
+        var adapted = _cssAdapter(clazz);
+
+        if (adapted != null) {
+          // Append the old one along with the adapted one
+          return adapted + ' ' + clazz;
+        }
+
+        return clazz;
+      };
+    }
+
+    var dropdownCss = this.options.get('dropdownCss') || {};
+
+    if ($.isFunction(dropdownCss)) {
+      dropdownCss = dropdownCss(this.$element);
+    }
+
+    CompatUtils.syncCssClasses($dropdown, this.$element, dropdownCssAdapter);
+
+    $dropdown.css(dropdownCss);
+    $dropdown.addClass(dropdownCssClass);
+
+    return $dropdown;
+  };
+
+  return DropdownCSS;
+});
+
+S2.define('select2/compat/initSelection',[
+  'jquery'
+], function ($) {
+  function InitSelection (decorated, $element, options) {
+    if (options.get('debug') && window.console && console.warn) {
+      console.warn(
+        'Select2: The `initSelection` option has been deprecated in favor' +
+        ' of a custom data adapter that overrides the `current` method. ' +
+        'This method is now called multiple times instead of a single ' +
+        'time when the instance is initialized. Support will be removed ' +
+        'for the `initSelection` option in future versions of Select2'
+      );
+    }
+
+    this.initSelection = options.get('initSelection');
+    this._isInitialized = false;
+
+    decorated.call(this, $element, options);
+  }
+
+  InitSelection.prototype.current = function (decorated, callback) {
+    var self = this;
+
+    if (this._isInitialized) {
+      decorated.call(this, callback);
+
+      return;
+    }
+
+    this.initSelection.call(null, this.$element, function (data) {
+      self._isInitialized = true;
+
+      if (!$.isArray(data)) {
+        data = [data];
+      }
+
+      callback(data);
+    });
+  };
+
+  return InitSelection;
+});
+
+S2.define('select2/compat/inputData',[
+  'jquery',
+  '../utils'
+], function ($, Utils) {
+  function InputData (decorated, $element, options) {
+    this._currentData = [];
+    this._valueSeparator = options.get('valueSeparator') || ',';
+
+    if ($element.prop('type') === 'hidden') {
+      if (options.get('debug') && console && console.warn) {
+        console.warn(
+          'Select2: Using a hidden input with Select2 is no longer ' +
+          'supported and may stop working in the future. It is recommended ' +
+          'to use a `<select>` element instead.'
+        );
+      }
+    }
+
+    decorated.call(this, $element, options);
+  }
+
+  InputData.prototype.current = function (_, callback) {
+    function getSelected (data, selectedIds) {
+      var selected = [];
+
+      if (data.selected || $.inArray(data.id, selectedIds) !== -1) {
+        data.selected = true;
+        selected.push(data);
+      } else {
+        data.selected = false;
+      }
+
+      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;
+        });
+      });
+
+      this.$element.val(data.id);
+      this.$element.trigger('change');
+    } else {
+      var value = this.$element.val();
+      value += this._valueSeparator + data.id;
+
+      this.$element.val(value);
+      this.$element.trigger('change');
+    }
+  };
+
+  InputData.prototype.unselect = function (_, data) {
+    var self = this;
+
+    data.selected = false;
+
+    this.current(function (allData) {
+      var values = [];
+
+      for (var d = 0; d < allData.length; d++) {
+        var item = allData[d];
+
+        if (data.id == item.id) {
+          continue;
+        }
+
+        values.push(item.id);
+      }
+
+      self.$element.val(values.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 Utils.GetData($option[0], 'data');
+    });
+
+    this._currentData.push.apply(this._currentData, options);
+  };
+
+  return InputData;
+});
+
+S2.define('select2/compat/matcher',[
+  'jquery'
+], function ($) {
+  function oldMatcher (matcher) {
+    function wrappedMatcher (params, data) {
+      var match = $.extend(true, {}, data);
+
+      if (params.term == null || $.trim(params.term) === '') {
+        return match;
+      }
+
+      if (data.children) {
+        for (var c = data.children.length - 1; c >= 0; c--) {
+          var child = data.children[c];
+
+          // Check if the child object matches
+          // The old matcher returned a boolean true or false
+          var doesMatch = matcher(params.term, child.text, child);
+
+          // If the child didn't match, pop it off
+          if (!doesMatch) {
+            match.children.splice(c, 1);
+          }
+        }
+
+        if (match.children.length > 0) {
+          return match;
+        }
+      }
+
+      if (matcher(params.term, data.text, data)) {
+        return match;
+      }
+
+      return null;
+    }
+
+    return wrappedMatcher;
+  }
+
+  return oldMatcher;
+});
+
+S2.define('select2/compat/query',[
+
+], function () {
+  function Query (decorated, $element, options) {
+    if (options.get('debug') && window.console && console.warn) {
+      console.warn(
+        'Select2: The `query` option has been deprecated in favor of a ' +
+        'custom data adapter that overrides the `query` method. Support ' +
+        'will be removed for the `query` option in future versions of ' +
+        'Select2.'
+      );
+    }
+
+    decorated.call(this, $element, options);
+  }
+
+  Query.prototype.query = function (_, params, callback) {
+    params.callback = callback;
+
+    var query = this.options.get('query');
+
+    query.call(null, params);
+  };
+
+  return Query;
+});
+
+S2.define('select2/dropdown/attachContainer',[
+
+], function () {
+  function AttachContainer (decorated, $element, options) {
+    decorated.call(this, $element, options);
+  }
+
+  AttachContainer.prototype.position =
+    function (decorated, $dropdown, $container) {
+    var $dropdownContainer = $container.find('.dropdown-wrapper');
+    $dropdownContainer.append($dropdown);
+
+    $dropdown.addClass('select2-dropdown--below');
+    $container.addClass('select2-container--below');
+  };
+
+  return AttachContainer;
+});
+
+S2.define('select2/dropdown/stopPropagation',[
+
+], function () {
+  function StopPropagation () { }
+
+  StopPropagation.prototype.bind = function (decorated, container, $container) {
+    decorated.call(this, container, $container);
+
+    var stoppedEvents = [
+    'blur',
+    'change',
+    'click',
+    'dblclick',
+    'focus',
+    'focusin',
+    'focusout',
+    'input',
+    'keydown',
+    'keyup',
+    'keypress',
+    'mousedown',
+    'mouseenter',
+    'mouseleave',
+    'mousemove',
+    'mouseover',
+    'mouseup',
+    'search',
+    'touchend',
+    'touchstart'
+    ];
+
+    this.$dropdown.on(stoppedEvents.join(' '), function (evt) {
+      evt.stopPropagation();
+    });
+  };
+
+  return StopPropagation;
+});
+
+S2.define('select2/selection/stopPropagation',[
+
+], function () {
+  function StopPropagation () { }
+
+  StopPropagation.prototype.bind = function (decorated, container, $container) {
+    decorated.call(this, container, $container);
+
+    var stoppedEvents = [
+      'blur',
+      'change',
+      'click',
+      'dblclick',
+      'focus',
+      'focusin',
+      'focusout',
+      'input',
+      'keydown',
+      'keyup',
+      'keypress',
+      'mousedown',
+      'mouseenter',
+      'mouseleave',
+      'mousemove',
+      'mouseover',
+      'mouseup',
+      'search',
+      'touchend',
+      'touchstart'
+    ];
+
+    this.$selection.on(stoppedEvents.join(' '), function (evt) {
+      evt.stopPropagation();
+    });
+  };
+
+  return StopPropagation;
+});
+
+/*!
+ * jQuery Mousewheel 3.1.13
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ */
+
+(function (factory) {
+    if ( typeof S2.define === 'function' && S2.define.amd ) {
+        // AMD. Register as an anonymous module.
+        S2.define('jquery-mousewheel',['jquery'], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS style for Browserify
+        module.exports = factory;
+    } else {
+        // Browser globals
+        factory(jQuery);
+    }
+}(function ($) {
+
+    var toFix  = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
+        toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
+                    ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
+        slice  = Array.prototype.slice,
+        nullLowestDeltaTimeout, lowestDelta;
+
+    if ( $.event.fixHooks ) {
+        for ( var i = toFix.length; i; ) {
+            $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
+        }
+    }
+
+    var special = $.event.special.mousewheel = {
+        version: '3.1.12',
+
+        setup: function() {
+            if ( this.addEventListener ) {
+                for ( var i = toBind.length; i; ) {
+                    this.addEventListener( toBind[--i], handler, false );
+                }
+            } else {
+                this.onmousewheel = handler;
+            }
+            // Store the line height and page height for this particular element
+            $.data(this, 'mousewheel-line-height', special.getLineHeight(this));
+            $.data(this, 'mousewheel-page-height', special.getPageHeight(this));
+        },
+
+        teardown: function() {
+            if ( this.removeEventListener ) {
+                for ( var i = toBind.length; i; ) {
+                    this.removeEventListener( toBind[--i], handler, false );
+                }
+            } else {
+                this.onmousewheel = null;
+            }
+            // Clean up the data we added to the element
+            $.removeData(this, 'mousewheel-line-height');
+            $.removeData(this, 'mousewheel-page-height');
+        },
+
+        getLineHeight: function(elem) {
+            var $elem = $(elem),
+                $parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
+            if (!$parent.length) {
+                $parent = $('body');
+            }
+            return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
+        },
+
+        getPageHeight: function(elem) {
+            return $(elem).height();
+        },
+
+        settings: {
+            adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
+            normalizeOffset: true  // calls getBoundingClientRect for each event
+        }
+    };
+
+    $.fn.extend({
+        mousewheel: function(fn) {
+            return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
+        },
+
+        unmousewheel: function(fn) {
+            return this.unbind('mousewheel', fn);
+        }
+    });
+
+
+    function handler(event) {
+        var orgEvent   = event || window.event,
+            args       = slice.call(arguments, 1),
+            delta      = 0,
+            deltaX     = 0,
+            deltaY     = 0,
+            absDelta   = 0,
+            offsetX    = 0,
+            offsetY    = 0;
+        event = $.event.fix(orgEvent);
+        event.type = 'mousewheel';
+
+        // Old school scrollwheel delta
+        if ( 'detail'      in orgEvent ) { deltaY = orgEvent.detail * -1;      }
+        if ( 'wheelDelta'  in orgEvent ) { deltaY = orgEvent.wheelDelta;       }
+        if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY;      }
+        if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }
+
+        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
+        if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
+            deltaX = deltaY * -1;
+            deltaY = 0;
+        }
+
+        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
+        delta = deltaY === 0 ? deltaX : deltaY;
+
+        // New school wheel delta (wheel event)
+        if ( 'deltaY' in orgEvent ) {
+            deltaY = orgEvent.deltaY * -1;
+            delta  = deltaY;
+        }
+        if ( 'deltaX' in orgEvent ) {
+            deltaX = orgEvent.deltaX;
+            if ( deltaY === 0 ) { delta  = deltaX * -1; }
+        }
+
+        // No change actually happened, no reason to go any further
+        if ( deltaY === 0 && deltaX === 0 ) { return; }
+
+        // Need to convert lines and pages to pixels if we aren't already in pixels
+        // There are three delta modes:
+        //   * deltaMode 0 is by pixels, nothing to do
+        //   * deltaMode 1 is by lines
+        //   * deltaMode 2 is by pages
+        if ( orgEvent.deltaMode === 1 ) {
+            var lineHeight = $.data(this, 'mousewheel-line-height');
+            delta  *= lineHeight;
+            deltaY *= lineHeight;
+            deltaX *= lineHeight;
+        } else if ( orgEvent.deltaMode === 2 ) {
+            var pageHeight = $.data(this, 'mousewheel-page-height');
+            delta  *= pageHeight;
+            deltaY *= pageHeight;
+            deltaX *= pageHeight;
+        }
+
+        // Store lowest absolute delta to normalize the delta values
+        absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
+
+        if ( !lowestDelta || absDelta < lowestDelta ) {
+            lowestDelta = absDelta;
+
+            // Adjust older deltas if necessary
+            if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
+                lowestDelta /= 40;
+            }
+        }
+
+        // Adjust older deltas if necessary
+        if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
+            // Divide all the things by 40!
+            delta  /= 40;
+            deltaX /= 40;
+            deltaY /= 40;
+        }
+
+        // Get a whole, normalized value for the deltas
+        delta  = Math[ delta  >= 1 ? 'floor' : 'ceil' ](delta  / lowestDelta);
+        deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
+        deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);
+
+        // Normalise offsetX and offsetY properties
+        if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
+            var boundingRect = this.getBoundingClientRect();
+            offsetX = event.clientX - boundingRect.left;
+            offsetY = event.clientY - boundingRect.top;
+        }
+
+        // Add information to the event object
+        event.deltaX = deltaX;
+        event.deltaY = deltaY;
+        event.deltaFactor = lowestDelta;
+        event.offsetX = offsetX;
+        event.offsetY = offsetY;
+        // Go ahead and set deltaMode to 0 since we converted to pixels
+        // Although this is a little odd since we overwrite the deltaX/Y
+        // properties with normalized deltas.
+        event.deltaMode = 0;
+
+        // Add event and delta to the front of the arguments
+        args.unshift(event, delta, deltaX, deltaY);
+
+        // Clearout lowestDelta after sometime to better
+        // handle multiple device types that give different
+        // a different lowestDelta
+        // Ex: trackpad = 3 and mouse wheel = 120
+        if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
+        nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
+
+        return ($.event.dispatch || $.event.handle).apply(this, args);
+    }
+
+    function nullLowestDelta() {
+        lowestDelta = null;
+    }
+
+    function shouldAdjustOldDeltas(orgEvent, absDelta) {
+        // If this is an older event and the delta is divisable by 120,
+        // then we are assuming that the browser is treating this as an
+        // older mouse wheel event and that we should divide the deltas
+        // by 40 to try and get a more usable deltaFactor.
+        // Side note, this actually impacts the reported scroll distance
+        // in older browsers and can cause scrolling to be slower than native.
+        // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
+        return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
+    }
+
+}));
+
+S2.define('jquery.select2',[
+  'jquery',
+  'jquery-mousewheel',
+
+  './select2/core',
+  './select2/defaults',
+  './select2/utils'
+], function ($, _, Select2, Defaults, Utils) {
+  if ($.fn.select2 == null) {
+    // All methods that should return the element
+    var thisMethods = ['open', 'close', 'destroy'];
+
+    $.fn.select2 = function (options) {
+      options = options || {};
+
+      if (typeof options === 'object') {
+        this.each(function () {
+          var instanceOptions = $.extend(true, {}, options);
+
+          var instance = new Select2($(this), instanceOptions);
+        });
+
+        return this;
+      } else if (typeof options === 'string') {
+        var ret;
+        var args = Array.prototype.slice.call(arguments, 1);
+
+        this.each(function () {
+          var instance = Utils.GetData(this, 'select2');
+
+          if (instance == null && window.console && console.error) {
+            console.error(
+              'The select2(\'' + options + '\') method was called on an ' +
+              'element that is not using Select2.'
+            );
+          }
+
+          ret = instance[options].apply(instance, args);
+        });
+
+        // Check if we should be returning `this`
+        if ($.inArray(options, thisMethods) > -1) {
+          return this;
+        }
+
+        return ret;
+      } else {
+        throw new Error('Invalid arguments for Select2: ' + options);
+      }
+    };
+  }
+
+  if ($.fn.select2.defaults == null) {
+    $.fn.select2.defaults = Defaults;
+  }
+
+  return Select2;
+});
+
+  // Return the AMD loader configuration so it can be used outside of this file
+  return {
+    define: S2.define,
+    require: S2.require
+  };
+}());
+
+  // Autoload the jQuery bindings
+  // We know that all of the modules exist above this, so we're safe
+  var select2 = S2.require('jquery.select2');
+
+  // Hold the AMD module references on the jQuery function that was just loaded
+  // This allows Select2 to use the internal loader outside of this file, such
+  // as in the language files.
+  jQuery.fn.select2.amd = S2;
+
+  // Return the Select2 instance for anyone who is importing it.
+  return select2;
+}));
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.min.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.min.js
new file mode 100644
index 0000000..1d0460d
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.full.min.js
@@ -0,0 +1,2 @@
+/*! Select2 4.0.8 | https://github.com/select2/select2/blob/master/LICENSE.md */
+!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(d){var e=function(){if(d&&d.fn&&d.fn.select2&&d.fn.select2.amd)var e=d.fn.select2.amd;var t,n,i,h,o,s,f,g,m,v,y,_,r,a,w,l;function b(e,t){return r.call(e,t)}function c(e,t){var n,i,r,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["* [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.js
new file mode 100644
index 0000000..d33caac
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/contrib/select2/select2-4.0.8.js
@@ -0,0 +1,5891 @@
+/*!
+ * Select2 4.0.8
+ * https://select2.github.io
+ *
+ * Released under the MIT license
+ * https://github.com/select2/select2/blob/master/LICENSE.md
+ */
+;(function (factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module.
+    define(['jquery'], factory);
+  } else if (typeof module === 'object' && module.exports) {
+    // Node/CommonJS
+    module.exports = function (root, jQuery) {
+      if (jQuery === undefined) {
+        // require('jQuery') returns a factory that requires window to
+        // build a jQuery instance, we normalize how we use modules
+        // that require this pattern but the window provided is a noop
+        // if it's defined (how jquery works)
+        if (typeof window !== 'undefined') {
+          jQuery = require('jquery');
+        }
+        else {
+          jQuery = require('jquery')(root);
+        }
+      }
+      factory(jQuery);
+      return jQuery;
+    };
+  } else {
+    // Browser globals
+    factory(jQuery);
+  }
+} (function (jQuery) {
+  // This is needed so we can catch the AMD loader configuration and use it
+  // The inner file should be wrapped (by `banner.start.js`) in a function that
+  // returns the AMD loader references.
+  var S2 =(function () {
+  // Restore the Select2 AMD loader so it can be used
+  // Needed mostly in the language files, where the loader is not inserted
+  if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
+    var S2 = jQuery.fn.select2.amd;
+  }
+var S2;(function () { if (!S2 || !S2.requirejs) {
+if (!S2) { S2 = {}; } else { require = S2; }
+/**
+ * @license almond 0.3.3 Copyright jQuery Foundation and other contributors.
+ * Released under MIT license, http://github.com/requirejs/almond/LICENSE
+ */
+//Going sloppy to avoid 'use strict' string cost, but strict practices should
+//be followed.
+/*global setTimeout: false */
+
+var requirejs, require, define;
+(function (undef) {
+    var main, req, makeMap, handlers,
+        defined = {},
+        waiting = {},
+        config = {},
+        defining = {},
+        hasOwn = Object.prototype.hasOwnProperty,
+        aps = [].slice,
+        jsSuffixRegExp = /\.js$/;
+
+    function hasProp(obj, prop) {
+        return hasOwn.call(obj, prop);
+    }
+
+    /**
+     * Given a relative module name, like ./something, normalize it to
+     * a real name that can be mapped to a path.
+     * @param {String} name the relative name
+     * @param {String} baseName a real name that the name arg is relative
+     * to.
+     * @returns {String} normalized name
+     */
+    function normalize(name, baseName) {
+        var nameParts, nameSegment, mapValue, foundMap, lastIndex,
+            foundI, foundStarMap, starI, i, j, part, normalizedBaseParts,
+            baseParts = baseName && baseName.split("/"),
+            map = config.map,
... 6528 lines suppressed ...


[myfaces-tobago] 02/09: Tobago-1999: suggest

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit a2a9e6ebe5ee25a00485dacca8e5ab4ca809aa0f
Author: Volker Weber <v....@inexso.de>
AuthorDate: Fri Jan 10 13:54:24 2020 +0100

    Tobago-1999: suggest
---
 .../component/AbstractUISelectManyBox.java         |  35 ++-
 .../component/AbstractUISelectOneChoice.java       |  35 ++-
 .../internal/component/AbstractUISuggest.java      | 101 +++++++++
 .../internal/component/UISelect2Component.java     |  20 ++
 .../internal/taglib/declaration/Select2.java       |   1 +
 .../internal/util/UISelect2ComponentUtil.java      | 130 +++++++++++
 .../tobago/model/AutoSuggestExtensionItem.java     |   4 +-
 .../tobago/model/UICustomItemContainer.java        |  27 +++
 .../tobago/renderkit/html/DataAttributes.java      |   4 +
 .../myfaces/tobago}/util/SelectItemUtils.java      |   3 +-
 .../tobago/example/demo/Select2Controller.java     | 198 +++++++++++++++-
 .../content/25-select/00-select2/select2.xhtml     |  54 ++++-
 .../standard/tag/SelectManyBoxRenderer.java        |  24 +-
 .../standard/tag/SelectOneChoiceRenderer.java      |  23 +-
 .../standard/standard/tag/SuggestRenderer.java     |  95 +++++---
 .../renderkit/html/util/HtmlRendererUtils.java     |  14 +-
 .../tobago/renderkit/util/SelectItemUtils.java     | 248 +--------------------
 .../standard/standard/script/tobago-select2.js     | 141 +++++++++++-
 18 files changed, 835 insertions(+), 322 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java
index 56c657d..f6ecc6b 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java
@@ -6,19 +6,42 @@
 package org.apache.myfaces.tobago.internal.component;
 
 import org.apache.myfaces.tobago.internal.component.AbstractUISelectOneChoice.Select2Keys;
+import org.apache.myfaces.tobago.internal.util.UISelect2ComponentUtil;
+import org.apache.myfaces.tobago.util.ComponentUtils;
 
+import javax.faces.component.StateHelper;
 import javax.faces.context.FacesContext;
-import java.util.ArrayList;
+import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
 
-public abstract class AbstractUISelectManyBox extends AbstractUISelectMany {
+public abstract class AbstractUISelectManyBox extends AbstractUISelectMany implements UISelect2Component {
+
+
+
+  public AbstractUISuggest getSuggest() {
+    return ComponentUtils.findDescendant(this, AbstractUISuggest.class);
+  }
 
   @Override
-  protected void validateValue(FacesContext context, Object convertedValue) {
-    if (!isAllowCustom()) {
-      super.validateValue(context, convertedValue);
-    }
+  protected void validateValue(FacesContext facesContext, Object convertedValue) {
+  UISelect2ComponentUtil.ensureCustomItemsContainer(facesContext, this);
+    super.validateValue(facesContext, UISelect2ComponentUtil.ensureCustomValues(facesContext, this, convertedValue));
+  }
+
+  @Override
+  public Object getValue() {
+    return UISelect2ComponentUtil.ensureCustomValues(FacesContext.getCurrentInstance(), this, super.getValue());
+  }
+
+  @Override
+  public void encodeChildren(FacesContext facesContext) throws IOException {
+    UISelect2ComponentUtil.ensureCustomItemsContainer(facesContext, this);
+    super.encodeChildren(facesContext);
+  }
+
+  public StateHelper getComponentStateHelper() {
+    return getStateHelper();
   }
 
   public boolean isAllowClear() {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
index 8555b95..771d2e0 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
@@ -19,7 +19,19 @@
 
 package org.apache.myfaces.tobago.internal.component;
 
-public abstract class AbstractUISelectOneChoice extends AbstractUISelectOneBase {
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.myfaces.tobago.internal.util.UISelect2ComponentUtil;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+
+import javax.faces.component.StateHelper;
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+
+public abstract class AbstractUISelectOneChoice extends AbstractUISelectOneBase implements UISelect2Component {
+
+  private static final Logger LOG = LoggerFactory.getLogger(AbstractUISelectOneChoice.class);
 
   enum Select2Keys {
     allowClear,
@@ -36,9 +48,30 @@ public abstract class AbstractUISelectOneChoice extends AbstractUISelectOneBase
     tokenSeparators
   }
 
+  public AbstractUISuggest getSuggest() {
+    return ComponentUtils.findDescendant(this, AbstractUISuggest.class);
+  }
+
+  @Override
+  protected void validateValue(FacesContext facesContext, Object value) {
+    UISelect2ComponentUtil.ensureCustomItemsContainer(facesContext, this);
+    super.validateValue(facesContext, UISelect2ComponentUtil.ensureCustomValue(facesContext, this, value));
+  }
 
+  @Override
+  public Object getValue() {
+    return UISelect2ComponentUtil.ensureCustomValue(FacesContext.getCurrentInstance(), this, super.getValue());
+  }
 
+  @Override
+  public void encodeChildren(FacesContext facesContext) throws IOException {
+    UISelect2ComponentUtil.ensureCustomItemsContainer(facesContext, this);
+    super.encodeChildren(facesContext);
+  }
 
+  public StateHelper getComponentStateHelper() {
+    return getStateHelper();
+  }
 
   public boolean isAllowClear() {
     Boolean allowClear = (Boolean) getStateHelper().eval(Select2Keys.allowClear);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISuggest.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISuggest.java
index 548dda8..2197709 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISuggest.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISuggest.java
@@ -19,26 +19,127 @@
 
 package org.apache.myfaces.tobago.internal.component;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import org.apache.myfaces.tobago.component.InputSuggest2;
 import org.apache.myfaces.tobago.component.SupportsMarkup;
+import org.apache.myfaces.tobago.component.UISelectItems;
+import org.apache.myfaces.tobago.component.UISelectManyBox;
+import org.apache.myfaces.tobago.component.UISelectOneChoice;
+import org.apache.myfaces.tobago.model.AutoSuggestItem;
+import org.apache.myfaces.tobago.model.AutoSuggestItems;
 import org.apache.myfaces.tobago.model.SuggestFilter;
 
+import javax.el.MethodExpression;
 import javax.faces.component.UIComponentBase;
+import javax.faces.component.UIInput;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.model.SelectItem;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 public abstract class AbstractUISuggest
     extends UIComponentBase implements SupportsMarkup, InputSuggest2 {
 
+  private static final Logger LOG = LoggerFactory.getLogger(AbstractUISuggest.class);
+
   public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.Suggest";
   public static final String COMPONENT_FAMILY = "org.apache.myfaces.tobago.Suggest";
 
+  private static final String ITEM_MAP_KEY
+      = "org.apache.myfaces.tobago.internal.component.AbstractUISuggest.ITEM_MAP_KEY";
+
   @Override
   public String getFamily() {
     return COMPONENT_FAMILY;
   }
 
+  public AutoSuggestItems getSuggestItems(FacesContext facesContext) {
+    final MethodExpression suggestMethodExpression = getSuggestMethodExpression();
+
+    UIInput in;
+    if (isSelect2()) {
+      String search = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
+          .get(getClientId());
+      LOG.trace(" search = \"{}\"", search);
+      in = new UIInput();
+      in.setSubmittedValue(search);
+    } else {
+      in = (UIInput) getParent();
+    }
+    AutoSuggestItems suggestItems
+        = createAutoSuggestItems(suggestMethodExpression.invoke(facesContext.getELContext(), new Object[]{in}));
+    if (!suggestItems.getItems().isEmpty()) {
+      //noinspection unchecked
+      Map<String, AutoSuggestItem> itemMap = (Map<String, AutoSuggestItem>) getStateHelper().get(ITEM_MAP_KEY);
+      if (itemMap == null) {
+        itemMap = new HashMap<String, AutoSuggestItem>();
+        getStateHelper().put(ITEM_MAP_KEY, itemMap);
+      }
+      for (AutoSuggestItem item : suggestItems.getItems()) {
+        if (!itemMap.containsKey(item.getValue())) {
+          itemMap.put(item.getValue(), item);
+        }
+      }
+    }
+    return suggestItems;
+  }
+
+  public boolean isSelect2() {
+    return getParent() instanceof UISelectManyBox || getParent() instanceof UISelectOneChoice;
+  }
+
+  private AutoSuggestItems createAutoSuggestItems(final Object object) {
+    if (object instanceof AutoSuggestItems) {
+      return (AutoSuggestItems) object;
+    }
+    final AutoSuggestItems autoSuggestItems = new AutoSuggestItems();
+    if (object instanceof List && !((List) object).isEmpty()) {
+      if (((List) object).get(0) instanceof AutoSuggestItem) {
+        //noinspection unchecked
+        autoSuggestItems.setItems((List<AutoSuggestItem>) object);
+      } else if (((List) object).get(0) instanceof String) {
+        final List<AutoSuggestItem> items = new ArrayList<AutoSuggestItem>(((List) object).size());
+        for (int i = 0; i < ((List) object).size(); i++) {
+          final AutoSuggestItem item = new AutoSuggestItem();
+          item.setLabel((String) ((List) object).get(i));
+          item.setValue((String) ((List) object).get(i));
+          items.add(item);
+        }
+        autoSuggestItems.setItems(items);
+      } else {
+        throw new ClassCastException("Can't create AutoSuggestItems from '" + object + "'. "
+            + "Elements needs to be " + String.class.getName() + " or " + AutoSuggestItem.class.getName());
+      }
+    } else {
+      autoSuggestItems.setItems(Collections.<AutoSuggestItem>emptyList());
+    }
+    return autoSuggestItems;
+  }
+
+  public SelectItem getSelectItem(FacesContext facesContext, Object value, Converter converter) {
+    //noinspection unchecked
+    Map<String, AutoSuggestItem> itemMap = (Map<String, AutoSuggestItem>) getStateHelper().get(ITEM_MAP_KEY);
+    if (itemMap != null) {
+      String id = converter != null ? converter.getAsString(facesContext, getParent(), value) : (String) value;
+      AutoSuggestItem autoSuggestItem = itemMap.get(id);
+      if (autoSuggestItem != null) {
+        return new SelectItem(value, autoSuggestItem.getLabel());
+      }
+    }
+    return null;
+  }
+
   public abstract void setDelay(Integer delay);
 
   public abstract void setMinimumCharacters(Integer minimumCharacters);
 
   public abstract void setFilter(SuggestFilter filter);
+
+  public abstract Integer getMinimumCharacters();
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java
new file mode 100644
index 0000000..f7d456a
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java
@@ -0,0 +1,20 @@
+package org.apache.myfaces.tobago.internal.component;
+
+import javax.faces.component.StateHelper;
+import javax.faces.component.UIComponent;
+import javax.faces.convert.Converter;
+import java.util.HashSet;
+import java.util.Map;
+
+public interface UISelect2Component {
+
+  AbstractUISuggest getSuggest();
+
+  boolean isAllowCustom();
+
+  Converter getConverter();
+
+  StateHelper getComponentStateHelper();
+
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java
index b5506f6..e682f5d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java
@@ -2,6 +2,7 @@ package org.apache.myfaces.tobago.internal.taglib.declaration;
 
 import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
 import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
 
 public interface Select2 {
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
new file mode 100644
index 0000000..539f559
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
@@ -0,0 +1,130 @@
+package org.apache.myfaces.tobago.internal.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.myfaces.tobago.internal.component.AbstractUISelectManyBox;
+import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
+import org.apache.myfaces.tobago.internal.component.UISelect2Component;
+import org.apache.myfaces.tobago.model.SelectItem;
+import org.apache.myfaces.tobago.model.UICustomItemContainer;
+import org.apache.myfaces.tobago.util.SelectItemUtils;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public class UISelect2ComponentUtil {
+
+  private static final Logger LOG = LoggerFactory.getLogger(UISelect2ComponentUtil.class);
+
+  private static final String VALID_CUSTOMITEM_MAP
+      = "org.apache.myfaces.tobago.internal.util.UISelect2ComponentUtil.valid_CustomItem_Map";
+
+  private UISelect2ComponentUtil() {
+  }
+
+  public static Object ensureCustomValue(FacesContext facesContext, UISelect2Component component, Object value) {
+    if (value != null && !"".equals(value)) {
+      AbstractUISuggest suggest = component.getSuggest();
+      if (component.isAllowCustom() || suggest != null) {
+        ensureCustomValue(facesContext, component, value, suggest);
+      }
+    }
+    return value;
+  }
+
+  public static Object ensureCustomValues(FacesContext facesContext, UISelect2Component component, Object values) {
+    if (values != null) {
+      AbstractUISuggest suggest = component.getSuggest();
+      if (component.isAllowCustom() || suggest != null) {
+        if (values instanceof Object[]) {
+          for (Object value : (Object[]) values) {
+            ensureCustomValue(facesContext, component, value, suggest);
+          }
+        } else if (values instanceof Collection) {
+          for (Object value : (Collection) values) {
+            ensureCustomValue(facesContext, component, value, suggest);
+          }
+        }
+      }
+    }
+    return values;
+  }
+
+  public static void ensureCustomValue(
+      FacesContext facesContext, UISelect2Component component, Object value, AbstractUISuggest suggest) {
+
+    if (!isInItemlist(facesContext, component, value)) {
+      javax.faces.model.SelectItem item = null;
+      if (suggest != null) {
+        item = suggest.getSelectItem(facesContext, value, component.getConverter());
+      }
+      if (item == null && component.isAllowCustom()) {
+        item = new SelectItem(value);
+      }
+      if (item != null) {
+        LOG.trace("ADD item = \"{}\"", item.getValue());
+        getValidCustomItemMap(component).add(item);
+      }
+    }
+  }
+
+  private static boolean isInItemlist(FacesContext facesContext, UISelect2Component component, Object value) {
+    LOG.trace("check for value = \"{}\"", value);
+    Iterable<javax.faces.model.SelectItem> items
+        = SelectItemUtils.getItemIterator(facesContext, (UIComponent) component);
+    for (javax.faces.model.SelectItem item : items) {
+      LOG.trace("check item value = \"{}\"", item.getValue());
+      if (item.getValue() != null && item.getValue().equals(value)) {
+        LOG.trace("TRUE");
+        return true;
+      }
+    }
+    LOG.trace("false");
+    return false;
+  }
+
+
+  public static Set<javax.faces.model.SelectItem> getValidCustomItemMap(UISelect2Component component) {
+    Object o = component.getComponentStateHelper().get(VALID_CUSTOMITEM_MAP);
+    //noinspection unchecked
+    HashSet<javax.faces.model.SelectItem> set = (HashSet<javax.faces.model.SelectItem>) o;
+    if (set == null) {
+      set = new HashSet<javax.faces.model.SelectItem>();
+      component.getComponentStateHelper().put(VALID_CUSTOMITEM_MAP, set);
+    }
+    return set;
+  }
+
+  public static void ensureCustomItemsContainer(FacesContext facesContext, UISelect2Component component) {
+    Set<javax.faces.model.SelectItem> validCustomItemMap = getValidCustomItemMap(component);
+    boolean done = false;
+    for (UIComponent child : ((UIComponent) component).getChildren()) {
+      if (child instanceof UICustomItemContainer) {
+        ((UICustomItemContainer) child).setValue(validCustomItemMap);
+        done = true;
+        break;
+      }
+    }
+    if (!done) {
+      ((UIComponent) component).getChildren().add(new UICustomItemContainer(validCustomItemMap));
+    }
+
+    if (component instanceof AbstractUISelectManyBox) {
+      // remove duplicates
+      Set<javax.faces.model.SelectItem> set = new HashSet<javax.faces.model.SelectItem>(validCustomItemMap);
+      validCustomItemMap.clear();
+      for (javax.faces.model.SelectItem selectItem : set) {
+        LOG.trace("cleanup check = \"{}\"", selectItem.getValue());
+        if (!isInItemlist(facesContext, component, selectItem.getValue())) {
+          LOG.trace("cleanup readd = \"{}\"", selectItem.getValue());
+          validCustomItemMap.add(selectItem);
+        }
+      }
+    }
+  }
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/AutoSuggestExtensionItem.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/AutoSuggestExtensionItem.java
index 3b22356..c6ef5a8 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/AutoSuggestExtensionItem.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/AutoSuggestExtensionItem.java
@@ -20,7 +20,9 @@
 package org.apache.myfaces.tobago.model;
 
 
-public class AutoSuggestExtensionItem {
+import java.io.Serializable;
+
+public class AutoSuggestExtensionItem implements Serializable {
   private String id;
   private String value;
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/UICustomItemContainer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/UICustomItemContainer.java
new file mode 100644
index 0000000..813d193
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/UICustomItemContainer.java
@@ -0,0 +1,27 @@
+package org.apache.myfaces.tobago.model;
+
+import javax.faces.component.UISelectItems;
+import java.util.Set;
+
+public class UICustomItemContainer extends UISelectItems {
+
+  public UICustomItemContainer() {
+  }
+
+  public UICustomItemContainer(Set<javax.faces.model.SelectItem> validCustomItems) {
+    setValue(validCustomItems);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return getClass().hashCode();
+  }
+
+}
\ No newline at end of file
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
index e4a48e4..061b686 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
@@ -228,12 +228,16 @@ public final class DataAttributes {
 
   public static final String SUGGEST_DELAY = "data-tobago-suggest-delay";
 
+  public static final String SUGGEST_ID = "data-tobago-suggest-id";
+
   public static final String SUGGEST_ITEM_FOR = "data-tobago-suggest-item-for";
 
   public static final String SUGGEST_MAX_ITEMS = "data-tobago-suggest-max-items";
 
   public static final String SUGGEST_MIN_CHARS = "data-tobago-suggest-min-chars";
 
+  public static final String SUGGEST_RESPONSE_DATA = "data-tobago-suggest-response-data";
+
   public static final String SUGGEST_TOTAL_COUNT = "data-tobago-suggest-total-count";
 
   public static final String SUGGEST_UPDATE = "data-tobago-suggest-update";
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/SelectItemUtils.java
similarity index 99%
copy from tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
copy to tobago-core/src/main/java/org/apache/myfaces/tobago/util/SelectItemUtils.java
index cdd4ea9..874a18a 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/SelectItemUtils.java
@@ -16,8 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-package org.apache.myfaces.tobago.renderkit.util;
+package org.apache.myfaces.tobago.util;
 
 import org.apache.myfaces.tobago.component.Attributes;
 import org.apache.myfaces.tobago.component.SupportsMarkup;
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
index 1e81080..6be927c 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
@@ -1,27 +1,207 @@
 package org.apache.myfaces.tobago.example.demo;
 
-import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.myfaces.tobago.model.AutoSuggestItem;
+import org.apache.myfaces.tobago.model.SelectItem;
 
 import javax.enterprise.context.SessionScoped;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIInput;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.convert.ConverterException;
 import javax.inject.Named;
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 
 @SessionScoped
 @Named
 public class Select2Controller implements Serializable {
 
-  private String one2;
+  private static final Logger LOG = LoggerFactory.getLogger(Select2Controller.class);
+
+  private LocaleConverter localeConverter = new LocaleConverter();
+
+  private String one2Value;
+
+  private String one3Value;
+
+  private Locale one4Locale;
+
+  private List<Locale> many8Locales;
+
+  private List<String> many7Countries;
+
+  private List<SelectItem> items;
+
+  public Select2Controller() {
+    items = new ArrayList<SelectItem>();
+    items.add(new SelectItem("letter", "Letter"));
+    items.add(new SelectItem("phone", "Phone"));
+    items.add(new SelectItem("eMail", "eMail"));
+    items.add(new SelectItem("fax", "Fax"));
+  }
+
+  public List<SelectItem> getItems() {
+    return items;
+  }
+
+  public List<SelectItem> getLocaleItems() {
+    return items;
+  }
+
+  public Converter getLocaleConverter() {
+    return localeConverter;
+  }
+
+  public List<AutoSuggestItem> suggestLocale(final UIInput input) {
+    return localeConverter.getSuggestLocale((String) input.getSubmittedValue());
+  }
+
+  public String getOne2Value() {
+    return one2Value;
+  }
+
+  public void setOne2Value(String one2Value) {
+    this.one2Value = one2Value;
+  }
+
+  public String getOne3Value() {
+    return one3Value;
+  }
+
+  public void setOne3Value(String one3Value) {
+    this.one3Value = one3Value;
+  }
+
+  public Locale getOne4Locale() {
+    LOG.warn("get one4Locale = \"{}\"", one4Locale);
+    return one4Locale;
+  }
+
+  public void setOne4Locale(Locale one4Locale) {
+    LOG.warn("set one4Locale = \"{}\"", one4Locale);
+    this.one4Locale = one4Locale;
+  }
+
+  public SelectItem[] getOne4LocaleItem() {
+    if (one4Locale != null) {
+      Locale displayLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
+      return new SelectItem[] {new SelectItem(one4Locale, one4Locale.getDisplayName(displayLocale))};
+    } else {
+      return new SelectItem[0];
+    }
+  }
+
+
+  public List<String> getMany7Countries() {
+    return many7Countries;
+  }
+
+  public void setMany7Countries(List<String> many7Countries) {
+    this.many7Countries = many7Countries;
+  }
+
+  public List<SelectItem> getMany7CountryItems() {
+    if (many7Countries != null && !many7Countries.isEmpty()) {
+      List<SelectItem> items = new ArrayList<SelectItem>();
+      for (String locale : many7Countries) {
+        items.add(new SelectItem(locale));
+      }
+      return items;
+    } else {
+      return Collections.emptyList();
+    }
+  }
 
-  public String getOne2() {
-    return one2;
+  public List<Locale> getMany8Locales() {
+    LOG.warn("get many8Locales = \"{}\"", many8Locales);
+    return many8Locales;
   }
 
-  public void setOne2(String one2) {
-    this.one2 = one2;
+  public void setMany8Locales(List<Locale> many8Locales) {
+    LOG.warn("set many8Locales = \"{}\"", many8Locales);
+    this.many8Locales = many8Locales;
   }
 
-  public String getCaseSensitiveMatcher() {
-    return "{\"matcher\": \"Tobago.Select2.caseSensitiveMatcher\"}";
-//    JsonUtils.encode()
+  public List<SelectItem> getMany8LocaleItems() {
+    if (many8Locales != null && !many8Locales.isEmpty()) {
+      Locale displayLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
+      List<SelectItem> items = new ArrayList<SelectItem>();
+      for (Locale locale : many8Locales) {
+        items.add(new SelectItem(locale, locale.getDisplayName(displayLocale)));
+      }
+      return items;
+    } else {
+      return Collections.emptyList();
+    }
+  }
+
+  private class LocaleConverter implements Converter {
+
+    private Map<String, Locale> localeMap;
+
+    public LocaleConverter() {
+      localeMap = new HashMap<String, Locale>();
+      for (final Locale locale : Locale.getAvailableLocales()) {
+        localeMap.put(locale.toString(), locale);
+      }
+    }
+
+    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
+        throws ConverterException {
+      if (value != null) {
+        Locale locale = localeMap.get(value);
+        if (locale != null) {
+          return locale;
+        } else {
+          Locale displayLocale = facesContext.getViewRoot().getLocale();
+          for (Locale mapLocale : localeMap.values()) {
+            if (mapLocale.getDisplayName(displayLocale).equals(value)) {
+              return mapLocale;
+            }
+          }
+          throw new ConverterException("Could not convert \"" + value + "\" to Locale");
+        }
+      } else {
+        return null;
+      }
+    }
+
+    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object o) throws ConverterException {
+      if (o != null) {
+        return ((Locale) o).toString();
+      } else {
+        return null;
+      }
+    }
+
+    public List<AutoSuggestItem> getSuggestLocale(String prefix) {
+      LOG.info("Creating items for prefix: '" + prefix + "'");
+      Locale displayLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
+      final List<AutoSuggestItem> result = new ArrayList<AutoSuggestItem>();
+      for (final Locale locale : localeMap.values()) {
+        if (StringUtils.startsWithIgnoreCase(locale.getDisplayName(displayLocale), prefix)) {
+          AutoSuggestItem suggestItem = new AutoSuggestItem();
+          suggestItem.setValue(locale.toString());
+          suggestItem.setLabel(locale.getDisplayName(displayLocale));
+          result.add(suggestItem);
+        }
+        if (result.size() > 100) { // this value should be greater than the value of the input control
+          break;
+        }
+      }
+      return result;
+    }
+
+
   }
 }
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
index 4e1edeb..d5febd3 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
@@ -67,7 +67,7 @@
         <tc:selectOneChoice id="one_2"
                             minimumResultsForSearch="10"
                             placeholder="Please select message type"
-                            value="#{select2Controller.one2}">
+                            value="#{select2Controller.one2Value}">
 
           <tc:selectItem itemLabel="Letter" itemValue="letter"/>
           <tc:selectItem itemLabel="Phone" itemValue="phone"/>
@@ -80,12 +80,18 @@
                             allowCustom="true"
                             placeholder="Custom input allowed"
                             allowClear="true"
-                            value="">
+                            value="#{select2Controller.one3Value}">
 
-          <tc:selectItem itemLabel="Letter" itemValue="letter"/>
-          <tc:selectItem itemLabel="Phone" itemValue="phone"/>
-          <tc:selectItem itemLabel="eMail" itemValue="eMail"/>
-          <tc:selectItem itemLabel="Fax" itemValue="fax"/>
+          <tc:selectItems value="#{select2Controller.items}"/>
+        </tc:selectOneChoice>
+
+        <tc:label value="Suggest locale" for="one_4"/>
+        <tc:selectOneChoice id="one_4"
+                            placeholder="Please select a locale"
+                            converter="#{select2Controller.localeConverter}"
+                            value="#{select2Controller.one4Locale}">
+          <tc:selectItems value="#{select2Controller.one4LocaleItem}"/>
+          <tc:suggest suggestMethod="#{select2Controller.suggestLocale}" minimumCharacters="2" />
         </tc:selectOneChoice>
 
       </tc:panel>
@@ -189,6 +195,42 @@
         </tc:selectManyBox>
 
       </tc:panel>
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="Suggest countries" for="many_7"/>
+        <tc:selectManyBox id="many_7"
+                          placeholder="Select countries"
+                          allowCustom="true"
+                          tokenSeparators=","
+                          value="#{select2Controller.many7Countries}">
+
+          <tc:selectItems value="#{select2Controller.many7CountryItems}"/>
+          <tc:suggest suggestMethod="#{countries.prefixed}" minimumCharacters="2" />
+          <tc:dataAttribute name="tobago-select2-extend" value='{"resultsAdapter": "suppressMessages"}'/>
+
+
+        </tc:selectManyBox>
+
+      </tc:panel>
+
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="Suggest locales" for="many_8"/>
+        <tc:selectManyBox id="many_8"
+                          placeholder="Select locales"
+                          converter="#{select2Controller.localeConverter}"
+                          value="#{select2Controller.many8Locales}">
+          <tc:selectItems value="#{select2Controller.many8LocaleItems}"/>
+          <tc:suggest suggestMethod="#{select2Controller.suggestLocale}" minimumCharacters="2" />
+        </tc:selectManyBox>
+
+      </tc:panel>
     </tc:panel>
 
 
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
index 4010fa6..3c202e8 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
@@ -19,13 +19,17 @@
 
 package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import org.apache.myfaces.tobago.component.UISelectManyBox;
-import org.apache.myfaces.tobago.component.UISelectManyListbox;
+import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
 import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
 import org.apache.myfaces.tobago.layout.Measure;
 import org.apache.myfaces.tobago.renderkit.SelectManyRendererBase;
 import org.apache.myfaces.tobago.renderkit.css.Classes;
 import org.apache.myfaces.tobago.renderkit.css.Style;
+import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
 import org.apache.myfaces.tobago.renderkit.html.Select2Options;
@@ -39,9 +43,6 @@ import javax.faces.context.FacesContext;
 import javax.faces.model.SelectItem;
 import java.io.IOException;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 public class SelectManyBoxRenderer extends SelectManyRendererBase {
 
   private static final Logger LOG = LoggerFactory.getLogger(SelectManyBoxRenderer.class);
@@ -70,15 +71,21 @@ public class SelectManyBoxRenderer extends SelectManyRendererBase {
 
     final UISelectManyBox select = (UISelectManyBox) component;
     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
+    AbstractUISuggest suggest = (AbstractUISuggest) select.getSuggest();
 
     final String id = select.getClientId(facesContext);
     final Iterable<SelectItem> items = SelectItemUtils.getItemIterator(facesContext, select);
     final boolean readonly = select.isReadonly();
-    final boolean disabled = (!items.iterator().hasNext() && !select.isAllowCustom())
-        || select.isDisabled() || readonly;
+    final boolean disabled = !(suggest != null || select.isAllowCustom() || items.iterator().hasNext())
+        || select.isDisabled()
+        || select.isReadonly();
     final Style style = new Style(facesContext, select);
 
-    ComponentUtils.putDataAttribute(select, "tobago-select2", Select2Options.of(select).toJson());
+    Select2Options select2Options = Select2Options.of(select);
+    if (suggest != null) {
+      select2Options.setMinimumInputLength(suggest.getMinimumCharacters());
+    }
+    ComponentUtils.putDataAttribute(select, "tobago-select2", select2Options.toJson());
 
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, select);
     writer.startElement(HtmlElements.DIV, select);
@@ -93,6 +100,9 @@ public class SelectManyBoxRenderer extends SelectManyRendererBase {
     writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
     writer.writeAttribute(HtmlAttributes.READONLY, readonly);
     writer.writeAttribute(HtmlAttributes.REQUIRED, select.isRequired());
+    if (suggest != null) {
+      writer.writeAttribute(DataAttributes.SUGGEST_ID, suggest.getClientId(facesContext), false);
+    }
     HtmlRendererUtils.renderFocus(id, select.isFocus(), ComponentUtils.isError(select), facesContext, writer);
     final Integer tabIndex = select.getTabIndex();
     if (tabIndex != null) {
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
index 8c4d5d8..c1963b3 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
@@ -19,23 +19,24 @@
 
 package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import org.apache.myfaces.tobago.component.UISelectOneChoice;
-import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
+import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
 import org.apache.myfaces.tobago.layout.Measure;
-import org.apache.myfaces.tobago.renderkit.html.Select2Options;
 import org.apache.myfaces.tobago.renderkit.HtmlUtils;
 import org.apache.myfaces.tobago.renderkit.SelectOneRendererBase;
 import org.apache.myfaces.tobago.renderkit.css.Classes;
 import org.apache.myfaces.tobago.renderkit.css.Style;
+import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
-import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
+import org.apache.myfaces.tobago.renderkit.html.Select2Options;
 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.renderkit.util.SelectItemUtils;
 import org.apache.myfaces.tobago.util.ComponentUtils;
 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
@@ -68,14 +69,20 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
 
     final UISelectOneChoice select = (UISelectOneChoice) component;
     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
+    AbstractUISuggest suggest = (AbstractUISuggest) select.getSuggest();
 
     final String id = select.getClientId(facesContext);
     final Iterable<SelectItem> items = SelectItemUtils.getItemIterator(facesContext, select);
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, select);
-    final boolean disabled = !items.iterator().hasNext() || select.isDisabled() || select.isReadonly();
+    final boolean disabled = !(suggest != null || select.isAllowCustom() || items.iterator().hasNext())
+        || select.isDisabled()
+        || select.isReadonly();
     final Style style = new Style(facesContext, select);
     final Select2Options select2Options = Select2Options.of(select);
     final boolean renderAsSelect2 = select2Options.hasAnyOption();
+    if (suggest != null) {
+      select2Options.setMinimumInputLength(suggest.getMinimumCharacters());
+    }
 
     if (renderAsSelect2) {
       String json = select2Options.toJson();
@@ -107,6 +114,10 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
     if (onchange != null) {
       writer.writeAttribute(HtmlAttributes.ONCHANGE, onchange, true);
     }
+    if (suggest != null) {
+      writer.writeAttribute(DataAttributes.SUGGEST_ID, suggest.getClientId(facesContext), false);
+    }
+
     HtmlRendererUtils.renderCommandFacet(select, facesContext , writer);
     HtmlRendererUtils.renderFocus(id, select.isFocus(), ComponentUtils.isError(select), facesContext, writer);
     if (renderAsSelect2 && select.getPlaceholder() != null && select.getPlaceholder().length() > 0) {
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
index ebc0c16..230655f 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
@@ -19,7 +19,12 @@
 
 package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import org.apache.myfaces.tobago.component.UIIn;
+import org.apache.myfaces.tobago.component.UISelectManyBox;
+import org.apache.myfaces.tobago.component.UISelectOneChoice;
 import org.apache.myfaces.tobago.component.UISuggest;
 import org.apache.myfaces.tobago.context.ResourceManagerUtils;
 import org.apache.myfaces.tobago.model.AutoSuggestItem;
@@ -29,11 +34,13 @@ import org.apache.myfaces.tobago.renderkit.css.Classes;
 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
 
 import javax.el.MethodExpression;
 import javax.faces.component.UIComponent;
+import javax.faces.component.UIInput;
 import javax.faces.context.FacesContext;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -42,22 +49,33 @@ import java.util.List;
 
 public class SuggestRenderer extends InputRendererBase {
 
+  private static final Logger LOG = LoggerFactory.getLogger(SuggestRenderer.class);
+
   @Override
   public void encodeEnd(final FacesContext facesContext, final UIComponent component) throws IOException {
 
     final UISuggest suggest = (UISuggest) component;
     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
-    final String id  = suggest.getClientId(facesContext);
-    final UIIn in = (UIIn) suggest.getParent();
-    String inClientId = in.getClientId(facesContext);
-    final MethodExpression suggestMethodExpression = suggest.getSuggestMethodExpression();
-    final AutoSuggestItems items
-        = createAutoSuggestItems(suggestMethodExpression.invoke(facesContext.getELContext(), new Object[]{in}));
     // todo: declare unused/unsupported stuff deprecated
 
+    if (suggest.getParent() instanceof UIIn) {
+      writeInSuggestElements(facesContext, writer, suggest);
+    } else if (suggest.isSelect2()) {
+      writeSelect2SuggestElements(facesContext, writer, suggest);
+    } else {
+    }
+  }
+
+  public void writeInSuggestElements(FacesContext facesContext, TobagoResponseWriter writer, UISuggest suggest)
+      throws IOException {
+    final UIIn in = (UIIn) suggest.getParent();
+    final String inClientId = in.getClientId(facesContext);
+
+    final AutoSuggestItems items = suggest.getSuggestItems(facesContext);
+
     writer.startElement(HtmlElements.DIV, null);
     writer.writeClassAttribute(Classes.create(suggest));
-    writer.writeIdAttribute(id);
+    writer.writeIdAttribute(suggest.getClientId(facesContext));
     writer.writeAttribute(DataAttributes.FOR, inClientId, false);
     writer.writeAttribute(DataAttributes.SUGGEST_MIN_CHARS, suggest.getMinimumCharacters());
     writer.writeAttribute(DataAttributes.SUGGEST_DELAY, suggest.getDelay());
@@ -107,32 +125,47 @@ public class SuggestRenderer extends InputRendererBase {
     writer.endElement(HtmlElements.DIV);
   }
 
-  private AutoSuggestItems createAutoSuggestItems(final Object object) {
-    if (object instanceof AutoSuggestItems) {
-      return (AutoSuggestItems) object;
+  public void writeSelect2SuggestElements(
+      FacesContext facesContext, TobagoResponseWriter writer, UISuggest suggest)
+      throws IOException {
+    final UIComponent selectManyBox = (UIComponent) suggest.getParent();
+    final String inClientId = selectManyBox.getClientId(facesContext);
+    String id = suggest.getClientId(facesContext);
+
+    final AutoSuggestItems items = suggest.getSuggestItems(facesContext);
+
+    writer.startElement(HtmlElements.INPUT, null);
+    writer.writeClassAttribute(Classes.create(suggest));
+    writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
+    writer.writeNameAttribute(id);
+    writer.writeIdAttribute(id);
+    writer.writeAttribute(DataAttributes.FOR, inClientId, false);
+    writer.writeAttribute(DataAttributes.SUGGEST_MIN_CHARS, suggest.getMinimumCharacters());
+    writer.writeAttribute(DataAttributes.SUGGEST_DELAY, suggest.getDelay());
+    writer.writeAttribute(DataAttributes.SUGGEST_MAX_ITEMS, suggest.getMaximumItems());
+    writer.writeAttribute(DataAttributes.SUGGEST_UPDATE, Boolean.toString(suggest.isUpdate()), false);
+    int totalCount = suggest.getTotalCount();
+    if (totalCount == -1) {
+      totalCount = items.getItems().size();
     }
-    final AutoSuggestItems autoSuggestItems = new AutoSuggestItems();
-    if (object instanceof List && !((List) object).isEmpty()) {
-      if (((List) object).get(0) instanceof AutoSuggestItem) {
-        //noinspection unchecked
-        autoSuggestItems.setItems((List<AutoSuggestItem>) object);
-      } else if (((List) object).get(0) instanceof String) {
-        final List<AutoSuggestItem> items = new ArrayList<AutoSuggestItem>(((List) object).size());
-        for (int i = 0; i < ((List) object).size(); i++) {
-          final AutoSuggestItem item = new AutoSuggestItem();
-          item.setLabel((String) ((List) object).get(i));
-          item.setValue((String) ((List) object).get(i));
-          items.add(item);
-        }
-        autoSuggestItems.setItems(items);
-      } else {
-        throw new ClassCastException("Can't create AutoSuggestItems from '" + object + "'. "
-            + "Elements needs to be " + String.class.getName() + " or " + AutoSuggestItem.class.getName());
-      }
-    } else {
-      autoSuggestItems.setItems(Collections.<AutoSuggestItem>emptyList());
+    writer.writeAttribute(DataAttributes.SUGGEST_TOTAL_COUNT, totalCount);
+
+    StringBuilder builder = new StringBuilder("{\"results\":[");
+    for (final AutoSuggestItem item : items.getItems()) {
+      builder.append("{");
+
+      builder.append("\"id\":\"").append(item.getValue()).append("\",");
+      builder.append("\"text\":\"").append(item.getLabel()).append("\"");
+
+      builder.append("},");
+    }
+    if (builder.toString().endsWith(",")) {
+      builder.setLength(builder.length() - 1);
     }
-    return autoSuggestItems;
+    builder.append("]}");
+    writer.writeAttribute(DataAttributes.SUGGEST_RESPONSE_DATA, builder.toString(), true);
+
+    writer.endElement(HtmlElements.INPUT);
   }
 
 }
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
index 74d835d..38025c3 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
@@ -949,11 +949,15 @@ public final class HtmlRendererUtils {
     for (final Map.Entry<Object, Object> entry : dataAttributes.entrySet()) {
       final Object mapKey = entry.getKey();
       final String name = mapKey instanceof ValueExpression
-          ? ((ValueExpression) mapKey).getValue(elContext).toString() : mapKey.toString();
-      final Object mapValue = entry.getValue();
-      final String value = mapValue instanceof ValueExpression
-          ? ((ValueExpression) mapValue).getValue(elContext).toString() : mapValue.toString();
-      writer.writeAttribute("data-" + name, value, true);
+          ? ((ValueExpression) mapKey).getValue(elContext).toString()
+          : mapKey.toString();
+      Object mapValue = entry.getValue();
+      mapValue = mapValue instanceof ValueExpression ? ((ValueExpression) mapValue).getValue(elContext) : mapValue;
+      if (mapValue == null) {
+        throw new NullPointerException("Data attribute value of " + name + " is null on component "
+            + component.getClass().getName() + " [" + component.getClientId(context) + "]");
+      }
+      writer.writeAttribute("data-" + name, mapValue.toString(), true);
     }
   }
 }
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
index cdd4ea9..fd3fd46 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
@@ -19,24 +19,10 @@
 
 package org.apache.myfaces.tobago.renderkit.util;
 
-import org.apache.myfaces.tobago.component.Attributes;
-import org.apache.myfaces.tobago.component.SupportsMarkup;
-import org.apache.myfaces.tobago.context.Markup;
-
-import javax.el.ValueExpression;
 import javax.faces.component.UIComponent;
-import javax.faces.component.UISelectItem;
-import javax.faces.component.UISelectItems;
 import javax.faces.context.FacesContext;
 import javax.faces.model.SelectItem;
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
 
 /**
  * Based on code from MyFaces core.
@@ -47,21 +33,7 @@ public class SelectItemUtils {
    * Creates a list of SelectItems to use for rendering.
    */
   public static Iterable<SelectItem> getItemIterator(final FacesContext facesContext, final UIComponent selector) {
-    if (selector.getChildCount() == 0) {
-      return Collections.emptyList();
-    } else {
-      return new Iterable<SelectItem>() {
-
-        private SelectItemsIterator iterator;
-
-        public Iterator<SelectItem> iterator() {
-          if (iterator == null) {
-            iterator = new SelectItemsIterator(facesContext, selector);
-          }
-          return iterator;
-        }
-      };
-    }
+    return org.apache.myfaces.tobago.util.SelectItemUtils.getItemIterator(facesContext, selector);
   }
 
   /**
@@ -70,222 +42,6 @@ public class SelectItemUtils {
    * Otherwise please use {@link #getItemIterator(javax.faces.context.FacesContext, javax.faces.component.UIComponent)}
    */
   public static List<SelectItem> getItemList(final FacesContext facesContext, final UIComponent selector) {
-    if (selector.getChildCount() == 0) {
-      return Collections.emptyList();
-    } else {
-      final Iterable<SelectItem> iterator = getItemIterator(facesContext, selector);
-      final List<SelectItem> result = new ArrayList<SelectItem>();
-      for (SelectItem selectItem : iterator) {
-        result.add(selectItem);
-      }
-      return result;
-    }
-  }
-
-  private static class SelectItemsIterator implements Iterator<SelectItem> {
-
-    private final FacesContext facesContext;
-    private final Iterator<UIComponent> children;
-    private Iterator<?> nestedItems;
-    private SelectItem nextItem;
-    private UISelectItems currentUISelectItems;
-
-    private SelectItemsIterator(final FacesContext facesContext, final UIComponent selector) {
-      this.children = selector.getChildren().iterator();
-      this.facesContext = facesContext;
-    }
-
-    @SuppressWarnings("unchecked")
-    public boolean hasNext() {
-      if (nextItem != null) {
-        return true;
-      }
-      if (nestedItems != null) {
-        if (nestedItems.hasNext()) {
-          return true;
-        }
-        nestedItems = null;
-      }
-
-      UIComponent child = null;
-      while (children.hasNext()) {
-        final UIComponent c = children.next();
-        // When there is other components nested that does
-        // not extends from UISelectItem or UISelectItems
-        // the behavior for this iterator is just skip this
-        // element(s) until an element that extends from these
-        // classes are found. If there is no more elements
-        // that conform this condition, just return false.
-        if (c instanceof UISelectItem || c instanceof UISelectItems) {
-          child = c;
-          break;
-        }
-      }
-      if (child == null) {
-        return false;
-      }
-
-      if (child instanceof UISelectItem) {
-        final UISelectItem uiSelectItem = (UISelectItem) child;
-        Object item = uiSelectItem.getValue();
-        if (item == null) {
-          // no value attribute --> create the SelectItem out of the other attributes
-          final Object itemValue = uiSelectItem.getItemValue();
-          String label = uiSelectItem.getItemLabel();
-          final String description = uiSelectItem.getItemDescription();
-          final boolean disabled = uiSelectItem.isItemDisabled();
-//          boolean escape = uiSelectItem.isItemEscaped();
-//          boolean noSelectionOption = uiSelectItem.isNoSelectionOption();
-          if (label == null) {
-            label = itemValue.toString();
-          }
-          String image = null;
-          Markup markup = null;
-          if (uiSelectItem instanceof org.apache.myfaces.tobago.component.UISelectItem) {
-            org.apache.myfaces.tobago.component.UISelectItem tobagoSelectItem
-                = (org.apache.myfaces.tobago.component.UISelectItem) uiSelectItem;
-            image = tobagoSelectItem.getItemImage();
-            markup = tobagoSelectItem.getCurrentMarkup();
-          }
-          item = new org.apache.myfaces.tobago.model.SelectItem(itemValue, label, description, disabled, image, markup);
-        } else if (!(item instanceof SelectItem)) {
-          ValueExpression expression = uiSelectItem.getValueExpression("value");
-          throw new IllegalArgumentException("ValueExpression '"
-              + (expression == null ? null : expression.getExpressionString()) + "' of UISelectItem : "
-              + child + " does not reference an Object of type SelectItem");
-        }
-        nextItem = (SelectItem) item;
-        return true;
-      } else { // UISelectItems
-        currentUISelectItems = ((UISelectItems) child);
-        final Object value = currentUISelectItems.getValue();
-
-        if (value instanceof SelectItem) {
-          nextItem = (SelectItem) value;
-          return true;
-        } else if (value != null && value.getClass().isArray()) {
-          // value is any kind of array (primitive or non-primitive)
-          // --> we have to use class Array to get the values
-          final int length = Array.getLength(value);
-          final Collection<Object> items = new ArrayList<Object>(length);
-          for (int i = 0; i < length; i++) {
-            items.add(Array.get(value, i));
-          }
-          nestedItems = items.iterator();
-          return hasNext();
-        } else if (value instanceof Iterable) {
-          // value is Iterable --> Collection, DataModel,...
-          nestedItems = ((Iterable<?>) value).iterator();
-          return hasNext();
-        } else if (value instanceof Map) {
-          final Map<Object, Object> map = ((Map<Object, Object>) value);
-          final Collection<SelectItem> items = new ArrayList<SelectItem>(map.size());
-          for (Map.Entry<Object, Object> entry : map.entrySet()) {
-            items.add(new org.apache.myfaces.tobago.model.SelectItem(entry.getValue(), entry.getKey().toString()));
-          }
-          nestedItems = items.iterator();
-          return hasNext();
-        }
-      }
-      return false;
-    }
-
-    public SelectItem next() {
-      if (!hasNext()) {
-        throw new NoSuchElementException();
-      }
-      if (nextItem != null) {
-        final SelectItem value = nextItem;
-        nextItem = null;
-        return value;
-      }
-      if (nestedItems != null) {
-        Object item = nestedItems.next();
-
-        if (!(item instanceof SelectItem)) {
-          // check new params of SelectItems (since 2.0): itemValue, itemLabel, itemDescription,...
-          // Note that according to the spec UISelectItems does not provide Getter and Setter
-          // methods for this values, so we have to use the attribute map
-          final Map<String, Object> attributeMap = currentUISelectItems.getAttributes();
-
-          // write the current item into the request map under the key listed in var, if available
-          boolean wroteRequestMapVarValue = false;
-          Object oldRequestMapVarValue = null;
-          final String var = (String) attributeMap.get(Attributes.VAR);
-          if (var != null && !"".equals(var)) {
-            // save the current value of the key listed in var from the request map
-            oldRequestMapVarValue = facesContext.getExternalContext().getRequestMap().put(var, item);
-            wroteRequestMapVarValue = true;
-          }
-
-          // check the itemValue attribute
-          Object itemValue = attributeMap.get(Attributes.ITEM_VALUE);
-          if (itemValue == null) {
-            // the itemValue attribute was not provided
-            // --> use the current item as the itemValue
-            itemValue = item;
-          }
-
-          // Spec: When iterating over the select items, toString()
-          // must be called on the string rendered attribute values
-          Object itemLabel = attributeMap.get(Attributes.ITEM_LABEL);
-          if (itemLabel == null) {
-            itemLabel = itemValue.toString();
-          } else {
-            itemLabel = itemLabel.toString();
-          }
-          Object itemDescription = attributeMap.get(Attributes.ITEM_DESCRIPTION);
-          if (itemDescription != null) {
-            itemDescription = itemDescription.toString();
-          }
-          final Boolean itemDisabled = getBooleanAttribute(currentUISelectItems, Attributes.ITEM_DISABLED, false);
-          final String itemImage = (String) attributeMap.get(Attributes.ITEM_IMAGE);
-          final Markup markup;
-          if (currentUISelectItems instanceof SupportsMarkup) {
-            markup = ((SupportsMarkup) currentUISelectItems).getCurrentMarkup();
-          } else {
-            markup = Markup.NULL;
-          }
-// TBD: should this be possible?
-//        Boolean itemLabelEscaped = getBooleanAttribute(currentUISelectItems, ITEM_LABEL_ESCAPED_PROP, true);
-// TBD ?
-//        Object noSelectionValue = attributeMap.get(NO_SELECTION_VALUE_PROP);
-          item = new org.apache.myfaces.tobago.model.SelectItem(
-              itemValue, (String) itemLabel, (String) itemDescription, itemDisabled, itemImage, markup);
-
-          // remove the value with the key from var from the request map, if previously written
-          if (wroteRequestMapVarValue) {
-            // If there was a previous value stored with the key from var in the request map, restore it
-            if (oldRequestMapVarValue != null) {
-              facesContext.getExternalContext().getRequestMap().put(var, oldRequestMapVarValue);
-            } else {
-              facesContext.getExternalContext().getRequestMap().remove(var);
-            }
-          }
-        }
-        return (SelectItem) item;
-      }
-      throw new NoSuchElementException();
-    }
-
-    public void remove() {
-      throw new UnsupportedOperationException();
-    }
-
-    private boolean getBooleanAttribute(
-        final UIComponent component, final String attrName, final boolean defaultValue) {
-      final Object value = component.getAttributes().get(attrName);
-      if (value == null) {
-        return defaultValue;
-      } else if (value instanceof Boolean) {
-        return (Boolean) value;
-      } else {
-        // If the value is a String, parse the boolean.
-        // This makes the following code work: <tag attribute="true" />,
-        // otherwise you would have to write <tag attribute="#{true}" />.
-        return Boolean.valueOf(value.toString());
-      }
-    }
+    return org.apache.myfaces.tobago.util.SelectItemUtils.getItemList(facesContext, selector);
   }
-
 }
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
index f9f4883..86b7c95 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
@@ -25,10 +25,34 @@ Tobago.Select2 = {
         .each( function () {
           var element = jQuery(this);
 
-          var select2Options = jQuery.extend({}, element.data("tobago-select2"), Tobago.Select2.getExtensions(element));
+          var select2Options = jQuery.extend({}, element.data("tobago-select2"));
+
+          var suggestId = element.data("tobago-suggest-id");
+          if (typeof suggestId === "string") {
+            var suggest = jQuery(Tobago.Utils.escapeClientId(suggestId));
+            if (suggest.length) {
+              select2Options.ajax = {
+                suggestId: suggestId,
+                url: "http://localhost/just/a/dummy/url",
+                transport: Tobago.Select2.transport
+              };
+              var delay = suggest.data("tobago-suggest-delay");
+              if (delay) {
+                select2Options.ajax.delay = delay;
+              }
+            } else {
+              console.error("Suggest2 ajax problem: could not find element with id " + suggestId);
+              suggestId = undefined;
+            }
+          }
 
           if (element.hasClass("tobago-selectManyBox")) {
             select2Options.containerCss = {height: element.data("tobago-style").height};
+            if (suggestId) {
+              select2Options.dropdownCssClass = undefined; // it makes no sense to hide the ajax response
+              select2Options.createTag = Tobago.Select2.createResponseTag;
+              select2Options.templateResult = Tobago.Select2.suggestTemplateResult;
+            }
           }
           console.info("Select2.init" + element.attr("id") + " with data: " // @DEV_ONLY
               + JSON.stringify(select2Options));                            // @DEV_ONLY
@@ -40,6 +64,7 @@ Tobago.Select2 = {
             console.info("select2Options.tokenizer: " + typeof eval(select2Options.tokenizer));
             select2Options.tokenizer = eval(select2Options.tokenizer)
           }
+
           var commands = element.data("tobago-commands");
 
           if (commands) {
@@ -70,17 +95,109 @@ Tobago.Select2 = {
             }
 
           }
+          select2Options = jQuery.extend(select2Options, Tobago.Select2.getExtensions(element));
+          console.info("Select2 select2Options " + element.attr("id") + " with data: " // @DEV_ONLY
+              + JSON.stringify(select2Options));                                       // @DEV_ONLY
           element.select2(select2Options);
         });
   },
 
+  createResponseTag: function (params) {
+
+    var term = jQuery.trim(params.term);
+
+    if (term === '') {
+      return null;
+    }
+
+    return {
+      id: term,
+      text: term,
+      hide: "query" === params._type
+    }
+  },
+
+  suggestTemplateResult: function (result) {
+    if (result.loading || result.hide) {
+      return null;
+    }
+    return result.text;
+  },
+
+  transport: function (params, success, failure) {
+
+    if (params.data && params.data.q !== undefined) {
+      console.debug("SELECT2 tobago params.data.q : " + params.data.q); // @DEV_ONLY
+      jQuery(Tobago.Utils.escapeClientId(params.suggestId)).val(params.data.q);
+    } else {
+      console.debug("SELECT2 tobago undef "); // @DEV_ONLY
+    }
+
+    Tobago.reloadComponent(null, params.suggestId, params.suggestId, {
+      createOverlay: false,
+      suggestId : params.suggestId,
+      afterDoUpdateSuccess: function (requestOptions) {
+        var data = jQuery(Tobago.Utils.escapeClientId(requestOptions.suggestId)).data("tobago-suggest-response-data");
+        console.debug("SELECT2 tobago SUCCESS"); // @DEV_ONLY
+        var currentValues = jQuery(Tobago.Utils.escapeClientId(params.suggestId))
+            .next().children('.tobago-selectManyBox').val();
+        if (currentValues !== undefined) {
+          // remove already selected items in select many box
+          data.results = Tobago.Select2.removeSelected(data.results, currentValues);
+        }
+        success(data);
+      },
+      afterDoUpdateError: function () {
+        console.debug("SELECT2 tobago ERROR"); // @DEV_ONLY
+        failure();
+      }
+
+    });
+
+    return {
+      abort: function () {
+        console.debug("SELECT2 tobago ABORT transport");  // @DEV_ONLY
+      }
+    }
+  },
+
+  removeSelected: function (results, currentValue) {
+    if (currentValue.length === 0) {
+      return results;
+    }
+    var newData = [];
+    for (var i = 0; i < results.length; i++) {
+      var item = results[i];
+      if (!currentValue.includes(item.id)) {
+        newData.push(item);
+      }
+    }
+    return newData;
+  },
+
   getExtensions: function (element) {
     var extend = element.data("tobago-select2-extend");
     if (extend !== undefined) {
       for (var extName in extend) {
         if (extend.hasOwnProperty(extName)
             && typeof extend[extName] === "string") {
-          extend[extName] = Tobago.Select2.registry[extend[extName]];
+          var extensionName = extend[extName];
+          var value = Tobago.Select2.registry[extensionName];
+          if (value) {
+            extend[extName] = value;
+          } else {
+            try {
+              value = jQuery(extensionName);
+            } catch (e) {}
+            if (!(value && value.length) && extend[extName].charAt(0) !== '#') {
+              try {
+                value = jQuery(Tobago.Utils.escapeClientId(extensionName));
+              } catch (e) {}
+            }
+            if (value && value.length) {
+              extend[extName] = value;
+            }
+          }
         }
       }
     }
@@ -141,5 +258,25 @@ Tobago.Select2.register('caseSensitiveMatcher', function (params, data) {
   return null;
 });
 
+Tobago.Select2.register('suppressMessages', (function() {
+
+  var suppressMessages = (function () {
+
+    function SuppressMessage (decorated, $element, options, dataAdapter) {
+      decorated.call(this, $element, options, dataAdapter);
+    }
+
+    SuppressMessage.prototype.displayMessage = function(decorated, params) {
+    };
+
+    return SuppressMessage;
+  })();
+
+  var Utils = jQuery.fn.select2.amd.require('select2/utils');
+  var resultAdapter = jQuery.fn.select2.amd.require('select2/results');
+  return Utils.Decorate(resultAdapter, suppressMessages);
+
+})());
+
 Tobago.registerListener(Tobago.Select2.init, Tobago.Phase.DOCUMENT_READY);
 Tobago.registerListener(Tobago.Select2.init, Tobago.Phase.AFTER_UPDATE);


[myfaces-tobago] 03/09: Tobago-1999: enable inline rendering of selectOneChoice

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 885574ecd9d46c217a70d2535ba8594ec91af0a5
Author: Volker Weber <v....@inexso.de>
AuthorDate: Fri Feb 14 10:34:11 2020 +0100

    Tobago-1999: enable inline rendering of selectOneChoice
---
 .../src/main/webapp/content/01-basic/00/input.xhtml        |  3 +++
 .../main/webapp/content/25-select/00-select2/select2.xhtml | 14 +++++++++++++-
 .../src/main/webapp/content/25-select/select.xhtml         | 13 ++++++++++++-
 .../standard/standard/tag/SelectOneChoiceRenderer.java     |  4 ++--
 4 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/01-basic/00/input.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/01-basic/00/input.xhtml
index e267e6c..e7c898d 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/01-basic/00/input.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/01-basic/00/input.xhtml
@@ -70,6 +70,9 @@
                  value="Some Text"/>
 
     <tc:panel>
+      <f:facet name="layout">
+        <tc:flowLayout/>
+      </f:facet>
       <tc:out value="An input field is "/>
       <tc:in value="here" inline="true"/>
       <tc:out value=" inside of a floating text."/>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
index d5febd3..b113a85 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
@@ -25,7 +25,7 @@
   <ui:param name="title" value="Select2 Controls"/>
   <tc:panel>
     <f:facet name="layout">
-      <tc:gridLayout rows="auto;auto;auto;*;auto"/>
+      <tc:gridLayout rows="auto;auto;25px;auto;*;auto"/>
     </f:facet>
 
     <tc:panel>
@@ -97,6 +97,18 @@
       </tc:panel>
     </tc:panel>
 
+    <tc:panel>
+      <f:facet name="layout">
+        <tc:flowLayout/>
+      </f:facet>
+      <tc:out value="You can use a tc:selectOneChoice like this "/>
+      <tc:selectOneChoice value="#{reference.manufacturer}"
+                          select2="true">
+        <f:selectItems value="#{reference.manufacturerSelectItems}"/>
+      </tc:selectOneChoice>
+      <tc:out value=" inside of a floating text."/>
+    </tc:panel>
+
 
     <tc:panel>
       <f:facet name="layout">
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/select.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/select.xhtml
index 269f431..c13c3bf 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/select.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/select.xhtml
@@ -26,7 +26,7 @@
   <tc:panel>
     <f:facet name="layout">
       <tc:gridLayout
-          rows="auto;auto;auto;auto;auto;auto;45px;45px;auto;auto;auto;200px;200px;auto;auto;auto;auto;auto;*;auto"/>
+          rows="auto;auto;auto;auto;auto;auto;45px;45px;auto;auto;auto;25px;200px;200px;auto;auto;auto;auto;auto;*;auto"/>
     </f:facet>
 
     <!-- code-sniplet-start id="selectBooleanCheckbox" -->
@@ -91,6 +91,17 @@
       <f:selectItems value="#{reference.manufacturerSelectItems}"/>
     </tx:selectOneChoice>
 
+    <tc:panel>
+      <f:facet name="layout">
+        <tc:flowLayout/>
+      </f:facet>
+      <tc:out value="You can use a tc:selectOneChoice like this "/>
+      <tc:selectOneChoice value="#{reference.manufacturer}">
+        <f:selectItems value="#{reference.manufacturerSelectItems}"/>
+      </tc:selectOneChoice>
+      <tc:out value=" inside of a floating text."/>
+    </tc:panel>
+
     <!-- code-sniplet-start id="selectOneListbox" -->
     <tx:selectOneListbox label="Contact via: ">
       <f:selectItem itemValue="Phone" itemLabel="Phone"/>
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
index c1963b3..8035747 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
@@ -89,7 +89,7 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
       LOG.trace("Select2 json = \"{}\"", json);
       ComponentUtils.putDataAttribute(select, "tobago-select2", json);
 
-      writer.startElement(HtmlElements.DIV, select);
+      writer.startElement(HtmlElements.SPAN, select);
       writer.writeStyleAttribute(style);
       style.setTop(Measure.ZERO);
       style.setLeft(Measure.ZERO);
@@ -130,7 +130,7 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
 
     writer.endElement(HtmlElements.SELECT);
     if (renderAsSelect2) {
-      writer.endElement(HtmlElements.DIV);
+      writer.endElement(HtmlElements.SPAN);
     }
 
     super.encodeEnd(facesContext, select);


[myfaces-tobago] 04/09: working on QS-1295

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 8cd2cd2982b0f49627e414476d3dd2c5c6582dee
Author: Volker Weber <v....@inexso.de>
AuthorDate: Fri Feb 14 09:56:48 2020 +0100

    working on QS-1295
---
 .../internal/component/UISelect2Component.java     |  3 --
 .../internal/util/UISelect2ComponentUtil.java      | 48 +++++++++++++++++++--
 .../apache/myfaces/tobago/model/SubmittedItem.java | 11 +++++
 .../tobago/example/demo/Select2Controller.java     | 49 ++++++++++++++++++++++
 .../content/25-select/00-select2/select2.xhtml     | 16 +++++++
 .../standard/tag/SelectManyBoxRenderer.java        |  1 +
 .../renderkit/html/util/HtmlRendererUtils.java     | 15 ++++++-
 7 files changed, 135 insertions(+), 8 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java
index f7d456a..fc7291a 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java
@@ -1,10 +1,7 @@
 package org.apache.myfaces.tobago.internal.component;
 
 import javax.faces.component.StateHelper;
-import javax.faces.component.UIComponent;
 import javax.faces.convert.Converter;
-import java.util.HashSet;
-import java.util.Map;
 
 public interface UISelect2Component {
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
index 539f559..758e05a 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
@@ -7,13 +7,20 @@ import org.apache.myfaces.tobago.internal.component.AbstractUISelectManyBox;
 import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
 import org.apache.myfaces.tobago.internal.component.UISelect2Component;
 import org.apache.myfaces.tobago.model.SelectItem;
+import org.apache.myfaces.tobago.model.SubmittedItem;
 import org.apache.myfaces.tobago.model.UICustomItemContainer;
 import org.apache.myfaces.tobago.util.SelectItemUtils;
 
 import javax.faces.component.UIComponent;
+import javax.faces.component.UIInput;
 import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 public class UISelect2ComponentUtil {
@@ -57,7 +64,7 @@ public class UISelect2ComponentUtil {
   public static void ensureCustomValue(
       FacesContext facesContext, UISelect2Component component, Object value, AbstractUISuggest suggest) {
 
-    if (!isInItemlist(facesContext, component, value)) {
+    if (!isInItemList(facesContext, component, value)) {
       javax.faces.model.SelectItem item = null;
       if (suggest != null) {
         item = suggest.getSelectItem(facesContext, value, component.getConverter());
@@ -72,7 +79,7 @@ public class UISelect2ComponentUtil {
     }
   }
 
-  private static boolean isInItemlist(FacesContext facesContext, UISelect2Component component, Object value) {
+  private static boolean isInItemList(FacesContext facesContext, UISelect2Component component, Object value) {
     LOG.trace("check for value = \"{}\"", value);
     Iterable<javax.faces.model.SelectItem> items
         = SelectItemUtils.getItemIterator(facesContext, (UIComponent) component);
@@ -119,7 +126,7 @@ public class UISelect2ComponentUtil {
       validCustomItemMap.clear();
       for (javax.faces.model.SelectItem selectItem : set) {
         LOG.trace("cleanup check = \"{}\"", selectItem.getValue());
-        if (!isInItemlist(facesContext, component, selectItem.getValue())) {
+        if (!isInItemList(facesContext, component, selectItem.getValue())) {
           LOG.trace("cleanup readd = \"{}\"", selectItem.getValue());
           validCustomItemMap.add(selectItem);
         }
@@ -127,4 +134,39 @@ public class UISelect2ComponentUtil {
     }
   }
 
+  public static Iterable<javax.faces.model.SelectItem> ensureSubmittedValues(FacesContext facesContext,
+      UIInput component, Iterable<javax.faces.model.SelectItem> items, String[] submittedValues) {
+    if (submittedValues == null || !(component instanceof UISelect2Component)) {
+      return items;
+    }
+    Converter converter = component.getConverter();
+    Map<String, javax.faces.model.SelectItem> optionValues = new HashMap<String, javax.faces.model.SelectItem>();
+    for (javax.faces.model.SelectItem item : items) {
+      if (converter != null) {
+        optionValues.put(converter.getAsString(facesContext, component, item.getValue()), item);
+      } else {
+        optionValues.put(item.getValue().toString(), item);
+      }
+    }
+
+
+    List<javax.faces.model.SelectItem> itemsToRender = new ArrayList<javax.faces.model.SelectItem>();
+    for (String submittedValue : submittedValues) {
+      if (optionValues.keySet().contains(submittedValue)) {
+        optionValues.remove(submittedValue);
+      } else {
+        itemsToRender.add(new SubmittedItem(submittedValue));
+      }
+    }
+    if (itemsToRender.isEmpty()) {
+      return items;
+    } else {
+      for (javax.faces.model.SelectItem item : items) {
+        if (optionValues.values().contains(item)) {
+          itemsToRender.add(item);
+        }
+      }
+      return itemsToRender;
+    }
+  }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/SubmittedItem.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/SubmittedItem.java
new file mode 100644
index 0000000..8a51cca
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/SubmittedItem.java
@@ -0,0 +1,11 @@
+package org.apache.myfaces.tobago.model;
+
+import org.apache.myfaces.tobago.context.Markup;
+
+public class SubmittedItem extends SelectItem {
+
+  public SubmittedItem(String value) {
+    super(value, value);
+    setMarkup(Markup.ERROR);
+  }
+}
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
index 6be927c..5217923 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
@@ -30,6 +30,8 @@ public class Select2Controller implements Serializable {
 
   private LocaleConverter localeConverter = new LocaleConverter();
 
+  private NumberConverter numberConverter = new NumberConverter();
+
   private String one2Value;
 
   private String one3Value;
@@ -38,6 +40,8 @@ public class Select2Controller implements Serializable {
 
   private List<Locale> many8Locales;
 
+  private List<Integer> many9Numbers;
+
   private List<String> many7Countries;
 
   private List<SelectItem> items;
@@ -145,6 +149,51 @@ public class Select2Controller implements Serializable {
     }
   }
 
+  public List<Integer> getMany9Numbers() {
+    LOG.warn("get many9Numbers = \"{}\"", many9Numbers);
+    return many9Numbers;
+  }
+
+  public void setMany9Numbers(List<Integer> many9Numbers) {
+    LOG.warn("set many9Numbers = \"{}\"", many9Numbers);
+    this.many9Numbers = many9Numbers;
+  }
+
+  public List<SelectItem> getMany9NumbersItems() {
+    if (many9Numbers != null && !many9Numbers.isEmpty()) {
+      List<SelectItem> items = new ArrayList<SelectItem>();
+      for (Integer number : many9Numbers) {
+        items.add(new SelectItem(number, Integer.toString(number)));
+      }
+      return items;
+    } else {
+      return Collections.emptyList();
+    }
+  }
+
+  public NumberConverter getNumberConverter() {
+    return numberConverter;
+  }
+
+  private class NumberConverter implements Converter {
+
+    public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
+      try {
+        return Integer.parseInt(value);
+      } catch (NumberFormatException e) {
+        throw new ConverterException(e.getMessage(), e);
+      }
+    }
+
+    public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
+      try {
+        return Integer.toString((Integer) value);
+      } catch (Exception e) {
+        throw new ConverterException(e.getMessage(), e);
+      }
+    }
+  }
+
   private class LocaleConverter implements Converter {
 
     private Map<String, Locale> localeMap;
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
index b113a85..f98b89e 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
@@ -243,6 +243,22 @@
         </tc:selectManyBox>
 
       </tc:panel>
+
+      <tc:panel>
+        <f:facet name="layout">
+          <tc:gridLayout columns="400px;1*" rows="45px"/>
+        </f:facet>
+
+        <tc:label value="Enter numbers" for="many_9"/>
+        <tc:selectManyBox id="many_9"
+                          placeholder="Enter numbers"
+                          allowCustom="true"
+                          converter="#{select2Controller.numberConverter}"
+                          value="#{select2Controller.many9Numbers}">
+          <tc:selectItems value="#{select2Controller.many9NumbersItems}"/>
+        </tc:selectManyBox>
+
+      </tc:panel>
     </tc:panel>
 
 
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
index 3c202e8..6f466f1 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
@@ -90,6 +90,7 @@ public class SelectManyBoxRenderer extends SelectManyRendererBase {
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, select);
     writer.startElement(HtmlElements.DIV, select);
     writer.writeStyleAttribute(style);
+    writer.writeClassAttribute(Classes.create(select, "container"));
     style.setTop(Measure.ZERO);
     style.setLeft(Measure.ZERO);
 
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
index 38025c3..fab6cfa 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
@@ -30,10 +30,15 @@ import org.apache.myfaces.tobago.component.UISheet;
 import org.apache.myfaces.tobago.context.Markup;
 import org.apache.myfaces.tobago.context.ResourceManagerUtils;
 import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
+import org.apache.myfaces.tobago.internal.component.AbstractUISelectMany;
+import org.apache.myfaces.tobago.internal.component.AbstractUISelectManyBox;
+import org.apache.myfaces.tobago.internal.component.UISelect2Component;
 import org.apache.myfaces.tobago.internal.util.Deprecation;
 import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
 import org.apache.myfaces.tobago.internal.util.StringUtils;
+import org.apache.myfaces.tobago.internal.util.UISelect2ComponentUtil;
 import org.apache.myfaces.tobago.internal.webapp.TobagoResponseWriterWrapper;
+import org.apache.myfaces.tobago.model.SubmittedItem;
 import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
 import org.apache.myfaces.tobago.renderkit.css.Classes;
 import org.apache.myfaces.tobago.renderkit.css.Style;
@@ -399,7 +404,7 @@ public final class HtmlRendererUtils {
       throws IOException {
     renderSelectItems(component, items, values, null, onlySelected, writer, facesContext);
   }
-  public static void renderSelectItems(final UIInput component, final Iterable<SelectItem> items, final Object[] values,
+  public static void renderSelectItems(final UIInput component, Iterable<SelectItem> items, final Object[] values,
       final String[] submittedValues, final Boolean onlySelected, final TobagoResponseWriter writer,
       final FacesContext facesContext) throws IOException {
 
@@ -408,6 +413,7 @@ public final class HtmlRendererUtils {
       LOG.debug("values = '{}'", Arrays.toString(values));
       LOG.debug("submittedValues = '{}'", Arrays.toString(submittedValues));
     }
+    items = UISelect2ComponentUtil.ensureSubmittedValues(facesContext, component, items, submittedValues);
     for (final SelectItem item : items) {
       if (item instanceof SelectItemGroup) {
         writer.startElement(HtmlElements.OPTGROUP, null);
@@ -426,7 +432,12 @@ public final class HtmlRendererUtils {
         if (itemValue instanceof String && values != null && values.length > 0 && !(values[0] instanceof String)) {
           itemValue = ComponentUtils.getConvertedValue(facesContext, component, (String) itemValue);
         }
-        final String formattedValue = RenderUtils.getFormattedValue(facesContext, component, itemValue);
+        final String formattedValue;
+        if (item instanceof SubmittedItem) {
+          formattedValue = item.getLabel();
+        } else {
+          formattedValue = RenderUtils.getFormattedValue(facesContext, component, itemValue);
+        }
         boolean contains;
         if (submittedValues == null) {
           contains = RenderUtils.contains(values, itemValue);


[myfaces-tobago] 05/09: Tobago-1999: styling

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit b298e0627f9527a4351e20f3fdc4f3ab40a2051d
Author: Volker Weber <v....@inexso.de>
AuthorDate: Thu Feb 27 13:50:27 2020 +0100

    Tobago-1999: styling
---
 .../html/scarborough/standard/style/tobago.css     | 23 +++++++++++++++++
 .../html/speyside/standard/style/tobago.less       | 30 ++++++++++++++++++++++
 .../src/main/resources/META-INF/tobago-config.xml  | 15 +++++++++++
 3 files changed, 68 insertions(+)

diff --git a/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/style/tobago.css b/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/style/tobago.css
index e8c6b6d..bdd7a09 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/style/tobago.css
+++ b/tobago-theme/tobago-theme-scarborough/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/style/tobago.css
@@ -805,6 +805,29 @@ li.tobago-menu-markup-selected {
   border: 1px solid #FF0000;
 }
 
+/* selectManyBox ------------------------------------------------------- */
+
+.tobago-selectManyBox-container .select2-selection--multiple {
+  font-family: arial, helvetica, sans-serif;
+  color: #000000 !important;
+  background: #ffffff !important;
+  height: 25px;
+}
+
+.tobago-selectManyBox-container-markup-readonly .select2-selection--multiple {
+  background-color: #bbccdd !important;
+}
+
+.tobago-selectManyBox-container-markup-disabled .select2-selection--multiple {
+  color: #778899 !important;
+  background: #bbccdd !important;
+}
+
+.tobago-selectManyBox-container-markup-error .select2-selection--multiple {
+  border-style: solid;
+  border-color: #ff0000 !important;
+}
+
 /* selectManyListbox ------------------------------------------------------- */
 
 .tobago-selectManyListbox {
diff --git a/tobago-theme/tobago-theme-speyside/src/main/less/org/apache/myfaces/tobago/renderkit/html/speyside/standard/style/tobago.less b/tobago-theme/tobago-theme-speyside/src/main/less/org/apache/myfaces/tobago/renderkit/html/speyside/standard/style/tobago.less
index 04ce858..025d4ce 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/less/org/apache/myfaces/tobago/renderkit/html/speyside/standard/style/tobago.less
+++ b/tobago-theme/tobago-theme-speyside/src/main/less/org/apache/myfaces/tobago/renderkit/html/speyside/standard/style/tobago.less
@@ -497,6 +497,36 @@ li.tobago-menu-markup-selected {
   color: @disabledTextColor;
 }
 
+/* selectManyBox ------------------------------------------------------- */
+
+.tobago-selectManyBox-container .select2-selection--multiple {
+  border: 1px solid darken(@borderColor, 10%) !important;
+  font: @font;
+  padding-top: -1px;
+  color: @textColor !important;
+  height: 21px;
+  .border-radius;
+  .transition
+  &:focus {
+    outline: none;
+    border-color: @focusColor !important;
+    .field-focus-shadow;
+  }
+}
+
+.tobago-selectManyBox-container-markup-disabled .select2-selection--multiple {
+  background-color: @disabledBackgroundColor !important;
+  color: @disabledTextColor;
+}
+
+.tobago-selectManyBox-container-markup-readonly .select2-selection--multiple {
+  background-color: @readonlyBackgroundColor !important;
+}
+
+.tobago-selectManyBox-container-markup-error .select2-selection--multiple {
+  border-color: @errorColor !important;
+}
+
 /* selectManyListbox ------------------------------------------------------- */
 
 .tobago-selectManyListbox {
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
index 7d00ddb..4bece02 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/tobago-config.xml
@@ -192,6 +192,21 @@
           </supported-markup>
         </renderer>
         <renderer>
+          <name>SelectManyBox</name>
+          <supported-markup>
+            <markup>container</markup>
+            <markup>disabled</markup>
+            <markup>readonly</markup>
+            <markup>required</markup>
+            <markup>fatal</markup>
+            <markup>error</markup>
+            <markup>warn</markup>
+            <markup>info</markup>
+            <markup>selected</markup>
+            <markup>inline</markup>
+          </supported-markup>
+        </renderer>
+        <renderer>
           <name>SelectManyCheckbox</name>
           <supported-markup>
             <markup>disabled</markup>