Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #7901: 7901.diff

File 7901.diff, 7.0 kB (added by savetheclocktower, 2 years ago)
  • test/unit/selector.html

    old new  
    2525  <h1 class="title">Some title <span>here</span></h1> 
    2626  <p id="p" class="first summary"> 
    2727    <strong id="strong">This</strong> is a short blurb 
    28     <a id="link_1" class="first internal" rel="external nofollow" href="#">with a link</a> or  
     28    <a id="link_1" class="first internal" rel="external nofollow" href="#">with a <em id="em2">link</em></a> or  
    2929    <a id="link_2" class="internal highlight" href="#"><em id="em">two</em></a>. 
    3030    Or <cite id="with_title" title="hello world!">a citation</cite>. 
    3131  </p> 
     
    144144    }}, 
    145145     
    146146    test$$MatchesAncestryWithTokensSeparatedByWhitespace: function() {with(this) { 
    147       assertEnumEqual($('em', 'span'), $$('#fixtures a *')); 
     147      assertEnumEqual($('em2', 'em', 'span'), $$('#fixtures a *')); 
    148148      assertEnumEqual([$('p')], $$('div#fixtures p')); 
    149149    }}, 
    150150     
     
    314314    }}, 
    315315     
    316316    testSelectorWithNot: function() {with(this) { 
    317       assertEnumEqual([$('link_2')], $$('#p a:not(:first-of-type)')); 
    318       assertEnumEqual([$('link_1')], $$('#p a:not(:last-of-type)')); 
    319       assertEnumEqual([$('link_2')], $$('#p a:not(:nth-of-type(1))')); 
    320       assertEnumEqual([$('link_1')], $$('#p a:not(:nth-last-of-type(1))')); 
    321       assertEnumEqual([$('link_2')], $$('#p a:not([rel~=nofollow])')); 
    322       assertEnumEqual([$('link_2')], $$('#p a:not([rel^=external])')); 
    323       assertEnumEqual([$('link_2')], $$('#p a:not([rel$=nofollow])')); 
     317      assertEnumEqual([$('link_2')], $$('#p a:not(a:first-of-type)'), 'first-of-type'); 
     318      assertEnumEqual([$('link_1')], $$('#p a:not(a:last-of-type)'), 'last-of-type'); 
     319      assertEnumEqual([$('link_2')], $$('#p a:not(a:nth-of-type(1))'), 'nth-of-type'); 
     320      assertEnumEqual([$('link_1')], $$('#p a:not(a:nth-last-of-type(1))'), 'nth-last-of-type'); 
     321      assertEnumEqual([$('link_2')], $$('#p a:not([rel~=nofollow])'), 'attribute 1'); 
     322      assertEnumEqual([$('link_2')], $$('#p a:not(a[rel^=external])'), 'attribute 2'); 
     323      assertEnumEqual([$('link_2')], $$('#p a:not(a[rel$=nofollow])'), 'attribute 3'); 
     324      assertEnumEqual([$('em')], $$('#p a:not(a[rel$="nofollow"]) > em'), 'attribute 4') 
    324325    }}, 
    325326     
    326327    testSelectorWithEnabledDisabledChecked: function() {with(this) { 
  • src/selector.js

    old new  
    4444   
    4545  compileXPathMatcher: function() { 
    4646    var e = this.expression, ps = Selector.patterns, 
    47         x = Selector.xpath, le, p, m; 
     47        x = Selector.xpath, le, m; 
    4848 
    4949    if (Selector._cache[e]) { 
    5050      this.xpath = Selector._cache[e]; return; 
     
    130130      'disabled':    "[@disabled]", 
    131131      'enabled':     "[not(@disabled)]", 
    132132      'not': function(m) { 
    133         if (!m[6]) return ''; 
    134         var p = Selector.patterns, x = Selector.xpath; 
    135         for (var i in p) { 
    136           if (mm = m[6].match(p[i])) { 
    137             var ss = typeof x[i] == 'function' ? x[i](mm) : new Template(x[i]).evaluate(mm); 
    138             m[6] = ss.substring(1, ss.length - 1); 
    139             break; 
     133        var e = m[6], p = Selector.patterns, 
     134            x = Selector.xpath, le, m, v; 
     135             
     136        var exclusion = []; 
     137        while (e && le != e && (/\S/).test(e)) { 
     138          le = e; 
     139          for (var i in p) { 
     140            if (m = e.match(p[i])) { 
     141              v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m); 
     142              exclusion.push("(" + v.substring(1, v.length - 1) + ")"); 
     143              e = e.replace(m[0], ''); 
     144              break; 
     145            } 
    140146          } 
    141         } 
    142         return "[not(" + m[6] + ")]"; 
     147        }         
     148        return "[not(" + exclusion.join(" and ") + ")]"; 
    143149      }, 
    144150      'nth-child':      function(m) {  
    145151        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); 
     
    184190    id:           'n = h.id(n, r, "#{1}", c);        c = false;', 
    185191    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', 
    186192    attr: function(m) { 
    187       m[3] = m[5] || m[6]
     193      m[3] = (m[5] || m[6])
    188194      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); 
    189195    }, 
    190     pseudo:       'n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;', 
     196    pseudo:       function(m) { 
     197      if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); 
     198      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);  
     199    }, 
    191200    descendant:   'c = "descendant";', 
    192201    child:        'c = "child";', 
    193202    adjacent:     'c = "adjacent";', 
     
    206215    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/, 
    207216    id:           /^#([\w\-\*]+)(\b|$)/, 
    208217    className:    /^\.([\w\-\*]+)(\b|$)/, 
    209     pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$)/, 
     218    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s)/, 
    210219    attrPresence: /^\[([\w]+)\]/, 
    211220    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/ 
    212221  }, 
     
    372381    }, 
    373382     
    374383    attr: function(nodes, root, attr, value, operator) { 
     384      if (!nodes) nodes = root.getElementsByTagName("*"); 
    375385      var handler = Selector.operators[operator], results = []; 
    376386      for (var i = 0, node; node = nodes[i]; i++) { 
    377387        var nodeValue = Element.readAttribute(node, attr); 
     
    382392    }, 
    383393                 
    384394    pseudo: function(nodes, name, value, root, combinator) { 
    385       if (combinator) nodes = this[combinator](nodes); 
     395      if (nodes && combinator) nodes = this[combinator](nodes); 
     396      if (!nodes) nodes = root.getElementsByTagName("*"); 
    386397      return Selector.pseudos[name](nodes, value, root); 
    387398    } 
    388399  }, 
     
    469480    }, 
    470481     
    471482    'not': function(nodes, selector, root) { 
    472       var h = Selector.handlers, exclusions = $A(nodes), selectorType, m; 
    473       for (var i in Selector.patterns) { 
    474         if (m = selector.match(Selector.patterns[i])) { 
    475           selectorType = i; break; 
    476         } 
    477       } 
    478       switch(selectorType) { 
    479         case 'className': case 'tagName': case 'id': // fallthroughs 
    480         case 'attrPresence': exclusions = h[selectorType](exclusions, root, m[1], false); break; 
    481         case 'attr': m[3] = m[5] || m[6]; exclusions = h.attr(exclusions, root, m[1], m[3], m[2]); break;         
    482         case 'pseudo': exclusions = h.pseudo(exclusions, m[1], m[6], root, false); break;    
    483         // only 'simple selectors' (one token) allowed in a :not clause 
    484         default: throw 'Illegal selector in :not clause.'; 
    485       } 
     483      var h = Selector.handlers, selectorType, m; 
     484      var exclusions = new Selector(selector).findElements(root); 
    486485      h.mark(exclusions); 
    487486      for (var i = 0, results = [], node; node = nodes[i]; i++) 
    488487        if (!node._counted) results.push(node);