From 779cd337b890556d4d019ade5683f81290013b67 Mon Sep 17 00:00:00 2001 From: Thomas Fuchs Date: Tue, 27 Mar 2007 18:14:53 +0000 Subject: [PATCH] Fix issues with Selector an+b logic, performance improvements. Closes #7873. [Andrew Dupont] --- CHANGELOG | 2 ++ src/selector.js | 47 +++++++++++++++++++++++++++++------------ test/unit/selector.html | 3 +++ 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 099530f..cddf872 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Fix issues with Selector an+b logic, performance improvements. Closes #7873. [Andrew Dupont] + * Fix an issue with Element.getDimensions with some element types on non IE-browsers. Closes #7683. [Andrew Dupont] * Fix Form.disable to work again on non-form elements. Closes #6887. [Mislav Marohnić] diff --git a/src/selector.js b/src/selector.js index eff1659..894da3d 100644 --- a/src/selector.js +++ b/src/selector.js @@ -162,18 +162,21 @@ Object.extend(Selector, { 'only-of-type': function(m) { var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); }, - nth: function(predicate, m) { - var mm, formula = m[6]; + nth: function(fragment, m) { + var mm, formula = m[6], predicate; if (formula == 'even') formula = '2n+0'; if (formula == 'odd') formula = '2n+1'; if (mm = formula.match(/^(\d+)$/)) // digit only - predicate += "= " + mm[1]; - if (mm = formula.match(/^(\d+)?n(\+(\d+))?/)) { // an+b + return '[' + fragment + "= " + mm[1] + ']'; + if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (mm[1] == "-") mm[1] = -1; var a = mm[1] ? Number(mm[1]) : 1; - var b = mm[3] ? Number(mm[3]) : 0; - predicate += "mod " + a + " = " + b; + var b = mm[2] ? Number(mm[2]) : 0; + predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + + "((#{fragment} - #{b}) div #{a} >= 0)]"; + return new Template(predicate).evaluate({ + fragment: fragment, a: a, b: b }); } - return "[" + predicate + "]"; } } }, @@ -265,14 +268,16 @@ Object.extend(Selector, { descendant: function(nodes) { var h = Selector.handlers; for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, Element.descendants(node)); + h.concat(results, node.getElementsByTagName('*')); return results; }, child: function(nodes) { var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, Element.immediateDescendants(node)); + for (var i = 0, results = [], node; node = nodes[i]; i++) { + for (var j = 0, children = [], child; child = node.childNodes[j]; j++) + if (child.nodeType == 1 && child.tagName != '!') results.push(child); + } return results; }, @@ -432,8 +437,18 @@ Object.extend(Selector, { return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); }, + // handles the an+b logic + getIndices: function(a, b, total) { + if (a == 0) return b > 0 ? [b] : []; + return $R(1, total).inject([], function(memo, i) { + if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); + return memo; + }); + }, + // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type nth: function(nodes, formula, root, reverse, ofType) { + if (nodes.length == 0) return []; if (formula == 'even') formula = '2n+0'; if (formula == 'odd') formula = '2n+1'; var h = Selector.handlers, results = [], indexed = [], m; @@ -448,11 +463,15 @@ Object.extend(Selector, { formula = Number(formula); for (var i = 0, node; node = nodes[i]; i++) if (node.nodeIndex == formula) results.push(node); - } else if (m = formula.match(/^(\d+)?n(\+(\d+))?$/)) { // an+b + } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (m[1] == "-") m[1] = -1; var a = m[1] ? Number(m[1]) : 1; - var b = m[3] ? Number(m[3]) : 0; - for (var i = 0, node; node = nodes[i]; i++) - if (node.nodeIndex % a == b) results.push(node); + var b = m[2] ? Number(m[2]) : 0; + var indices = Selector.pseudos.getIndices(a, b, nodes.length); + for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { + for (var j = 0; j < l; j++) + if (node.nodeIndex == indices[j]) results.push(node); + } } h.unmark(nodes); h.unmark(indexed); diff --git a/test/unit/selector.html b/test/unit/selector.html index 7d52a57..aef9afc 100644 --- a/test/unit/selector.html +++ b/test/unit/selector.html @@ -298,6 +298,8 @@ assertEnumEqual([$('link_2')], $$('#p *:nth-last-child(2)'), 'nth-last-child'); assertEnumEqual([$('link_2')], $$('#p *:nth-child(3)'), 'nth-child'); assertEnumEqual([$('link_2')], $$('#p a:nth-child(3)'), 'nth-child'); + assertEnumEqual($('item_2', 'item_3'), $$('#list > li:nth-child(n+2)')); + assertEnumEqual($('item_1', 'item_2'), $$('#list > li:nth-child(-n+2)')); $RunBenchmarks && wait(500, function() { benchmark(function() { $$('#level1 *:first-child') }, 1000, ':first-child'); benchmark(function() { $$('#level1 *:last-child') }, 1000, ':last-child'); @@ -346,6 +348,7 @@ assertEnumEqual($$('ul > li:first-child'), $$('ul > li:nth-child(1)')); assertEnumEqual($$('ul > li:last-child'), $$('ul > li:nth-last-child(1)')); assertEnumEqual($$('#troubleForm *:enabled'), $$('#troubleForm *:not(:disabled)')); + assertEnumEqual($$('ul > li:nth-child(n-999)'), $$('ul > li')); }}, testSelectorsThatShouldReturnNothing: function() {with(this) {