]> source.dussan.org Git - jquery-ui.git/commitdiff
All: Stop relying on jquery-patch.js internally, add tests
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Fri, 10 May 2024 12:45:59 +0000 (14:45 +0200)
committerMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Tue, 14 May 2024 22:38:40 +0000 (00:38 +0200)
Avoid relying on jQuery patches. Instead:
* use `CSS.escape` instead of `jQuery.escapeSelector`
* use `.filter()` with a proper handler instead of `.even()`

Keep `jquery-patch.js` for backwards compatibility, though.

Also, add tests for jquery-patch.

Ref gh-2249

16 files changed:
build/tasks/testswarm.js
tests/lib/bootstrap.js
tests/runner/flags/suites.js
tests/unit/accordion/common.js
tests/unit/all.html
tests/unit/index.html
tests/unit/jquery-patch/all.html [new file with mode: 0644]
tests/unit/jquery-patch/core.js [new file with mode: 0644]
tests/unit/jquery-patch/jquery-patch.html [new file with mode: 0644]
ui/core.js
ui/jquery-patch.js
ui/labels.js
ui/widgets/accordion.js
ui/widgets/checkboxradio.js
ui/widgets/selectmenu.js
ui/widgets/tabs.js

index 1f5bb81b29ca84b710de78dee97233ee082ea4da..8ab2b7643c0cde5e477cc7a3c7ec316da12cf1bf 100644 (file)
@@ -35,6 +35,7 @@ var versions = {
                "Droppable": "droppable/droppable.html",
                "Effects": "effects/effects.html",
                "Form Reset Mixin": "form-reset-mixin/form-reset-mixin.html",
+               "jQuery Patch": "jquery-patch/jquery-patch.html",
                "Menu": "menu/menu.html",
                "Position": "position/position.html",
                "Progressbar": "progressbar/progressbar.html",
index e0df9ebf5da5d86564ec6fe85072ff2febf9bdef..b86444139338918ca0a59e4e1d30000dd9a46d75 100644 (file)
@@ -171,14 +171,6 @@ function migrateUrl() {
                }
        }
 
-       var jQueryVersion = parseUrl().jquery;
-
-       // Load the jQuery fixes, if necessary
-       if ( !jQueryVersion ||
-               ( jQueryVersion.indexOf( "git" ) === -1 && parseFloat( jQueryVersion ) < 4 ) ) {
-               modules.unshift( "ui/jquery-patch" );
-       }
-
        requireTests( modules, { backCompat: backCompat } );
 } )();
 
index aa7732bf187f5ee10b8da0c5ea94e82b08e6fa43..a635ac4e5619b01807f25aca9337e8b1001acfc2 100644 (file)
@@ -11,6 +11,7 @@ export const suites = [
        "droppable",
        "effects",
        "form-reset-mixin",
+       "jquery-patch",
        "menu",
        "position",
        "progressbar",
index 926d5d9c36e7d4a8f42c5b4d4947378bd45d4f7c..4f1ba7e5da0c098d4af6d47a5ac8036775ebf059 100644 (file)
@@ -16,7 +16,17 @@ common.testWidget( "accordion", {
                disabled: false,
                event: "click",
                header: function( elem ) {
-                       return elem.find( "> li > :first-child" ).add( elem.find( "> :not(li)" ).even() );
+                       return elem
+                               .find( "> li > :first-child" )
+                               .add(
+                                       elem.find( "> :not(li)" )
+
+                                               // Support: jQuery <3.5 only
+                                               // We could use `.even()` but that's unavailable in older jQuery.
+                                               .filter( function( i ) {
+                                                       return i % 2 === 0;
+                                               } )
+                               );
                },
                heightStyle: "auto",
                icons: {
index 3a39956f07d56e483bcdfb37c89b9be611bda9d5..67d7141301fbcfccefa00c9327aa30ce8d66e7aa 100644 (file)
@@ -28,6 +28,7 @@
                        "droppable/droppable.html",
                        "effects/effects.html",
                        "form-reset-mixin/form-reset-mixin.html",
+                       "jquery-patch/jquery-patch.html",
                        "menu/menu.html",
                        "position/position.html",
                        "progressbar/progressbar.html",
index 091e39c4185328f5f0c673d6bdbba74ec431c0de..80224682a83180197c610472e25d29a6281ceddd 100644 (file)
@@ -55,6 +55,7 @@
 
                <h2>Utilities</h2>
                <ul>
+                       <li><a href="jquery-patch/jquery-patch.html">jQuery Patch</a></li>
                        <li><a href="position/position.html">Position</a></li>
                </ul>
 
diff --git a/tests/unit/jquery-patch/all.html b/tests/unit/jquery-patch/all.html
new file mode 100644 (file)
index 0000000..d26b503
--- /dev/null
@@ -0,0 +1,26 @@
+<!doctype html>
+<html lang="en">
+<head>
+       <meta charset="utf-8">
+       <title>jQuery UI Form Reset Mixin Test Suite</title>
+
+       <script src="../../../external/jquery/jquery.js"></script>
+
+       <link rel="stylesheet" href="../../../external/qunit/qunit.css">
+       <link rel="stylesheet" href="../../lib/vendor/qunit-composite/qunit-composite.css">
+       <script src="../../../external/qunit/qunit.js"></script>
+       <script src="../../lib/vendor/qunit-composite/qunit-composite.js"></script>
+       <script src="../subsuite.js"></script>
+
+       <script>
+       testAllVersions( "jquery-patch" );
+       </script>
+</head>
+<body>
+
+<div id="qunit"></div>
+<div id="qunit-fixture">
+
+</div>
+</body>
+</html>
diff --git a/tests/unit/jquery-patch/core.js b/tests/unit/jquery-patch/core.js
new file mode 100644 (file)
index 0000000..5ce7771
--- /dev/null
@@ -0,0 +1,141 @@
+define( [
+       "qunit",
+       "jquery",
+       "lib/helper",
+       "ui/jquery-patch"
+], function( QUnit, $, helper ) {
+"use strict";
+
+QUnit.module( "jquery-patch: core", { afterEach: helper.moduleAfterEach }  );
+
+QUnit.test( "jQuery.escapeSelector", function( assert ) {
+       assert.expect( 58 );
+
+       // Edge cases
+       assert.equal( jQuery.escapeSelector(), "undefined", "Converts undefined to string" );
+       assert.equal( jQuery.escapeSelector( "-" ), "\\-", "Escapes standalone dash" );
+       assert.equal( jQuery.escapeSelector( "-a" ), "-a", "Doesn't escape leading dash followed by non-number" );
+       assert.equal( jQuery.escapeSelector( "--" ), "--", "Doesn't escape standalone double dash" );
+       assert.equal( jQuery.escapeSelector( "\uFFFD" ), "\uFFFD",
+               "Doesn't escape standalone replacement character" );
+       assert.equal( jQuery.escapeSelector( "a\uFFFD" ), "a\uFFFD",
+               "Doesn't escape trailing replacement character" );
+       assert.equal( jQuery.escapeSelector( "\uFFFDb" ), "\uFFFDb",
+               "Doesn't escape leading replacement character" );
+       assert.equal( jQuery.escapeSelector( "a\uFFFDb" ), "a\uFFFDb",
+               "Doesn't escape embedded replacement character" );
+
+       // Derived from CSSOM tests
+       // https://test.csswg.org/harness/test/cssom-1_dev/section/7.1/
+
+       // String conversion
+       assert.equal( jQuery.escapeSelector( true ), "true", "Converts boolean true to string" );
+       assert.equal( jQuery.escapeSelector( false ), "false", "Converts boolean true to string" );
+       assert.equal( jQuery.escapeSelector( null ), "null", "Converts null to string" );
+       assert.equal( jQuery.escapeSelector( "" ), "", "Doesn't modify empty string" );
+
+       // Null bytes
+       assert.equal( jQuery.escapeSelector( "\0" ), "\uFFFD",
+               "Escapes null-character input as replacement character" );
+       assert.equal( jQuery.escapeSelector( "a\0" ), "a\uFFFD",
+               "Escapes trailing-null input as replacement character" );
+       assert.equal( jQuery.escapeSelector( "\0b" ), "\uFFFDb",
+               "Escapes leading-null input as replacement character" );
+       assert.equal( jQuery.escapeSelector( "a\0b" ), "a\uFFFDb",
+               "Escapes embedded-null input as replacement character" );
+
+       // Number prefix
+       assert.equal( jQuery.escapeSelector( "0a" ), "\\30 a", "Escapes leading 0" );
+       assert.equal( jQuery.escapeSelector( "1a" ), "\\31 a", "Escapes leading 1" );
+       assert.equal( jQuery.escapeSelector( "2a" ), "\\32 a", "Escapes leading 2" );
+       assert.equal( jQuery.escapeSelector( "3a" ), "\\33 a", "Escapes leading 3" );
+       assert.equal( jQuery.escapeSelector( "4a" ), "\\34 a", "Escapes leading 4" );
+       assert.equal( jQuery.escapeSelector( "5a" ), "\\35 a", "Escapes leading 5" );
+       assert.equal( jQuery.escapeSelector( "6a" ), "\\36 a", "Escapes leading 6" );
+       assert.equal( jQuery.escapeSelector( "7a" ), "\\37 a", "Escapes leading 7" );
+       assert.equal( jQuery.escapeSelector( "8a" ), "\\38 a", "Escapes leading 8" );
+       assert.equal( jQuery.escapeSelector( "9a" ), "\\39 a", "Escapes leading 9" );
+
+       // Letter-number prefix
+       assert.equal( jQuery.escapeSelector( "a0b" ), "a0b", "Doesn't escape embedded 0" );
+       assert.equal( jQuery.escapeSelector( "a1b" ), "a1b", "Doesn't escape embedded 1" );
+       assert.equal( jQuery.escapeSelector( "a2b" ), "a2b", "Doesn't escape embedded 2" );
+       assert.equal( jQuery.escapeSelector( "a3b" ), "a3b", "Doesn't escape embedded 3" );
+       assert.equal( jQuery.escapeSelector( "a4b" ), "a4b", "Doesn't escape embedded 4" );
+       assert.equal( jQuery.escapeSelector( "a5b" ), "a5b", "Doesn't escape embedded 5" );
+       assert.equal( jQuery.escapeSelector( "a6b" ), "a6b", "Doesn't escape embedded 6" );
+       assert.equal( jQuery.escapeSelector( "a7b" ), "a7b", "Doesn't escape embedded 7" );
+       assert.equal( jQuery.escapeSelector( "a8b" ), "a8b", "Doesn't escape embedded 8" );
+       assert.equal( jQuery.escapeSelector( "a9b" ), "a9b", "Doesn't escape embedded 9" );
+
+       // Dash-number prefix
+       assert.equal( jQuery.escapeSelector( "-0a" ), "-\\30 a", "Escapes 0 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-1a" ), "-\\31 a", "Escapes 1 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-2a" ), "-\\32 a", "Escapes 2 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-3a" ), "-\\33 a", "Escapes 3 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-4a" ), "-\\34 a", "Escapes 4 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-5a" ), "-\\35 a", "Escapes 5 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-6a" ), "-\\36 a", "Escapes 6 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-7a" ), "-\\37 a", "Escapes 7 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-8a" ), "-\\38 a", "Escapes 8 after leading dash" );
+       assert.equal( jQuery.escapeSelector( "-9a" ), "-\\39 a", "Escapes 9 after leading dash" );
+
+       // Double dash prefix
+       assert.equal( jQuery.escapeSelector( "--a" ), "--a", "Doesn't escape leading double dash" );
+
+       // Miscellany
+       assert.equal( jQuery.escapeSelector( "\x01\x02\x1E\x1F" ), "\\1 \\2 \\1e \\1f ",
+               "Escapes C0 control characters" );
+       assert.equal( jQuery.escapeSelector( "\x80\x2D\x5F\xA9" ), "\x80\x2D\x5F\xA9",
+               "Doesn't escape general punctuation or non-ASCII ISO-8859-1 characters" );
+       assert.equal(
+               jQuery.escapeSelector( "\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90" +
+                       "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" ),
+               "\\7f \x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90" +
+               "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F",
+               "Escapes DEL control character"
+       );
+       assert.equal( jQuery.escapeSelector( "\xA0\xA1\xA2" ), "\xA0\xA1\xA2",
+               "Doesn't escape non-ASCII ISO-8859-1 characters" );
+       assert.equal( jQuery.escapeSelector( "a0123456789b" ), "a0123456789b",
+               "Doesn't escape embedded numbers" );
+       assert.equal( jQuery.escapeSelector( "abcdefghijklmnopqrstuvwxyz" ), "abcdefghijklmnopqrstuvwxyz",
+               "Doesn't escape lowercase ASCII letters" );
+       assert.equal( jQuery.escapeSelector( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+               "Doesn't escape uppercase ASCII letters" );
+       assert.equal( jQuery.escapeSelector( "\x20\x21\x78\x79" ), "\\ \\!xy",
+               "Escapes non-word ASCII characters" );
+
+       // Astral symbol (U+1D306 TETRAGRAM FOR CENTRE)
+       assert.equal( jQuery.escapeSelector( "\uD834\uDF06" ), "\uD834\uDF06",
+               "Doesn't escape astral characters" );
+
+       // Lone surrogates
+       assert.equal( jQuery.escapeSelector( "\uDF06" ), "\uDF06", "Doesn't escape lone low surrogate" );
+       assert.equal( jQuery.escapeSelector( "\uD834" ), "\uD834", "Doesn't escape lone high surrogate" );
+} );
+
+QUnit.test( "even()/odd()", function( assert ) {
+       assert.expect( 8 );
+
+       var lis,
+               ul = jQuery( "<ul><li>One</li><li>Two</li><li>Three</li><li>Four</li></ul>" ),
+               none = jQuery();
+
+       ul.appendTo( "#qunit-fixture" );
+
+       lis = ul.find( "li" );
+
+       assert.strictEqual( lis.even().length, 2, "even() length" );
+       assert.strictEqual( lis.even().eq( 0 ).text(), "One", "even(): 1st" );
+       assert.strictEqual( lis.even().eq( 1 ).text(), "Three", "even(): 2nd" );
+
+       assert.strictEqual( lis.odd().length, 2, "odd() length" );
+       assert.strictEqual( lis.odd().eq( 0 ).text(), "Two", "odd(): 1st" );
+       assert.strictEqual( lis.odd().eq( 1 ).text(), "Four", "odd(): 2nd" );
+
+       assert.deepEqual( none.even().get(), [], "even() none" );
+       assert.deepEqual( none.odd().get(), [], "odd() none" );
+} );
+
+} );
diff --git a/tests/unit/jquery-patch/jquery-patch.html b/tests/unit/jquery-patch/jquery-patch.html
new file mode 100644 (file)
index 0000000..a20e25d
--- /dev/null
@@ -0,0 +1,26 @@
+<!doctype html>
+<html lang="en">
+<head>
+       <meta charset="utf-8">
+       <title>jQuery UI Form Reset Mixin Test Suite</title>
+
+       <script src="../../../external/requirejs/require.js"></script>
+       <script src="../../lib/css.js"></script>
+       <script src="../../lib/bootstrap.js" data-modules="core">
+       </script>
+</head>
+<body>
+
+<div id="qunit"></div>
+<div id="qunit-fixture">
+
+<form id="main">
+       <input id="input1">
+       <input id="input2">
+       <input id="input3">
+       <input id="input4">
+</form>
+
+</div>
+</body>
+</html>
index 33d7974e2fe1759d9960bc36d56557d4ec80c8af..d4651c689401ebd42606609d1a2d216682061081 100644 (file)
@@ -9,7 +9,6 @@ define( [
        "./focusable",
        "./keycode",
        "./labels",
-       "./jquery-patch",
        "./plugin",
        "./scroll-parent",
        "./tabbable",
index e03b32edf523d9e77fd36ea5e814f88910efd3af..a4001048d881c9748961d4ba6c66a0242b9a21bc 100644 (file)
@@ -1,5 +1,5 @@
 /*!
- * jQuery UI Support for jQuery core 1.8.x and newer @VERSION
+ * jQuery UI Legacy jQuery Core patches @VERSION
  * https://jqueryui.com
  *
  * Copyright OpenJS Foundation and other contributors
@@ -8,9 +8,9 @@
  *
  */
 
-//>>label: jQuery 1.8+ Support
+//>>label: Legacy jQuery Core patches
 //>>group: Core
-//>>description: Support version 1.8.x and newer of jQuery core
+//>>description: Backport `.even()`, `.odd()` and `$.escapeSelector` to older jQuery Core versions (deprecated)
 
 ( function( factory ) {
 "use strict";
 // This method has been defined in jQuery 3.0.0.
 // Code from https://github.com/jquery/jquery/blob/e539bac79e666bba95bba86d690b4e609dca2286/src/selector/escapeSelector.js
 if ( !$.escapeSelector ) {
-
-       // CSS string/identifier serialization
-       // https://drafts.csswg.org/cssom/#common-serializing-idioms
-       var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
-
-       var fcssescape = function( ch, asCodePoint ) {
-               if ( asCodePoint ) {
-
-                       // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
-                       if ( ch === "\0" ) {
-                               return "\uFFFD";
-                       }
-
-                       // Control characters and (dependent upon position) numbers get escaped as code points
-                       return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
-               }
-
-               // Other potentially-special ASCII characters get backslash-escaped
-               return "\\" + ch;
-       };
-
-       $.escapeSelector = function( sel ) {
-               return ( sel + "" ).replace( rcssescape, fcssescape );
+       $.escapeSelector = function( id ) {
+               return CSS.escape( id + "" );
        };
 }
 
index 4f6533451a9ee4c12f1806c2a8b93dfe3a45a72c..5ff44c28bb1b315cf8ee4968e97a0d4c9d975ca6 100644 (file)
@@ -55,7 +55,7 @@ return $.fn.labels = function() {
                ancestors = ancestor.add( ancestor.length ? ancestor.siblings() : this.siblings() );
 
                // Create a selector for the label based on the id
-               selector = "label[for='" + $.escapeSelector( id ) + "']";
+               selector = "label[for='" + CSS.escape( id ) + "']";
 
                labels = labels.add( ancestors.find( selector ).addBack( selector ) );
 
index b6a7a7eeec3f1670c37fec616792150af2ae4789..ff6e4631da86128590d9e272bfe4a9c0a0d62f27 100644 (file)
@@ -52,7 +52,17 @@ return $.widget( "ui.accordion", {
                collapsible: false,
                event: "click",
                header: function( elem ) {
-                       return elem.find( "> li > :first-child" ).add( elem.find( "> :not(li)" ).even() );
+                       return elem
+                               .find( "> li > :first-child" )
+                               .add(
+                                       elem.find( "> :not(li)" )
+
+                                               // Support: jQuery <3.5 only
+                                               // We could use `.even()` but that's unavailable in older jQuery.
+                                               .filter( function( i ) {
+                                                       return i % 2 === 0;
+                                               } )
+                               );
                },
                heightStyle: "auto",
                icons: {
index fc812116ea872bd2cf7c628476e709a083194d3f..379252758414892119b0a8094e92c00ce108b9a8 100644 (file)
@@ -156,7 +156,7 @@ $.widget( "ui.checkboxradio", [ $.ui.formResetMixin, {
        _getRadioGroup: function() {
                var group;
                var name = this.element[ 0 ].name;
-               var nameSelector = "input[name='" + $.escapeSelector( name ) + "']";
+               var nameSelector = "input[name='" + CSS.escape( name ) + "']";
 
                if ( !name ) {
                        return $( [] );
index eecd368f51cd1db3e1bbd2650ebcfb0b1d9fb53d..f1b48fa6032354943fa00b51d0c6ab00ca894d8c 100644 (file)
@@ -415,7 +415,7 @@ return $.widget( "ui.selectmenu", [ $.ui.formResetMixin, {
                        }
 
                        if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" +
-                               $.escapeSelector( this.ids.button ) ).length ) {
+                               CSS.escape( this.ids.button ) ).length ) {
                                this.close( event );
                        }
                }
index e191dfbb4143362064fdd674a1f35a57141a95e8..72b868e4f05a7a776e21fcfe7d4977a9d67d2356 100644 (file)
@@ -722,7 +722,7 @@ $.widget( "ui.tabs", {
                // meta-function to give users option to provide a href string instead of a numerical index.
                if ( typeof index === "string" ) {
                        index = this.anchors.index( this.anchors.filter( "[href$='" +
-                               $.escapeSelector( index ) + "']" ) );
+                               CSS.escape( index ) + "']" ) );
                }
 
                return index;