parent
5c1c534894
commit
8d2ee6bbda
|
@ -1,12 +1,13 @@
|
||||||
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isPresent, isBlank, RegExpWrapper, RegExpMatcherWrapper, StringWrapper} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, BaseException} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
const _EMPTY_ATTR_VALUE = '';
|
const _EMPTY_ATTR_VALUE = '';
|
||||||
|
|
||||||
// TODO: Can't use `const` here as
|
// TODO: Can't use `const` here as
|
||||||
// in Dart this is not transpiled into `final` yet...
|
// in Dart this is not transpiled into `final` yet...
|
||||||
var _SELECTOR_REGEXP =
|
var _SELECTOR_REGEXP =
|
||||||
RegExpWrapper.create('^([-\\w]+)|' + // "tag"
|
RegExpWrapper.create('(\\:not\\()|' + //":not("
|
||||||
|
'([-\\w]+)|' + // "tag"
|
||||||
'(?:\\.([-\\w]+))|' + // ".class"
|
'(?:\\.([-\\w]+))|' + // ".class"
|
||||||
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])'); // "[name]", "[name=value]" or "[name*=value]"
|
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])'); // "[name]", "[name=value]" or "[name*=value]"
|
||||||
|
|
||||||
|
@ -19,21 +20,35 @@ export class CssSelector {
|
||||||
element:string;
|
element:string;
|
||||||
classNames:List;
|
classNames:List;
|
||||||
attrs:List;
|
attrs:List;
|
||||||
static parse(selector:string):CssSelector {
|
notSelector: CssSelector;
|
||||||
|
static parse(selector:string): CssSelector {
|
||||||
var cssSelector = new CssSelector();
|
var cssSelector = new CssSelector();
|
||||||
var matcher = RegExpWrapper.matcher(_SELECTOR_REGEXP, selector);
|
var matcher = RegExpWrapper.matcher(_SELECTOR_REGEXP, selector);
|
||||||
var match;
|
var match;
|
||||||
|
var current = cssSelector;
|
||||||
while (isPresent(match = RegExpMatcherWrapper.next(matcher))) {
|
while (isPresent(match = RegExpMatcherWrapper.next(matcher))) {
|
||||||
if (isPresent(match[1])) {
|
if (isPresent(match[1])) {
|
||||||
cssSelector.setElement(match[1]);
|
if (isPresent(cssSelector.notSelector)) {
|
||||||
|
throw new BaseException('Nesting :not is not allowed in a selector');
|
||||||
|
}
|
||||||
|
current.notSelector = new CssSelector();
|
||||||
|
current = current.notSelector;
|
||||||
}
|
}
|
||||||
if (isPresent(match[2])) {
|
if (isPresent(match[2])) {
|
||||||
cssSelector.addClassName(match[2]);
|
current.setElement(match[2]);
|
||||||
}
|
}
|
||||||
if (isPresent(match[3])) {
|
if (isPresent(match[3])) {
|
||||||
cssSelector.addAttribute(match[3], match[4]);
|
current.addClassName(match[3]);
|
||||||
|
}
|
||||||
|
if (isPresent(match[4])) {
|
||||||
|
current.addAttribute(match[4], match[5]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isPresent(cssSelector.notSelector) && isBlank(cssSelector.element)
|
||||||
|
&& ListWrapper.isEmpty(cssSelector.classNames) && ListWrapper.isEmpty(cssSelector.attrs)) {
|
||||||
|
cssSelector.element = "*";
|
||||||
|
}
|
||||||
|
|
||||||
return cssSelector;
|
return cssSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +56,7 @@ export class CssSelector {
|
||||||
this.element = null;
|
this.element = null;
|
||||||
this.classNames = ListWrapper.create();
|
this.classNames = ListWrapper.create();
|
||||||
this.attrs = ListWrapper.create();
|
this.attrs = ListWrapper.create();
|
||||||
|
this.notSelector = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setElement(element:string = null) {
|
setElement(element:string = null) {
|
||||||
|
@ -85,6 +101,9 @@ export class CssSelector {
|
||||||
res += ']';
|
res += ']';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isPresent(this.notSelector)) {
|
||||||
|
res += ":not(" + this.notSelector.toString() + ")";
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,20 +207,22 @@ export class SelectorMatcher {
|
||||||
* whose css selector is contained in the given css selector.
|
* whose css selector is contained in the given css selector.
|
||||||
* @param cssSelector A css selector
|
* @param cssSelector A css selector
|
||||||
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
|
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
|
||||||
|
* @return boolean true if a match was found
|
||||||
*/
|
*/
|
||||||
match(cssSelector:CssSelector, matchedCallback:Function) {
|
match(cssSelector:CssSelector, matchedCallback:Function):boolean {
|
||||||
|
var result = false;
|
||||||
var element = cssSelector.element;
|
var element = cssSelector.element;
|
||||||
var classNames = cssSelector.classNames;
|
var classNames = cssSelector.classNames;
|
||||||
var attrs = cssSelector.attrs;
|
var attrs = cssSelector.attrs;
|
||||||
|
|
||||||
this._matchTerminal(this._elementMap, element, matchedCallback);
|
result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
|
||||||
this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback);
|
result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || result;
|
||||||
|
|
||||||
if (isPresent(classNames)) {
|
if (isPresent(classNames)) {
|
||||||
for (var index = 0; index<classNames.length; index++) {
|
for (var index = 0; index<classNames.length; index++) {
|
||||||
var className = classNames[index];
|
var className = classNames[index];
|
||||||
this._matchTerminal(this._classMap, className, matchedCallback);
|
result = this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
|
||||||
this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback);
|
result = this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,43 +233,51 @@ export class SelectorMatcher {
|
||||||
|
|
||||||
var valuesMap = MapWrapper.get(this._attrValueMap, attrName);
|
var valuesMap = MapWrapper.get(this._attrValueMap, attrName);
|
||||||
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
||||||
this._matchTerminal(valuesMap, _EMPTY_ATTR_VALUE, matchedCallback);
|
result = this._matchTerminal(valuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
this._matchTerminal(valuesMap, attrValue, matchedCallback);
|
result = this._matchTerminal(valuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||||
|
|
||||||
valuesMap = MapWrapper.get(this._attrValuePartialMap, attrName)
|
valuesMap = MapWrapper.get(this._attrValuePartialMap, attrName)
|
||||||
this._matchPartial(valuesMap, attrValue, cssSelector, matchedCallback);
|
result = this._matchPartial(valuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_matchTerminal(map:Map<string,string> = null, name, matchedCallback) {
|
_matchTerminal(map:Map<string,string> = null, name, cssSelector, matchedCallback):boolean {
|
||||||
if (isBlank(map) || isBlank(name)) {
|
if (isBlank(map) || isBlank(name)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectables = MapWrapper.get(map, name);
|
||||||
|
var starSelectables = MapWrapper.get(map, "*");
|
||||||
|
if (isPresent(starSelectables)) {
|
||||||
|
selectables = ListWrapper.concat(selectables, starSelectables);
|
||||||
}
|
}
|
||||||
var selectables = MapWrapper.get(map, name)
|
|
||||||
if (isBlank(selectables)) {
|
if (isBlank(selectables)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
var selectable;
|
var selectable;
|
||||||
|
var result = false;
|
||||||
for (var index=0; index<selectables.length; index++) {
|
for (var index=0; index<selectables.length; index++) {
|
||||||
selectable = selectables[index];
|
selectable = selectables[index];
|
||||||
matchedCallback(selectable.selector, selectable.cbContext);
|
result = selectable.finalize(cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_matchPartial(map:Map<string,string> = null, name, cssSelector, matchedCallback) {
|
_matchPartial(map:Map<string,string> = null, name, cssSelector, matchedCallback):boolean {
|
||||||
if (isBlank(map) || isBlank(name)) {
|
if (isBlank(map) || isBlank(name)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
var nestedSelector = MapWrapper.get(map, name)
|
var nestedSelector = MapWrapper.get(map, name)
|
||||||
if (isBlank(nestedSelector)) {
|
if (isBlank(nestedSelector)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO(perf): get rid of recursion and measure again
|
// TODO(perf): get rid of recursion and measure again
|
||||||
// TODO(perf): don't pass the whole selector into the recursion,
|
// TODO(perf): don't pass the whole selector into the recursion,
|
||||||
// but only the not processed parts
|
// but only the not processed parts
|
||||||
nestedSelector.match(cssSelector, matchedCallback);
|
return nestedSelector.match(cssSelector, matchedCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,10 +285,25 @@ export class SelectorMatcher {
|
||||||
// Store context to pass back selector and context when a selector is matched
|
// Store context to pass back selector and context when a selector is matched
|
||||||
class SelectorContext {
|
class SelectorContext {
|
||||||
selector:CssSelector;
|
selector:CssSelector;
|
||||||
|
notSelector:CssSelector;
|
||||||
cbContext; // callback context
|
cbContext; // callback context
|
||||||
|
|
||||||
constructor(selector:CssSelector, cbContext) {
|
constructor(selector:CssSelector, cbContext) {
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
|
this.notSelector = selector.notSelector;
|
||||||
this.cbContext = cbContext;
|
this.cbContext = cbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalize(cssSelector: CssSelector, callback) {
|
||||||
|
var result = true;
|
||||||
|
if (isPresent(this.notSelector)) {
|
||||||
|
var notMatcher = new SelectorMatcher();
|
||||||
|
notMatcher.addSelectable(this.notSelector, null);
|
||||||
|
result = !notMatcher.match(cssSelector, null);
|
||||||
|
}
|
||||||
|
if (result && isPresent(callback)) {
|
||||||
|
callback(this.selector, this.cbContext);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,10 @@ export function main() {
|
||||||
it('should select by element name case insensitive', () => {
|
it('should select by element name case insensitive', () => {
|
||||||
matcher.addSelectable(s1 = CssSelector.parse('someTag'), 1);
|
matcher.addSelectable(s1 = CssSelector.parse('someTag'), 1);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('SOMEOTHERTAG'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('SOMEOTHERTAG'), selectableCollector)).toEqual(false);
|
||||||
expect(matched).toEqual([]);
|
expect(matched).toEqual([]);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('SOMETAG'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('SOMETAG'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1]);
|
expect(matched).toEqual([s1,1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -36,14 +36,14 @@ export function main() {
|
||||||
matcher.addSelectable(s1 = CssSelector.parse('.someClass'), 1);
|
matcher.addSelectable(s1 = CssSelector.parse('.someClass'), 1);
|
||||||
matcher.addSelectable(s2 = CssSelector.parse('.someClass.class2'), 2);
|
matcher.addSelectable(s2 = CssSelector.parse('.someClass.class2'), 2);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('.SOMEOTHERCLASS'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('.SOMEOTHERCLASS'), selectableCollector)).toEqual(false);
|
||||||
expect(matched).toEqual([]);
|
expect(matched).toEqual([]);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('.SOMECLASS'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('.SOMECLASS'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1]);
|
expect(matched).toEqual([s1,1]);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
matcher.match(CssSelector.parse('.someClass.class2'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('.someClass.class2'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1,s2,2]);
|
expect(matched).toEqual([s1,1,s2,2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,18 +51,18 @@ export function main() {
|
||||||
matcher.addSelectable(s1 = CssSelector.parse('[someAttr]'), 1);
|
matcher.addSelectable(s1 = CssSelector.parse('[someAttr]'), 1);
|
||||||
matcher.addSelectable(s2 = CssSelector.parse('[someAttr][someAttr2]'), 2);
|
matcher.addSelectable(s2 = CssSelector.parse('[someAttr][someAttr2]'), 2);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('[SOMEOTHERATTR]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('[SOMEOTHERATTR]'), selectableCollector)).toEqual(false);
|
||||||
expect(matched).toEqual([]);
|
expect(matched).toEqual([]);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('[SOMEATTR]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('[SOMEATTR]'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1]);
|
expect(matched).toEqual([s1,1]);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
matcher.match(CssSelector.parse('[SOMEATTR=someValue]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('[SOMEATTR=someValue]'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1]);
|
expect(matched).toEqual([s1,1]);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
matcher.match(CssSelector.parse('[someAttr][someAttr2]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('[someAttr][someAttr2]'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1,s2,2]);
|
expect(matched).toEqual([s1,1,s2,2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -80,29 +80,29 @@ export function main() {
|
||||||
it('should select by attr name and value case insensitive', () => {
|
it('should select by attr name and value case insensitive', () => {
|
||||||
matcher.addSelectable(s1 = CssSelector.parse('[someAttr=someValue]'), 1);
|
matcher.addSelectable(s1 = CssSelector.parse('[someAttr=someValue]'), 1);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('[SOMEATTR=SOMEOTHERATTR]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('[SOMEATTR=SOMEOTHERATTR]'), selectableCollector)).toEqual(false);
|
||||||
expect(matched).toEqual([]);
|
expect(matched).toEqual([]);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('[SOMEATTR=SOMEVALUE]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('[SOMEATTR=SOMEVALUE]'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1]);
|
expect(matched).toEqual([s1,1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select by element name, class name and attribute name with value', () => {
|
it('should select by element name, class name and attribute name with value', () => {
|
||||||
matcher.addSelectable(s1 = CssSelector.parse('someTag.someClass[someAttr=someValue]'), 1);
|
matcher.addSelectable(s1 = CssSelector.parse('someTag.someClass[someAttr=someValue]'), 1);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('someOtherTag.someOtherClass[someOtherAttr]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('someOtherTag.someOtherClass[someOtherAttr]'), selectableCollector)).toEqual(false);
|
||||||
expect(matched).toEqual([]);
|
expect(matched).toEqual([]);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('someTag.someOtherClass[someOtherAttr]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('someTag.someOtherClass[someOtherAttr]'), selectableCollector)).toEqual(false);
|
||||||
expect(matched).toEqual([]);
|
expect(matched).toEqual([]);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('someTag.someClass[someOtherAttr]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('someTag.someClass[someOtherAttr]'), selectableCollector)).toEqual(false);
|
||||||
expect(matched).toEqual([]);
|
expect(matched).toEqual([]);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('someTag.someClass[someAttr]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('someTag.someClass[someAttr]'), selectableCollector)).toEqual(false);
|
||||||
expect(matched).toEqual([]);
|
expect(matched).toEqual([]);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('someTag.someClass[someAttr=someValue]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('someTag.someClass[someAttr=someValue]'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1]);
|
expect(matched).toEqual([s1,1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,21 +112,42 @@ export function main() {
|
||||||
matcher.addSelectable(s3 = CssSelector.parse('.class1.class2'), 3);
|
matcher.addSelectable(s3 = CssSelector.parse('.class1.class2'), 3);
|
||||||
matcher.addSelectable(s4 = CssSelector.parse('.class2.class1'), 4);
|
matcher.addSelectable(s4 = CssSelector.parse('.class2.class1'), 4);
|
||||||
|
|
||||||
matcher.match(CssSelector.parse('[someAttr].someClass'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('[someAttr].someClass'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1,s2,2]);
|
expect(matched).toEqual([s1,1,s2,2]);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
matcher.match(CssSelector.parse('.someClass[someAttr]'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('.someClass[someAttr]'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s1,1,s2,2]);
|
expect(matched).toEqual([s1,1,s2,2]);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
matcher.match(CssSelector.parse('.class1.class2'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('.class1.class2'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s3,3,s4,4]);
|
expect(matched).toEqual([s3,3,s4,4]);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
matcher.match(CssSelector.parse('.class2.class1'), selectableCollector);
|
expect(matcher.match(CssSelector.parse('.class2.class1'), selectableCollector)).toEqual(true);
|
||||||
expect(matched).toEqual([s4,4,s3,3]);
|
expect(matched).toEqual([s4,4,s3,3]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not select with a matching :not selector', () => {
|
||||||
|
matcher.addSelectable(CssSelector.parse('p:not(.someClass)'), 1);
|
||||||
|
matcher.addSelectable(CssSelector.parse('p:not([someAttr])'), 2);
|
||||||
|
matcher.addSelectable(CssSelector.parse(':not(.someClass)'), 3);
|
||||||
|
matcher.addSelectable(CssSelector.parse(':not(p)'), 4);
|
||||||
|
matcher.addSelectable(CssSelector.parse(':not(p[someAttr])'), 5);
|
||||||
|
|
||||||
|
expect(matcher.match(CssSelector.parse('p.someClass[someAttr]'), selectableCollector)).toEqual(false);
|
||||||
|
expect(matched).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select with a non matching :not selector', () => {
|
||||||
|
matcher.addSelectable(s1 = CssSelector.parse('p:not(.someClass)'), 1);
|
||||||
|
matcher.addSelectable(s2 = CssSelector.parse('p:not(.someOtherClass[someAttr])'), 2);
|
||||||
|
matcher.addSelectable(s3 = CssSelector.parse(':not(.someClass)'), 3);
|
||||||
|
matcher.addSelectable(s4 = CssSelector.parse(':not(.someOtherClass[someAttr])'), 4);
|
||||||
|
|
||||||
|
expect(matcher.match(CssSelector.parse('p[someOtherAttr].someOtherClass'), selectableCollector)).toEqual(true);
|
||||||
|
expect(matched).toEqual([s1,1,s2,2,s3,3,s4,4]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('CssSelector.parse', () => {
|
describe('CssSelector.parse', () => {
|
||||||
|
@ -164,5 +185,36 @@ export function main() {
|
||||||
|
|
||||||
expect(cssSelector.toString()).toEqual('sometag.someclass[attrname=attrvalue]');
|
expect(cssSelector.toString()).toEqual('sometag.someclass[attrname=attrvalue]');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should detect :not', () => {
|
||||||
|
var cssSelector = CssSelector.parse('sometag:not([attrname=attrvalue].someclass)');
|
||||||
|
expect(cssSelector.element).toEqual('sometag');
|
||||||
|
expect(cssSelector.attrs.length).toEqual(0);
|
||||||
|
expect(cssSelector.classNames.length).toEqual(0);
|
||||||
|
|
||||||
|
var notSelector = cssSelector.notSelector;
|
||||||
|
expect(notSelector.element).toEqual(null);
|
||||||
|
expect(notSelector.attrs).toEqual(['attrname', 'attrvalue']);
|
||||||
|
expect(notSelector.classNames).toEqual(['someclass']);
|
||||||
|
|
||||||
|
expect(cssSelector.toString()).toEqual('sometag:not(.someclass[attrname=attrvalue])');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect :not without truthy', () => {
|
||||||
|
var cssSelector = CssSelector.parse(':not([attrname=attrvalue].someclass)');
|
||||||
|
expect(cssSelector.element).toEqual("*");
|
||||||
|
|
||||||
|
var notSelector = cssSelector.notSelector;
|
||||||
|
expect(notSelector.attrs).toEqual(['attrname', 'attrvalue']);
|
||||||
|
expect(notSelector.classNames).toEqual(['someclass']);
|
||||||
|
|
||||||
|
expect(cssSelector.toString()).toEqual('*:not(.someclass[attrname=attrvalue])');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when nested :not', () => {
|
||||||
|
expect(() => {
|
||||||
|
CssSelector.parse('sometag:not(:not([attrname=attrvalue].someclass))')
|
||||||
|
}).toThrowError('Nesting :not is not allowed in a selector');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
Loading…
Reference in New Issue