From cf27de2ec9c3cff48c31d53ce13c861f6a175c02 Mon Sep 17 00:00:00 2001 From: evazion Date: Tue, 6 Feb 2018 19:11:01 -0600 Subject: [PATCH] Fix #3532: Make Enter key accept current tag during autocomplete. Previously we patched the jqueryui-autocomplete library in order to customize how the Tab and Enter keys behaved. Specifically, we wanted to prevent the Tab key from moving the focus out of the tag input box, and we wanted to prevent the Enter key from submitting the page when editing tags. These things can achieved without patching the library by using `event.preventDefault` and `event.stopImmediatePropagation` to prevent other event handlers from running after these keys trigger the `autocompleteselect` event. --- app/assets/javascripts/application.js | 2 +- app/assets/javascripts/autocomplete.js.erb | 17 +- app/assets/javascripts/uploads.js | 5 +- vendor/assets/javascripts/build_patches.sh | 3 - .../jquery-ui-autocomplete-custom.js | 611 ------------------ .../javascripts/jquery-ui-autocomplete.patch | 39 -- 6 files changed, 9 insertions(+), 668 deletions(-) delete mode 100755 vendor/assets/javascripts/build_patches.sh delete mode 100644 vendor/assets/javascripts/jquery-ui-autocomplete-custom.js delete mode 100644 vendor/assets/javascripts/jquery-ui-autocomplete.patch diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index ddbc1151b..bb80c2714 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -3,7 +3,7 @@ //= require jquery-ui-1.11.2.min.js //= require jquery.hotkeys.js //= require jquery.timeout.js -//= require jquery-ui-autocomplete-custom.js +//= require jquery-ui-autocomplete-1.11.2.js //= require jquery.storageapi.js //= require jquery.dropdown.min.js //= require jquery.hammer.js diff --git a/app/assets/javascripts/autocomplete.js.erb b/app/assets/javascripts/autocomplete.js.erb index 11de03677..45c910a5e 100644 --- a/app/assets/javascripts/autocomplete.js.erb +++ b/app/assets/javascripts/autocomplete.js.erb @@ -59,6 +59,11 @@ this.value += after_caret_text; this.selectionStart = this.selectionEnd = original_start; + // Prevent the enter key from submitting the tag edit form (Danbooru.Upload.initialize_enter_on_tags). + event.stopImmediatePropagation(); + // Prevent the tab key from moving the focus to the next element. + event.preventDefault(); + return false; }, source: function(req, resp) { @@ -114,6 +119,8 @@ this.value += after_caret_text; this.selectionStart = this.selectionEnd = original_start; + event.stopImmediatePropagation(); + event.preventDefault(); return false; }, source: function(req, resp) { @@ -217,16 +224,6 @@ } }); - $fields_multiple.on("autocompleteselect", function() { - Danbooru.autocompleting = true; - }); - - $fields_multiple.on("autocompleteclose", function() { - // this is needed otherwise the var is disabled by the time the - // keydown is triggered - setTimeout(function() {Danbooru.autocompleting = false;}, 100); - }); - $fields_single.autocomplete({ minLength: 1, autoFocus: true, diff --git a/app/assets/javascripts/uploads.js b/app/assets/javascripts/uploads.js index 319695d5f..4e16defe3 100644 --- a/app/assets/javascripts/uploads.js +++ b/app/assets/javascripts/uploads.js @@ -66,10 +66,7 @@ var $submit = $textarea.parents("form").find('input[type="submit"]'); $textarea.on("keydown.danbooru.submit", null, "return", function(e) { - if (!Danbooru.autocompleting) { - $submit.click(); - } - + $submit.click(); e.preventDefault(); }); } diff --git a/vendor/assets/javascripts/build_patches.sh b/vendor/assets/javascripts/build_patches.sh deleted file mode 100755 index d74ed55e2..000000000 --- a/vendor/assets/javascripts/build_patches.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -diff -u jquery-ui-autocomplete-1.11.2.js jquery-ui-autocomplete-custom.js > jquery-ui-autocomplete.patch diff --git a/vendor/assets/javascripts/jquery-ui-autocomplete-custom.js b/vendor/assets/javascripts/jquery-ui-autocomplete-custom.js deleted file mode 100644 index 89a360afe..000000000 --- a/vendor/assets/javascripts/jquery-ui-autocomplete-custom.js +++ /dev/null @@ -1,611 +0,0 @@ -/*! - * jQuery UI Autocomplete 1.11.2 - * http://jqueryui.com - * - * Copyright 2014 jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - * - * http://api.jqueryui.com/autocomplete/ - */ - - -$.widget( "ui.autocomplete", { - version: "1.11.2", - defaultElement: "", - options: { - appendTo: null, - autoFocus: false, - delay: 300, - minLength: 1, - position: { - my: "left top", - at: "left bottom", - collision: "none" - }, - source: null, - - // callbacks - change: null, - close: null, - focus: null, - open: null, - response: null, - search: null, - select: null - }, - - requestIndex: 0, - pending: 0, - - _create: function() { - // Some browsers only repeat keydown events, not keypress events, - // so we use the suppressKeyPress flag to determine if we've already - // handled the keydown event. #7269 - // Unfortunately the code for & in keypress is the same as the up arrow, - // so we use the suppressKeyPressRepeat flag to avoid handling keypress - // events when we know the keydown event was used to modify the - // search term. #7799 - var suppressKeyPress, suppressKeyPressRepeat, suppressInput, - nodeName = this.element[ 0 ].nodeName.toLowerCase(), - isTextarea = nodeName === "textarea", - isInput = nodeName === "input"; - - this.isMultiLine = - // Textareas are always multi-line - isTextarea ? true : - // Inputs are always single-line, even if inside a contentEditable element - // IE also treats inputs as contentEditable - isInput ? false : - // All other element types are determined by whether or not they're contentEditable - this.element.prop( "isContentEditable" ); - - this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; - this.isNewMenu = true; - - this.element - .addClass( "ui-autocomplete-input" ) - .attr( "autocomplete", "off" ); - - this._on( this.element, { - click: function(event) { - if ( this.menu.element.is( ":visible" ) ) { - this._value( this.term ); - this.close( event ); - // Different browsers have different default behavior for escape - // Single press can mean undo or clear - // Double press in IE means clear the whole form - event.preventDefault(); - } - }, - keydown: function( event ) { - if ( this.element.prop( "readOnly" ) ) { - suppressKeyPress = true; - suppressInput = true; - suppressKeyPressRepeat = true; - return; - } - - suppressKeyPress = false; - suppressInput = false; - suppressKeyPressRepeat = false; - var keyCode = $.ui.keyCode; - switch ( event.keyCode ) { - case keyCode.PAGE_UP: - suppressKeyPress = true; - this._move( "previousPage", event ); - break; - case keyCode.PAGE_DOWN: - suppressKeyPress = true; - this._move( "nextPage", event ); - break; - case keyCode.UP: - suppressKeyPress = true; - this._keyEvent( "previous", event ); - break; - case keyCode.DOWN: - suppressKeyPress = true; - this._keyEvent( "next", event ); - break; - case keyCode.TAB: - if ( this.menu.active ) { - event.preventDefault(); - this.menu.select( event ); - } - break; - case keyCode.ESCAPE: - if ( this.menu.element.is( ":visible" ) ) { - if ( !this.isMultiLine ) { - this._value( this.term ); - } - this.close( event ); - // Different browsers have different default behavior for escape - // Single press can mean undo or clear - // Double press in IE means clear the whole form - event.preventDefault(); - } - break; - default: - suppressKeyPressRepeat = true; - // search timeout should be triggered before the input value is changed - this._searchTimeout( event ); - break; - } - }, - keypress: function( event ) { - if ( suppressKeyPress ) { - suppressKeyPress = false; - if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { - event.preventDefault(); - } - return; - } - if ( suppressKeyPressRepeat ) { - return; - } - - // replicate some key handlers to allow them to repeat in Firefox and Opera - var keyCode = $.ui.keyCode; - switch ( event.keyCode ) { - case keyCode.PAGE_UP: - this._move( "previousPage", event ); - break; - case keyCode.PAGE_DOWN: - this._move( "nextPage", event ); - break; - case keyCode.UP: - this._keyEvent( "previous", event ); - break; - case keyCode.DOWN: - this._keyEvent( "next", event ); - break; - } - }, - input: function( event ) { - if ( suppressInput ) { - suppressInput = false; - event.preventDefault(); - return; - } - this._searchTimeout( event ); - }, - focus: function() { - this.selectedItem = null; - this.previous = this._value(); - }, - blur: function( event ) { - if ( this.cancelBlur ) { - delete this.cancelBlur; - return; - } - - clearTimeout( this.searching ); - this.close( event ); - this._change( event ); - } - }); - - this._initSource(); - this.menu = $( "