|
@@ -1,551 +1,551 @@
|
|
|
-define([
|
|
|
- '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);
|
|
|
- };
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- 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._sync = Utils.bind(this._syncAttributes, this);
|
|
|
-
|
|
|
- if (this.$element[0].attachEvent) {
|
|
|
- this.$element[0].attachEvent('onpropertychange', this._sync);
|
|
|
- }
|
|
|
-
|
|
|
- var observer = window.MutationObserver ||
|
|
|
- window.WebKitMutationObserver ||
|
|
|
- window.MozMutationObserver
|
|
|
- ;
|
|
|
-
|
|
|
- if (observer != null) {
|
|
|
- this._observer = new observer(function (mutations) {
|
|
|
- $.each(mutations, self._sync);
|
|
|
- });
|
|
|
- this._observer.observe(this.$element[0], {
|
|
|
- attributes: true,
|
|
|
- subtree: false
|
|
|
- });
|
|
|
- } else if (this.$element[0].addEventListener) {
|
|
|
- this.$element[0].addEventListener('DOMAttrModified', self._sync, 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', {});
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * 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'
|
|
|
- };
|
|
|
-
|
|
|
- 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._sync);
|
|
|
- }
|
|
|
-
|
|
|
- if (this._observer != null) {
|
|
|
- this._observer.disconnect();
|
|
|
- this._observer = null;
|
|
|
- } else if (this.$element[0].removeEventListener) {
|
|
|
- this.$element[0]
|
|
|
- .removeEventListener('DOMAttrModified', this._sync, false);
|
|
|
- }
|
|
|
-
|
|
|
- this._sync = 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.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;
|
|
|
-});
|
|
|
+define([
|
|
|
+ '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);
|
|
|
+ };
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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._sync = Utils.bind(this._syncAttributes, this);
|
|
|
+
|
|
|
+ if (this.$element[0].attachEvent) {
|
|
|
+ this.$element[0].attachEvent('onpropertychange', this._sync);
|
|
|
+ }
|
|
|
+
|
|
|
+ var observer = window.MutationObserver ||
|
|
|
+ window.WebKitMutationObserver ||
|
|
|
+ window.MozMutationObserver
|
|
|
+ ;
|
|
|
+
|
|
|
+ if (observer != null) {
|
|
|
+ this._observer = new observer(function (mutations) {
|
|
|
+ $.each(mutations, self._sync);
|
|
|
+ });
|
|
|
+ this._observer.observe(this.$element[0], {
|
|
|
+ attributes: true,
|
|
|
+ subtree: false
|
|
|
+ });
|
|
|
+ } else if (this.$element[0].addEventListener) {
|
|
|
+ this.$element[0].addEventListener('DOMAttrModified', self._sync, 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', {});
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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'
|
|
|
+ };
|
|
|
+
|
|
|
+ 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._sync);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this._observer != null) {
|
|
|
+ this._observer.disconnect();
|
|
|
+ this._observer = null;
|
|
|
+ } else if (this.$element[0].removeEventListener) {
|
|
|
+ this.$element[0]
|
|
|
+ .removeEventListener('DOMAttrModified', this._sync, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ this._sync = 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.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;
|
|
|
+});
|