fix(selectors): use Maps instead of objects

This commit is contained in:
Dzmitry Shylovich 2016-10-18 01:19:18 +03:00 committed by vsavkin
parent b4265e0685
commit d321b0ebf5
2 changed files with 31 additions and 25 deletions

View File

@ -135,12 +135,12 @@ export class SelectorMatcher {
return notMatcher; return notMatcher;
} }
private _elementMap: {[k: string]: SelectorContext[]} = {}; private _elementMap = new Map<string, SelectorContext[]>();
private _elementPartialMap: {[k: string]: SelectorMatcher} = {}; private _elementPartialMap = new Map<string, SelectorMatcher>();
private _classMap: {[k: string]: SelectorContext[]} = {}; private _classMap = new Map<string, SelectorContext[]>();
private _classPartialMap: {[k: string]: SelectorMatcher} = {}; private _classPartialMap = new Map<string, SelectorMatcher>();
private _attrValueMap: {[k: string]: {[k: string]: SelectorContext[]}} = {}; private _attrValueMap = new Map<string, Map<string, SelectorContext[]>>();
private _attrValuePartialMap: {[k: string]: {[k: string]: SelectorMatcher}} = {}; private _attrValuePartialMap = new Map<string, Map<string, SelectorMatcher>>();
private _listContexts: SelectorListContext[] = []; private _listContexts: SelectorListContext[] = [];
addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) { addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) {
@ -195,18 +195,18 @@ export class SelectorMatcher {
const value = attrs[i + 1]; const value = attrs[i + 1];
if (isTerminal) { if (isTerminal) {
const terminalMap = matcher._attrValueMap; const terminalMap = matcher._attrValueMap;
let terminalValuesMap = terminalMap[name]; let terminalValuesMap = terminalMap.get(name);
if (!terminalValuesMap) { if (!terminalValuesMap) {
terminalValuesMap = {}; terminalValuesMap = new Map<string, SelectorContext[]>();
terminalMap[name] = terminalValuesMap; terminalMap.set(name, terminalValuesMap);
} }
this._addTerminal(terminalValuesMap, value, selectable); this._addTerminal(terminalValuesMap, value, selectable);
} else { } else {
let partialMap = matcher._attrValuePartialMap; let partialMap = matcher._attrValuePartialMap;
let partialValuesMap = partialMap[name]; let partialValuesMap = partialMap.get(name);
if (!partialValuesMap) { if (!partialValuesMap) {
partialValuesMap = {}; partialValuesMap = new Map<string, SelectorMatcher>();
partialMap[name] = partialValuesMap; partialMap.set(name, partialValuesMap);
} }
matcher = this._addPartial(partialValuesMap, value); matcher = this._addPartial(partialValuesMap, value);
} }
@ -215,20 +215,20 @@ export class SelectorMatcher {
} }
private _addTerminal( private _addTerminal(
map: {[k: string]: SelectorContext[]}, name: string, selectable: SelectorContext) { map: Map<string, SelectorContext[]>, name: string, selectable: SelectorContext) {
let terminalList = map[name]; let terminalList = map.get(name);
if (!terminalList) { if (!terminalList) {
terminalList = []; terminalList = [];
map[name] = terminalList; map.set(name, terminalList);
} }
terminalList.push(selectable); terminalList.push(selectable);
} }
private _addPartial(map: {[k: string]: SelectorMatcher}, name: string): SelectorMatcher { private _addPartial(map: Map<string, SelectorMatcher>, name: string): SelectorMatcher {
let matcher = map[name]; let matcher = map.get(name);
if (!matcher) { if (!matcher) {
matcher = new SelectorMatcher(); matcher = new SelectorMatcher();
map[name] = matcher; map.set(name, matcher);
} }
return matcher; return matcher;
} }
@ -270,7 +270,7 @@ export class SelectorMatcher {
const name = attrs[i]; const name = attrs[i];
const value = attrs[i + 1]; const value = attrs[i + 1];
const terminalValuesMap = this._attrValueMap[name]; const terminalValuesMap = this._attrValueMap.get(name);
if (value) { if (value) {
result = result =
this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result; this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
@ -278,7 +278,7 @@ export class SelectorMatcher {
result = result =
this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result; this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
const partialValuesMap = this._attrValuePartialMap[name]; const partialValuesMap = this._attrValuePartialMap.get(name);
if (value) { if (value) {
result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result; result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
} }
@ -291,14 +291,14 @@ export class SelectorMatcher {
/** @internal */ /** @internal */
_matchTerminal( _matchTerminal(
map: {[k: string]: SelectorContext[]}, name: string, cssSelector: CssSelector, map: Map<string, SelectorContext[]>, name: string, cssSelector: CssSelector,
matchedCallback: (c: CssSelector, a: any) => void): boolean { matchedCallback: (c: CssSelector, a: any) => void): boolean {
if (!map || typeof name !== 'string') { if (!map || typeof name !== 'string') {
return false; return false;
} }
let selectables = map[name]; let selectables = map.get(name);
const starSelectables = map['*']; const starSelectables = map.get('*');
if (starSelectables) { if (starSelectables) {
selectables = selectables.concat(starSelectables); selectables = selectables.concat(starSelectables);
} }
@ -316,13 +316,13 @@ export class SelectorMatcher {
/** @internal */ /** @internal */
_matchPartial( _matchPartial(
map: {[k: string]: SelectorMatcher}, name: string, cssSelector: CssSelector, map: Map<string, SelectorMatcher>, name: string, cssSelector: CssSelector,
matchedCallback: (c: CssSelector, a: any) => void): boolean { matchedCallback: (c: CssSelector, a: any) => void): boolean {
if (!map || typeof name !== 'string') { if (!map || typeof name !== 'string') {
return false; return false;
} }
const nestedSelector = map[name]; const nestedSelector = map.get(name);
if (!nestedSelector) { if (!nestedSelector) {
return false; return false;
} }

View File

@ -58,6 +58,12 @@ export function main() {
expect(matched).toEqual([s1[0], 1, s2[0], 2]); expect(matched).toEqual([s1[0], 1, s2[0], 2]);
}); });
it('should not throw for class name "constructor"', () => {
expect(matcher.match(CssSelector.parse('.constructor')[0], selectableCollector))
.toEqual(false);
expect(matched).toEqual([]);
});
it('should select by attr name case sensitive independent of the value', () => { it('should select by attr name case sensitive independent of the value', () => {
matcher.addSelectables(s1 = CssSelector.parse('[someAttr]'), 1); matcher.addSelectables(s1 = CssSelector.parse('[someAttr]'), 1);
matcher.addSelectables(s2 = CssSelector.parse('[someAttr][someAttr2]'), 2); matcher.addSelectables(s2 = CssSelector.parse('[someAttr][someAttr2]'), 2);