Skip to content

Commit 9887579

Browse files
committed
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
1 parent f90eab8 commit 9887579

File tree

16 files changed

+228
-41
lines changed

16 files changed

+228
-41
lines changed

build/tasks/testswarm.js

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var versions = {
3535
"Droppable": "droppable/droppable.html",
3636
"Effects": "effects/effects.html",
3737
"Form Reset Mixin": "form-reset-mixin/form-reset-mixin.html",
38+
"jQuery Patch": "jquery-patch/jquery-patch.html",
3839
"Menu": "menu/menu.html",
3940
"Position": "position/position.html",
4041
"Progressbar": "progressbar/progressbar.html",

tests/lib/bootstrap.js

-8
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,6 @@ function migrateUrl() {
171171
}
172172
}
173173

174-
var jQueryVersion = parseUrl().jquery;
175-
176-
// Load the jQuery fixes, if necessary
177-
if ( !jQueryVersion ||
178-
( jQueryVersion.indexOf( "git" ) === -1 && parseFloat( jQueryVersion ) < 4 ) ) {
179-
modules.unshift( "ui/jquery-patch" );
180-
}
181-
182174
requireTests( modules, { backCompat: backCompat } );
183175
} )();
184176

tests/runner/flags/suites.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const suites = [
1111
"droppable",
1212
"effects",
1313
"form-reset-mixin",
14+
"jquery-patch",
1415
"menu",
1516
"position",
1617
"progressbar",

tests/unit/accordion/common.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,17 @@ common.testWidget( "accordion", {
1616
disabled: false,
1717
event: "click",
1818
header: function( elem ) {
19-
return elem.find( "> li > :first-child" ).add( elem.find( "> :not(li)" ).even() );
19+
return elem
20+
.find( "> li > :first-child" )
21+
.add(
22+
elem.find( "> :not(li)" )
23+
24+
// Support: jQuery <3.5 only
25+
// We could use `.even()` but that's unavailable in older jQuery.
26+
.filter( function( i ) {
27+
return i % 2 === 0;
28+
} )
29+
);
2030
},
2131
heightStyle: "auto",
2232
icons: {

tests/unit/all.html

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"droppable/droppable.html",
2929
"effects/effects.html",
3030
"form-reset-mixin/form-reset-mixin.html",
31+
"jquery-patch/jquery-patch.html",
3132
"menu/menu.html",
3233
"position/position.html",
3334
"progressbar/progressbar.html",

tests/unit/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ <h2>Widgets</h2>
5555

5656
<h2>Utilities</h2>
5757
<ul>
58+
<li><a href="jquery-patch/jquery-patch.html">jQuery Patch</a></li>
5859
<li><a href="position/position.html">Position</a></li>
5960
</ul>
6061

tests/unit/jquery-patch/all.html

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>jQuery UI Form Reset Mixin Test Suite</title>
6+
7+
<script src="../../../external/jquery/jquery.js"></script>
8+
9+
<link rel="stylesheet" href="../../../external/qunit/qunit.css">
10+
<link rel="stylesheet" href="../../lib/vendor/qunit-composite/qunit-composite.css">
11+
<script src="../../../external/qunit/qunit.js"></script>
12+
<script src="../../lib/vendor/qunit-composite/qunit-composite.js"></script>
13+
<script src="../subsuite.js"></script>
14+
15+
<script>
16+
testAllVersions( "jquery-patch" );
17+
</script>
18+
</head>
19+
<body>
20+
21+
<div id="qunit"></div>
22+
<div id="qunit-fixture">
23+
24+
</div>
25+
</body>
26+
</html>

tests/unit/jquery-patch/core.js

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
define( [
2+
"qunit",
3+
"jquery",
4+
"lib/helper",
5+
"ui/jquery-patch"
6+
], function( QUnit, $, helper ) {
7+
"use strict";
8+
9+
QUnit.module( "jquery-patch: core", { afterEach: helper.moduleAfterEach } );
10+
11+
QUnit.test( "jQuery.escapeSelector", function( assert ) {
12+
assert.expect( 58 );
13+
14+
// Edge cases
15+
assert.equal( jQuery.escapeSelector(), "undefined", "Converts undefined to string" );
16+
assert.equal( jQuery.escapeSelector( "-" ), "\\-", "Escapes standalone dash" );
17+
assert.equal( jQuery.escapeSelector( "-a" ), "-a", "Doesn't escape leading dash followed by non-number" );
18+
assert.equal( jQuery.escapeSelector( "--" ), "--", "Doesn't escape standalone double dash" );
19+
assert.equal( jQuery.escapeSelector( "\uFFFD" ), "\uFFFD",
20+
"Doesn't escape standalone replacement character" );
21+
assert.equal( jQuery.escapeSelector( "a\uFFFD" ), "a\uFFFD",
22+
"Doesn't escape trailing replacement character" );
23+
assert.equal( jQuery.escapeSelector( "\uFFFDb" ), "\uFFFDb",
24+
"Doesn't escape leading replacement character" );
25+
assert.equal( jQuery.escapeSelector( "a\uFFFDb" ), "a\uFFFDb",
26+
"Doesn't escape embedded replacement character" );
27+
28+
// Derived from CSSOM tests
29+
// https://test.csswg.org/harness/test/cssom-1_dev/section/7.1/
30+
31+
// String conversion
32+
assert.equal( jQuery.escapeSelector( true ), "true", "Converts boolean true to string" );
33+
assert.equal( jQuery.escapeSelector( false ), "false", "Converts boolean true to string" );
34+
assert.equal( jQuery.escapeSelector( null ), "null", "Converts null to string" );
35+
assert.equal( jQuery.escapeSelector( "" ), "", "Doesn't modify empty string" );
36+
37+
// Null bytes
38+
assert.equal( jQuery.escapeSelector( "\0" ), "\uFFFD",
39+
"Escapes null-character input as replacement character" );
40+
assert.equal( jQuery.escapeSelector( "a\0" ), "a\uFFFD",
41+
"Escapes trailing-null input as replacement character" );
42+
assert.equal( jQuery.escapeSelector( "\0b" ), "\uFFFDb",
43+
"Escapes leading-null input as replacement character" );
44+
assert.equal( jQuery.escapeSelector( "a\0b" ), "a\uFFFDb",
45+
"Escapes embedded-null input as replacement character" );
46+
47+
// Number prefix
48+
assert.equal( jQuery.escapeSelector( "0a" ), "\\30 a", "Escapes leading 0" );
49+
assert.equal( jQuery.escapeSelector( "1a" ), "\\31 a", "Escapes leading 1" );
50+
assert.equal( jQuery.escapeSelector( "2a" ), "\\32 a", "Escapes leading 2" );
51+
assert.equal( jQuery.escapeSelector( "3a" ), "\\33 a", "Escapes leading 3" );
52+
assert.equal( jQuery.escapeSelector( "4a" ), "\\34 a", "Escapes leading 4" );
53+
assert.equal( jQuery.escapeSelector( "5a" ), "\\35 a", "Escapes leading 5" );
54+
assert.equal( jQuery.escapeSelector( "6a" ), "\\36 a", "Escapes leading 6" );
55+
assert.equal( jQuery.escapeSelector( "7a" ), "\\37 a", "Escapes leading 7" );
56+
assert.equal( jQuery.escapeSelector( "8a" ), "\\38 a", "Escapes leading 8" );
57+
assert.equal( jQuery.escapeSelector( "9a" ), "\\39 a", "Escapes leading 9" );
58+
59+
// Letter-number prefix
60+
assert.equal( jQuery.escapeSelector( "a0b" ), "a0b", "Doesn't escape embedded 0" );
61+
assert.equal( jQuery.escapeSelector( "a1b" ), "a1b", "Doesn't escape embedded 1" );
62+
assert.equal( jQuery.escapeSelector( "a2b" ), "a2b", "Doesn't escape embedded 2" );
63+
assert.equal( jQuery.escapeSelector( "a3b" ), "a3b", "Doesn't escape embedded 3" );
64+
assert.equal( jQuery.escapeSelector( "a4b" ), "a4b", "Doesn't escape embedded 4" );
65+
assert.equal( jQuery.escapeSelector( "a5b" ), "a5b", "Doesn't escape embedded 5" );
66+
assert.equal( jQuery.escapeSelector( "a6b" ), "a6b", "Doesn't escape embedded 6" );
67+
assert.equal( jQuery.escapeSelector( "a7b" ), "a7b", "Doesn't escape embedded 7" );
68+
assert.equal( jQuery.escapeSelector( "a8b" ), "a8b", "Doesn't escape embedded 8" );
69+
assert.equal( jQuery.escapeSelector( "a9b" ), "a9b", "Doesn't escape embedded 9" );
70+
71+
// Dash-number prefix
72+
assert.equal( jQuery.escapeSelector( "-0a" ), "-\\30 a", "Escapes 0 after leading dash" );
73+
assert.equal( jQuery.escapeSelector( "-1a" ), "-\\31 a", "Escapes 1 after leading dash" );
74+
assert.equal( jQuery.escapeSelector( "-2a" ), "-\\32 a", "Escapes 2 after leading dash" );
75+
assert.equal( jQuery.escapeSelector( "-3a" ), "-\\33 a", "Escapes 3 after leading dash" );
76+
assert.equal( jQuery.escapeSelector( "-4a" ), "-\\34 a", "Escapes 4 after leading dash" );
77+
assert.equal( jQuery.escapeSelector( "-5a" ), "-\\35 a", "Escapes 5 after leading dash" );
78+
assert.equal( jQuery.escapeSelector( "-6a" ), "-\\36 a", "Escapes 6 after leading dash" );
79+
assert.equal( jQuery.escapeSelector( "-7a" ), "-\\37 a", "Escapes 7 after leading dash" );
80+
assert.equal( jQuery.escapeSelector( "-8a" ), "-\\38 a", "Escapes 8 after leading dash" );
81+
assert.equal( jQuery.escapeSelector( "-9a" ), "-\\39 a", "Escapes 9 after leading dash" );
82+
83+
// Double dash prefix
84+
assert.equal( jQuery.escapeSelector( "--a" ), "--a", "Doesn't escape leading double dash" );
85+
86+
// Miscellany
87+
assert.equal( jQuery.escapeSelector( "\x01\x02\x1E\x1F" ), "\\1 \\2 \\1e \\1f ",
88+
"Escapes C0 control characters" );
89+
assert.equal( jQuery.escapeSelector( "\x80\x2D\x5F\xA9" ), "\x80\x2D\x5F\xA9",
90+
"Doesn't escape general punctuation or non-ASCII ISO-8859-1 characters" );
91+
assert.equal(
92+
jQuery.escapeSelector( "\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90" +
93+
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" ),
94+
"\\7f \x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90" +
95+
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F",
96+
"Escapes DEL control character"
97+
);
98+
assert.equal( jQuery.escapeSelector( "\xA0\xA1\xA2" ), "\xA0\xA1\xA2",
99+
"Doesn't escape non-ASCII ISO-8859-1 characters" );
100+
assert.equal( jQuery.escapeSelector( "a0123456789b" ), "a0123456789b",
101+
"Doesn't escape embedded numbers" );
102+
assert.equal( jQuery.escapeSelector( "abcdefghijklmnopqrstuvwxyz" ), "abcdefghijklmnopqrstuvwxyz",
103+
"Doesn't escape lowercase ASCII letters" );
104+
assert.equal( jQuery.escapeSelector( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
105+
"Doesn't escape uppercase ASCII letters" );
106+
assert.equal( jQuery.escapeSelector( "\x20\x21\x78\x79" ), "\\ \\!xy",
107+
"Escapes non-word ASCII characters" );
108+
109+
// Astral symbol (U+1D306 TETRAGRAM FOR CENTRE)
110+
assert.equal( jQuery.escapeSelector( "\uD834\uDF06" ), "\uD834\uDF06",
111+
"Doesn't escape astral characters" );
112+
113+
// Lone surrogates
114+
assert.equal( jQuery.escapeSelector( "\uDF06" ), "\uDF06", "Doesn't escape lone low surrogate" );
115+
assert.equal( jQuery.escapeSelector( "\uD834" ), "\uD834", "Doesn't escape lone high surrogate" );
116+
} );
117+
118+
QUnit.test( "even()/odd()", function( assert ) {
119+
assert.expect( 8 );
120+
121+
var lis,
122+
ul = jQuery( "<ul><li>One</li><li>Two</li><li>Three</li><li>Four</li></ul>" ),
123+
none = jQuery();
124+
125+
ul.appendTo( "#qunit-fixture" );
126+
127+
lis = ul.find( "li" );
128+
129+
assert.strictEqual( lis.even().length, 2, "even() length" );
130+
assert.strictEqual( lis.even().eq( 0 ).text(), "One", "even(): 1st" );
131+
assert.strictEqual( lis.even().eq( 1 ).text(), "Three", "even(): 2nd" );
132+
133+
assert.strictEqual( lis.odd().length, 2, "odd() length" );
134+
assert.strictEqual( lis.odd().eq( 0 ).text(), "Two", "odd(): 1st" );
135+
assert.strictEqual( lis.odd().eq( 1 ).text(), "Four", "odd(): 2nd" );
136+
137+
assert.deepEqual( none.even().get(), [], "even() none" );
138+
assert.deepEqual( none.odd().get(), [], "odd() none" );
139+
} );
140+
141+
} );
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>jQuery UI Form Reset Mixin Test Suite</title>
6+
7+
<script src="../../../external/requirejs/require.js"></script>
8+
<script src="../../lib/css.js"></script>
9+
<script src="../../lib/bootstrap.js" data-modules="core">
10+
</script>
11+
</head>
12+
<body>
13+
14+
<div id="qunit"></div>
15+
<div id="qunit-fixture">
16+
17+
<form id="main">
18+
<input id="input1">
19+
<input id="input2">
20+
<input id="input3">
21+
<input id="input4">
22+
</form>
23+
24+
</div>
25+
</body>
26+
</html>

ui/core.js

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ define( [
99
"./focusable",
1010
"./keycode",
1111
"./labels",
12-
"./jquery-patch",
1312
"./plugin",
1413
"./scroll-parent",
1514
"./tabbable",

ui/jquery-patch.js

+5-26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*!
2-
* jQuery UI Support for jQuery core 1.8.x and newer @VERSION
2+
* jQuery UI Legacy jQuery Core patches @VERSION
33
* https://jqueryui.com
44
*
55
* Copyright OpenJS Foundation and other contributors
@@ -8,9 +8,9 @@
88
*
99
*/
1010

11-
//>>label: jQuery 1.8+ Support
11+
//>>label: Legacy jQuery Core patches
1212
//>>group: Core
13-
//>>description: Support version 1.8.x and newer of jQuery core
13+
//>>description: Backport `.even()`, `.odd()` and `$.escapeSelector` to older jQuery Core versions (deprecated)
1414

1515
( function( factory ) {
1616
"use strict";
@@ -31,29 +31,8 @@
3131
// This method has been defined in jQuery 3.0.0.
3232
// Code from https://github.com/jquery/jquery/blob/e539bac79e666bba95bba86d690b4e609dca2286/src/selector/escapeSelector.js
3333
if ( !$.escapeSelector ) {
34-
35-
// CSS string/identifier serialization
36-
// https://drafts.csswg.org/cssom/#common-serializing-idioms
37-
var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
38-
39-
var fcssescape = function( ch, asCodePoint ) {
40-
if ( asCodePoint ) {
41-
42-
// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
43-
if ( ch === "\0" ) {
44-
return "\uFFFD";
45-
}
46-
47-
// Control characters and (dependent upon position) numbers get escaped as code points
48-
return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
49-
}
50-
51-
// Other potentially-special ASCII characters get backslash-escaped
52-
return "\\" + ch;
53-
};
54-
55-
$.escapeSelector = function( sel ) {
56-
return ( sel + "" ).replace( rcssescape, fcssescape );
34+
$.escapeSelector = function( id ) {
35+
return CSS.escape( id + "" );
5736
};
5837
}
5938

ui/labels.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ return $.fn.labels = function() {
5555
ancestors = ancestor.add( ancestor.length ? ancestor.siblings() : this.siblings() );
5656

5757
// Create a selector for the label based on the id
58-
selector = "label[for='" + $.escapeSelector( id ) + "']";
58+
selector = "label[for='" + CSS.escape( id ) + "']";
5959

6060
labels = labels.add( ancestors.find( selector ).addBack( selector ) );
6161

ui/widgets/accordion.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,17 @@ return $.widget( "ui.accordion", {
5252
collapsible: false,
5353
event: "click",
5454
header: function( elem ) {
55-
return elem.find( "> li > :first-child" ).add( elem.find( "> :not(li)" ).even() );
55+
return elem
56+
.find( "> li > :first-child" )
57+
.add(
58+
elem.find( "> :not(li)" )
59+
60+
// Support: jQuery <3.5 only
61+
// We could use `.even()` but that's unavailable in older jQuery.
62+
.filter( function( i ) {
63+
return i % 2 === 0;
64+
} )
65+
);
5666
},
5767
heightStyle: "auto",
5868
icons: {

ui/widgets/checkboxradio.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ $.widget( "ui.checkboxradio", [ $.ui.formResetMixin, {
156156
_getRadioGroup: function() {
157157
var group;
158158
var name = this.element[ 0 ].name;
159-
var nameSelector = "input[name='" + $.escapeSelector( name ) + "']";
159+
var nameSelector = "input[name='" + CSS.escape( name ) + "']";
160160

161161
if ( !name ) {
162162
return $( [] );

ui/widgets/selectmenu.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ return $.widget( "ui.selectmenu", [ $.ui.formResetMixin, {
415415
}
416416

417417
if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" +
418-
$.escapeSelector( this.ids.button ) ).length ) {
418+
CSS.escape( this.ids.button ) ).length ) {
419419
this.close( event );
420420
}
421421
}

ui/widgets/tabs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ $.widget( "ui.tabs", {
722722
// meta-function to give users option to provide a href string instead of a numerical index.
723723
if ( typeof index === "string" ) {
724724
index = this.anchors.index( this.anchors.filter( "[href$='" +
725-
$.escapeSelector( index ) + "']" ) );
725+
CSS.escape( index ) + "']" ) );
726726
}
727727

728728
return index;

0 commit comments

Comments
 (0)