[TASK] Update autocomplete JS widget for BE 81/51781/4
authorMarkus Klein <markus.klein@typo3.org>
Mon, 20 Feb 2017 00:41:08 +0000 (01:41 +0100)
committerWouter Wolters <typo3@wouterwolters.nl>
Mon, 27 Feb 2017 21:48:24 +0000 (22:48 +0100)
Resolves: #79938
Releases: master, 7.6
Change-Id: I50e84f11595ea6e79280084bfbf94d75be646de1
Reviewed-on: https://review.typo3.org/51781
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez <typo3@scripting-base.de>
Tested-by: Andreas Fernandez <typo3@scripting-base.de>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Build/Gruntfile.js
Build/bower.json
typo3/sysext/backend/Resources/Public/JavaScript/LiveSearch.js
typo3/sysext/core/Resources/Public/JavaScript/Contrib/jquery.autocomplete.js

index afb6387..f8789e6 100644 (file)
@@ -285,9 +285,8 @@ module.exports = function(grunt) {
                                                see https://github.com/claviska/jquery-minicolors/issues/206
                                        'jquery.minicolors.js': 'jquery-minicolors/jquery.minicolors.min.js',
                                         */
-                                       /* disabled until autocomplete groupBy is fixed by the author
-                                               see https://github.com/devbridge/jQuery-Autocomplete/pull/387
-                                       'jquery.autocomplete.js': 'devbridge-autocomplete/src/jquery.autocomplete.js',
+                                       /* disabled until autocomplete formatGroup is fixed to pass on the index too
+                                        'jquery.autocomplete.js': 'devbridge-autocomplete/src/jquery.autocomplete.js',
                                         */
                                        'd3/d3.js': 'd3/d3.min.js',
                                        /**
index ecc029a..a53594c 100644 (file)
@@ -40,7 +40,7 @@
     "imagesloaded": "^4.1.0",
     "taboverride": "^4.0.3",
     "seiyria-bootstrap-slider": "^9.5.3",
-    "devbridge-autocomplete": "^1.2.24",
+    "devbridge-autocomplete": "^1.3.0",
     "region-flags": "*",
     "typo3-icons": "^1.1.0",
     "matchHeight": "matchheight#^0.7.0",
index 9ef0d2b..1120d6f 100644 (file)
@@ -29,9 +29,7 @@ define([
        var dropdownToggle = '.t3js-toolbar-search-dropdowntoggle';
        var searchFieldSelector = '.t3js-topbar-navigation-search-field';
        var formSelector = '.t3js-topbar-navigation-search';
-       var autocompleteContainer = '.t3js-toolbar-search-autocomplete';
        var url = TYPO3.settings.ajaxUrls['livesearch'];
-       var category = '';
 
        Viewport.Topbar.Toolbar.registerEvent(function() {
                $(searchFieldSelector).autocomplete({
@@ -42,7 +40,7 @@ define([
                        minChars: 2,
                        width: '100%',
                        groupBy: 'typeLabel',
-                       containerClass: 'autocomplete-suggestions ' + autocompleteContainer.substr(1, autocompleteContainer.length),
+                       containerClass: toolbarItem.substr(1, toolbarItem.length),
                        appendTo: containerSelector + ' .dropdown-menu',
                        forceFixPosition: false,
                        preserveInput: true,
@@ -61,13 +59,7 @@ define([
                                        })
                                };
                        },
-                       // Format group is currently modified inside autocomplete to be allowed to be configurable
-                       formatGroup: function(suggestion, value, i) {
-                               var currentCategory = suggestion.data['typeLabel'];
-                               if (category === currentCategory) {
-                                       return '';
-                               }
-                               category = currentCategory;
+                       formatGroup: function(suggestion, category, i) {
                                var html = '';
                                // add a divider if it's not the first group
                                if (i > 0) {
@@ -76,7 +68,7 @@ define([
                                return html + '<h3 class="dropdown-headline">' + category + '</h3>';
                        },
                        // Rendering of each item
-                       formatResult: function(suggestion, value) {
+                       formatResult: function(suggestion, value, i) {
                                return ''
                                        + '<div class="dropdown-table">'
                                        + '<div class="dropdown-table-row">'
@@ -121,7 +113,7 @@ define([
                        TYPO3.ModuleMenu.App.showModule('web_list', 'id=0&search_levels=-1&search_field=' + encodeURIComponent($(searchFieldSelector).val()));
                        $(searchFieldSelector).val('').trigger('change');
                });
-               $(containerSelector).on('click', '.dropdown-list-link', function(evt) {
+               $('.' + $(searchFieldSelector).autocomplete().options.containerClass).on('click.autocomplete', '.dropdown-list-link', function(evt) {
                        evt.preventDefault();
                        jump($(this).data('target'), 'web_list', 'web', $(this).data('pageid'));
                        $(searchFieldSelector).val('').trigger('change');
index 82daa25..50d1494 100644 (file)
@@ -1,17 +1,17 @@
 /**
 *  Ajax Autocomplete for jQuery, version %version%
-*  (c) 2015 Tomas Kirda
+*  (c) 2017 Tomas Kirda
 *
 *  Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
 *  For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
 */
 
-/*jslint  browser: true, white: true, plusplus: true, vars: true */
+/*jslint  browser: true, white: true, single: true, this: true, multivar: true */
 /*global define, window, document, jQuery, exports, require */
 
 // Expose plugin as an AMD module if AMD loader is present:
 (function (factory) {
-    'use strict';
+    "use strict";
     if (typeof define === 'function' && define.amd) {
         // AMD. Register as an anonymous module.
         define(['jquery'], factory);
@@ -29,7 +29,7 @@
         utils = (function () {
             return {
                 escapeRegExChars: function (value) {
-                    return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+                    return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
                 },
                 createNode: function (containerClass) {
                     var div = document.createElement('div');
@@ -52,7 +52,7 @@
         };
 
     function Autocomplete(el, options) {
-        var noop = function () { },
+        var noop = $.noop,
             that = this,
             defaults = {
                 ajaxSettings: {},
@@ -67,6 +67,7 @@
                 deferRequestBy: 0,
                 params: {},
                 formatResult: Autocomplete.formatResult,
+                formatGroup: Autocomplete.formatGroup,
                 delimiter: null,
                 zIndex: 9999,
                 type: 'GET',
     $.Autocomplete = Autocomplete;
 
     Autocomplete.formatResult = function (suggestion, currentValue) {
+        // Do not replace anything if there current value is empty
+        if (!currentValue) {
+            return suggestion.value;
+        }
+
         var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
 
         return suggestion.value
             .replace(/&lt;(\/?strong)&gt;/g, '<$1>');
     };
 
+    Autocomplete.formatGroup = function (suggestion, category) {
+        return '<div class="autocomplete-group"><strong>' + category + '</strong></div>';
+    };
+
     Autocomplete.prototype = {
 
         killerFn: null,
             that.element.setAttribute('autocomplete', 'off');
 
             that.killerFn = function (e) {
-                if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
+                if (!$(e.target).closest('.' + that.options.containerClass).length) {
                     that.killSuggestions();
                     that.disableKillerFn();
                 }
 
             // Only set width if it was provided:
             if (options.width !== 'auto') {
-                container.width(options.width);
+                container.css('width', options.width);
             }
 
             // Listen for mouse over event on suggestions list:
             // Listen for click event on suggestions list:
             container.on('click.autocomplete', suggestionSelector, function () {
                 that.select($(this).data('index'));
+                return false;
             });
 
             that.fixPositionCapture = function () {
 
         onFocus: function () {
             var that = this;
+
             that.fixPosition();
-            if (that.options.minChars === 0 && that.el.val().length === 0) {
+
+            if (that.el.val().length >= that.options.minChars) {
                 that.onValueChange();
             }
         },
                 }
             }
 
-            // -2px to account for suggestions border.
             if (that.options.width === 'auto') {
-                styles.width = (that.el.outerWidth() - 2) + 'px';
+                styles.width = that.el.outerWidth() + 'px';
             }
 
             $container.css(styles);
             that.stopKillSuggestions();
             that.intervalId = window.setInterval(function () {
                 if (that.visible) {
-                    that.el.val(that.currentValue);
+                    // No need to restore value when
+                    // preserveInput === true,
+                    // because we did not change it
+                    if (!that.options.preserveInput) {
+                        that.el.val(that.currentValue);
+                    }
+
                     that.hide();
                 }
 
         },
 
         suggest: function () {
-            if (this.suggestions.length === 0) {
+            if (!this.suggestions.length) {
                 if (this.options.showNoSuggestionNotice) {
                     this.noSuggestions();
                 } else {
                 beforeRender = options.beforeRender,
                 html = '',
                 category,
-                formatGroup = typeof options.formatGroup == 'function' ? options.formatGroup : function (suggestion, index) {
+                               // modified for TYPO3, added index parameter
+                formatGroup = function (suggestion, value, index) {
                         var currentCategory = suggestion.data[groupBy];
 
                         if (category === currentCategory){
 
                         category = currentCategory;
 
-                        return '<div class="autocomplete-group"><strong>' + category + '</strong></div>';
+                                               // modified for TYPO3, added index parameter
+                        return options.formatGroup(suggestion, category, index);
                     };
 
             if (options.triggerSelectOnValidInput && that.isExactMatch(value)) {
                     html += formatGroup(suggestion, value, i);
                 }
 
-                html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>';
+                html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, i) + '</div>';
             });
 
             this.adjustContainerWidth();
             container.html(html);
 
             if ($.isFunction(beforeRender)) {
-                beforeRender.call(that.element, container);
+                beforeRender.call(that.element, container, that.suggestions);
             }
 
             that.fixPosition();
             // If width is auto, adjust width before displaying suggestions,
             // because if instance was created before input had width, it will be zero.
             // Also it adjusts if input width has changed.
-            // -2px to account for suggestions border.
             if (options.width === 'auto') {
-                width = that.el.outerWidth() - 2;
-                container.width(width > 0 ? width : 300);
+                width = that.el.outerWidth();
+                container.css('width', width > 0 ? width : 300);
+            } else if(options.width === 'flex') {
+                // Trust the source! Unset the width property so it will be the max length
+                // the containing elements.
+                container.css('width', '');
             }
         },
 
             // Cache results if cache is not disabled:
             if (!options.noCache) {
                 that.cachedResponse[cacheKey] = result;
-                if (options.preventBadQueries && result.suggestions.length === 0) {
+                if (options.preventBadQueries && !result.suggestions.length) {
                     that.badQueries.push(originalQuery);
                 }
             }
             var that = this;
             that.hide();
             that.onSelect(i);
+            that.disableKillerFn();
         },
 
         moveUp: function () {
         var dataKey = 'autocomplete';
         // If function invoked without argument return
         // instance of the first matched element:
-        if (arguments.length === 0) {
+        if (!arguments.length) {
             return this.first().data(dataKey);
         }