1
0
mirror of synced 2025-02-04 06:09:23 +03:00
This commit is contained in:
Nadim Afana 2016-05-05 15:38:31 -07:00
parent 07aa8a45b0
commit 54e126a664
12 changed files with 2700 additions and 2636 deletions

View File

@ -1,57 +1,58 @@
define([ define([
'jquery', 'jquery',
'jquery-mousewheel', 'jquery-mousewheel',
'./select2/core', './select2/core',
'./select2/defaults' './select2/defaults',
], function ($, _, Select2, Defaults) { './select2/utils'
if ($.fn.select2 == null) { ], function ($, _, Select2, Defaults, Utils) {
// All methods that should return the element if ($.fn.select2 == null) {
var thisMethods = ['open', 'close', 'destroy']; // All methods that should return the element
var thisMethods = ['open', 'close', 'destroy'];
$.fn.select2 = function (options) {
options = options || {}; $.fn.select2 = function (options) {
options = options || {};
if (typeof options === 'object') {
this.each(function () { if (typeof options === 'object') {
var instanceOptions = $.extend(true, {}, options); this.each(function () {
var instanceOptions = $.extend(true, {}, options);
var instance = new Select2($(this), instanceOptions);
}); var instance = new Select2($(this), instanceOptions);
});
return this;
} else if (typeof options === 'string') { return this;
var ret; } else if (typeof options === 'string') {
var args = Array.prototype.slice.call(arguments, 1); var ret;
var args = Array.prototype.slice.call(arguments, 1);
this.each(function () {
var instance = $(this).data('select2'); this.each(function () {
var instance = Utils.GetData(this, 'select2');
if (instance == null && window.console && console.error) {
console.error( if (instance == null && window.console && console.error) {
'The select2(\'' + options + '\') method was called on an ' + console.error(
'element that is not using Select2.' 'The select2(\'' + options + '\') method was called on an ' +
); 'element that is not using Select2.'
} );
}
ret = instance[options].apply(instance, args);
}); ret = instance[options].apply(instance, args);
});
// Check if we should be returning `this`
if ($.inArray(options, thisMethods) > -1) { // Check if we should be returning `this`
return this; if ($.inArray(options, thisMethods) > -1) {
} return this;
}
return ret;
} else { return ret;
throw new Error('Invalid arguments for Select2: ' + options); } else {
} throw new Error('Invalid arguments for Select2: ' + options);
}; }
} };
}
if ($.fn.select2.defaults == null) {
$.fn.select2.defaults = Defaults; if ($.fn.select2.defaults == null) {
} $.fn.select2.defaults = Defaults;
}
return Select2;
}); return Select2;
});

1101
src/js/select2/core.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,285 +1,285 @@
define([ define([
'./base', './base',
'../utils', '../utils',
'jquery' 'jquery'
], function (BaseAdapter, Utils, $) { ], function (BaseAdapter, Utils, $) {
function SelectAdapter ($element, options) { function SelectAdapter ($element, options) {
this.$element = $element; this.$element = $element;
this.options = options; this.options = options;
SelectAdapter.__super__.constructor.call(this); SelectAdapter.__super__.constructor.call(this);
} }
Utils.Extend(SelectAdapter, BaseAdapter); Utils.Extend(SelectAdapter, BaseAdapter);
SelectAdapter.prototype.current = function (callback) { SelectAdapter.prototype.current = function (callback) {
var data = []; var data = [];
var self = this; var self = this;
this.$element.find(':selected').each(function () { this.$element.find(':selected').each(function () {
var $option = $(this); var $option = $(this);
var option = self.item($option); var option = self.item($option);
data.push(option); data.push(option);
}); });
callback(data); callback(data);
}; };
SelectAdapter.prototype.select = function (data) { SelectAdapter.prototype.select = function (data) {
var self = this; var self = this;
data.selected = true; data.selected = true;
// If data.element is a DOM node, use it instead // If data.element is a DOM node, use it instead
if ($(data.element).is('option')) { if ($(data.element).is('option')) {
data.element.selected = true; data.element.selected = true;
this.$element.trigger('change'); this.$element.trigger('change');
return; return;
} }
if (this.$element.prop('multiple')) { if (this.$element.prop('multiple')) {
this.current(function (currentData) { this.current(function (currentData) {
var val = []; var val = [];
data = [data]; data = [data];
data.push.apply(data, currentData); data.push.apply(data, currentData);
for (var d = 0; d < data.length; d++) { for (var d = 0; d < data.length; d++) {
var id = data[d].id; var id = data[d].id;
if ($.inArray(id, val) === -1) { if ($.inArray(id, val) === -1) {
val.push(id); val.push(id);
} }
} }
self.$element.val(val); self.$element.val(val);
self.$element.trigger('change'); self.$element.trigger('change');
}); });
} else { } else {
var val = data.id; var val = data.id;
this.$element.val(val); this.$element.val(val);
this.$element.trigger('change'); this.$element.trigger('change');
} }
}; };
SelectAdapter.prototype.unselect = function (data) { SelectAdapter.prototype.unselect = function (data) {
var self = this; var self = this;
if (!this.$element.prop('multiple')) { if (!this.$element.prop('multiple')) {
return; return;
} }
data.selected = false; data.selected = false;
if ($(data.element).is('option')) { if ($(data.element).is('option')) {
data.element.selected = false; data.element.selected = false;
this.$element.trigger('change'); this.$element.trigger('change');
return; return;
} }
this.current(function (currentData) { this.current(function (currentData) {
var val = []; var val = [];
for (var d = 0; d < currentData.length; d++) { for (var d = 0; d < currentData.length; d++) {
var id = currentData[d].id; var id = currentData[d].id;
if (id !== data.id && $.inArray(id, val) === -1) { if (id !== data.id && $.inArray(id, val) === -1) {
val.push(id); val.push(id);
} }
} }
self.$element.val(val); self.$element.val(val);
self.$element.trigger('change'); self.$element.trigger('change');
}); });
}; };
SelectAdapter.prototype.bind = function (container, $container) { SelectAdapter.prototype.bind = function (container, $container) {
var self = this; var self = this;
this.container = container; this.container = container;
container.on('select', function (params) { container.on('select', function (params) {
self.select(params.data); self.select(params.data);
}); });
container.on('unselect', function (params) { container.on('unselect', function (params) {
self.unselect(params.data); self.unselect(params.data);
}); });
}; };
SelectAdapter.prototype.destroy = function () { SelectAdapter.prototype.destroy = function () {
// Remove anything added to child elements // Remove anything added to child elements
this.$element.find('*').each(function () { this.$element.find('*').each(function () {
// Remove any custom data set by Select2 // Remove any custom data set by Select2
$.removeData(this, 'data'); Utils.RemoveData(this);
}); });
}; };
SelectAdapter.prototype.query = function (params, callback) { SelectAdapter.prototype.query = function (params, callback) {
var data = []; var data = [];
var self = this; var self = this;
var $options = this.$element.children(); var $options = this.$element.children();
$options.each(function () { $options.each(function () {
var $option = $(this); var $option = $(this);
if (!$option.is('option') && !$option.is('optgroup')) { if (!$option.is('option') && !$option.is('optgroup')) {
return; return;
} }
var option = self.item($option); var option = self.item($option);
var matches = self.matches(params, option); var matches = self.matches(params, option);
if (matches !== null) { if (matches !== null) {
data.push(matches); data.push(matches);
} }
}); });
callback({ callback({
results: data results: data
}); });
}; };
SelectAdapter.prototype.addOptions = function ($options) { SelectAdapter.prototype.addOptions = function ($options) {
Utils.appendMany(this.$element, $options); Utils.appendMany(this.$element, $options);
}; };
SelectAdapter.prototype.option = function (data) { SelectAdapter.prototype.option = function (data) {
var option; var option;
if (data.children) { if (data.children) {
option = document.createElement('optgroup'); option = document.createElement('optgroup');
option.label = data.text; option.label = data.text;
} else { } else {
option = document.createElement('option'); option = document.createElement('option');
if (option.textContent !== undefined) { if (option.textContent !== undefined) {
option.textContent = data.text; option.textContent = data.text;
} else { } else {
option.innerText = data.text; option.innerText = data.text;
} }
} }
if (data.id) { if (data.id) {
option.value = data.id; option.value = data.id;
} }
if (data.disabled) { if (data.disabled) {
option.disabled = true; option.disabled = true;
} }
if (data.selected) { if (data.selected) {
option.selected = true; option.selected = true;
} }
if (data.title) { if (data.title) {
option.title = data.title; option.title = data.title;
} }
var $option = $(option); var $option = $(option);
var normalizedData = this._normalizeItem(data); var normalizedData = this._normalizeItem(data);
normalizedData.element = option; normalizedData.element = option;
// Override the option's data with the combined data // Override the option's data with the combined data
$.data(option, 'data', normalizedData); Utils.StoreData(option, 'data', normalizedData);
return $option; return $option;
}; };
SelectAdapter.prototype.item = function ($option) { SelectAdapter.prototype.item = function ($option) {
var data = {}; var data = {};
data = $.data($option[0], 'data'); data = Utils.GetData($option[0], 'data');
if (data != null) { if (data != null) {
return data; return data;
} }
if ($option.is('option')) { if ($option.is('option')) {
data = { data = {
id: $option.val(), id: $option.val(),
text: $option.text(), text: $option.text(),
disabled: $option.prop('disabled'), disabled: $option.prop('disabled'),
selected: $option.prop('selected'), selected: $option.prop('selected'),
title: $option.prop('title') title: $option.prop('title')
}; };
} else if ($option.is('optgroup')) { } else if ($option.is('optgroup')) {
data = { data = {
text: $option.prop('label'), text: $option.prop('label'),
children: [], children: [],
title: $option.prop('title') title: $option.prop('title')
}; };
var $children = $option.children('option'); var $children = $option.children('option');
var children = []; var children = [];
for (var c = 0; c < $children.length; c++) { for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]); var $child = $($children[c]);
var child = this.item($child); var child = this.item($child);
children.push(child); children.push(child);
} }
data.children = children; data.children = children;
} }
data = this._normalizeItem(data); data = this._normalizeItem(data);
data.element = $option[0]; data.element = $option[0];
$.data($option[0], 'data', data); Utils.StoreData($option[0], 'data', data);
return data; return data;
}; };
SelectAdapter.prototype._normalizeItem = function (item) { SelectAdapter.prototype._normalizeItem = function (item) {
if (!$.isPlainObject(item)) { if (!$.isPlainObject(item)) {
item = { item = {
id: item, id: item,
text: item text: item
}; };
} }
item = $.extend({}, { item = $.extend({}, {
text: '' text: ''
}, item); }, item);
var defaults = { var defaults = {
selected: false, selected: false,
disabled: false disabled: false
}; };
if (item.id != null) { if (item.id != null) {
item.id = item.id.toString(); item.id = item.id.toString();
} }
if (item.text != null) { if (item.text != null) {
item.text = item.text.toString(); item.text = item.text.toString();
} }
if (item._resultId == null && item.id && this.container != null) { if (item._resultId == null && item.id && this.container != null) {
item._resultId = this.generateResultId(this.container, item); item._resultId = this.generateResultId(this.container, item);
} }
return $.extend({}, defaults, item); return $.extend({}, defaults, item);
}; };
SelectAdapter.prototype.matches = function (params, data) { SelectAdapter.prototype.matches = function (params, data) {
var matcher = this.options.get('matcher'); var matcher = this.options.get('matcher');
return matcher(params, data); return matcher(params, data);
}; };
return SelectAdapter; return SelectAdapter;
}); });

View File

@ -1,222 +1,222 @@
define([ define([
'jquery', 'jquery',
'../utils' '../utils'
], function ($, Utils) { ], function ($, Utils) {
function AttachBody (decorated, $element, options) { function AttachBody (decorated, $element, options) {
this.$dropdownParent = options.get('dropdownParent') || $(document.body); this.$dropdownParent = options.get('dropdownParent') || $(document.body);
decorated.call(this, $element, options); decorated.call(this, $element, options);
} }
AttachBody.prototype.bind = function (decorated, container, $container) { AttachBody.prototype.bind = function (decorated, container, $container) {
var self = this; var self = this;
var setupResultsEvents = false; var setupResultsEvents = false;
decorated.call(this, container, $container); decorated.call(this, container, $container);
container.on('open', function () { container.on('open', function () {
self._showDropdown(); self._showDropdown();
self._attachPositioningHandler(container); self._attachPositioningHandler(container);
if (!setupResultsEvents) { if (!setupResultsEvents) {
setupResultsEvents = true; setupResultsEvents = true;
container.on('results:all', function () { container.on('results:all', function () {
self._positionDropdown(); self._positionDropdown();
self._resizeDropdown(); self._resizeDropdown();
}); });
container.on('results:append', function () { container.on('results:append', function () {
self._positionDropdown(); self._positionDropdown();
self._resizeDropdown(); self._resizeDropdown();
}); });
} }
}); });
container.on('close', function () { container.on('close', function () {
self._hideDropdown(); self._hideDropdown();
self._detachPositioningHandler(container); self._detachPositioningHandler(container);
}); });
this.$dropdownContainer.on('mousedown', function (evt) { this.$dropdownContainer.on('mousedown', function (evt) {
evt.stopPropagation(); evt.stopPropagation();
}); });
}; };
AttachBody.prototype.destroy = function (decorated) { AttachBody.prototype.destroy = function (decorated) {
decorated.call(this); decorated.call(this);
this.$dropdownContainer.remove(); this.$dropdownContainer.remove();
}; };
AttachBody.prototype.position = function (decorated, $dropdown, $container) { AttachBody.prototype.position = function (decorated, $dropdown, $container) {
// Clone all of the container classes // Clone all of the container classes
$dropdown.attr('class', $container.attr('class')); $dropdown.attr('class', $container.attr('class'));
$dropdown.removeClass('select2'); $dropdown.removeClass('select2');
$dropdown.addClass('select2-container--open'); $dropdown.addClass('select2-container--open');
$dropdown.css({ $dropdown.css({
position: 'absolute', position: 'absolute',
top: -999999 top: -999999
}); });
this.$container = $container; this.$container = $container;
}; };
AttachBody.prototype.render = function (decorated) { AttachBody.prototype.render = function (decorated) {
var $container = $('<span></span>'); var $container = $('<span></span>');
var $dropdown = decorated.call(this); var $dropdown = decorated.call(this);
$container.append($dropdown); $container.append($dropdown);
this.$dropdownContainer = $container; this.$dropdownContainer = $container;
return $container; return $container;
}; };
AttachBody.prototype._hideDropdown = function (decorated) { AttachBody.prototype._hideDropdown = function (decorated) {
this.$dropdownContainer.detach(); this.$dropdownContainer.detach();
}; };
AttachBody.prototype._attachPositioningHandler = AttachBody.prototype._attachPositioningHandler =
function (decorated, container) { function (decorated, container) {
var self = this; var self = this;
var scrollEvent = 'scroll.select2.' + container.id; var scrollEvent = 'scroll.select2.' + container.id;
var resizeEvent = 'resize.select2.' + container.id; var resizeEvent = 'resize.select2.' + container.id;
var orientationEvent = 'orientationchange.select2.' + container.id; var orientationEvent = 'orientationchange.select2.' + container.id;
var $watchers = this.$container.parents().filter(Utils.hasScroll); var $watchers = this.$container.parents().filter(Utils.hasScroll);
$watchers.each(function () { $watchers.each(function () {
$(this).data('select2-scroll-position', { Utils.StoreData(this, 'select2-scroll-position', {
x: $(this).scrollLeft(), x: $(this).scrollLeft(),
y: $(this).scrollTop() y: $(this).scrollTop()
}); });
}); });
$watchers.on(scrollEvent, function (ev) { $watchers.on(scrollEvent, function (ev) {
var position = $(this).data('select2-scroll-position'); var position = Utils.GetData(this, 'select2-scroll-position');
$(this).scrollTop(position.y); $(this).scrollTop(position.y);
}); });
$(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent, $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
function (e) { function (e) {
self._positionDropdown(); self._positionDropdown();
self._resizeDropdown(); self._resizeDropdown();
}); });
}; };
AttachBody.prototype._detachPositioningHandler = AttachBody.prototype._detachPositioningHandler =
function (decorated, container) { function (decorated, container) {
var scrollEvent = 'scroll.select2.' + container.id; var scrollEvent = 'scroll.select2.' + container.id;
var resizeEvent = 'resize.select2.' + container.id; var resizeEvent = 'resize.select2.' + container.id;
var orientationEvent = 'orientationchange.select2.' + container.id; var orientationEvent = 'orientationchange.select2.' + container.id;
var $watchers = this.$container.parents().filter(Utils.hasScroll); var $watchers = this.$container.parents().filter(Utils.hasScroll);
$watchers.off(scrollEvent); $watchers.off(scrollEvent);
$(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent); $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
}; };
AttachBody.prototype._positionDropdown = function () { AttachBody.prototype._positionDropdown = function () {
var $window = $(window); var $window = $(window);
var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above'); var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below'); var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
var newDirection = null; var newDirection = null;
var offset = this.$container.offset(); var offset = this.$container.offset();
offset.bottom = offset.top + this.$container.outerHeight(false); offset.bottom = offset.top + this.$container.outerHeight(false);
var container = { var container = {
height: this.$container.outerHeight(false) height: this.$container.outerHeight(false)
}; };
container.top = offset.top; container.top = offset.top;
container.bottom = offset.top + container.height; container.bottom = offset.top + container.height;
var dropdown = { var dropdown = {
height: this.$dropdown.outerHeight(false) height: this.$dropdown.outerHeight(false)
}; };
var viewport = { var viewport = {
top: $window.scrollTop(), top: $window.scrollTop(),
bottom: $window.scrollTop() + $window.height() bottom: $window.scrollTop() + $window.height()
}; };
var enoughRoomAbove = viewport.top < (offset.top - dropdown.height); var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height); var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
var css = { var css = {
left: offset.left, left: offset.left,
top: container.bottom top: container.bottom
}; };
// Determine what the parent element is to use for calciulating the offset // Determine what the parent element is to use for calciulating the offset
var $offsetParent = this.$dropdownParent; var $offsetParent = this.$dropdownParent;
// For statically positoned elements, we need to get the element // For statically positoned elements, we need to get the element
// that is determining the offset // that is determining the offset
if ($offsetParent.css('position') === 'static') { if ($offsetParent.css('position') === 'static') {
$offsetParent = $offsetParent.offsetParent(); $offsetParent = $offsetParent.offsetParent();
} }
var parentOffset = $offsetParent.offset(); var parentOffset = $offsetParent.offset();
css.top -= parentOffset.top; css.top -= parentOffset.top;
css.left -= parentOffset.left; css.left -= parentOffset.left;
if (!isCurrentlyAbove && !isCurrentlyBelow) { if (!isCurrentlyAbove && !isCurrentlyBelow) {
newDirection = 'below'; newDirection = 'below';
} }
if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) { if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
newDirection = 'above'; newDirection = 'above';
} else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) { } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
newDirection = 'below'; newDirection = 'below';
} }
if (newDirection == 'above' || if (newDirection == 'above' ||
(isCurrentlyAbove && newDirection !== 'below')) { (isCurrentlyAbove && newDirection !== 'below')) {
css.top = container.top - parentOffset.top - dropdown.height; css.top = container.top - parentOffset.top - dropdown.height;
} }
if (newDirection != null) { if (newDirection != null) {
this.$dropdown this.$dropdown
.removeClass('select2-dropdown--below select2-dropdown--above') .removeClass('select2-dropdown--below select2-dropdown--above')
.addClass('select2-dropdown--' + newDirection); .addClass('select2-dropdown--' + newDirection);
this.$container this.$container
.removeClass('select2-container--below select2-container--above') .removeClass('select2-container--below select2-container--above')
.addClass('select2-container--' + newDirection); .addClass('select2-container--' + newDirection);
} }
this.$dropdownContainer.css(css); this.$dropdownContainer.css(css);
}; };
AttachBody.prototype._resizeDropdown = function () { AttachBody.prototype._resizeDropdown = function () {
var css = { var css = {
width: this.$container.outerWidth(false) + 'px' width: this.$container.outerWidth(false) + 'px'
}; };
if (this.options.get('dropdownAutoWidth')) { if (this.options.get('dropdownAutoWidth')) {
css.minWidth = css.width; css.minWidth = css.width;
css.position = 'relative'; css.position = 'relative';
css.width = 'auto'; css.width = 'auto';
} }
this.$dropdown.css(css); this.$dropdown.css(css);
}; };
AttachBody.prototype._showDropdown = function (decorated) { AttachBody.prototype._showDropdown = function (decorated) {
this.$dropdownContainer.appendTo(this.$dropdownParent); this.$dropdownContainer.appendTo(this.$dropdownParent);
this._positionDropdown(); this._positionDropdown();
this._resizeDropdown(); this._resizeDropdown();
}; };
return AttachBody; return AttachBody;
}); });

View File

@ -1,40 +1,40 @@
define([ define([
'../utils'
], function () { ], function (Utils) {
function SelectOnClose () { } function SelectOnClose () { }
SelectOnClose.prototype.bind = function (decorated, container, $container) { SelectOnClose.prototype.bind = function (decorated, container, $container) {
var self = this; var self = this;
decorated.call(this, container, $container); decorated.call(this, container, $container);
container.on('close', function () { container.on('close', function () {
self._handleSelectOnClose(); self._handleSelectOnClose();
}); });
}; };
SelectOnClose.prototype._handleSelectOnClose = function () { SelectOnClose.prototype._handleSelectOnClose = function () {
var $highlightedResults = this.getHighlightedResults(); var $highlightedResults = this.getHighlightedResults();
// Only select highlighted results // Only select highlighted results
if ($highlightedResults.length < 1) { if ($highlightedResults.length < 1) {
return; return;
} }
var data = $highlightedResults.data('data'); var data = Utils.GetData($highlightedResults[0], 'data');
// Don't re-select already selected resulte // Don't re-select already selected resulte
if ( if (
(data.element != null && data.element.selected) || (data.element != null && data.element.selected) ||
(data.element == null && data.selected) (data.element == null && data.selected)
) { ) {
return; return;
} }
this.trigger('select', { this.trigger('select', {
data: data data: data
}); });
}; };
return SelectOnClose; return SelectOnClose;
}); });

View File

@ -1,122 +1,122 @@
define([ define([
'require', 'require',
'jquery', 'jquery',
'./defaults', './defaults',
'./utils' './utils'
], function (require, $, Defaults, Utils) { ], function (require, $, Defaults, Utils) {
function Options (options, $element) { function Options (options, $element) {
this.options = options; this.options = options;
if ($element != null) { if ($element != null) {
this.fromElement($element); this.fromElement($element);
} }
this.options = Defaults.apply(this.options); this.options = Defaults.apply(this.options);
if ($element && $element.is('input')) { if ($element && $element.is('input')) {
var InputCompat = require(this.get('amdBase') + 'compat/inputData'); var InputCompat = require(this.get('amdBase') + 'compat/inputData');
this.options.dataAdapter = Utils.Decorate( this.options.dataAdapter = Utils.Decorate(
this.options.dataAdapter, this.options.dataAdapter,
InputCompat InputCompat
); );
} }
} }
Options.prototype.fromElement = function ($e) { Options.prototype.fromElement = function ($e) {
var excludedData = ['select2']; var excludedData = ['select2'];
if (this.options.multiple == null) { if (this.options.multiple == null) {
this.options.multiple = $e.prop('multiple'); this.options.multiple = $e.prop('multiple');
} }
if (this.options.disabled == null) { if (this.options.disabled == null) {
this.options.disabled = $e.prop('disabled'); this.options.disabled = $e.prop('disabled');
} }
if (this.options.language == null) { if (this.options.language == null) {
if ($e.prop('lang')) { if ($e.prop('lang')) {
this.options.language = $e.prop('lang').toLowerCase(); this.options.language = $e.prop('lang').toLowerCase();
} else if ($e.closest('[lang]').prop('lang')) { } else if ($e.closest('[lang]').prop('lang')) {
this.options.language = $e.closest('[lang]').prop('lang'); this.options.language = $e.closest('[lang]').prop('lang');
} }
} }
if (this.options.dir == null) { if (this.options.dir == null) {
if ($e.prop('dir')) { if ($e.prop('dir')) {
this.options.dir = $e.prop('dir'); this.options.dir = $e.prop('dir');
} else if ($e.closest('[dir]').prop('dir')) { } else if ($e.closest('[dir]').prop('dir')) {
this.options.dir = $e.closest('[dir]').prop('dir'); this.options.dir = $e.closest('[dir]').prop('dir');
} else { } else {
this.options.dir = 'ltr'; this.options.dir = 'ltr';
} }
} }
$e.prop('disabled', this.options.disabled); $e.prop('disabled', this.options.disabled);
$e.prop('multiple', this.options.multiple); $e.prop('multiple', this.options.multiple);
if ($e.data('select2Tags')) { if (Utils.GetData($e[0], 'select2Tags')) {
if (this.options.debug && window.console && console.warn) { if (this.options.debug && window.console && console.warn) {
console.warn( console.warn(
'Select2: The `data-select2-tags` attribute has been changed to ' + 'Select2: The `data-select2-tags` attribute has been changed to ' +
'use the `data-data` and `data-tags="true"` attributes and will be ' + 'use the `data-data` and `data-tags="true"` attributes and will be ' +
'removed in future versions of Select2.' 'removed in future versions of Select2.'
); );
} }
$e.data('data', $e.data('select2Tags')); Utils.StoreData($e[0], 'data', Utils.GetData($e[0], 'select2Tags'));
$e.data('tags', true); Utils.StoreData($e[0], 'tags', true);
} }
if ($e.data('ajaxUrl')) { if (Utils.GetData($e[0], 'ajaxUrl')) {
if (this.options.debug && window.console && console.warn) { if (this.options.debug && window.console && console.warn) {
console.warn( console.warn(
'Select2: The `data-ajax-url` attribute has been changed to ' + 'Select2: The `data-ajax-url` attribute has been changed to ' +
'`data-ajax--url` and support for the old attribute will be removed' + '`data-ajax--url` and support for the old attribute will be removed' +
' in future versions of Select2.' ' in future versions of Select2.'
); );
} }
$e.attr('ajax--url', $e.data('ajaxUrl')); $e.attr('ajax--url', Utils.GetData($e[0], 'ajaxUrl'));
$e.data('ajax--url', $e.data('ajaxUrl')); Utils.StoreData($e[0], 'ajax--url', Utils.GetData($e[0], 'ajaxUrl'));
} }
var dataset = {}; var dataset = {};
// Prefer the element's `dataset` attribute if it exists // Prefer the element's `dataset` attribute if it exists
// jQuery 1.x does not correctly handle data attributes with multiple dashes // jQuery 1.x does not correctly handle data attributes with multiple dashes
if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) { if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) {
dataset = $.extend(true, {}, $e[0].dataset, $e.data()); dataset = $.extend(true, {}, $e[0].dataset, Utils.GetData($e[0]));
} else { } else {
dataset = $e.data(); dataset = Utils.GetData($e[0]);
} }
var data = $.extend(true, {}, dataset); var data = $.extend(true, {}, dataset);
data = Utils._convertData(data); data = Utils._convertData(data);
for (var key in data) { for (var key in data) {
if ($.inArray(key, excludedData) > -1) { if ($.inArray(key, excludedData) > -1) {
continue; continue;
} }
if ($.isPlainObject(this.options[key])) { if ($.isPlainObject(this.options[key])) {
$.extend(this.options[key], data[key]); $.extend(this.options[key], data[key]);
} else { } else {
this.options[key] = data[key]; this.options[key] = data[key];
} }
} }
return this; return this;
}; };
Options.prototype.get = function (key) { Options.prototype.get = function (key) {
return this.options[key]; return this.options[key];
}; };
Options.prototype.set = function (key, val) { Options.prototype.set = function (key, val) {
this.options[key] = val; this.options[key] = val;
}; };
return Options; return Options;
}); });

File diff suppressed because it is too large Load Diff

View File

@ -1,97 +1,98 @@
define([ define([
'jquery', 'jquery',
'../keys' '../keys',
], function ($, KEYS) { '../utils'
function AllowClear () { } ], function ($, KEYS, Utils) {
function AllowClear () { }
AllowClear.prototype.bind = function (decorated, container, $container) {
var self = this; AllowClear.prototype.bind = function (decorated, container, $container) {
var self = this;
decorated.call(this, container, $container);
decorated.call(this, container, $container);
if (this.placeholder == null) {
if (this.options.get('debug') && window.console && console.error) { if (this.placeholder == null) {
console.error( if (this.options.get('debug') && window.console && console.error) {
'Select2: The `allowClear` option should be used in combination ' + console.error(
'with the `placeholder` option.' 'Select2: The `allowClear` option should be used in combination ' +
); 'with the `placeholder` option.'
} );
} }
}
this.$selection.on('mousedown', '.select2-selection__clear',
function (evt) { this.$selection.on('mousedown', '.select2-selection__clear',
self._handleClear(evt); function (evt) {
}); self._handleClear(evt);
});
container.on('keypress', function (evt) {
self._handleKeyboardClear(evt, container); container.on('keypress', function (evt) {
}); self._handleKeyboardClear(evt, container);
}; });
};
AllowClear.prototype._handleClear = function (_, evt) {
// Ignore the event if it is disabled AllowClear.prototype._handleClear = function (_, evt) {
if (this.options.get('disabled')) { // Ignore the event if it is disabled
return; if (this.options.get('disabled')) {
} return;
}
var $clear = this.$selection.find('.select2-selection__clear');
var $clear = this.$selection.find('.select2-selection__clear');
// Ignore the event if nothing has been selected
if ($clear.length === 0) { // Ignore the event if nothing has been selected
return; if ($clear.length === 0) {
} return;
}
evt.stopPropagation();
evt.stopPropagation();
var data = $clear.data('data');
var data = Utils.GetData($clear[0], 'data');
for (var d = 0; d < data.length; d++) {
var unselectData = { for (var d = 0; d < data.length; d++) {
data: data[d] var unselectData = {
}; data: data[d]
};
// Trigger the `unselect` event, so people can prevent it from being
// cleared. // Trigger the `unselect` event, so people can prevent it from being
this.trigger('unselect', unselectData); // cleared.
this.trigger('unselect', unselectData);
// If the event was prevented, don't clear it out.
if (unselectData.prevented) { // If the event was prevented, don't clear it out.
return; if (unselectData.prevented) {
} return;
} }
}
this.$element.val(this.placeholder.id).trigger('change');
this.$element.val(this.placeholder.id).trigger('change');
this.trigger('toggle', {});
}; this.trigger('toggle', {});
};
AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
if (container.isOpen()) { AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
return; if (container.isOpen()) {
} return;
}
if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
this._handleClear(evt); if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
} this._handleClear(evt);
}; }
};
AllowClear.prototype.update = function (decorated, data) {
decorated.call(this, data); AllowClear.prototype.update = function (decorated, data) {
decorated.call(this, data);
if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
data.length === 0) { if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
return; data.length === 0) {
} return;
}
var $remove = $(
'<span class="select2-selection__clear">' + var $remove = $(
'&times;' + '<span class="select2-selection__clear">' +
'</span>' '&times;' +
); '</span>'
$remove.data('data', data); );
Utils.StoreData($remove[0], 'data', data);
this.$selection.find('.select2-selection__rendered').prepend($remove);
}; this.$selection.find('.select2-selection__rendered').prepend($remove);
};
return AllowClear;
}); return AllowClear;
});

View File

@ -1,158 +1,158 @@
define([ define([
'jquery', 'jquery',
'../utils', '../utils',
'../keys' '../keys'
], function ($, Utils, KEYS) { ], function ($, Utils, KEYS) {
function BaseSelection ($element, options) { function BaseSelection ($element, options) {
this.$element = $element; this.$element = $element;
this.options = options; this.options = options;
BaseSelection.__super__.constructor.call(this); BaseSelection.__super__.constructor.call(this);
} }
Utils.Extend(BaseSelection, Utils.Observable); Utils.Extend(BaseSelection, Utils.Observable);
BaseSelection.prototype.render = function () { BaseSelection.prototype.render = function () {
var $selection = $( var $selection = $(
'<span class="select2-selection" role="combobox" ' + '<span class="select2-selection" role="combobox" ' +
' aria-haspopup="true" aria-expanded="false">' + ' aria-haspopup="true" aria-expanded="false">' +
'</span>' '</span>'
); );
this._tabindex = 0; this._tabindex = 0;
if (this.$element.data('old-tabindex') != null) { if (Utils.GetData(this.$element[0], 'old-tabindex') != null) {
this._tabindex = this.$element.data('old-tabindex'); this._tabindex = Utils.GetData(this.$element[0], 'old-tabindex');
} else if (this.$element.attr('tabindex') != null) { } else if (Utils.GetData(this.$element[0], 'tabindex') != null) {
this._tabindex = this.$element.attr('tabindex'); this._tabindex = Utils.GetData(this.$element[0], 'tabindex');
} }
$selection.attr('title', this.$element.attr('title')); $selection.attr('title', this.$element.attr('title'));
$selection.attr('tabindex', this._tabindex); $selection.attr('tabindex', this._tabindex);
this.$selection = $selection; this.$selection = $selection;
return $selection; return $selection;
}; };
BaseSelection.prototype.bind = function (container, $container) { BaseSelection.prototype.bind = function (container, $container) {
var self = this; var self = this;
var id = container.id + '-container'; var id = container.id + '-container';
var resultsId = container.id + '-results'; var resultsId = container.id + '-results';
this.container = container; this.container = container;
this.$selection.on('focus', function (evt) { this.$selection.on('focus', function (evt) {
self.trigger('focus', evt); self.trigger('focus', evt);
}); });
this.$selection.on('blur', function (evt) { this.$selection.on('blur', function (evt) {
self._handleBlur(evt); self._handleBlur(evt);
}); });
this.$selection.on('keydown', function (evt) { this.$selection.on('keydown', function (evt) {
self.trigger('keypress', evt); self.trigger('keypress', evt);
if (evt.which === KEYS.SPACE) { if (evt.which === KEYS.SPACE) {
evt.preventDefault(); evt.preventDefault();
} }
}); });
container.on('results:focus', function (params) { container.on('results:focus', function (params) {
self.$selection.attr('aria-activedescendant', params.data._resultId); self.$selection.attr('aria-activedescendant', params.data._resultId);
}); });
container.on('selection:update', function (params) { container.on('selection:update', function (params) {
self.update(params.data); self.update(params.data);
}); });
container.on('open', function () { container.on('open', function () {
// When the dropdown is open, aria-expanded="true" // When the dropdown is open, aria-expanded="true"
self.$selection.attr('aria-expanded', 'true'); self.$selection.attr('aria-expanded', 'true');
self.$selection.attr('aria-owns', resultsId); self.$selection.attr('aria-owns', resultsId);
self._attachCloseHandler(container); self._attachCloseHandler(container);
}); });
container.on('close', function () { container.on('close', function () {
// When the dropdown is closed, aria-expanded="false" // When the dropdown is closed, aria-expanded="false"
self.$selection.attr('aria-expanded', 'false'); self.$selection.attr('aria-expanded', 'false');
self.$selection.removeAttr('aria-activedescendant'); self.$selection.removeAttr('aria-activedescendant');
self.$selection.removeAttr('aria-owns'); self.$selection.removeAttr('aria-owns');
self.$selection.focus(); self.$selection.focus();
self._detachCloseHandler(container); self._detachCloseHandler(container);
}); });
container.on('enable', function () { container.on('enable', function () {
self.$selection.attr('tabindex', self._tabindex); self.$selection.attr('tabindex', self._tabindex);
}); });
container.on('disable', function () { container.on('disable', function () {
self.$selection.attr('tabindex', '-1'); self.$selection.attr('tabindex', '-1');
}); });
}; };
BaseSelection.prototype._handleBlur = function (evt) { BaseSelection.prototype._handleBlur = function (evt) {
var self = this; var self = this;
// This needs to be delayed as the active element is the body when the tab // This needs to be delayed as the active element is the body when the tab
// key is pressed, possibly along with others. // key is pressed, possibly along with others.
window.setTimeout(function () { window.setTimeout(function () {
// Don't trigger `blur` if the focus is still in the selection // Don't trigger `blur` if the focus is still in the selection
if ( if (
(document.activeElement == self.$selection[0]) || (document.activeElement == self.$selection[0]) ||
($.contains(self.$selection[0], document.activeElement)) ($.contains(self.$selection[0], document.activeElement))
) { ) {
return; return;
} }
self.trigger('blur', evt); self.trigger('blur', evt);
}, 1); }, 1);
}; };
BaseSelection.prototype._attachCloseHandler = function (container) { BaseSelection.prototype._attachCloseHandler = function (container) {
var self = this; var self = this;
$(document.body).on('mousedown.select2.' + container.id, function (e) { $(document.body).on('mousedown.select2.' + container.id, function (e) {
var $target = $(e.target); var $target = $(e.target);
var $select = $target.closest('.select2'); var $select = $target.closest('.select2');
var $all = $('.select2.select2-container--open'); var $all = $('.select2.select2-container--open');
$all.each(function () { $all.each(function () {
var $this = $(this); var $this = $(this);
if (this == $select[0]) { if (this == $select[0]) {
return; return;
} }
var $element = $this.data('element'); var $element = Utils.GetData(this, 'element');
$element.select2('close'); $element.select2('close');
}); });
}); });
}; };
BaseSelection.prototype._detachCloseHandler = function (container) { BaseSelection.prototype._detachCloseHandler = function (container) {
$(document.body).off('mousedown.select2.' + container.id); $(document.body).off('mousedown.select2.' + container.id);
}; };
BaseSelection.prototype.position = function ($selection, $container) { BaseSelection.prototype.position = function ($selection, $container) {
var $selectionContainer = $container.find('.selection'); var $selectionContainer = $container.find('.selection');
$selectionContainer.append($selection); $selectionContainer.append($selection);
}; };
BaseSelection.prototype.destroy = function () { BaseSelection.prototype.destroy = function () {
this._detachCloseHandler(this.container); this._detachCloseHandler(this.container);
}; };
BaseSelection.prototype.update = function (data) { BaseSelection.prototype.update = function (data) {
throw new Error('The `update` method must be defined in child classes.'); throw new Error('The `update` method must be defined in child classes.');
}; };
return BaseSelection; return BaseSelection;
}); });

View File

@ -1,109 +1,109 @@
define([ define([
'jquery', 'jquery',
'./base', './base',
'../utils' '../utils'
], function ($, BaseSelection, Utils) { ], function ($, BaseSelection, Utils) {
function MultipleSelection ($element, options) { function MultipleSelection ($element, options) {
MultipleSelection.__super__.constructor.apply(this, arguments); MultipleSelection.__super__.constructor.apply(this, arguments);
} }
Utils.Extend(MultipleSelection, BaseSelection); Utils.Extend(MultipleSelection, BaseSelection);
MultipleSelection.prototype.render = function () { MultipleSelection.prototype.render = function () {
var $selection = MultipleSelection.__super__.render.call(this); var $selection = MultipleSelection.__super__.render.call(this);
$selection.addClass('select2-selection--multiple'); $selection.addClass('select2-selection--multiple');
$selection.html( $selection.html(
'<ul class="select2-selection__rendered"></ul>' '<ul class="select2-selection__rendered"></ul>'
); );
return $selection; return $selection;
}; };
MultipleSelection.prototype.bind = function (container, $container) { MultipleSelection.prototype.bind = function (container, $container) {
var self = this; var self = this;
MultipleSelection.__super__.bind.apply(this, arguments); MultipleSelection.__super__.bind.apply(this, arguments);
this.$selection.on('click', function (evt) { this.$selection.on('click', function (evt) {
self.trigger('toggle', { self.trigger('toggle', {
originalEvent: evt originalEvent: evt
}); });
}); });
this.$selection.on( this.$selection.on(
'click', 'click',
'.select2-selection__choice__remove', '.select2-selection__choice__remove',
function (evt) { function (evt) {
// Ignore the event if it is disabled // Ignore the event if it is disabled
if (self.options.get('disabled')) { if (self.options.get('disabled')) {
return; return;
} }
var $remove = $(this); var $remove = $(this);
var $selection = $remove.parent(); var $selection = $remove.parent();
var data = $selection.data('data'); var data = Utils.GetData($selection[0], 'data');
self.trigger('unselect', { self.trigger('unselect', {
originalEvent: evt, originalEvent: evt,
data: data data: data
}); });
} }
); );
}; };
MultipleSelection.prototype.clear = function () { MultipleSelection.prototype.clear = function () {
this.$selection.find('.select2-selection__rendered').empty(); this.$selection.find('.select2-selection__rendered').empty();
}; };
MultipleSelection.prototype.display = function (data, container) { MultipleSelection.prototype.display = function (data, container) {
var template = this.options.get('templateSelection'); var template = this.options.get('templateSelection');
var escapeMarkup = this.options.get('escapeMarkup'); var escapeMarkup = this.options.get('escapeMarkup');
return escapeMarkup(template(data, container)); return escapeMarkup(template(data, container));
}; };
MultipleSelection.prototype.selectionContainer = function () { MultipleSelection.prototype.selectionContainer = function () {
var $container = $( var $container = $(
'<li class="select2-selection__choice">' + '<li class="select2-selection__choice">' +
'<span class="select2-selection__choice__remove" role="presentation">' + '<span class="select2-selection__choice__remove" role="presentation">' +
'&times;' + '&times;' +
'</span>' + '</span>' +
'</li>' '</li>'
); );
return $container; return $container;
}; };
MultipleSelection.prototype.update = function (data) { MultipleSelection.prototype.update = function (data) {
this.clear(); this.clear();
if (data.length === 0) { if (data.length === 0) {
return; return;
} }
var $selections = []; var $selections = [];
for (var d = 0; d < data.length; d++) { for (var d = 0; d < data.length; d++) {
var selection = data[d]; var selection = data[d];
var $selection = this.selectionContainer(); var $selection = this.selectionContainer();
var formatted = this.display(selection, $selection); var formatted = this.display(selection, $selection);
$selection.append(formatted); $selection.append(formatted);
$selection.prop('title', selection.title || selection.text); $selection.prop('title', selection.title || selection.text);
$selection.data('data', selection); Utils.StoreData($selection[0], 'data', selection);
$selections.push($selection); $selections.push($selection);
} }
var $rendered = this.$selection.find('.select2-selection__rendered'); var $rendered = this.$selection.find('.select2-selection__rendered');
Utils.appendMany($rendered, $selections); Utils.appendMany($rendered, $selections);
}; };
return MultipleSelection; return MultipleSelection;
}); });

View File

@ -1,222 +1,222 @@
define([ define([
'jquery', 'jquery',
'../utils', '../utils',
'../keys' '../keys'
], function ($, Utils, KEYS) { ], function ($, Utils, KEYS) {
function Search (decorated, $element, options) { function Search (decorated, $element, options) {
decorated.call(this, $element, options); decorated.call(this, $element, options);
} }
Search.prototype.render = function (decorated) { Search.prototype.render = function (decorated) {
var $search = $( var $search = $(
'<li class="select2-search select2-search--inline">' + '<li class="select2-search select2-search--inline">' +
'<input class="select2-search__field" type="search" tabindex="-1"' + '<input class="select2-search__field" type="search" tabindex="-1"' +
' autocomplete="off" autocorrect="off" autocapitalize="off"' + ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
' spellcheck="false" role="textbox" aria-autocomplete="list" />' + ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
'</li>' '</li>'
); );
this.$searchContainer = $search; this.$searchContainer = $search;
this.$search = $search.find('input'); this.$search = $search.find('input');
var $rendered = decorated.call(this); var $rendered = decorated.call(this);
this._transferTabIndex(); this._transferTabIndex();
return $rendered; return $rendered;
}; };
Search.prototype.bind = function (decorated, container, $container) { Search.prototype.bind = function (decorated, container, $container) {
var self = this; var self = this;
decorated.call(this, container, $container); decorated.call(this, container, $container);
container.on('open', function () { container.on('open', function () {
self.$search.trigger('focus'); self.$search.trigger('focus');
}); });
container.on('close', function () { container.on('close', function () {
self.$search.val(''); self.$search.val('');
self.$search.removeAttr('aria-activedescendant'); self.$search.removeAttr('aria-activedescendant');
self.$search.trigger('focus'); self.$search.trigger('focus');
}); });
container.on('enable', function () { container.on('enable', function () {
self.$search.prop('disabled', false); self.$search.prop('disabled', false);
self._transferTabIndex(); self._transferTabIndex();
}); });
container.on('disable', function () { container.on('disable', function () {
self.$search.prop('disabled', true); self.$search.prop('disabled', true);
}); });
container.on('focus', function (evt) { container.on('focus', function (evt) {
self.$search.trigger('focus'); self.$search.trigger('focus');
}); });
container.on('results:focus', function (params) { container.on('results:focus', function (params) {
self.$search.attr('aria-activedescendant', params.id); self.$search.attr('aria-activedescendant', params.id);
}); });
this.$selection.on('focusin', '.select2-search--inline', function (evt) { this.$selection.on('focusin', '.select2-search--inline', function (evt) {
self.trigger('focus', evt); self.trigger('focus', evt);
}); });
this.$selection.on('focusout', '.select2-search--inline', function (evt) { this.$selection.on('focusout', '.select2-search--inline', function (evt) {
self._handleBlur(evt); self._handleBlur(evt);
}); });
this.$selection.on('keydown', '.select2-search--inline', function (evt) { this.$selection.on('keydown', '.select2-search--inline', function (evt) {
evt.stopPropagation(); evt.stopPropagation();
self.trigger('keypress', evt); self.trigger('keypress', evt);
self._keyUpPrevented = evt.isDefaultPrevented(); self._keyUpPrevented = evt.isDefaultPrevented();
var key = evt.which; var key = evt.which;
if (key === KEYS.BACKSPACE && self.$search.val() === '') { if (key === KEYS.BACKSPACE && self.$search.val() === '') {
var $previousChoice = self.$searchContainer var $previousChoice = self.$searchContainer
.prev('.select2-selection__choice'); .prev('.select2-selection__choice');
if ($previousChoice.length > 0) { if ($previousChoice.length > 0) {
var item = $previousChoice.data('data'); var item = Utils.GetData($previousChoice[0], 'data');
self.searchRemoveChoice(item); self.searchRemoveChoice(item);
evt.preventDefault(); evt.preventDefault();
} }
} }
}); });
// Try to detect the IE version should the `documentMode` property that // Try to detect the IE version should the `documentMode` property that
// is stored on the document. This is only implemented in IE and is // is stored on the document. This is only implemented in IE and is
// slightly cleaner than doing a user agent check. // slightly cleaner than doing a user agent check.
// This property is not available in Edge, but Edge also doesn't have // This property is not available in Edge, but Edge also doesn't have
// this bug. // this bug.
var msie = document.documentMode; var msie = document.documentMode;
var disableInputEvents = msie && msie <= 11; var disableInputEvents = msie && msie <= 11;
// Workaround for browsers which do not support the `input` event // Workaround for browsers which do not support the `input` event
// This will prevent double-triggering of events for browsers which support // This will prevent double-triggering of events for browsers which support
// both the `keyup` and `input` events. // both the `keyup` and `input` events.
this.$selection.on( this.$selection.on(
'input.searchcheck', 'input.searchcheck',
'.select2-search--inline', '.select2-search--inline',
function (evt) { function (evt) {
// IE will trigger the `input` event when a placeholder is used on a // 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 // search box. To get around this issue, we are forced to ignore all
// `input` events in IE and keep using `keyup`. // `input` events in IE and keep using `keyup`.
if (disableInputEvents) { if (disableInputEvents) {
self.$selection.off('input.search input.searchcheck'); self.$selection.off('input.search input.searchcheck');
return; return;
} }
// Unbind the duplicated `keyup` event // Unbind the duplicated `keyup` event
self.$selection.off('keyup.search'); self.$selection.off('keyup.search');
} }
); );
this.$selection.on( this.$selection.on(
'keyup.search input.search', 'keyup.search input.search',
'.select2-search--inline', '.select2-search--inline',
function (evt) { function (evt) {
// IE will trigger the `input` event when a placeholder is used on a // 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 // search box. To get around this issue, we are forced to ignore all
// `input` events in IE and keep using `keyup`. // `input` events in IE and keep using `keyup`.
if (disableInputEvents && evt.type === 'input') { if (disableInputEvents && evt.type === 'input') {
self.$selection.off('input.search input.searchcheck'); self.$selection.off('input.search input.searchcheck');
return; return;
} }
var key = evt.which; var key = evt.which;
// We can freely ignore events from modifier keys // We can freely ignore events from modifier keys
if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) { if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
return; return;
} }
// Tabbing will be handled during the `keydown` phase // Tabbing will be handled during the `keydown` phase
if (key == KEYS.TAB) { if (key == KEYS.TAB) {
return; return;
} }
self.handleSearch(evt); self.handleSearch(evt);
} }
); );
}; };
/** /**
* This method will transfer the tabindex attribute from the rendered * 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 * selection to the search box. This allows for the search box to be used as
* the primary focus instead of the selection container. * the primary focus instead of the selection container.
* *
* @private * @private
*/ */
Search.prototype._transferTabIndex = function (decorated) { Search.prototype._transferTabIndex = function (decorated) {
this.$search.attr('tabindex', this.$selection.attr('tabindex')); this.$search.attr('tabindex', this.$selection.attr('tabindex'));
this.$selection.attr('tabindex', '-1'); this.$selection.attr('tabindex', '-1');
}; };
Search.prototype.createPlaceholder = function (decorated, placeholder) { Search.prototype.createPlaceholder = function (decorated, placeholder) {
this.$search.attr('placeholder', placeholder.text); this.$search.attr('placeholder', placeholder.text);
}; };
Search.prototype.update = function (decorated, data) { Search.prototype.update = function (decorated, data) {
var searchHadFocus = this.$search[0] == document.activeElement; var searchHadFocus = this.$search[0] == document.activeElement;
this.$search.attr('placeholder', ''); this.$search.attr('placeholder', '');
decorated.call(this, data); decorated.call(this, data);
this.$selection.find('.select2-selection__rendered') this.$selection.find('.select2-selection__rendered')
.append(this.$searchContainer); .append(this.$searchContainer);
this.resizeSearch(); this.resizeSearch();
if (searchHadFocus) { if (searchHadFocus) {
this.$search.focus(); this.$search.focus();
} }
}; };
Search.prototype.handleSearch = function () { Search.prototype.handleSearch = function () {
this.resizeSearch(); this.resizeSearch();
if (!this._keyUpPrevented) { if (!this._keyUpPrevented) {
var input = this.$search.val(); var input = this.$search.val();
this.trigger('query', { this.trigger('query', {
term: input term: input
}); });
} }
this._keyUpPrevented = false; this._keyUpPrevented = false;
}; };
Search.prototype.searchRemoveChoice = function (decorated, item) { Search.prototype.searchRemoveChoice = function (decorated, item) {
this.trigger('unselect', { this.trigger('unselect', {
data: item data: item
}); });
this.$search.val(item.text); this.$search.val(item.text);
this.handleSearch(); this.handleSearch();
}; };
Search.prototype.resizeSearch = function () { Search.prototype.resizeSearch = function () {
this.$search.css('width', '25px'); this.$search.css('width', '25px');
var width = ''; var width = '';
if (this.$search.attr('placeholder') !== '') { if (this.$search.attr('placeholder') !== '') {
width = this.$selection.find('.select2-selection__rendered').innerWidth(); width = this.$selection.find('.select2-selection__rendered').innerWidth();
} else { } else {
var minimumWidth = this.$search.val().length + 1; var minimumWidth = this.$search.val().length + 1;
width = (minimumWidth * 0.75) + 'em'; width = (minimumWidth * 0.75) + 'em';
} }
this.$search.css('width', width); this.$search.css('width', width);
}; };
return Search; return Search;
}); });

View File

@ -1,262 +1,323 @@
define([ define([
'jquery' 'jquery'
], function ($) { ], function ($) {
var Utils = {}; var Utils = {};
Utils.Extend = function (ChildClass, SuperClass) { Utils.Extend = function (ChildClass, SuperClass) {
var __hasProp = {}.hasOwnProperty; var __hasProp = {}.hasOwnProperty;
function BaseConstructor () { function BaseConstructor () {
this.constructor = ChildClass; this.constructor = ChildClass;
} }
for (var key in SuperClass) { for (var key in SuperClass) {
if (__hasProp.call(SuperClass, key)) { if (__hasProp.call(SuperClass, key)) {
ChildClass[key] = SuperClass[key]; ChildClass[key] = SuperClass[key];
} }
} }
BaseConstructor.prototype = SuperClass.prototype; BaseConstructor.prototype = SuperClass.prototype;
ChildClass.prototype = new BaseConstructor(); ChildClass.prototype = new BaseConstructor();
ChildClass.__super__ = SuperClass.prototype; ChildClass.__super__ = SuperClass.prototype;
return ChildClass; return ChildClass;
}; };
function getMethods (theClass) { function getMethods (theClass) {
var proto = theClass.prototype; var proto = theClass.prototype;
var methods = []; var methods = [];
for (var methodName in proto) { for (var methodName in proto) {
var m = proto[methodName]; var m = proto[methodName];
if (typeof m !== 'function') { if (typeof m !== 'function') {
continue; continue;
} }
if (methodName === 'constructor') { if (methodName === 'constructor') {
continue; continue;
} }
methods.push(methodName); methods.push(methodName);
} }
return methods; return methods;
} }
Utils.Decorate = function (SuperClass, DecoratorClass) { Utils.Decorate = function (SuperClass, DecoratorClass) {
var decoratedMethods = getMethods(DecoratorClass); var decoratedMethods = getMethods(DecoratorClass);
var superMethods = getMethods(SuperClass); var superMethods = getMethods(SuperClass);
function DecoratedClass () { function DecoratedClass () {
var unshift = Array.prototype.unshift; var unshift = Array.prototype.unshift;
var argCount = DecoratorClass.prototype.constructor.length; var argCount = DecoratorClass.prototype.constructor.length;
var calledConstructor = SuperClass.prototype.constructor; var calledConstructor = SuperClass.prototype.constructor;
if (argCount > 0) { if (argCount > 0) {
unshift.call(arguments, SuperClass.prototype.constructor); unshift.call(arguments, SuperClass.prototype.constructor);
calledConstructor = DecoratorClass.prototype.constructor; calledConstructor = DecoratorClass.prototype.constructor;
} }
calledConstructor.apply(this, arguments); calledConstructor.apply(this, arguments);
} }
DecoratorClass.displayName = SuperClass.displayName; DecoratorClass.displayName = SuperClass.displayName;
function ctr () { function ctr () {
this.constructor = DecoratedClass; this.constructor = DecoratedClass;
} }
DecoratedClass.prototype = new ctr(); DecoratedClass.prototype = new ctr();
for (var m = 0; m < superMethods.length; m++) { for (var m = 0; m < superMethods.length; m++) {
var superMethod = superMethods[m]; var superMethod = superMethods[m];
DecoratedClass.prototype[superMethod] = DecoratedClass.prototype[superMethod] =
SuperClass.prototype[superMethod]; SuperClass.prototype[superMethod];
} }
var calledMethod = function (methodName) { var calledMethod = function (methodName) {
// Stub out the original method if it's not decorating an actual method // Stub out the original method if it's not decorating an actual method
var originalMethod = function () {}; var originalMethod = function () {};
if (methodName in DecoratedClass.prototype) { if (methodName in DecoratedClass.prototype) {
originalMethod = DecoratedClass.prototype[methodName]; originalMethod = DecoratedClass.prototype[methodName];
} }
var decoratedMethod = DecoratorClass.prototype[methodName]; var decoratedMethod = DecoratorClass.prototype[methodName];
return function () { return function () {
var unshift = Array.prototype.unshift; var unshift = Array.prototype.unshift;
unshift.call(arguments, originalMethod); unshift.call(arguments, originalMethod);
return decoratedMethod.apply(this, arguments); return decoratedMethod.apply(this, arguments);
}; };
}; };
for (var d = 0; d < decoratedMethods.length; d++) { for (var d = 0; d < decoratedMethods.length; d++) {
var decoratedMethod = decoratedMethods[d]; var decoratedMethod = decoratedMethods[d];
DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod); DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
} }
return DecoratedClass; return DecoratedClass;
}; };
var Observable = function () { var Observable = function () {
this.listeners = {}; this.listeners = {};
}; };
Observable.prototype.on = function (event, callback) { Observable.prototype.on = function (event, callback) {
this.listeners = this.listeners || {}; this.listeners = this.listeners || {};
if (event in this.listeners) { if (event in this.listeners) {
this.listeners[event].push(callback); this.listeners[event].push(callback);
} else { } else {
this.listeners[event] = [callback]; this.listeners[event] = [callback];
} }
}; };
Observable.prototype.trigger = function (event) { Observable.prototype.trigger = function (event) {
var slice = Array.prototype.slice; var slice = Array.prototype.slice;
this.listeners = this.listeners || {}; this.listeners = this.listeners || {};
if (event in this.listeners) { if (event in this.listeners) {
this.invoke(this.listeners[event], slice.call(arguments, 1)); this.invoke(this.listeners[event], slice.call(arguments, 1));
} }
if ('*' in this.listeners) { if ('*' in this.listeners) {
this.invoke(this.listeners['*'], arguments); this.invoke(this.listeners['*'], arguments);
} }
}; };
Observable.prototype.invoke = function (listeners, params) { Observable.prototype.invoke = function (listeners, params) {
for (var i = 0, len = listeners.length; i < len; i++) { for (var i = 0, len = listeners.length; i < len; i++) {
listeners[i].apply(this, params); listeners[i].apply(this, params);
} }
}; };
Utils.Observable = Observable; Utils.Observable = Observable;
Utils.generateChars = function (length) { Utils.generateChars = function (length) {
var chars = ''; var chars = '';
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
var randomChar = Math.floor(Math.random() * 36); var randomChar = Math.floor(Math.random() * 36);
chars += randomChar.toString(36); chars += randomChar.toString(36);
} }
return chars; return chars;
}; };
Utils.bind = function (func, context) { Utils.bind = function (func, context) {
return function () { return function () {
func.apply(context, arguments); func.apply(context, arguments);
}; };
}; };
Utils._convertData = function (data) { Utils._convertData = function (data) {
for (var originalKey in data) { for (var originalKey in data) {
var keys = originalKey.split('-'); var keys = originalKey.split('-');
var dataLevel = data; var dataLevel = data;
if (keys.length === 1) { if (keys.length === 1) {
continue; continue;
} }
for (var k = 0; k < keys.length; k++) { for (var k = 0; k < keys.length; k++) {
var key = keys[k]; var key = keys[k];
// Lowercase the first letter // Lowercase the first letter
// By default, dash-separated becomes camelCase // By default, dash-separated becomes camelCase
key = key.substring(0, 1).toLowerCase() + key.substring(1); key = key.substring(0, 1).toLowerCase() + key.substring(1);
if (!(key in dataLevel)) { if (!(key in dataLevel)) {
dataLevel[key] = {}; dataLevel[key] = {};
} }
if (k == keys.length - 1) { if (k == keys.length - 1) {
dataLevel[key] = data[originalKey]; dataLevel[key] = data[originalKey];
} }
dataLevel = dataLevel[key]; dataLevel = dataLevel[key];
} }
delete data[originalKey]; delete data[originalKey];
} }
return data; return data;
}; };
Utils.hasScroll = function (index, el) { Utils.hasScroll = function (index, el) {
// Adapted from the function created by @ShadowScripter // Adapted from the function created by @ShadowScripter
// and adapted by @BillBarry on the Stack Exchange Code Review website. // and adapted by @BillBarry on the Stack Exchange Code Review website.
// The original code can be found at // The original code can be found at
// http://codereview.stackexchange.com/q/13338 // http://codereview.stackexchange.com/q/13338
// and was designed to be used with the Sizzle selector engine. // and was designed to be used with the Sizzle selector engine.
var $el = $(el); var $el = $(el);
var overflowX = el.style.overflowX; var overflowX = el.style.overflowX;
var overflowY = el.style.overflowY; var overflowY = el.style.overflowY;
//Check both x and y declarations //Check both x and y declarations
if (overflowX === overflowY && if (overflowX === overflowY &&
(overflowY === 'hidden' || overflowY === 'visible')) { (overflowY === 'hidden' || overflowY === 'visible')) {
return false; return false;
} }
if (overflowX === 'scroll' || overflowY === 'scroll') { if (overflowX === 'scroll' || overflowY === 'scroll') {
return true; return true;
} }
return ($el.innerHeight() < el.scrollHeight || return ($el.innerHeight() < el.scrollHeight ||
$el.innerWidth() < el.scrollWidth); $el.innerWidth() < el.scrollWidth);
}; };
Utils.escapeMarkup = function (markup) { Utils.escapeMarkup = function (markup) {
var replaceMap = { var replaceMap = {
'\\': '&#92;', '\\': '&#92;',
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;', '>': '&gt;',
'"': '&quot;', '"': '&quot;',
'\'': '&#39;', '\'': '&#39;',
'/': '&#47;' '/': '&#47;'
}; };
// Do not try to escape the markup if it's not a string // Do not try to escape the markup if it's not a string
if (typeof markup !== 'string') { if (typeof markup !== 'string') {
return markup; return markup;
} }
return String(markup).replace(/[&<>"'\/\\]/g, function (match) { return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
return replaceMap[match]; return replaceMap[match];
}); });
}; };
// Append an array of jQuery nodes to a given element. // Append an array of jQuery nodes to a given element.
Utils.appendMany = function ($element, $nodes) { Utils.appendMany = function ($element, $nodes) {
// jQuery 1.7.x does not support $.fn.append() with an array // jQuery 1.7.x does not support $.fn.append() with an array
// Fall back to a jQuery object collection using $.fn.add() // Fall back to a jQuery object collection using $.fn.add()
if ($.fn.jquery.substr(0, 3) === '1.7') { if ($.fn.jquery.substr(0, 3) === '1.7') {
var $jqNodes = $(); var $jqNodes = $();
$.map($nodes, function (node) { $.map($nodes, function (node) {
$jqNodes = $jqNodes.add(node); $jqNodes = $jqNodes.add(node);
}); });
$nodes = $jqNodes; $nodes = $jqNodes;
} }
$element.append($nodes); $element.append($nodes);
}; };
return Utils; // Cache objects in Utils.__cache instead of $.data
}); 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]) {
return Utils.__cache[id][name];
}
return null;
} 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;
});