Skip to content

Commit cd43cb6

Browse files
committed
Tabs: Use CSS.escape for sanitizing selectors
The previous private `_sanitizeSelector` API was not correctly escaping backslashes and is now removed. The native API should always be correct.
1 parent 49bb397 commit cd43cb6

File tree

3 files changed

+38
-12
lines changed

3 files changed

+38
-12
lines changed

tests/unit/tabs/core.js

+31
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,37 @@ QUnit.test( "non-tab list items", function( assert ) {
8484
"first actual tab is active" );
8585
} );
8686

87+
QUnit.test( "ID escaping backslashes", function( assert ) {
88+
assert.expect( 5 );
89+
90+
location.hash = "#fragment\b-2";
91+
92+
var element = $( "#tabs1" )
93+
.find( "a[href='#fragment-2']" )
94+
.attr( "href", "#fragment\b-2" )
95+
.end()
96+
.find( "#fragment-2" )
97+
.attr( "id", "fragment\b-2" )
98+
.end()
99+
.tabs();
100+
var tabs = element.find( ".ui-tabs-nav li" );
101+
var anchors = tabs.find( ".ui-tabs-anchor" );
102+
var panels = element.find( ".ui-tabs-panel" );
103+
104+
assert.strictEqual( element.tabs( "option", "active" ), 1,
105+
"should set the active option" );
106+
107+
assert.strictEqual( anchors.length, 3, "should decorate all anchors" );
108+
assert.strictEqual( panels.length, 3, "should decorate all panels" );
109+
110+
assert.strictEqual( panels.eq( 1 ).attr( "aria-labelledby" ), anchors.eq( 1 ).attr( "id" ),
111+
"panel 2 aria-labelledby equals anchor 2 id" );
112+
assert.strictEqual( tabs.eq( 1 ).attr( "aria-controls" ), "fragment\b-2",
113+
"tab 2 aria-controls" );
114+
115+
location.hash = "";
116+
} );
117+
87118
QUnit.test( "aria-controls", function( assert ) {
88119
assert.expect( 7 );
89120
var element = $( "#tabs1" ).tabs(),

tests/unit/tabs/helper.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ return $.extend( helper, {
5858
var expected = $.makeArray( arguments ).slice( 2 ),
5959
actual = tabs.find( ".ui-tabs-nav li" ).map( function() {
6060
var tab = $( this ),
61-
panel = $( $.ui.tabs.prototype._sanitizeSelector(
62-
"#" + tab.attr( "aria-controls" ) ) ),
61+
panel = $( "#" + CSS.escape( tab.attr( "aria-controls" ) ) ),
6362
tabIsActive = tab.hasClass( "ui-state-active" ),
6463
panelIsActive = panel.css( "display" ) !== "none";
6564

ui/widgets/tabs.js

+6-10
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ $.widget( "ui.tabs", {
121121
_initialActive: function() {
122122
var active = this.options.active,
123123
collapsible = this.options.collapsible,
124-
locationHash = location.hash.substring( 1 );
124+
locationHashDecoded = decodeURIComponent( location.hash.substring( 1 ) );
125125

126126
if ( active === null ) {
127127

128128
// check the fragment identifier in the URL
129-
if ( locationHash ) {
129+
if ( locationHashDecoded ) {
130130
this.tabs.each( function( i, tab ) {
131-
if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
131+
if ( $( tab ).attr( "aria-controls" ) === locationHashDecoded ) {
132132
active = i;
133133
return false;
134134
}
@@ -312,10 +312,6 @@ $.widget( "ui.tabs", {
312312
}
313313
},
314314

315-
_sanitizeSelector: function( hash ) {
316-
return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
317-
},
318-
319315
refresh: function() {
320316
var options = this.options,
321317
lis = this.tablist.children( ":has(a[href])" );
@@ -434,9 +430,9 @@ $.widget( "ui.tabs", {
434430

435431
// Inline tab
436432
if ( that._isLocal( anchor ) ) {
437-
selector = anchor.hash;
433+
selector = decodeURIComponent( anchor.hash );
438434
panelId = selector.substring( 1 );
439-
panel = that.element.find( that._sanitizeSelector( selector ) );
435+
panel = that.element.find( "#" + CSS.escape( panelId ) );
440436

441437
// remote tab
442438
} else {
@@ -874,7 +870,7 @@ $.widget( "ui.tabs", {
874870

875871
_getPanelForTab: function( tab ) {
876872
var id = $( tab ).attr( "aria-controls" );
877-
return this.element.find( this._sanitizeSelector( "#" + id ) );
873+
return this.element.find( "#" + CSS.escape( id ) );
878874
}
879875
} );
880876

0 commit comments

Comments
 (0)