Added UTF8 Selector support (#1001) and fixed infinite selector loops (#1025). You can now select elements by UTF8-based IDs, classes, and attributes.
This commit is contained in:
parent
ec2b688920
commit
af79bb1f25
|
@ -1,5 +1,6 @@
|
||||||
<html id="html">
|
<html id="html">
|
||||||
<head>
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/css; charset=utf-8" />
|
||||||
<title>jQuery Test Suite</title>
|
<title>jQuery Test Suite</title>
|
||||||
<link rel="Stylesheet" media="screen" href="data/testsuite.css" />
|
<link rel="Stylesheet" media="screen" href="data/testsuite.css" />
|
||||||
<script type="text/javascript" src="../dist/jquery.js"></script>
|
<script type="text/javascript" src="../dist/jquery.js"></script>
|
||||||
|
@ -77,6 +78,11 @@
|
||||||
<param name="p1" value="x1" />
|
<param name="p1" value="x1" />
|
||||||
<param name="p2" value="x2" />
|
<param name="p2" value="x2" />
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
|
<span id="台北Táiběi"></span>
|
||||||
|
<span id="台北" lang="中文"></span>
|
||||||
|
<span id="utf8class1" class="台北Táiběi 台北"></span>
|
||||||
|
<span id="utf8class2" class="台北"></span>
|
||||||
</form>
|
</form>
|
||||||
<b id="floatTest">Float test.</b>
|
<b id="floatTest">Float test.</b>
|
||||||
<iframe id="iframe"></iframe>
|
<iframe id="iframe"></iframe>
|
||||||
|
|
|
@ -67,16 +67,16 @@ jQuery.extend({
|
||||||
// The regular expressions that power the parsing engine
|
// The regular expressions that power the parsing engine
|
||||||
parse: [
|
parse: [
|
||||||
// Match: [@value='test'], [@foo]
|
// Match: [@value='test'], [@foo]
|
||||||
/^\[ *(@)([a-z0-9_-]*) *([!*$^=]*) *('?"?)(.*?)\4 *\]/i,
|
/^\[ *(@)([\w-]+) *([!*$^=]*) *('?"?)(.*?)\4 *\]/i,
|
||||||
|
|
||||||
// Match: [div], [div p]
|
// Match: [div], [div p]
|
||||||
/^(\[)\s*(.*?(\[.*?\])?[^[]*?)\s*\]/,
|
/^(\[)\s*(.*?(\[.*?\])?[^[]*?)\s*\]/,
|
||||||
|
|
||||||
// Match: :contains('foo')
|
// Match: :contains('foo')
|
||||||
/^(:)([a-z0-9_-]*)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/i,
|
/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/i,
|
||||||
|
|
||||||
// Match: :even, :last-chlid
|
// Match: :even, :last-chlid, #id, .class
|
||||||
/^([:.#]*)([a-z0-9_*-]*)/i
|
/^([:.#]*)([\w\u0128-\uFFFF*-]+)/i
|
||||||
],
|
],
|
||||||
|
|
||||||
token: [
|
token: [
|
||||||
|
@ -134,7 +134,7 @@ jQuery.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the search
|
// Initialize the search
|
||||||
var ret = [context], done = [], last = null;
|
var ret = [context], done = [], last;
|
||||||
|
|
||||||
// Continue while a selector expression exists, and while
|
// Continue while a selector expression exists, and while
|
||||||
// we're no longer looping upon ourselves
|
// we're no longer looping upon ourselves
|
||||||
|
@ -142,13 +142,13 @@ jQuery.extend({
|
||||||
var r = [];
|
var r = [];
|
||||||
last = t;
|
last = t;
|
||||||
|
|
||||||
t = jQuery.trim(t).replace( /^\/\//i, "" );
|
t = jQuery.trim(t).replace( /^\/\//, "" );
|
||||||
|
|
||||||
var foundToken = false;
|
var foundToken = false;
|
||||||
|
|
||||||
// An attempt at speeding up child selectors that
|
// An attempt at speeding up child selectors that
|
||||||
// point to a specific element tag
|
// point to a specific element tag
|
||||||
var re = /^[\/>]\s*([a-z0-9*-]+)/i;
|
var re = /^[\/>]\s*([\w*-]+)/i;
|
||||||
var m = re.exec(t);
|
var m = re.exec(t);
|
||||||
|
|
||||||
if ( m ) {
|
if ( m ) {
|
||||||
|
@ -205,7 +205,7 @@ jQuery.extend({
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Optomize for the case nodeName#idName
|
// Optomize for the case nodeName#idName
|
||||||
var re2 = /^([a-z0-9_-]+)(#)([a-z0-9\\*_-]*)/i;
|
var re2 = /^(\w+)(#)([\w\u0128-\uFFFF*-]+)/i;
|
||||||
var m = re2.exec(t);
|
var m = re2.exec(t);
|
||||||
|
|
||||||
// Re-organize the results, so that they're consistent
|
// Re-organize the results, so that they're consistent
|
||||||
|
@ -215,21 +215,21 @@ jQuery.extend({
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, do a traditional filter check for
|
// Otherwise, do a traditional filter check for
|
||||||
// ID, class, and element selectors
|
// ID, class, and element selectors
|
||||||
re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;
|
re2 = /^([#.]?)([\w\u0128-\uFFFF*-]*)/i;
|
||||||
m = re2.exec(t);
|
m = re2.exec(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
var last = ret[ret.length-1];
|
var elem = ret[ret.length-1];
|
||||||
|
|
||||||
// Try to do a global search by ID, where we can
|
// Try to do a global search by ID, where we can
|
||||||
if ( m[1] == "#" && last && last.getElementById ) {
|
if ( m[1] == "#" && elem && elem.getElementById ) {
|
||||||
// Optimization for HTML document case
|
// Optimization for HTML document case
|
||||||
var oid = last.getElementById(m[2]);
|
var oid = elem.getElementById(m[2]);
|
||||||
|
|
||||||
// Do a quick check for the existence of the actual ID attribute
|
// Do a quick check for the existence of the actual ID attribute
|
||||||
// to avoid selecting by the name attribute in IE
|
// to avoid selecting by the name attribute in IE
|
||||||
if ( jQuery.browser.msie && oid && oid.id != m[2] )
|
if ( jQuery.browser.msie && oid && oid.id != m[2] )
|
||||||
oid = jQuery('[@id="'+m[2]+'"]', last)[0];
|
oid = jQuery('[@id="'+m[2]+'"]', elem)[0];
|
||||||
|
|
||||||
// Do a quick check for node name (where applicable) so
|
// Do a quick check for node name (where applicable) so
|
||||||
// that div#foo searches will be really fast
|
// that div#foo searches will be really fast
|
||||||
|
@ -292,8 +292,14 @@ jQuery.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An error occurred with the selector;
|
||||||
|
// just return an empty set instead
|
||||||
|
if ( t )
|
||||||
|
ret = [];
|
||||||
|
|
||||||
// Remove the root context
|
// Remove the root context
|
||||||
if ( ret && ret[0] == context ) ret.shift();
|
if ( ret && ret[0] == context )
|
||||||
|
ret.shift();
|
||||||
|
|
||||||
// And combine the results
|
// And combine the results
|
||||||
done = jQuery.merge( done, ret );
|
done = jQuery.merge( done, ret );
|
||||||
|
@ -302,8 +308,11 @@ jQuery.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
filter: function(t,r,not) {
|
filter: function(t,r,not) {
|
||||||
|
var last;
|
||||||
|
|
||||||
// Look for common filter expressions
|
// Look for common filter expressions
|
||||||
while ( t && /^[a-z[({<*:.#]/i.test(t) ) {
|
while ( t && t != last ) {
|
||||||
|
last = t;
|
||||||
|
|
||||||
var p = jQuery.parse, m;
|
var p = jQuery.parse, m;
|
||||||
|
|
||||||
|
@ -325,6 +334,9 @@ jQuery.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ( !m )
|
||||||
|
continue;
|
||||||
|
|
||||||
// :not() is a special case that can be optimized by
|
// :not() is a special case that can be optimized by
|
||||||
// keeping it out of the expression list
|
// keeping it out of the expression list
|
||||||
if ( m[1] == ":" && m[2] == "not" )
|
if ( m[1] == ":" && m[2] == "not" )
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module("selector");
|
module("selector");
|
||||||
|
|
||||||
test("expressions - element", function() {
|
test("element", function() {
|
||||||
expect(6);
|
expect(6);
|
||||||
ok( $("*").size() >= 30, "Select all" );
|
ok( $("*").size() >= 30, "Select all" );
|
||||||
t( "Element Selector", "div", ["main","foo"] );
|
t( "Element Selector", "div", ["main","foo"] );
|
||||||
|
@ -10,13 +10,28 @@ test("expressions - element", function() {
|
||||||
ok( $("param", "#object1").length == 2, "Object/param as context" );
|
ok( $("param", "#object1").length == 2, "Object/param as context" );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("expressions - id", function() {
|
test("broken", function() {
|
||||||
expect(13);
|
expect(7);
|
||||||
|
t( "Broken Selector", "[", [] );
|
||||||
|
t( "Broken Selector", "(", [] );
|
||||||
|
t( "Broken Selector", "{", [] );
|
||||||
|
t( "Broken Selector", "<", [] );
|
||||||
|
t( "Broken Selector", "()", [] );
|
||||||
|
t( "Broken Selector", "<>", [] );
|
||||||
|
t( "Broken Selector", "{}", [] );
|
||||||
|
});
|
||||||
|
|
||||||
|
test("id", function() {
|
||||||
|
expect(17);
|
||||||
t( "ID Selector", "#body", ["body"] );
|
t( "ID Selector", "#body", ["body"] );
|
||||||
t( "ID Selector w/ Element", "body#body", ["body"] );
|
t( "ID Selector w/ Element", "body#body", ["body"] );
|
||||||
t( "ID Selector w/ Element", "ul#first", [] );
|
t( "ID Selector w/ Element", "ul#first", [] );
|
||||||
t( "ID selector with existing ID descendant", "#firstp #simon1", ["simon1"] );
|
t( "ID selector with existing ID descendant", "#firstp #simon1", ["simon1"] );
|
||||||
t( "ID selector with non-existant descendant", "#firstp #foobar", [] );
|
t( "ID selector with non-existant descendant", "#firstp #foobar", [] );
|
||||||
|
t( "ID selector using UTF8", "#台北Táiběi", ["台北Táiběi"] );
|
||||||
|
t( "Multiple ID selectors using UTF8", "#台北Táiběi, #台北", ["台北Táiběi","台北"] );
|
||||||
|
t( "Descendant ID selector using UTF8", "div #台北", ["台北"] );
|
||||||
|
t( "Child ID selector using UTF8", "form > #台北", ["台北"] );
|
||||||
|
|
||||||
t( "ID Selector, child ID present", "#form > #radio1", ["radio1"] ); // bug #267
|
t( "ID Selector, child ID present", "#form > #radio1", ["radio1"] ); // bug #267
|
||||||
t( "ID Selector, not an ancestor ID", "#form #first", [] );
|
t( "ID Selector, not an ancestor ID", "#form #first", [] );
|
||||||
|
@ -32,16 +47,22 @@ test("expressions - id", function() {
|
||||||
t( "ID selector with non-existant ancestor", "#asdfasdf #foobar", [] ); // bug #986
|
t( "ID selector with non-existant ancestor", "#asdfasdf #foobar", [] ); // bug #986
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("class", function() {
|
||||||
test("expressions - class", function() {
|
expect(10);
|
||||||
expect(4);
|
|
||||||
t( "Class Selector", ".blog", ["mark","simon"] );
|
t( "Class Selector", ".blog", ["mark","simon"] );
|
||||||
t( "Class Selector", ".blog.link", ["simon"] );
|
t( "Class Selector", ".blog.link", ["simon"] );
|
||||||
t( "Class Selector w/ Element", "a.blog", ["mark","simon"] );
|
t( "Class Selector w/ Element", "a.blog", ["mark","simon"] );
|
||||||
t( "Parent Class Selector", "p .blog", ["mark","simon"] );
|
t( "Parent Class Selector", "p .blog", ["mark","simon"] );
|
||||||
|
|
||||||
|
t( "Class selector using UTF8", ".台北Táiběi", ["utf8class1"] );
|
||||||
|
t( "Class selector using UTF8", ".台北", ["utf8class1","utf8class2"] );
|
||||||
|
t( "Class selector using UTF8", ".台北Táiběi.台北", ["utf8class1"] );
|
||||||
|
t( "Class selector using UTF8", ".台北Táiběi, .台北", ["utf8class1","utf8class2"] );
|
||||||
|
t( "Descendant class selector using UTF8", "div .台北Táiběi", ["utf8class1"] );
|
||||||
|
t( "Child class selector using UTF8", "form > .台北Táiběi", ["utf8class1"] );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("expressions - multiple", function() {
|
test("multiple", function() {
|
||||||
expect(4);
|
expect(4);
|
||||||
t( "Comma Support", "a.blog, div", ["mark","simon","main","foo"] );
|
t( "Comma Support", "a.blog, div", ["mark","simon","main","foo"] );
|
||||||
t( "Comma Support", "a.blog , div", ["mark","simon","main","foo"] );
|
t( "Comma Support", "a.blog , div", ["mark","simon","main","foo"] );
|
||||||
|
@ -49,7 +70,7 @@ test("expressions - multiple", function() {
|
||||||
t( "Comma Support", "a.blog,div", ["mark","simon","main","foo"] );
|
t( "Comma Support", "a.blog,div", ["mark","simon","main","foo"] );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("expressions - child and adjacent", function() {
|
test("child and adjacent", function() {
|
||||||
expect(14);
|
expect(14);
|
||||||
t( "Child", "p > a", ["simon1","google","groups","mark","yahoo","simon"] );
|
t( "Child", "p > a", ["simon1","google","groups","mark","yahoo","simon"] );
|
||||||
t( "Child", "p> a", ["simon1","google","groups","mark","yahoo","simon"] );
|
t( "Child", "p> a", ["simon1","google","groups","mark","yahoo","simon"] );
|
||||||
|
@ -67,8 +88,8 @@ test("expressions - child and adjacent", function() {
|
||||||
t( "First Child", "p:first-child", ["firstp","sndp"] );
|
t( "First Child", "p:first-child", ["firstp","sndp"] );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("expressions - attributes", function() {
|
test("attributes", function() {
|
||||||
expect(19);
|
expect(20);
|
||||||
t( "Attribute Exists", "a[@title]", ["google"] );
|
t( "Attribute Exists", "a[@title]", ["google"] );
|
||||||
t( "Attribute Exists", "*[@title]", ["google"] );
|
t( "Attribute Exists", "*[@title]", ["google"] );
|
||||||
t( "Attribute Exists", "[@title]", ["google"] );
|
t( "Attribute Exists", "[@title]", ["google"] );
|
||||||
|
@ -80,6 +101,8 @@ test("expressions - attributes", function() {
|
||||||
t( "Multiple Attribute Equals", "input[@type=\"hidden\"],input[@type='radio']", ["hidden1","radio1","radio2"] );
|
t( "Multiple Attribute Equals", "input[@type=\"hidden\"],input[@type='radio']", ["hidden1","radio1","radio2"] );
|
||||||
t( "Multiple Attribute Equals", "input[@type=hidden],input[@type=radio]", ["hidden1","radio1","radio2"] );
|
t( "Multiple Attribute Equals", "input[@type=hidden],input[@type=radio]", ["hidden1","radio1","radio2"] );
|
||||||
|
|
||||||
|
t( "Attribute selector using UTF8", "span[@lang=中文]", ["台北"] );
|
||||||
|
|
||||||
t( "Attribute Begins With", "a[@href ^= 'http://www']", ["google","yahoo"] );
|
t( "Attribute Begins With", "a[@href ^= 'http://www']", ["google","yahoo"] );
|
||||||
t( "Attribute Ends With", "a[@href $= 'org/']", ["mark"] );
|
t( "Attribute Ends With", "a[@href $= 'org/']", ["mark"] );
|
||||||
t( "Attribute Contains", "a[@href *= 'google']", ["google","groups"] );
|
t( "Attribute Contains", "a[@href *= 'google']", ["google","groups"] );
|
||||||
|
@ -95,7 +118,7 @@ test("expressions - attributes", function() {
|
||||||
t( ":not() Equals quoted attribute", "select:not([@name='select1'])", ["select2", "select3"]);
|
t( ":not() Equals quoted attribute", "select:not([@name='select1'])", ["select2", "select3"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("expressions - pseudo (:) selctors", function() {
|
test("pseudo (:) selctors", function() {
|
||||||
expect(30);
|
expect(30);
|
||||||
t( "First Child", "p:first-child", ["firstp","sndp"] );
|
t( "First Child", "p:first-child", ["firstp","sndp"] );
|
||||||
t( "Last Child", "p:last-child", ["sap"] );
|
t( "Last Child", "p:last-child", ["sap"] );
|
||||||
|
@ -131,7 +154,7 @@ test("expressions - pseudo (:) selctors", function() {
|
||||||
t( "Form element :checkbox:checked, :radio:checked", ":checkbox:checked, :radio:checked", ["check1", "radio2"] );
|
t( "Form element :checkbox:checked, :radio:checked", ":checkbox:checked, :radio:checked", ["check1", "radio2"] );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("expressions - basic xpath", function() {
|
test("basic xpath", function() {
|
||||||
expect(15);
|
expect(15);
|
||||||
ok( jQuery.find("//*").length >= 30, "All Elements (//*)" );
|
ok( jQuery.find("//*").length >= 30, "All Elements (//*)" );
|
||||||
t( "All Div Elements", "//div", ["main","foo"] );
|
t( "All Div Elements", "//div", ["main","foo"] );
|
||||||
|
|
Loading…
Reference in a new issue