core.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. define([
  2. 'jquery',
  3. './options',
  4. './utils',
  5. './keys'
  6. ], function ($, Options, Utils, KEYS) {
  7. var Select2 = function ($element, options) {
  8. this.$element = $element;
  9. this.id = this._generateId($element);
  10. options = options || {};
  11. this.options = new Options(options, $element);
  12. Select2.__super__.constructor.call(this);
  13. // Set up containers and adapters
  14. var DataAdapter = this.options.get('dataAdapter');
  15. this.data = new DataAdapter($element, this.options);
  16. var $container = this.render();
  17. this._placeContainer($container);
  18. var SelectionAdapter = this.options.get('selectionAdapter');
  19. this.selection = new SelectionAdapter($element, this.options);
  20. var $selection = this.selection.render();
  21. this._placeSelection($selection);
  22. var DropdownAdapter = this.options.get('dropdownAdapter');
  23. this.dropdown = new DropdownAdapter($element, this.options);
  24. var $dropdown = this.dropdown.render();
  25. this._placeDropdown($dropdown);
  26. var ResultsAdapter = this.options.get('resultsAdapter');
  27. this.results = new ResultsAdapter($element, this.options, this.data);
  28. var $results = this.results.render();
  29. this._placeResults($results);
  30. // Bind events
  31. var self = this;
  32. // Bind the container to all of the adapters
  33. this._bindAdapters();
  34. // Register any DOM event handlers
  35. this._registerDomEvents();
  36. // Register any internal event handlers
  37. this._registerSelectionEvents();
  38. this._registerDropdownEvents();
  39. this._registerResultsEvents();
  40. this._registerEvents();
  41. // Set the initial state
  42. this.data.current(function (initialData) {
  43. self.trigger('selection:update', {
  44. data: initialData
  45. });
  46. });
  47. // Hide the original select
  48. $element.hide();
  49. $element.attr('tabindex', '-1');
  50. $element.data('select2', this);
  51. };
  52. Utils.Extend(Select2, Utils.Observable);
  53. Select2.prototype._generateId = function ($element) {
  54. var id = '';
  55. if ($element.attr('id') != null) {
  56. id = $element.attr('id');
  57. } else if ($element.attr('name') != null) {
  58. id = $element.attr('name') + '-' + Utils.generateChars(2);
  59. } else {
  60. id = Utils.generateChars(4);
  61. }
  62. id = 'select2-' + id;
  63. return id;
  64. };
  65. Select2.prototype._placeContainer = function ($container) {
  66. $container.insertAfter(this.$element);
  67. $container.width(this.$element.outerWidth(false));
  68. };
  69. Select2.prototype._placeSelection = function ($selection) {
  70. var $selectionContainer = this.$container.find('.selection');
  71. $selectionContainer.append($selection);
  72. };
  73. Select2.prototype._placeDropdown = function ($dropdown) {
  74. this.$dropdown = $dropdown;
  75. var $dropdownContainer = this.$container.find('.dropdown-wrapper');
  76. $dropdownContainer.append($dropdown);
  77. };
  78. Select2.prototype._placeResults = function ($results) {
  79. var $resultsContainer = this.$dropdown.find('.results');
  80. $resultsContainer.append($results);
  81. };
  82. Select2.prototype._bindAdapters = function () {
  83. this.data.bind(this, this.$container);
  84. this.selection.bind(this, this.$container);
  85. this.dropdown.bind(this, this.$container);
  86. this.results.bind(this, this.$container);
  87. };
  88. Select2.prototype._registerDomEvents = function () {
  89. var self = this;
  90. this.$element.on('change', function () {
  91. self.data.current(function (data) {
  92. self.trigger('selection:update', {
  93. data: data
  94. });
  95. });
  96. });
  97. };
  98. Select2.prototype._registerSelectionEvents = function () {
  99. var self = this;
  100. this.selection.on('open', function () {
  101. self.open();
  102. });
  103. this.selection.on('close', function () {
  104. self.close();
  105. });
  106. this.selection.on('toggle', function () {
  107. self.toggleDropdown();
  108. });
  109. this.selection.on('results:select', function () {
  110. self.trigger('results:select');
  111. });
  112. this.selection.on('results:previous', function () {
  113. self.trigger('results:previous');
  114. });
  115. this.selection.on('results:next', function () {
  116. self.trigger('results:next');
  117. });
  118. this.selection.on('unselected', function (params) {
  119. self.trigger('unselect', params);
  120. self.close();
  121. });
  122. this.selection.on('keypress', function (e) {
  123. self.trigger('keypress', e);
  124. });
  125. };
  126. Select2.prototype._registerDropdownEvents = function () {
  127. var self = this;
  128. this.dropdown.on('query', function (params) {
  129. self.trigger('query', params);
  130. });
  131. this.dropdown.on('keypress', function (e) {
  132. self.trigger('keypress', e);
  133. });
  134. };
  135. Select2.prototype._registerResultsEvents = function () {
  136. var self = this;
  137. this.results.on('selected', function (params) {
  138. self.trigger('select', params);
  139. self.close();
  140. });
  141. this.results.on('unselected', function (params) {
  142. self.trigger('unselect', params);
  143. self.close();
  144. });
  145. this.results.on('results:focus', function (params) {
  146. self.trigger('results:focus', params);
  147. });
  148. };
  149. Select2.prototype._registerEvents = function () {
  150. var self = this;
  151. this.on('open', function () {
  152. self.$container.addClass('open');
  153. });
  154. this.on('close', function () {
  155. self.$container.removeClass('open');
  156. });
  157. this.on('query', function (params) {
  158. this.data.query(params, function (data) {
  159. self.trigger('results:all', {
  160. data: data,
  161. query: params
  162. });
  163. });
  164. });
  165. this.on('keypress', function (evt) {
  166. var key = evt.which;
  167. if (self.isOpen()) {
  168. if (key === KEYS.ENTER) {
  169. self.trigger('results:select');
  170. evt.preventDefault();
  171. } else if (key === KEYS.UP) {
  172. self.trigger('results:previous');
  173. evt.preventDefault();
  174. } else if (key === KEYS.DOWN) {
  175. self.trigger('results:next');
  176. evt.preventDefault();
  177. } else if (key === KEYS.ESC || key === KEYS.TAB) {
  178. self.close();
  179. evt.preventDefault();
  180. }
  181. } else {
  182. if (key === KEYS.ENTER || key === KEYS.SPACE) {
  183. self.open();
  184. evt.preventDefault();
  185. }
  186. }
  187. });
  188. };
  189. Select2.prototype.toggleDropdown = function () {
  190. if (this.isOpen()) {
  191. this.close();
  192. } else {
  193. this.open();
  194. }
  195. };
  196. Select2.prototype.open = function () {
  197. if (this.isOpen()) {
  198. return;
  199. }
  200. this.trigger('query', {});
  201. this.trigger('open');
  202. };
  203. Select2.prototype.close = function () {
  204. if (!this.isOpen()) {
  205. return;
  206. }
  207. this.trigger('close');
  208. };
  209. Select2.prototype.isOpen = function () {
  210. return this.$container.hasClass('open');
  211. };
  212. Select2.prototype.render = function () {
  213. var $container = $(
  214. '<span class="select2 select2-container select2-theme-default">' +
  215. '<span class="selection"></span>' +
  216. '<span class="dropdown-wrapper" aria-hidden="true"></span>' +
  217. '</span>'
  218. );
  219. this.$container = $container;
  220. $container.data('element', this.$element);
  221. return $container;
  222. };
  223. return Select2;
  224. });