Browse Source

Basic multiple select implementation

Kevin Brown 10 years ago
parent
commit
14d3d99868

+ 35 - 6
dist/css/select2.css

@@ -18,6 +18,19 @@
     padding-left: 8px;
     padding-left: 8px;
     text-overflow: ellipsis; }
     text-overflow: ellipsis; }
 
 
+.select2-container .selection .multiple-select {
+  box-sizing: border-box;
+  cursor: pointer;
+  display: block;
+  min-height: 32px;
+  user-select: none;
+  -webkit-user-select: none; }
+  .select2-container .selection .multiple-select .rendered-selection {
+    display: block;
+    overflow: hidden;
+    padding-left: 8px;
+    text-overflow: ellipsis; }
+
 .select2-container .dropdown {
 .select2-container .dropdown {
   background-color: white;
   background-color: white;
   border: 1px solid #aaa;
   border: 1px solid #aaa;
@@ -26,8 +39,8 @@
   display: block;
   display: block;
   position: absolute;
   position: absolute;
   left: -100000px;
   left: -100000px;
-  top: -100000px;
-  width: 100%; }
+  width: 100%;
+  z-index: 100; }
   .select2-container .dropdown .results {
   .select2-container .dropdown .results {
     display: block; }
     display: block; }
     .select2-container .dropdown .results .options {
     .select2-container .dropdown .results .options {
@@ -40,10 +53,10 @@
         user-select: none;
         user-select: none;
         -webkit-user-select: none; }
         -webkit-user-select: none; }
 .select2-container.open .dropdown {
 .select2-container.open .dropdown {
+  border-top: none;
   border-top-left-radius: 0;
   border-top-left-radius: 0;
   border-top-right-radius: 0;
   border-top-right-radius: 0;
-  left: 0;
-  top: 28px; }
+  left: 0; }
 
 
 .select2-container.select2-theme-default .selection .single-select {
 .select2-container.select2-theme-default .selection .single-select {
   background-color: #eee;
   background-color: #eee;
@@ -52,8 +65,24 @@
   .select2-container.select2-theme-default .selection .single-select .rendered-selection {
   .select2-container.select2-theme-default .selection .single-select .rendered-selection {
     color: #444;
     color: #444;
     line-height: 28px; }
     line-height: 28px; }
-.select2-container.select2-theme-default.open .selection .single-select {
-  border-bottom: none;
+.select2-container.select2-theme-default .selection .multiple-select {
+  background-color: white;
+  border: 1px solid #aaa;
+  border-radius: 4px; }
+  .select2-container.select2-theme-default .selection .multiple-select .rendered-selection {
+    list-style: none;
+    margin: 0;
+    padding: 5px;
+    padding-bottom: 0; }
+    .select2-container.select2-theme-default .selection .multiple-select .rendered-selection .choice {
+      background-color: #e4e4e4;
+      border: 1px solid #aaa;
+      border-radius: 4px;
+      float: left;
+      margin-right: 5px;
+      margin-bottom: 5px;
+      padding: 0 5px; }
+.select2-container.select2-theme-default.open .selection .single-select, .select2-container.select2-theme-default.open .selection .multiple-select {
   border-bottom-left-radius: 0;
   border-bottom-left-radius: 0;
   border-bottom-right-radius: 0; }
   border-bottom-right-radius: 0; }
 .select2-container.select2-theme-default .dropdown .results {
 .select2-container.select2-theme-default .dropdown .results {

+ 120 - 30
dist/js/select2.amd.full.js

@@ -59,6 +59,8 @@ define('select2/utils',[], function () {
       calledConstructor.apply(this, arguments);
       calledConstructor.apply(this, arguments);
     }
     }
 
 
+    DecoratorClass.displayName = SuperClass.displayName;
+
     function ctr () {
     function ctr () {
       this.constructor = DecoratedClass;
       this.constructor = DecoratedClass;
     }
     }
@@ -162,29 +164,32 @@ define('select2/data/select',[
   };
   };
 
 
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
-    var val;
+    var self = this;
 
 
     if (this.$element.prop("multiple")) {
     if (this.$element.prop("multiple")) {
-      var currentData = this.current();
+      this.current(function (currentData) {
+        var val = [];
 
 
-      data = [data];
-      data.push(currentData);
+        data = [data];
+        data.push.apply(data, currentData);
 
 
-      val = [];
+        for (var d = 0; d < data.length; d++) {
+          id = data[d].id;
 
 
-      for (var d = 0; d < data.length; d++) {
-        id = data[d].id;
-
-        if (ids.indexOf(id) === -1) {
-          val.push(id);
+          if (val.indexOf(id) === -1) {
+            val.push(id);
+          }
         }
         }
-      }
+
+        self.$element.val(val);
+        self.$element.trigger("change");
+      });
     } else {
     } else {
-      val = data.id;
-    }
+      var val = data.id;
 
 
-    this.$element.val(val);
-    this.$element.trigger("change");
+      this.$element.val(val);
+      this.$element.trigger("change");
+    }
   }
   }
 
 
   SelectAdapter.prototype.query = function (params, callback) {
   SelectAdapter.prototype.query = function (params, callback) {
@@ -361,19 +366,19 @@ define('select2/dropdown',[
   return Dropdown;
   return Dropdown;
 })
 })
 ;
 ;
-define('select2/selection',[
-  './utils'
+define('select2/selection/single',[
+  '../utils'
 ], function (Utils) {
 ], function (Utils) {
-  function Selection ($element, options) {
+  function SingleSelection ($element, options) {
     this.$element = $element;
     this.$element = $element;
     this.options = options;
     this.options = options;
 
 
-    Selection.__super__.constructor.call(this);
+    SingleSelection.__super__.constructor.call(this);
   }
   }
 
 
-  Utils.Extend(Selection, Utils.Observable);
+  Utils.Extend(SingleSelection, Utils.Observable);
 
 
-  Selection.prototype.render = function () {
+  SingleSelection.prototype.render = function () {
     var $selection = $(
     var $selection = $(
       '<span class="single-select">' +
       '<span class="single-select">' +
         '<span class="rendered-selection"></span>' +
         '<span class="rendered-selection"></span>' +
@@ -385,7 +390,7 @@ define('select2/selection',[
     return $selection;
     return $selection;
   }
   }
 
 
-  Selection.prototype.bind = function ($container) {
+  SingleSelection.prototype.bind = function ($container) {
     var self = this;
     var self = this;
 
 
     this.$selection.on('click', function (evt) {
     this.$selection.on('click', function (evt) {
@@ -395,15 +400,15 @@ define('select2/selection',[
     });
     });
   }
   }
 
 
-  Selection.prototype.clear = function () {
-    this.$selection.find(".rendered-selection").text("");
+  SingleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
   }
   }
 
 
-  Selection.prototype.display = function (data) {
+  SingleSelection.prototype.display = function (data) {
     return data.text;
     return data.text;
   }
   }
 
 
-  Selection.prototype.update = function (data) {
+  SingleSelection.prototype.update = function (data) {
     if (data.length == 0) {
     if (data.length == 0) {
       this.clear();
       this.clear();
       return;
       return;
@@ -416,22 +421,102 @@ define('select2/selection',[
     this.$selection.find(".rendered-selection").html(formatted);
     this.$selection.find(".rendered-selection").html(formatted);
   }
   }
 
 
-  return Selection;
+  return SingleSelection;
+});
+
+define('select2/selection/multiple',[
+  '../utils'
+], function (Utils) {
+  function MultipleSelection ($element, options) {
+    this.$element = $element;
+    this.options = options;
+
+    MultipleSelection.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(MultipleSelection, Utils.Observable);
+
+  MultipleSelection.prototype.render = function () {
+    var $selection = $(
+      '<span class="multiple-select">' +
+        '<ul class="rendered-selection"></ul>' +
+      '</span>'
+    );
+
+    this.$selection = $selection;
+
+    return $selection;
+  }
+
+  MultipleSelection.prototype.bind = function ($container) {
+    var self = this;
+
+    this.$selection.on('click', function (evt) {
+      self.trigger("toggle", {
+        originalEvent: evt
+      });
+    });
+  }
+
+  MultipleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
+  }
+
+  MultipleSelection.prototype.display = function (data) {
+    return data.text;
+  }
+
+  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 formatted = this.display(selection);
+
+      var $selection = $('<ul class="choice"></ul>');
+
+      $selection.text(formatted);
+      $selection.data("data", data);
+
+      $selections.push($selection);
+    }
+
+    this.$selection.find(".rendered-selection").append($selections);
+  }
+
+  return MultipleSelection;
 });
 });
 
 
 define('select2/options',[
 define('select2/options',[
   './data/select',
   './data/select',
   './results',
   './results',
   './dropdown',
   './dropdown',
-  './selection'
-], function (SelectData, ResultsList, Dropdown, Selection) {
+  './selection/single',
+  './selection/multiple'
+], function (SelectData, ResultsList, Dropdown, SingleSelection,
+             MultipleSelection) {
   function Options (options) {
   function Options (options) {
     this.options = options;
     this.options = options;
 
 
     this.dataAdapter = SelectData;
     this.dataAdapter = SelectData;
     this.resultsAdapter = ResultsList;
     this.resultsAdapter = ResultsList;
     this.dropdownAdapter = Dropdown;
     this.dropdownAdapter = Dropdown;
-    this.selectionAdapter = options.selectionAdapter || Selection;
+    this.selectionAdapter = options.selectionAdapter;
+
+    if (this.selectionAdapter == null) {
+      if (this.options.multiple) {
+        this.selectionAdapter = MultipleSelection;
+      } else {
+        this.selectionAdapter = SingleSelection;
+      }
+    }
   }
   }
 
 
   return Options;
   return Options;
@@ -444,6 +529,11 @@ define('select2/core',[
 ], function ($, Options, Utils) {
 ], function ($, Options, Utils) {
   var Select2 = function ($element, options) {
   var Select2 = function ($element, options) {
     this.$element = $element;
     this.$element = $element;
+
+    options = options || {};
+
+    options.multiple = options.multiple || $element.prop("multiple");
+
     this.options = new Options(options);
     this.options = new Options(options);
 
 
     Select2.__super__.constructor.call(this);
     Select2.__super__.constructor.call(this);

+ 120 - 30
dist/js/select2.amd.js

@@ -59,6 +59,8 @@ define('select2/utils',[], function () {
       calledConstructor.apply(this, arguments);
       calledConstructor.apply(this, arguments);
     }
     }
 
 
+    DecoratorClass.displayName = SuperClass.displayName;
+
     function ctr () {
     function ctr () {
       this.constructor = DecoratedClass;
       this.constructor = DecoratedClass;
     }
     }
@@ -162,29 +164,32 @@ define('select2/data/select',[
   };
   };
 
 
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
-    var val;
+    var self = this;
 
 
     if (this.$element.prop("multiple")) {
     if (this.$element.prop("multiple")) {
-      var currentData = this.current();
+      this.current(function (currentData) {
+        var val = [];
 
 
-      data = [data];
-      data.push(currentData);
+        data = [data];
+        data.push.apply(data, currentData);
 
 
-      val = [];
+        for (var d = 0; d < data.length; d++) {
+          id = data[d].id;
 
 
-      for (var d = 0; d < data.length; d++) {
-        id = data[d].id;
-
-        if (ids.indexOf(id) === -1) {
-          val.push(id);
+          if (val.indexOf(id) === -1) {
+            val.push(id);
+          }
         }
         }
-      }
+
+        self.$element.val(val);
+        self.$element.trigger("change");
+      });
     } else {
     } else {
-      val = data.id;
-    }
+      var val = data.id;
 
 
-    this.$element.val(val);
-    this.$element.trigger("change");
+      this.$element.val(val);
+      this.$element.trigger("change");
+    }
   }
   }
 
 
   SelectAdapter.prototype.query = function (params, callback) {
   SelectAdapter.prototype.query = function (params, callback) {
@@ -361,19 +366,19 @@ define('select2/dropdown',[
   return Dropdown;
   return Dropdown;
 })
 })
 ;
 ;
-define('select2/selection',[
-  './utils'
+define('select2/selection/single',[
+  '../utils'
 ], function (Utils) {
 ], function (Utils) {
-  function Selection ($element, options) {
+  function SingleSelection ($element, options) {
     this.$element = $element;
     this.$element = $element;
     this.options = options;
     this.options = options;
 
 
-    Selection.__super__.constructor.call(this);
+    SingleSelection.__super__.constructor.call(this);
   }
   }
 
 
-  Utils.Extend(Selection, Utils.Observable);
+  Utils.Extend(SingleSelection, Utils.Observable);
 
 
-  Selection.prototype.render = function () {
+  SingleSelection.prototype.render = function () {
     var $selection = $(
     var $selection = $(
       '<span class="single-select">' +
       '<span class="single-select">' +
         '<span class="rendered-selection"></span>' +
         '<span class="rendered-selection"></span>' +
@@ -385,7 +390,7 @@ define('select2/selection',[
     return $selection;
     return $selection;
   }
   }
 
 
-  Selection.prototype.bind = function ($container) {
+  SingleSelection.prototype.bind = function ($container) {
     var self = this;
     var self = this;
 
 
     this.$selection.on('click', function (evt) {
     this.$selection.on('click', function (evt) {
@@ -395,15 +400,15 @@ define('select2/selection',[
     });
     });
   }
   }
 
 
-  Selection.prototype.clear = function () {
-    this.$selection.find(".rendered-selection").text("");
+  SingleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
   }
   }
 
 
-  Selection.prototype.display = function (data) {
+  SingleSelection.prototype.display = function (data) {
     return data.text;
     return data.text;
   }
   }
 
 
-  Selection.prototype.update = function (data) {
+  SingleSelection.prototype.update = function (data) {
     if (data.length == 0) {
     if (data.length == 0) {
       this.clear();
       this.clear();
       return;
       return;
@@ -416,22 +421,102 @@ define('select2/selection',[
     this.$selection.find(".rendered-selection").html(formatted);
     this.$selection.find(".rendered-selection").html(formatted);
   }
   }
 
 
-  return Selection;
+  return SingleSelection;
+});
+
+define('select2/selection/multiple',[
+  '../utils'
+], function (Utils) {
+  function MultipleSelection ($element, options) {
+    this.$element = $element;
+    this.options = options;
+
+    MultipleSelection.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(MultipleSelection, Utils.Observable);
+
+  MultipleSelection.prototype.render = function () {
+    var $selection = $(
+      '<span class="multiple-select">' +
+        '<ul class="rendered-selection"></ul>' +
+      '</span>'
+    );
+
+    this.$selection = $selection;
+
+    return $selection;
+  }
+
+  MultipleSelection.prototype.bind = function ($container) {
+    var self = this;
+
+    this.$selection.on('click', function (evt) {
+      self.trigger("toggle", {
+        originalEvent: evt
+      });
+    });
+  }
+
+  MultipleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
+  }
+
+  MultipleSelection.prototype.display = function (data) {
+    return data.text;
+  }
+
+  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 formatted = this.display(selection);
+
+      var $selection = $('<ul class="choice"></ul>');
+
+      $selection.text(formatted);
+      $selection.data("data", data);
+
+      $selections.push($selection);
+    }
+
+    this.$selection.find(".rendered-selection").append($selections);
+  }
+
+  return MultipleSelection;
 });
 });
 
 
 define('select2/options',[
 define('select2/options',[
   './data/select',
   './data/select',
   './results',
   './results',
   './dropdown',
   './dropdown',
-  './selection'
-], function (SelectData, ResultsList, Dropdown, Selection) {
+  './selection/single',
+  './selection/multiple'
+], function (SelectData, ResultsList, Dropdown, SingleSelection,
+             MultipleSelection) {
   function Options (options) {
   function Options (options) {
     this.options = options;
     this.options = options;
 
 
     this.dataAdapter = SelectData;
     this.dataAdapter = SelectData;
     this.resultsAdapter = ResultsList;
     this.resultsAdapter = ResultsList;
     this.dropdownAdapter = Dropdown;
     this.dropdownAdapter = Dropdown;
-    this.selectionAdapter = options.selectionAdapter || Selection;
+    this.selectionAdapter = options.selectionAdapter;
+
+    if (this.selectionAdapter == null) {
+      if (this.options.multiple) {
+        this.selectionAdapter = MultipleSelection;
+      } else {
+        this.selectionAdapter = SingleSelection;
+      }
+    }
   }
   }
 
 
   return Options;
   return Options;
@@ -444,6 +529,11 @@ define('select2/core',[
 ], function ($, Options, Utils) {
 ], function ($, Options, Utils) {
   var Select2 = function ($element, options) {
   var Select2 = function ($element, options) {
     this.$element = $element;
     this.$element = $element;
+
+    options = options || {};
+
+    options.multiple = options.multiple || $element.prop("multiple");
+
     this.options = new Options(options);
     this.options = new Options(options);
 
 
     Select2.__super__.constructor.call(this);
     Select2.__super__.constructor.call(this);

+ 120 - 30
dist/js/select2.full.js

@@ -9596,6 +9596,8 @@ define('select2/utils',[], function () {
       calledConstructor.apply(this, arguments);
       calledConstructor.apply(this, arguments);
     }
     }
 
 
+    DecoratorClass.displayName = SuperClass.displayName;
+
     function ctr () {
     function ctr () {
       this.constructor = DecoratedClass;
       this.constructor = DecoratedClass;
     }
     }
@@ -9699,29 +9701,32 @@ define('select2/data/select',[
   };
   };
 
 
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
-    var val;
+    var self = this;
 
 
     if (this.$element.prop("multiple")) {
     if (this.$element.prop("multiple")) {
-      var currentData = this.current();
+      this.current(function (currentData) {
+        var val = [];
 
 
-      data = [data];
-      data.push(currentData);
+        data = [data];
+        data.push.apply(data, currentData);
 
 
-      val = [];
+        for (var d = 0; d < data.length; d++) {
+          id = data[d].id;
 
 
-      for (var d = 0; d < data.length; d++) {
-        id = data[d].id;
-
-        if (ids.indexOf(id) === -1) {
-          val.push(id);
+          if (val.indexOf(id) === -1) {
+            val.push(id);
+          }
         }
         }
-      }
+
+        self.$element.val(val);
+        self.$element.trigger("change");
+      });
     } else {
     } else {
-      val = data.id;
-    }
+      var val = data.id;
 
 
-    this.$element.val(val);
-    this.$element.trigger("change");
+      this.$element.val(val);
+      this.$element.trigger("change");
+    }
   }
   }
 
 
   SelectAdapter.prototype.query = function (params, callback) {
   SelectAdapter.prototype.query = function (params, callback) {
@@ -9898,19 +9903,19 @@ define('select2/dropdown',[
   return Dropdown;
   return Dropdown;
 })
 })
 ;
 ;
-define('select2/selection',[
-  './utils'
+define('select2/selection/single',[
+  '../utils'
 ], function (Utils) {
 ], function (Utils) {
-  function Selection ($element, options) {
+  function SingleSelection ($element, options) {
     this.$element = $element;
     this.$element = $element;
     this.options = options;
     this.options = options;
 
 
-    Selection.__super__.constructor.call(this);
+    SingleSelection.__super__.constructor.call(this);
   }
   }
 
 
-  Utils.Extend(Selection, Utils.Observable);
+  Utils.Extend(SingleSelection, Utils.Observable);
 
 
-  Selection.prototype.render = function () {
+  SingleSelection.prototype.render = function () {
     var $selection = $(
     var $selection = $(
       '<span class="single-select">' +
       '<span class="single-select">' +
         '<span class="rendered-selection"></span>' +
         '<span class="rendered-selection"></span>' +
@@ -9922,7 +9927,7 @@ define('select2/selection',[
     return $selection;
     return $selection;
   }
   }
 
 
-  Selection.prototype.bind = function ($container) {
+  SingleSelection.prototype.bind = function ($container) {
     var self = this;
     var self = this;
 
 
     this.$selection.on('click', function (evt) {
     this.$selection.on('click', function (evt) {
@@ -9932,15 +9937,15 @@ define('select2/selection',[
     });
     });
   }
   }
 
 
-  Selection.prototype.clear = function () {
-    this.$selection.find(".rendered-selection").text("");
+  SingleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
   }
   }
 
 
-  Selection.prototype.display = function (data) {
+  SingleSelection.prototype.display = function (data) {
     return data.text;
     return data.text;
   }
   }
 
 
-  Selection.prototype.update = function (data) {
+  SingleSelection.prototype.update = function (data) {
     if (data.length == 0) {
     if (data.length == 0) {
       this.clear();
       this.clear();
       return;
       return;
@@ -9953,22 +9958,102 @@ define('select2/selection',[
     this.$selection.find(".rendered-selection").html(formatted);
     this.$selection.find(".rendered-selection").html(formatted);
   }
   }
 
 
-  return Selection;
+  return SingleSelection;
+});
+
+define('select2/selection/multiple',[
+  '../utils'
+], function (Utils) {
+  function MultipleSelection ($element, options) {
+    this.$element = $element;
+    this.options = options;
+
+    MultipleSelection.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(MultipleSelection, Utils.Observable);
+
+  MultipleSelection.prototype.render = function () {
+    var $selection = $(
+      '<span class="multiple-select">' +
+        '<ul class="rendered-selection"></ul>' +
+      '</span>'
+    );
+
+    this.$selection = $selection;
+
+    return $selection;
+  }
+
+  MultipleSelection.prototype.bind = function ($container) {
+    var self = this;
+
+    this.$selection.on('click', function (evt) {
+      self.trigger("toggle", {
+        originalEvent: evt
+      });
+    });
+  }
+
+  MultipleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
+  }
+
+  MultipleSelection.prototype.display = function (data) {
+    return data.text;
+  }
+
+  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 formatted = this.display(selection);
+
+      var $selection = $('<ul class="choice"></ul>');
+
+      $selection.text(formatted);
+      $selection.data("data", data);
+
+      $selections.push($selection);
+    }
+
+    this.$selection.find(".rendered-selection").append($selections);
+  }
+
+  return MultipleSelection;
 });
 });
 
 
 define('select2/options',[
 define('select2/options',[
   './data/select',
   './data/select',
   './results',
   './results',
   './dropdown',
   './dropdown',
-  './selection'
-], function (SelectData, ResultsList, Dropdown, Selection) {
+  './selection/single',
+  './selection/multiple'
+], function (SelectData, ResultsList, Dropdown, SingleSelection,
+             MultipleSelection) {
   function Options (options) {
   function Options (options) {
     this.options = options;
     this.options = options;
 
 
     this.dataAdapter = SelectData;
     this.dataAdapter = SelectData;
     this.resultsAdapter = ResultsList;
     this.resultsAdapter = ResultsList;
     this.dropdownAdapter = Dropdown;
     this.dropdownAdapter = Dropdown;
-    this.selectionAdapter = options.selectionAdapter || Selection;
+    this.selectionAdapter = options.selectionAdapter;
+
+    if (this.selectionAdapter == null) {
+      if (this.options.multiple) {
+        this.selectionAdapter = MultipleSelection;
+      } else {
+        this.selectionAdapter = SingleSelection;
+      }
+    }
   }
   }
 
 
   return Options;
   return Options;
@@ -9981,6 +10066,11 @@ define('select2/core',[
 ], function ($, Options, Utils) {
 ], function ($, Options, Utils) {
   var Select2 = function ($element, options) {
   var Select2 = function ($element, options) {
     this.$element = $element;
     this.$element = $element;
+
+    options = options || {};
+
+    options.multiple = options.multiple || $element.prop("multiple");
+
     this.options = new Options(options);
     this.options = new Options(options);
 
 
     Select2.__super__.constructor.call(this);
     Select2.__super__.constructor.call(this);

+ 120 - 30
dist/js/select2.js

@@ -487,6 +487,8 @@ define('select2/utils',[], function () {
       calledConstructor.apply(this, arguments);
       calledConstructor.apply(this, arguments);
     }
     }
 
 
+    DecoratorClass.displayName = SuperClass.displayName;
+
     function ctr () {
     function ctr () {
       this.constructor = DecoratedClass;
       this.constructor = DecoratedClass;
     }
     }
@@ -590,29 +592,32 @@ define('select2/data/select',[
   };
   };
 
 
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
-    var val;
+    var self = this;
 
 
     if (this.$element.prop("multiple")) {
     if (this.$element.prop("multiple")) {
-      var currentData = this.current();
+      this.current(function (currentData) {
+        var val = [];
 
 
-      data = [data];
-      data.push(currentData);
+        data = [data];
+        data.push.apply(data, currentData);
 
 
-      val = [];
+        for (var d = 0; d < data.length; d++) {
+          id = data[d].id;
 
 
-      for (var d = 0; d < data.length; d++) {
-        id = data[d].id;
-
-        if (ids.indexOf(id) === -1) {
-          val.push(id);
+          if (val.indexOf(id) === -1) {
+            val.push(id);
+          }
         }
         }
-      }
+
+        self.$element.val(val);
+        self.$element.trigger("change");
+      });
     } else {
     } else {
-      val = data.id;
-    }
+      var val = data.id;
 
 
-    this.$element.val(val);
-    this.$element.trigger("change");
+      this.$element.val(val);
+      this.$element.trigger("change");
+    }
   }
   }
 
 
   SelectAdapter.prototype.query = function (params, callback) {
   SelectAdapter.prototype.query = function (params, callback) {
@@ -789,19 +794,19 @@ define('select2/dropdown',[
   return Dropdown;
   return Dropdown;
 })
 })
 ;
 ;
-define('select2/selection',[
-  './utils'
+define('select2/selection/single',[
+  '../utils'
 ], function (Utils) {
 ], function (Utils) {
-  function Selection ($element, options) {
+  function SingleSelection ($element, options) {
     this.$element = $element;
     this.$element = $element;
     this.options = options;
     this.options = options;
 
 
-    Selection.__super__.constructor.call(this);
+    SingleSelection.__super__.constructor.call(this);
   }
   }
 
 
-  Utils.Extend(Selection, Utils.Observable);
+  Utils.Extend(SingleSelection, Utils.Observable);
 
 
-  Selection.prototype.render = function () {
+  SingleSelection.prototype.render = function () {
     var $selection = $(
     var $selection = $(
       '<span class="single-select">' +
       '<span class="single-select">' +
         '<span class="rendered-selection"></span>' +
         '<span class="rendered-selection"></span>' +
@@ -813,7 +818,7 @@ define('select2/selection',[
     return $selection;
     return $selection;
   }
   }
 
 
-  Selection.prototype.bind = function ($container) {
+  SingleSelection.prototype.bind = function ($container) {
     var self = this;
     var self = this;
 
 
     this.$selection.on('click', function (evt) {
     this.$selection.on('click', function (evt) {
@@ -823,15 +828,15 @@ define('select2/selection',[
     });
     });
   }
   }
 
 
-  Selection.prototype.clear = function () {
-    this.$selection.find(".rendered-selection").text("");
+  SingleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
   }
   }
 
 
-  Selection.prototype.display = function (data) {
+  SingleSelection.prototype.display = function (data) {
     return data.text;
     return data.text;
   }
   }
 
 
-  Selection.prototype.update = function (data) {
+  SingleSelection.prototype.update = function (data) {
     if (data.length == 0) {
     if (data.length == 0) {
       this.clear();
       this.clear();
       return;
       return;
@@ -844,22 +849,102 @@ define('select2/selection',[
     this.$selection.find(".rendered-selection").html(formatted);
     this.$selection.find(".rendered-selection").html(formatted);
   }
   }
 
 
-  return Selection;
+  return SingleSelection;
+});
+
+define('select2/selection/multiple',[
+  '../utils'
+], function (Utils) {
+  function MultipleSelection ($element, options) {
+    this.$element = $element;
+    this.options = options;
+
+    MultipleSelection.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(MultipleSelection, Utils.Observable);
+
+  MultipleSelection.prototype.render = function () {
+    var $selection = $(
+      '<span class="multiple-select">' +
+        '<ul class="rendered-selection"></ul>' +
+      '</span>'
+    );
+
+    this.$selection = $selection;
+
+    return $selection;
+  }
+
+  MultipleSelection.prototype.bind = function ($container) {
+    var self = this;
+
+    this.$selection.on('click', function (evt) {
+      self.trigger("toggle", {
+        originalEvent: evt
+      });
+    });
+  }
+
+  MultipleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
+  }
+
+  MultipleSelection.prototype.display = function (data) {
+    return data.text;
+  }
+
+  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 formatted = this.display(selection);
+
+      var $selection = $('<ul class="choice"></ul>');
+
+      $selection.text(formatted);
+      $selection.data("data", data);
+
+      $selections.push($selection);
+    }
+
+    this.$selection.find(".rendered-selection").append($selections);
+  }
+
+  return MultipleSelection;
 });
 });
 
 
 define('select2/options',[
 define('select2/options',[
   './data/select',
   './data/select',
   './results',
   './results',
   './dropdown',
   './dropdown',
-  './selection'
-], function (SelectData, ResultsList, Dropdown, Selection) {
+  './selection/single',
+  './selection/multiple'
+], function (SelectData, ResultsList, Dropdown, SingleSelection,
+             MultipleSelection) {
   function Options (options) {
   function Options (options) {
     this.options = options;
     this.options = options;
 
 
     this.dataAdapter = SelectData;
     this.dataAdapter = SelectData;
     this.resultsAdapter = ResultsList;
     this.resultsAdapter = ResultsList;
     this.dropdownAdapter = Dropdown;
     this.dropdownAdapter = Dropdown;
-    this.selectionAdapter = options.selectionAdapter || Selection;
+    this.selectionAdapter = options.selectionAdapter;
+
+    if (this.selectionAdapter == null) {
+      if (this.options.multiple) {
+        this.selectionAdapter = MultipleSelection;
+      } else {
+        this.selectionAdapter = SingleSelection;
+      }
+    }
   }
   }
 
 
   return Options;
   return Options;
@@ -872,6 +957,11 @@ define('select2/core',[
 ], function ($, Options, Utils) {
 ], function ($, Options, Utils) {
   var Select2 = function ($element, options) {
   var Select2 = function ($element, options) {
     this.$element = $element;
     this.$element = $element;
+
+    options = options || {};
+
+    options.multiple = options.multiple || $element.prop("multiple");
+
     this.options = new Options(options);
     this.options = new Options(options);
 
 
     Select2.__super__.constructor.call(this);
     Select2.__super__.constructor.call(this);

+ 5 - 7
playground/basic/basic.html

@@ -12,9 +12,7 @@
 </head>
 </head>
 <body>
 <body>
 
 
-
-<input type="text" style="width:300px" autofocus/><br/>
-<select style="width:300px" id="source">
+<select style="width:300px" id="single">
     <option value="AK">Alaska</option>
     <option value="AK">Alaska</option>
     <option value="HI">Hawaii</option>
     <option value="HI">Hawaii</option>
     <option value="CA">California</option>
     <option value="CA">California</option>
@@ -66,11 +64,10 @@
     <option value="VA">Virginia</option>
     <option value="VA">Virginia</option>
     <option value="WV">West Virginia</option>
     <option value="WV">West Virginia</option>
 </select><br/>
 </select><br/>
-<input type="text" style="width:300px" /><br/>
-<select style="width:300px">
+<select style="width:300px" id="multiple" multiple="multiple">
     <option value="AK">Alaska</option>
     <option value="AK">Alaska</option>
     <option value="HI">Hawaii</option>
     <option value="HI">Hawaii</option>
-    <option value="CA">California</option>
+    <option value="CA" selected="selected">California</option>
     <option value="NV">Nevada</option>
     <option value="NV">Nevada</option>
     <option value="OR">Oregon</option>
     <option value="OR">Oregon</option>
     <option value="WA">Washington</option>
     <option value="WA">Washington</option>
@@ -122,7 +119,8 @@
 
 
 <script>
 <script>
 require(["select2/core"], function (Select2) {
 require(["select2/core"], function (Select2) {
-    var s2 = new Select2($("#source"));
+    var single = new Select2($("#single"));
+    var multiple = new Select2($("#multiple"));
 });
 });
 </script>
 </script>
 
 

+ 5 - 0
src/js/select2/core.js

@@ -5,6 +5,11 @@ define([
 ], function ($, Options, Utils) {
 ], function ($, Options, Utils) {
   var Select2 = function ($element, options) {
   var Select2 = function ($element, options) {
     this.$element = $element;
     this.$element = $element;
+
+    options = options || {};
+
+    options.multiple = options.multiple || $element.prop("multiple");
+
     this.options = new Options(options);
     this.options = new Options(options);
 
 
     Select2.__super__.constructor.call(this);
     Select2.__super__.constructor.call(this);

+ 18 - 15
src/js/select2/data/select.js

@@ -26,29 +26,32 @@ define([
   };
   };
 
 
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
-    var val;
+    var self = this;
 
 
     if (this.$element.prop("multiple")) {
     if (this.$element.prop("multiple")) {
-      var currentData = this.current();
-
-      data = [data];
-      data.push(currentData);
+      this.current(function (currentData) {
+        var val = [];
 
 
-      val = [];
+        data = [data];
+        data.push.apply(data, currentData);
 
 
-      for (var d = 0; d < data.length; d++) {
-        id = data[d].id;
+        for (var d = 0; d < data.length; d++) {
+          id = data[d].id;
 
 
-        if (ids.indexOf(id) === -1) {
-          val.push(id);
+          if (val.indexOf(id) === -1) {
+            val.push(id);
+          }
         }
         }
-      }
+
+        self.$element.val(val);
+        self.$element.trigger("change");
+      });
     } else {
     } else {
-      val = data.id;
-    }
+      var val = data.id;
 
 
-    this.$element.val(val);
-    this.$element.trigger("change");
+      this.$element.val(val);
+      this.$element.trigger("change");
+    }
   }
   }
 
 
   SelectAdapter.prototype.query = function (params, callback) {
   SelectAdapter.prototype.query = function (params, callback) {

+ 13 - 3
src/js/select2/options.js

@@ -2,15 +2,25 @@ define([
   './data/select',
   './data/select',
   './results',
   './results',
   './dropdown',
   './dropdown',
-  './selection'
-], function (SelectData, ResultsList, Dropdown, Selection) {
+  './selection/single',
+  './selection/multiple'
+], function (SelectData, ResultsList, Dropdown, SingleSelection,
+             MultipleSelection) {
   function Options (options) {
   function Options (options) {
     this.options = options;
     this.options = options;
 
 
     this.dataAdapter = SelectData;
     this.dataAdapter = SelectData;
     this.resultsAdapter = ResultsList;
     this.resultsAdapter = ResultsList;
     this.dropdownAdapter = Dropdown;
     this.dropdownAdapter = Dropdown;
-    this.selectionAdapter = options.selectionAdapter || Selection;
+    this.selectionAdapter = options.selectionAdapter;
+
+    if (this.selectionAdapter == null) {
+      if (this.options.multiple) {
+        this.selectionAdapter = MultipleSelection;
+      } else {
+        this.selectionAdapter = SingleSelection;
+      }
+    }
   }
   }
 
 
   return Options;
   return Options;

+ 69 - 0
src/js/select2/selection/multiple.js

@@ -0,0 +1,69 @@
+define([
+  '../utils'
+], function (Utils) {
+  function MultipleSelection ($element, options) {
+    this.$element = $element;
+    this.options = options;
+
+    MultipleSelection.__super__.constructor.call(this);
+  }
+
+  Utils.Extend(MultipleSelection, Utils.Observable);
+
+  MultipleSelection.prototype.render = function () {
+    var $selection = $(
+      '<span class="multiple-select">' +
+        '<ul class="rendered-selection"></ul>' +
+      '</span>'
+    );
+
+    this.$selection = $selection;
+
+    return $selection;
+  }
+
+  MultipleSelection.prototype.bind = function ($container) {
+    var self = this;
+
+    this.$selection.on('click', function (evt) {
+      self.trigger("toggle", {
+        originalEvent: evt
+      });
+    });
+  }
+
+  MultipleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
+  }
+
+  MultipleSelection.prototype.display = function (data) {
+    return data.text;
+  }
+
+  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 formatted = this.display(selection);
+
+      var $selection = $('<ul class="choice"></ul>');
+
+      $selection.text(formatted);
+      $selection.data("data", data);
+
+      $selections.push($selection);
+    }
+
+    this.$selection.find(".rendered-selection").append($selections);
+  }
+
+  return MultipleSelection;
+});

+ 11 - 11
src/js/select2/selection.js → src/js/select2/selection/single.js

@@ -1,16 +1,16 @@
 define([
 define([
-  './utils'
+  '../utils'
 ], function (Utils) {
 ], function (Utils) {
-  function Selection ($element, options) {
+  function SingleSelection ($element, options) {
     this.$element = $element;
     this.$element = $element;
     this.options = options;
     this.options = options;
 
 
-    Selection.__super__.constructor.call(this);
+    SingleSelection.__super__.constructor.call(this);
   }
   }
 
 
-  Utils.Extend(Selection, Utils.Observable);
+  Utils.Extend(SingleSelection, Utils.Observable);
 
 
-  Selection.prototype.render = function () {
+  SingleSelection.prototype.render = function () {
     var $selection = $(
     var $selection = $(
       '<span class="single-select">' +
       '<span class="single-select">' +
         '<span class="rendered-selection"></span>' +
         '<span class="rendered-selection"></span>' +
@@ -22,7 +22,7 @@ define([
     return $selection;
     return $selection;
   }
   }
 
 
-  Selection.prototype.bind = function ($container) {
+  SingleSelection.prototype.bind = function ($container) {
     var self = this;
     var self = this;
 
 
     this.$selection.on('click', function (evt) {
     this.$selection.on('click', function (evt) {
@@ -32,15 +32,15 @@ define([
     });
     });
   }
   }
 
 
-  Selection.prototype.clear = function () {
-    this.$selection.find(".rendered-selection").text("");
+  SingleSelection.prototype.clear = function () {
+    this.$selection.find(".rendered-selection").empty();
   }
   }
 
 
-  Selection.prototype.display = function (data) {
+  SingleSelection.prototype.display = function (data) {
     return data.text;
     return data.text;
   }
   }
 
 
-  Selection.prototype.update = function (data) {
+  SingleSelection.prototype.update = function (data) {
     if (data.length == 0) {
     if (data.length == 0) {
       this.clear();
       this.clear();
       return;
       return;
@@ -53,5 +53,5 @@ define([
     this.$selection.find(".rendered-selection").html(formatted);
     this.$selection.find(".rendered-selection").html(formatted);
   }
   }
 
 
-  return Selection;
+  return SingleSelection;
 });
 });

+ 2 - 0
src/js/select2/utils.js

@@ -59,6 +59,8 @@ define([], function () {
       calledConstructor.apply(this, arguments);
       calledConstructor.apply(this, arguments);
     }
     }
 
 
+    DecoratorClass.displayName = SuperClass.displayName;
+
     function ctr () {
     function ctr () {
       this.constructor = DecoratedClass;
       this.constructor = DecoratedClass;
     }
     }

+ 3 - 2
src/scss/_dropdown.scss

@@ -11,10 +11,11 @@
 
 
     position: absolute;
     position: absolute;
     left: -100000px;
     left: -100000px;
-    top: -100000px;
 
 
     width: 100%;
     width: 100%;
 
 
+    z-index: 100;
+
     .results {
     .results {
       display: block;
       display: block;
 
 
@@ -35,10 +36,10 @@
   }
   }
 
 
   &.open .dropdown {
   &.open .dropdown {
+    border-top: none;
     border-top-left-radius: 0;
     border-top-left-radius: 0;
     border-top-right-radius: 0;
     border-top-right-radius: 0;
 
 
     left: 0;
     left: 0;
-    top: 28px;
   }
   }
 }
 }

+ 20 - 0
src/scss/_multiple.scss

@@ -0,0 +1,20 @@
+.select2-container {
+  .selection .multiple-select {
+    box-sizing: border-box;
+
+    cursor: pointer;
+    display: block;
+
+    min-height: 32px;
+
+    user-select: none;
+    -webkit-user-select: none;
+
+    .rendered-selection {
+      display: block;
+      overflow: hidden;
+      padding-left: 8px;
+      text-overflow: ellipsis;
+    }
+  }
+}

+ 2 - 0
src/scss/core.scss

@@ -8,6 +8,8 @@
 }
 }
 
 
 @import "single";
 @import "single";
+@import "multiple";
+
 @import "dropdown";
 @import "dropdown";
 
 
 @import "theme/default/layout";
 @import "theme/default/layout";

+ 42 - 12
src/scss/theme/default/layout.scss

@@ -1,20 +1,50 @@
 .select2-container.select2-theme-default {
 .select2-container.select2-theme-default {
-  .selection .single-select {
-    background-color: #eee;
-    border: 1px solid #aaa;
-    border-radius: 4px;
-
-    .rendered-selection {
-      color: #444;
-      line-height: 28px;
+  .selection {
+    .single-select {
+      background-color: #eee;
+      border: 1px solid #aaa;
+      border-radius: 4px;
+
+      .rendered-selection {
+        color: #444;
+        line-height: 28px;
+      }
+    }
+
+    .multiple-select {
+      background-color: white;
+      border: 1px solid #aaa;
+      border-radius: 4px;
+
+      .rendered-selection {
+        list-style: none;
+        margin: 0;
+        padding: 5px;
+        padding-bottom: 0;
+
+        .choice {
+          background-color: #e4e4e4;
+
+          border: 1px solid #aaa;
+          border-radius: 4px;
+
+          float: left;
+
+          margin-right: 5px;
+          margin-bottom: 5px;
+          padding: 0 5px;
+        }
+      }
     }
     }
   }
   }
 
 
   &.open {
   &.open {
-    .selection .single-select {
-      border-bottom: none;
-      border-bottom-left-radius: 0;
-      border-bottom-right-radius: 0;
+    .selection {
+      .single-select,
+      .multiple-select {
+        border-bottom-left-radius: 0;
+        border-bottom-right-radius: 0;
+      }
     }
     }
   }
   }