static generateCandidatesRecursive()

in src/js/webview/CSSSelectorResolver.js [191:268]


  static generateCandidatesRecursive(
    element: Element,
    currentLevel: number,
    lastLevel: number
  ): string[] {
    // Ends recursion when reaching the root or the lastLevel
    if (currentLevel > lastLevel || element === document.body) {
      return [];
    }

    let classes = element.classList;
    let tagName = element.tagName.toLowerCase();
    let id = element.getAttribute('id');

    let candidates = new Set();
    let selector = '';

    // .class candidates
    for (let className of classes) {
      if (!className.startsWith('facebook-instant-articles-builder')) {
        selector = '.' + className;
        candidates.add(selector);
      }
    }

    // tagName.class candidates
    for (let className of classes) {
      if (!className.startsWith('facebook-instant-articles-builder')) {
        selector = tagName + '.' + className;
        candidates.add(selector);
      }
    }

    // #id candidate
    if (id) {
      selector = '#' + id;
      candidates.add(selector);
    }

    // tagName candidate
    selector = tagName;
    candidates.add(selector);

    // tagName#id candidate
    if (id) {
      selector = tagName + '.' + id;
      candidates.add(selector);
    }

    // empty selector candidate (creating selectors with ancestors
    // that omit the intermediate elements)
    if (currentLevel > 0 && currentLevel < lastLevel) {
      candidates.add('');
    }

    // Recursively find the candidates for the parentNode (1 level less deeply)
    let parent = element.parentElement;
    let parentCandidates = [];
    if (parent != null) {
      parentCandidates = this.generateCandidatesRecursive(
        parent,
        currentLevel + 1,
        lastLevel
      );
    }

    // candidates = candidates ∪ (parentCandidates × candidates)
    let candidatesClone = [...candidates];
    for (let parentCandidate of parentCandidates) {
      for (let candidate of candidatesClone) {
        let newCandidate = parentCandidate.trim() + ' ' + candidate.trim();
        candidates.add(newCandidate.trim());
      }
    }

    // return as array
    return Array.from(candidates);
  }