aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>2024-05-10 14:45:59 +0200
committerMichał Gołębiowski-Owczarek <m.goleb@gmail.com>2024-05-15 00:38:40 +0200
commit9887579b61972647f1478e64c5d7987f9d9cb039 (patch)
treec592a65dd9a8f9a6a7a3a31892eec299e1799a80
parentf90eab84b5e9a65ce62f18106e3db0ee316913f6 (diff)
downloadjquery-ui-9887579b61972647f1478e64c5d7987f9d9cb039.tar.gz
jquery-ui-9887579b61972647f1478e64c5d7987f9d9cb039.zip
All: Stop relying on jquery-patch.js internally, add tests
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
-rw-r--r--build/tasks/testswarm.js1
-rw-r--r--tests/lib/bootstrap.js8
-rw-r--r--tests/runner/flags/suites.js1
-rw-r--r--tests/unit/accordion/common.js12
-rw-r--r--tests/unit/all.html1
-rw-r--r--tests/unit/index.html1
-rw-r--r--tests/unit/jquery-patch/all.html26
-rw-r--r--tests/unit/jquery-patch/core.js141
-rw-r--r--tests/unit/jquery-patch/jquery-patch.html26
-rw-r--r--ui/core.js1
-rw-r--r--ui/jquery-patch.js31
-rw-r--r--ui/labels.js2
-rw-r--r--ui/widgets/accordion.js12
-rw-r--r--ui/widgets/checkboxradio.js2
-rw-r--r--ui/widgets/selectmenu.js2
-rw-r--r--ui/widgets/tabs.js2
16 files changed, 228 insertions, 41 deletions
diff --git a/build/tasks/testswarm.js b/build/tasks/testswarm.js
index 1f5bb81b2..8ab2b7643 100644
--- a/build/tasks/testswarm.js
+++ b/build/tasks/testswarm.js
@@ -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",
diff --git a/tests/lib/bootstrap.js b/tests/lib/bootstrap.js
index e0df9ebf5..b86444139 100644
--- a/tests/lib/bootstrap.js
+++ b/tests/lib/bootstrap.js
@@ -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 } );
} )();
diff --git a/tests/runner/flags/suites.js b/tests/runner/flags/suites.js
index aa7732bf1..a635ac4e5 100644
--- a/tests/runner/flags/suites.js
+++ b/tests/runner/flags/suites.js
@@ -11,6 +11,7 @@ export const suites = [
"droppable",
"effects",
"form-reset-mixin",
+ "jquery-patch",
"menu",
"position",
"progressbar",
diff --git a/tests/unit/accordion/common.js b/tests/unit/accordion/common.js
index 926d5d9c3..4f1ba7e5d 100644
--- a/tests/unit/accordion/common.js
+++ b/tests/unit/accordion/common.js
@@ -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: {
diff --git a/tests/unit/all.html b/tests/unit/all.html
index 3a39956f0..67d714130 100644
--- a/tests/unit/all.html
+++ b/tests/unit/all.html
@@ -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",
diff --git a/tests/unit/index.html b/tests/unit/index.html
index 091e39c41..80224682a 100644
--- a/tests/unit/index.html
+++ b/tests/unit/index.html
@@ -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
index 000000000..d26b50373
--- /dev/null
+++ b/tests/unit/jquery-patch/all.html
@@ -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
index 000000000..5ce7771f8
--- /dev/null
+++ b/tests/unit/jquery-patch/core.js
@@ -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
index 000000000..a20e25d4a
--- /dev/null
+++ b/tests/unit/jquery-patch/jquery-patch.html
@@ -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>
diff --git a/ui/core.js b/ui/core.js
index 33d7974e2..d4651c689 100644
--- a/ui/core.js
+++ b/ui/core.js
@@ -9,7 +9,6 @@ define( [
"./focusable",
"./keycode",
"./labels",
- "./jquery-patch",
"./plugin",
"./scroll-parent",
"./tabbable",
diff --git a/ui/jquery-patch.js b/ui/jquery-patch.js
index e03b32edf..a4001048d 100644
--- a/ui/jquery-patch.js
+++ b/ui/jquery-patch.js
@@ -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";
@@ -31,29 +31,8 @@
// 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 + "" );
};
}
diff --git a/ui/labels.js b/ui/labels.js
index 4f6533451..5ff44c28b 100644
--- a/ui/labels.js
+++ b/ui/labels.js
@@ -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 ) );
diff --git a/ui/widgets/accordion.js b/ui/widgets/accordion.js
index b6a7a7eee..ff6e4631d 100644
--- a/ui/widgets/accordion.js
+++ b/ui/widgets/accordion.js
@@ -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: {
diff --git a/ui/widgets/checkboxradio.js b/ui/widgets/checkboxradio.js
index fc812116e..379252758 100644
--- a/ui/widgets/checkboxradio.js
+++ b/ui/widgets/checkboxradio.js
@@ -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 $( [] );
diff --git a/ui/widgets/selectmenu.js b/ui/widgets/selectmenu.js
index eecd368f5..f1b48fa60 100644
--- a/ui/widgets/selectmenu.js
+++ b/ui/widgets/selectmenu.js
@@ -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 );
}
}
diff --git a/ui/widgets/tabs.js b/ui/widgets/tabs.js
index e191dfbb4..72b868e4f 100644
--- a/ui/widgets/tabs.js
+++ b/ui/widgets/tabs.js
@@ -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;