From 05849d2afb8fa6fa200e40332a23eecc5df91071 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 9 Nov 2015 13:57:47 -0500 Subject: [PATCH] FIX: Upgrade Select2 widget - fixes focus on mobile --- vendor/assets/javascripts/select2.js | 306 +++++++++++++++++++++------ 1 file changed, 247 insertions(+), 59 deletions(-) diff --git a/vendor/assets/javascripts/select2.js b/vendor/assets/javascripts/select2.js index 7590b822953..195ccee5bb0 100644 --- a/vendor/assets/javascripts/select2.js +++ b/vendor/assets/javascripts/select2.js @@ -1,7 +1,7 @@ /* Copyright 2012 Igor Vaynberg -Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014 +Version: 3.5.4 Timestamp: Sun Aug 30 13:30:32 EDT 2015 This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU General Public License version 2 (the "GPL License"). You may choose either license to govern your @@ -814,7 +814,7 @@ the specific language governing permissions and limitations under the Apache Lic // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal. this.dropdown.on("click mouseup mousedown touchstart touchend focusin", function (e) { e.stopPropagation(); }); - this.nextSearchTerm = undefined; + this.lastSearchTerm = undefined; if ($.isFunction(this.opts.initSelection)) { // initialize selection based on the current value of the source element @@ -870,17 +870,21 @@ the specific language governing permissions and limitations under the Apache Lic select2.container.remove(); select2.liveRegion.remove(); select2.dropdown.remove(); - element - .show() - .removeData("select2") - .off(".select2") - .prop("autofocus", this.autofocus || false); - if (this.elementTabIndex) { - element.attr({tabindex: this.elementTabIndex}); + element.removeData("select2") + .off(".select2"); + if (!element.is("input[type='hidden']")) { + element + .show() + .prop("autofocus", this.autofocus || false); + if (this.elementTabIndex) { + element.attr({tabindex: this.elementTabIndex}); + } else { + element.removeAttr("tabindex"); + } + element.show(); } else { - element.removeAttr("tabindex"); + element.css("display", ""); } - element.show(); } cleanupJQueryElements.call(this, @@ -932,6 +936,155 @@ the specific language governing permissions and limitations under the Apache Lic }); } + opts.debug = opts.debug || $.fn.select2.defaults.debug; + + // Warnings for options renamed/removed in Select2 4.0.0 + // Only when it's enabled through debug mode + if (opts.debug && console && console.warn) { + // id was removed + if (opts.id != null) { + console.warn( + 'Select2: The `id` option has been removed in Select2 4.0.0, ' + + 'consider renaming your `id` property or mapping the property before your data makes it to Select2. ' + + 'You can read more at https://select2.github.io/announcements-4.0.html#changed-id' + ); + } + + // text was removed + if (opts.text != null) { + console.warn( + 'Select2: The `text` option has been removed in Select2 4.0.0, ' + + 'consider renaming your `text` property or mapping the property before your data makes it to Select2. ' + + 'You can read more at https://select2.github.io/announcements-4.0.html#changed-id' + ); + } + + // sortResults was renamed to results + if (opts.sortResults != null) { + console.warn( + 'Select2: the `sortResults` option has been renamed to `sorter` in Select2 4.0.0. ' + ); + } + + // selectOnBlur was renamed to selectOnClose + if (opts.selectOnBlur != null) { + console.warn( + 'Select2: The `selectOnBlur` option has been renamed to `selectOnClose` in Select2 4.0.0.' + ); + } + + // ajax.results was renamed to ajax.processResults + if (opts.ajax != null && opts.ajax.results != null) { + console.warn( + 'Select2: The `ajax.results` option has been renamed to `ajax.processResults` in Select2 4.0.0.' + ); + } + + // format* options were renamed to language.* + if (opts.formatNoResults != null) { + console.warn( + 'Select2: The `formatNoResults` option has been renamed to `language.noResults` in Select2 4.0.0.' + ); + } + if (opts.formatSearching != null) { + console.warn( + 'Select2: The `formatSearching` option has been renamed to `language.searching` in Select2 4.0.0.' + ); + } + if (opts.formatInputTooShort != null) { + console.warn( + 'Select2: The `formatInputTooShort` option has been renamed to `language.inputTooShort` in Select2 4.0.0.' + ); + } + if (opts.formatInputTooLong != null) { + console.warn( + 'Select2: The `formatInputTooLong` option has been renamed to `language.inputTooLong` in Select2 4.0.0.' + ); + } + if (opts.formatLoading != null) { + console.warn( + 'Select2: The `formatLoading` option has been renamed to `language.loadingMore` in Select2 4.0.0.' + ); + } + if (opts.formatSelectionTooBig != null) { + console.warn( + 'Select2: The `formatSelectionTooBig` option has been renamed to `language.maximumSelected` in Select2 4.0.0.' + ); + } + + if (opts.element.data('select2Tags')) { + console.warn( + 'Select2: The `data-select2-tags` attribute has been renamed to `data-tags` in Select2 4.0.0.' + ); + } + } + + // Aliasing options renamed in Select2 4.0.0 + + // data-select2-tags -> data-tags + if (opts.element.data('tags') != null) { + var elemTags = opts.element.data('tags'); + + // data-tags should actually be a boolean + if (!$.isArray(elemTags)) { + elemTags = []; + } + + opts.element.data('select2Tags', elemTags); + } + + // sortResults -> sorter + if (opts.sorter != null) { + opts.sortResults = opts.sorter; + } + + // selectOnBlur -> selectOnClose + if (opts.selectOnClose != null) { + opts.selectOnBlur = opts.selectOnClose; + } + + // ajax.results -> ajax.processResults + if (opts.ajax != null) { + if ($.isFunction(opts.ajax.processResults)) { + opts.ajax.results = opts.ajax.processResults; + } + } + + // Formatters/language options + if (opts.language != null) { + var lang = opts.language; + + // formatNoMatches -> language.noMatches + if ($.isFunction(lang.noMatches)) { + opts.formatNoMatches = lang.noMatches; + } + + // formatSearching -> language.searching + if ($.isFunction(lang.searching)) { + opts.formatSearching = lang.searching; + } + + // formatInputTooShort -> language.inputTooShort + if ($.isFunction(lang.inputTooShort)) { + opts.formatInputTooShort = lang.inputTooShort; + } + + // formatInputTooLong -> language.inputTooLong + if ($.isFunction(lang.inputTooLong)) { + opts.formatInputTooLong = lang.inputTooLong; + } + + // formatLoading -> language.loadingMore + if ($.isFunction(lang.loadingMore)) { + opts.formatLoading = lang.loadingMore; + } + + // formatSelectionTooBig -> language.maximumSelected + if ($.isFunction(lang.maximumSelected)) { + opts.formatSelectionTooBig = lang.maximumSelected; + } + } + opts = $.extend({}, { populateResults: function(container, results, query) { var populate, id=this.opts.id, liveRegion=this.liveRegion; @@ -975,7 +1128,6 @@ the specific language governing permissions and limitations under the Apache Lic if (compound) { - innerContainer=$(""); innerContainer.addClass("select2-result-sub"); populate(result.children, innerContainer, depth+1); @@ -1046,7 +1198,6 @@ the specific language governing permissions and limitations under the Apache Lic opts.id=function(e) { return e.id; }; } else { if (!("query" in opts)) { - if ("ajax" in opts) { ajaxUrl = opts.element.data("ajax-url"); if (ajaxUrl && ajaxUrl.length > 0) { @@ -1325,10 +1476,11 @@ the specific language governing permissions and limitations under the Apache Lic }; if (above) { - css.top = offset.top - dropHeight; - css.bottom = 'auto'; this.container.addClass("select2-drop-above"); $dropdown.addClass("select2-drop-above"); + dropHeight = $dropdown.outerHeight(false); + css.top = offset.top - dropHeight; + css.bottom = 'auto'; } else { css.top = dropTop; @@ -1481,6 +1633,9 @@ the specific language governing permissions and limitations under the Apache Lic this.clearSearch(); this.search.removeClass("select2-active"); + + // Remove the aria active descendant for highlighted element + this.search.removeAttr("aria-activedescendant"); this.opts.element.trigger($.Event("select2-close")); }, @@ -1499,6 +1654,27 @@ the specific language governing permissions and limitations under the Apache Lic }, + /** + * @return {Boolean} Whether or not search value was changed. + * @private + */ + prefillNextSearchTerm: function () { + // initializes search's value with nextSearchTerm (if defined by user) + // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter + if(this.search.val() !== "") { + return false; + } + + var nextSearchTerm = this.opts.nextSearchTerm(this.data(), this.lastSearchTerm); + if(nextSearchTerm !== undefined){ + this.search.val(nextSearchTerm); + this.search.select(); + return true; + } + + return false; + }, + //abstract getMaximumSelectionSize: function() { return evaluate(this.opts.maximumSelectionSize, this.opts.element); @@ -1812,6 +1988,9 @@ the specific language governing permissions and limitations under the Apache Lic if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) { render("
  • " + evaluate(opts.formatNoMatches, opts.element, search.val()) + "
  • "); + if(this.showSearch){ + this.showSearch(search.val()); + } return; } @@ -1916,7 +2095,7 @@ the specific language governing permissions and limitations under the Apache Lic } else if (this.opts.width === "copy" || this.opts.width === "resolve") { // check if there is inline style on the element that contains width style = this.opts.element.attr('style'); - if (style !== undefined) { + if (typeof(style) === "string") { attrs = style.split(';'); for (i = 0, l = attrs.length; i < l; i = i + 1) { attr = attrs[i].replace(/\s/g, ''); @@ -2015,14 +2194,7 @@ the specific language governing permissions and limitations under the Apache Lic } } - // initializes search's value with nextSearchTerm (if defined by user) - // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter - if(this.search.val() === "") { - if(this.nextSearchTerm != undefined){ - this.search.val(this.nextSearchTerm); - this.search.select(); - } - } + this.prefillNextSearchTerm(); this.focusser.prop("disabled", true).val(""); this.updateResults(true); @@ -2109,7 +2281,7 @@ the specific language governing permissions and limitations under the Apache Lic this.focusser.attr("id", "s2id_autogen"+idSuffix); elementLabel = $("label[for='" + this.opts.element.attr("id") + "']"); - this.opts.element.focus(this.bind(function () { this.focus(); })); + this.opts.element.on('focus.select2', this.bind(function () { this.focus(); })); this.focusser.prev() .text(elementLabel.text()) @@ -2165,7 +2337,7 @@ the specific language governing permissions and limitations under the Apache Lic // without this the search field loses focus which is annoying if (document.activeElement === this.body.get(0)) { window.setTimeout(this.bind(function() { - if (this.opened()) { + if (this.opened() && this.results && this.results.length > 1) { this.search.focus(); } }), 0); @@ -2317,7 +2489,7 @@ the specific language governing permissions and limitations under the Apache Lic self.updateSelection(selected); self.close(); self.setPlaceholder(); - self.nextSearchTerm = self.opts.nextSearchTerm(selected, self.search.val()); + self.lastSearchTerm = self.search.val(); } }); } @@ -2454,7 +2626,7 @@ the specific language governing permissions and limitations under the Apache Lic this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data }); - this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val()); + this.lastSearchTerm = this.search.val(); this.close(); if ((!options || !options.noFocus) && this.opts.shouldFocusInput(this)) { @@ -2508,9 +2680,23 @@ the specific language governing permissions and limitations under the Apache Lic if (arguments.length > 1) { triggerChange = arguments[1]; + + if (this.opts.debug && console && console.warn) { + console.warn( + 'Select2: The second option to `select2("val")` is not supported in Select2 4.0.0. ' + + 'The `change` event will always be triggered in 4.0.0.' + ); + } } if (this.select) { + if (this.opts.debug && console && console.warn) { + console.warn( + 'Select2: Setting the value on a