findMatch method

List<MatchSelector> findMatch(
  1. Extend extend,
  2. List<Selector> 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

Implementation

List<MatchSelector> findMatch(
    Extend extend, List<Selector> haystackSelectorPath) {
  final extendVisitor = this;
  final matches = <MatchSelector>[];
  final needleElements = extend.selector.elements;
  final potentialMatches = <MatchSelector>[];

  // loop through the haystack elements
  for (var haystackSelectorIndex = 0;
      haystackSelectorIndex < haystackSelectorPath.length;
      haystackSelectorIndex++) {
    final hackstackSelector = haystackSelectorPath[haystackSelectorIndex];

    for (var hackstackElementIndex = 0;
        hackstackElementIndex < hackstackSelector.elements.length;
        hackstackElementIndex++) {
      final 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.add(MatchSelector()
          ..pathIndex = haystackSelectorIndex
          ..index = hackstackElementIndex
          ..matched = 0
          ..initialCombinator = haystackElement.combinator);
      }

      for (var i = 0; i < potentialMatches.length; i++) {
        var potentialMatch = potentialMatches[i];

        // 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
        var targetCombinator = haystackElement.combinator.value;
        if (targetCombinator == '' && hackstackElementIndex == 0) {
          targetCombinator = ' ';
        }

        // if we don't match, null our match to indicate failure
        if (!extendVisitor.isElementValuesEqual(
                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 != null) {
          potentialMatch.finished =
              potentialMatch.matched == needleElements.length;

          if (potentialMatch.finished &&
              (!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 != null) {
          if (potentialMatch.finished) {
            potentialMatch
              ..length = needleElements.length
              ..endPathIndex = haystackSelectorIndex
              ..endPathElementIndex =
                  hackstackElementIndex + 1; // index after end of match
            potentialMatches.length =
                0; // we don't allow matches to overlap, so start matching again
            matches.add(potentialMatch);
          }
        } else {
          potentialMatches.removeAt(i);
          i--;
        }
      }
    }
  }
  return matches;

//2.3.1
//  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,
//          extendVisitor = this,
//          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(i = 0; i < potentialMatches.length; i++) {
//                  potentialMatch = potentialMatches[i];
//
//                  // 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 we don't match, null our match to indicate failure
//                  if (!extendVisitor.isElementValuesEqual(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 === needleElements.length;
//                      if (potentialMatch.finished &&
//                          (!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 = 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);
//                      }
//                  } else {
//                      potentialMatches.splice(i, 1);
//                      i--;
//                  }
//              }
//          }
//      }
//      return matches;
//  },
}