123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- define([
- 'jquery',
- './utils'
- ], function ($, Utils) {
- function Results ($element, options, dataAdapter) {
- this.$element = $element;
- this.data = dataAdapter;
- this.options = options;
- Results.__super__.constructor.call(this);
- }
- Utils.Extend(Results, Utils.Observable);
- Results.prototype.render = function () {
- var $results = $(
- '<ul class="select2-results__options" role="listbox"></ul>'
- );
- if (this.options.get('multiple')) {
- $results.attr('aria-multiselectable', 'true');
- }
- this.$results = $results;
- return $results;
- };
- Results.prototype.clear = function () {
- this.$results.empty();
- };
- Results.prototype.displayMessage = function (params) {
- var escapeMarkup = this.options.get('escapeMarkup');
- this.clear();
- this.hideLoading();
- var $message = $(
- '<li role="alert" aria-live="assertive"' +
- ' class="select2-results__option"></li>'
- );
- var message = this.options.get('translations').get(params.message);
- $message.append(
- escapeMarkup(
- message(params.args)
- )
- );
- $message[0].className += ' select2-results__message';
- this.$results.append($message);
- };
- Results.prototype.hideMessages = function () {
- this.$results.find('.select2-results__message').remove();
- };
- Results.prototype.append = function (data) {
- this.hideLoading();
- var $options = [];
- if (data.results == null || data.results.length === 0) {
- if (this.$results.children().length === 0) {
- this.trigger('results:message', {
- message: 'noResults'
- });
- }
- return;
- }
- data.results = this.sort(data.results);
- for (var d = 0; d < data.results.length; d++) {
- var item = data.results[d];
- var $option = this.option(item);
- $options.push($option);
- }
- this.$results.append($options);
- };
- Results.prototype.position = function ($results, $dropdown) {
- var $resultsContainer = $dropdown.find('.select2-results');
- $resultsContainer.append($results);
- };
- Results.prototype.sort = function (data) {
- var sorter = this.options.get('sorter');
- return sorter(data);
- };
- Results.prototype.highlightFirstItem = function () {
- var $options = this.$results
- .find('.select2-results__option--selectable');
- var $selected = $options.filter('.select2-results__option--selected');
- // Check if there are any selected options
- if ($selected.length > 0) {
- // If there are selected options, highlight the first
- $selected.first().trigger('mouseenter');
- } else {
- // If there are no selected options, highlight the first option
- // in the dropdown
- $options.first().trigger('mouseenter');
- }
- this.ensureHighlightVisible();
- };
- Results.prototype.setClasses = function () {
- var self = this;
- this.data.current(function (selected) {
- var selectedIds = selected.map(function (s) {
- return s.id.toString();
- });
- var $options = self.$results
- .find('.select2-results__option--selectable');
- $options.each(function () {
- var $option = $(this);
- var item = Utils.GetData(this, 'data');
- // id needs to be converted to a string when comparing
- var id = '' + item.id;
- if ((item.element != null && item.element.selected) ||
- (item.element == null && selectedIds.indexOf(id) > -1)) {
- this.classList.add('select2-results__option--selected');
- $option.attr('aria-selected', 'true');
- } else {
- this.classList.remove('select2-results__option--selected');
- $option.attr('aria-selected', 'false');
- }
- });
- });
- };
- Results.prototype.showLoading = function (params) {
- this.hideLoading();
- var loadingMore = this.options.get('translations').get('searching');
- var loading = {
- disabled: true,
- loading: true,
- text: loadingMore(params)
- };
- var $loading = this.option(loading);
- $loading.className += ' loading-results';
- this.$results.prepend($loading);
- };
- Results.prototype.hideLoading = function () {
- this.$results.find('.loading-results').remove();
- };
- Results.prototype.option = function (data) {
- var option = document.createElement('li');
- option.classList.add('select2-results__option');
- option.classList.add('select2-results__option--selectable');
- var attrs = {
- 'role': 'option'
- };
- var matches = window.Element.prototype.matches ||
- window.Element.prototype.msMatchesSelector ||
- window.Element.prototype.webkitMatchesSelector;
- if ((data.element != null && matches.call(data.element, ':disabled')) ||
- (data.element == null && data.disabled)) {
- attrs['aria-disabled'] = 'true';
- option.classList.remove('select2-results__option--selectable');
- option.classList.add('select2-results__option--disabled');
- }
- if (data.id == null) {
- option.classList.remove('select2-results__option--selectable');
- }
- if (data._resultId != null) {
- option.id = data._resultId;
- }
- if (data.title) {
- option.title = data.title;
- }
- if (data.children) {
- attrs.role = 'group';
- attrs['aria-label'] = data.text;
- option.classList.remove('select2-results__option--selectable');
- option.classList.add('select2-results__option--group');
- }
- for (var attr in attrs) {
- var val = attrs[attr];
- option.setAttribute(attr, val);
- }
- if (data.children) {
- var $option = $(option);
- var label = document.createElement('strong');
- label.className = 'select2-results__group';
- this.template(data, label);
- var $children = [];
- for (var c = 0; c < data.children.length; c++) {
- var child = data.children[c];
- var $child = this.option(child);
- $children.push($child);
- }
- var $childrenContainer = $('<ul></ul>', {
- 'class': 'select2-results__options select2-results__options--nested',
- 'role': 'none'
- });
- $childrenContainer.append($children);
- $option.append(label);
- $option.append($childrenContainer);
- } else {
- this.template(data, option);
- }
- Utils.StoreData(option, 'data', data);
- return option;
- };
- Results.prototype.bind = function (container, $container) {
- var self = this;
- var id = container.id + '-results';
- this.$results.attr('id', id);
- container.on('results:all', function (params) {
- self.clear();
- self.append(params.data);
- if (container.isOpen()) {
- self.setClasses();
- self.highlightFirstItem();
- }
- });
- container.on('results:append', function (params) {
- self.append(params.data);
- if (container.isOpen()) {
- self.setClasses();
- }
- });
- container.on('query', function (params) {
- self.hideMessages();
- self.showLoading(params);
- });
- container.on('select', function () {
- if (!container.isOpen()) {
- return;
- }
- self.setClasses();
- if (self.options.get('scrollAfterSelect')) {
- self.highlightFirstItem();
- }
- });
- container.on('unselect', function () {
- if (!container.isOpen()) {
- return;
- }
- self.setClasses();
- if (self.options.get('scrollAfterSelect')) {
- self.highlightFirstItem();
- }
- });
- container.on('open', function () {
- // When the dropdown is open, aria-expended="true"
- self.$results.attr('aria-expanded', 'true');
- self.$results.attr('aria-hidden', 'false');
- self.setClasses();
- self.ensureHighlightVisible();
- });
- container.on('close', function () {
- // When the dropdown is closed, aria-expended="false"
- self.$results.attr('aria-expanded', 'false');
- self.$results.attr('aria-hidden', 'true');
- self.$results.removeAttr('aria-activedescendant');
- });
- container.on('results:toggle', function () {
- var $highlighted = self.getHighlightedResults();
- if ($highlighted.length === 0) {
- return;
- }
- $highlighted.trigger('mouseup');
- });
- container.on('results:select', function () {
- var $highlighted = self.getHighlightedResults();
- if ($highlighted.length === 0) {
- return;
- }
- var data = Utils.GetData($highlighted[0], 'data');
- if ($highlighted.hasClass('select2-results__option--selected')) {
- self.trigger('close', {});
- } else {
- self.trigger('select', {
- data: data
- });
- }
- });
- container.on('results:previous', function () {
- var $highlighted = self.getHighlightedResults();
- var $options = self.$results.find('.select2-results__option--selectable');
- var currentIndex = $options.index($highlighted);
- // If we are already at the top, don't move further
- // If no options, currentIndex will be -1
- if (currentIndex <= 0) {
- return;
- }
- var nextIndex = currentIndex - 1;
- // If none are highlighted, highlight the first
- if ($highlighted.length === 0) {
- nextIndex = 0;
- }
- var $next = $options.eq(nextIndex);
- $next.trigger('mouseenter');
- var currentOffset = self.$results.offset().top;
- var nextTop = $next.offset().top;
- var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);
- if (nextIndex === 0) {
- self.$results.scrollTop(0);
- } else if (nextTop - currentOffset < 0) {
- self.$results.scrollTop(nextOffset);
- }
- });
- container.on('results:next', function () {
- var $highlighted = self.getHighlightedResults();
- var $options = self.$results.find('.select2-results__option--selectable');
- var currentIndex = $options.index($highlighted);
- var nextIndex = currentIndex + 1;
- // If we are at the last option, stay there
- if (nextIndex >= $options.length) {
- return;
- }
- var $next = $options.eq(nextIndex);
- $next.trigger('mouseenter');
- var currentOffset = self.$results.offset().top +
- self.$results.outerHeight(false);
- var nextBottom = $next.offset().top + $next.outerHeight(false);
- var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;
- if (nextIndex === 0) {
- self.$results.scrollTop(0);
- } else if (nextBottom > currentOffset) {
- self.$results.scrollTop(nextOffset);
- }
- });
- container.on('results:focus', function (params) {
- params.element[0].classList.add('select2-results__option--highlighted');
- params.element[0].setAttribute('aria-selected', 'true');
- });
- container.on('results:message', function (params) {
- self.displayMessage(params);
- });
- if ($.fn.mousewheel) {
- this.$results.on('mousewheel', function (e) {
- var top = self.$results.scrollTop();
- var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
- var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
- var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
- if (isAtTop) {
- self.$results.scrollTop(0);
- e.preventDefault();
- e.stopPropagation();
- } else if (isAtBottom) {
- self.$results.scrollTop(
- self.$results.get(0).scrollHeight - self.$results.height()
- );
- e.preventDefault();
- e.stopPropagation();
- }
- });
- }
- this.$results.on('mouseup', '.select2-results__option--selectable',
- function (evt) {
- var $this = $(this);
- var data = Utils.GetData(this, 'data');
- if ($this.hasClass('select2-results__option--selected')) {
- if (self.options.get('multiple')) {
- self.trigger('unselect', {
- originalEvent: evt,
- data: data
- });
- } else {
- self.trigger('close', {
- originalEvent: evt,
- data: data
- });
- }
- return;
- }
- self.trigger('select', {
- originalEvent: evt,
- data: data
- });
- });
- this.$results.on('mouseenter', '.select2-results__option--selectable',
- function (evt) {
- var data = Utils.GetData(this, 'data');
- self.getHighlightedResults()
- .removeClass('select2-results__option--highlighted')
- .attr('aria-selected', 'false');
- self.trigger('results:focus', {
- data: data,
- element: $(this)
- });
- });
- };
- Results.prototype.getHighlightedResults = function () {
- var $highlighted = this.$results
- .find('.select2-results__option--highlighted');
- return $highlighted;
- };
- Results.prototype.destroy = function () {
- this.$results.remove();
- };
- Results.prototype.ensureHighlightVisible = function () {
- var $highlighted = this.getHighlightedResults();
- if ($highlighted.length === 0) {
- return;
- }
- var $options = this.$results.find('.select2-results__option--selectable');
- var currentIndex = $options.index($highlighted);
- var currentOffset = this.$results.offset().top;
- var nextTop = $highlighted.offset().top;
- var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);
- var offsetDelta = nextTop - currentOffset;
- nextOffset -= $highlighted.outerHeight(false) * 2;
- if (currentIndex <= 2) {
- this.$results.scrollTop(0);
- } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
- this.$results.scrollTop(nextOffset);
- }
- };
- Results.prototype.template = function (result, container) {
- var template = this.options.get('templateResult');
- var escapeMarkup = this.options.get('escapeMarkup');
- var content = template(result, container);
- if (content == null) {
- container.style.display = 'none';
- } else if (typeof content === 'string') {
- container.innerHTML = escapeMarkup(content);
- } else {
- $(container).append(content);
- }
- };
- return Results;
- });
|