mirror of
https://github.com/less/less.js.git
synced 2026-01-22 21:58:14 -05:00
refactor extend visitor to be more readable and maintainable
This commit is contained in:
@@ -95,112 +95,146 @@
|
||||
if (rulesetNode.root) {
|
||||
return;
|
||||
}
|
||||
var i, j, k, selector, element, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [];
|
||||
var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this;
|
||||
|
||||
// look at each selector path in the ruleset, find any extend matches and then copy, find and replace
|
||||
|
||||
for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) {
|
||||
for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) {
|
||||
|
||||
selectorPath = rulesetNode.paths[pathIndex];
|
||||
matches = this.findMatch(allExtends[extendIndex], selectorPath);
|
||||
|
||||
for(k = 0; k < allExtends.length; k++) {
|
||||
for(i = 0; i < rulesetNode.paths.length; i++) {
|
||||
selectorPath = rulesetNode.paths[i];
|
||||
var matches = this.findMatch(allExtends[k], selectorPath);
|
||||
if (matches.length) {
|
||||
allExtends[k].selfSelectors.forEach(function(selfSelector) {
|
||||
var currentSelectorPathIndex = 0,
|
||||
currentSelectorPathElementIndex = 0,
|
||||
path = [];
|
||||
for(j = 0; j < matches.length; j++) {
|
||||
match = matches[j];
|
||||
var selector = selectorPath[match.pathIndex],
|
||||
firstElement = new tree.Element(
|
||||
match.initialCombinator,
|
||||
selfSelector.elements[0].value,
|
||||
selfSelector.elements[0].index
|
||||
);
|
||||
|
||||
if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
|
||||
path[path.length-1].elements = path[path.length-1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
|
||||
currentSelectorPathElementIndex = 0;
|
||||
currentSelectorPathIndex++;
|
||||
}
|
||||
|
||||
path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
|
||||
|
||||
path.push(new tree.Selector(
|
||||
selector.elements
|
||||
.slice(currentSelectorPathElementIndex, match.index)
|
||||
.concat([firstElement])
|
||||
.concat(selfSelector.elements.slice(1))
|
||||
));
|
||||
currentSelectorPathIndex = match.endPathIndex;
|
||||
currentSelectorPathElementIndex = match.endPathElementIndex;
|
||||
if (currentSelectorPathElementIndex >= selector.elements.length) {
|
||||
currentSelectorPathElementIndex = 0;
|
||||
currentSelectorPathIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
|
||||
path[path.length-1].elements = path[path.length-1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
|
||||
currentSelectorPathElementIndex = 0;
|
||||
currentSelectorPathIndex++;
|
||||
}
|
||||
|
||||
path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length));
|
||||
|
||||
selectorsToAdd.push(path);
|
||||
allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) {
|
||||
selectorsToAdd.push(extendVisitor.extendSelector(matches, selfSelector));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd);
|
||||
},
|
||||
findMatch: function (extend, selectorPath) {
|
||||
var i, k, l, element, hasMatch, potentialMatches = [], potentialMatch, matches = [];
|
||||
for(k = 0; k < selectorPath.length; k++) {
|
||||
selector = selectorPath[k];
|
||||
for(i = 0; i < selector.elements.length; i++) {
|
||||
if (extend.allowBefore || (k == 0 && i == 0)) {
|
||||
potentialMatches.push({pathIndex: k, index: i, matched: 0, initialCombinator: selector.elements[i].combinator});
|
||||
findMatch: function (extend, haystackSelectorPath) {
|
||||
//
|
||||
// look through the haystack selector path to try and find the needle - extend.selector
|
||||
// returns an array of selector matches that can then be replaced
|
||||
//
|
||||
var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement,
|
||||
targetCombinator, i,
|
||||
needleElements = extend.selector.elements,
|
||||
potentialMatches = [], potentialMatch, matches = [];
|
||||
|
||||
// loop through the haystack elements
|
||||
for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) {
|
||||
hackstackSelector = haystackSelectorPath[haystackSelectorIndex];
|
||||
|
||||
for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; hackstackElementIndex++) {
|
||||
|
||||
haystackElement = hackstackSelector.elements[hackstackElementIndex];
|
||||
|
||||
// if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
|
||||
if (extend.allowBefore || (haystackSelectorIndex == 0 && hackstackElementIndex == 0)) {
|
||||
potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator});
|
||||
}
|
||||
|
||||
for(l = 0; l < potentialMatches.length; l++) {
|
||||
potentialMatch = potentialMatches[l];
|
||||
for(i = 0; i < potentialMatches.length; i++) {
|
||||
potentialMatch = potentialMatches[i];
|
||||
|
||||
var targetCombinator = selector.elements[i].combinator.value;
|
||||
if (targetCombinator == '' && i === 0) {
|
||||
// selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
|
||||
// then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
|
||||
// what the resulting combinator will be
|
||||
targetCombinator = haystackElement.combinator.value;
|
||||
if (targetCombinator == '' && hackstackElementIndex === 0) {
|
||||
targetCombinator = ' ';
|
||||
}
|
||||
if (extend.selector.elements[potentialMatch.matched].value !== selector.elements[i].value ||
|
||||
(potentialMatch.matched > 0 && extend.selector.elements[potentialMatch.matched].combinator.value !== targetCombinator)) {
|
||||
|
||||
// if we don't match, null our match to indicate failure
|
||||
if (needleElements[potentialMatch.matched].value !== haystackElement.value ||
|
||||
(potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) {
|
||||
potentialMatch = null;
|
||||
} else {
|
||||
potentialMatch.matched++;
|
||||
}
|
||||
|
||||
// if we are still valid and have finished, test whether we have elements after and whether these are allowed
|
||||
if (potentialMatch) {
|
||||
potentialMatch.finished = potentialMatch.matched === extend.selector.elements.length;
|
||||
potentialMatch.finished = potentialMatch.matched === needleElements.length;
|
||||
if (potentialMatch.finished &&
|
||||
(!extend.allowAfter && (i+1 < selector.elements.length ||
|
||||
k+1 < selectorPath.length))) {
|
||||
(!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) {
|
||||
potentialMatch = null;
|
||||
}
|
||||
}
|
||||
// if null we remove, if not, we are still valid, so either push as a valid match or continue
|
||||
if (potentialMatch) {
|
||||
if (potentialMatch.finished) {
|
||||
potentialMatch.length = extend.selector.elements.length;
|
||||
potentialMatch.endPathIndex = k;
|
||||
potentialMatch.endPathElementIndex = i+1; // index after end of match
|
||||
potentialMatches.length = 0;
|
||||
potentialMatch.length = needleElements.length;
|
||||
potentialMatch.endPathIndex = haystackSelectorIndex;
|
||||
potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match
|
||||
potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again
|
||||
matches.push(potentialMatch);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
potentialMatches.splice(l, 1);
|
||||
l--;
|
||||
potentialMatches.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
},
|
||||
extendSelector:function (matches, replacementSelector) {
|
||||
|
||||
//for a set of matches, replace each match with the replacement selector
|
||||
|
||||
var currentSelectorPathIndex = 0,
|
||||
currentSelectorPathElementIndex = 0,
|
||||
path = [],
|
||||
matchIndex,
|
||||
selector,
|
||||
firstElement;
|
||||
|
||||
for (matchIndex = 0; matchIndex < matches.length; matchIndex++) {
|
||||
match = matches[matchIndex];
|
||||
selector = selectorPath[match.pathIndex];
|
||||
firstElement = new tree.Element(
|
||||
match.initialCombinator,
|
||||
replacementSelector.elements[0].value,
|
||||
replacementSelector.elements[0].index
|
||||
);
|
||||
|
||||
if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
|
||||
path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
|
||||
currentSelectorPathElementIndex = 0;
|
||||
currentSelectorPathIndex++;
|
||||
}
|
||||
|
||||
path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
|
||||
|
||||
path.push(new tree.Selector(
|
||||
selector.elements
|
||||
.slice(currentSelectorPathElementIndex, match.index)
|
||||
.concat([firstElement])
|
||||
.concat(replacementSelector.elements.slice(1))
|
||||
));
|
||||
currentSelectorPathIndex = match.endPathIndex;
|
||||
currentSelectorPathElementIndex = match.endPathElementIndex;
|
||||
if (currentSelectorPathElementIndex >= selector.elements.length) {
|
||||
currentSelectorPathElementIndex = 0;
|
||||
currentSelectorPathIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
|
||||
path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
|
||||
currentSelectorPathElementIndex = 0;
|
||||
currentSelectorPathIndex++;
|
||||
}
|
||||
|
||||
path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length));
|
||||
|
||||
return path;
|
||||
},
|
||||
visitRulesetOut: function (rulesetNode) {
|
||||
},
|
||||
visitMedia: function (mediaNode, visitArgs) {
|
||||
|
||||
Reference in New Issue
Block a user