parsePositionShorthand static method

List<String> parsePositionShorthand(
  1. String input
)

Parse background-position shorthand to background-position-x and background-position-y list.

Implementation

static List<String> parsePositionShorthand(String input) {
  if (_cachedParsedPosition.containsKey(input)) {
    return _cachedParsedPosition[input]!;
  }

  final List<String> tokens = input.split(splitRegExp).where((s) => s.isNotEmpty).toList();

  if (tokens.length == 2) {
    final String a = tokens[0];
    final String b = tokens[1];
    if (!_looksNumeric(a) && !_looksNumeric(b)) {
      if (_isVertKW(a) && _isHorizKW(b)) {
        final List<String> swapped = <String>[b, a];
        _cachedParsedPosition[input] = swapped;
        cssLogger.finer('[CSSPosition] Parsed background-position "$input" => x="${swapped[0]}", y="${swapped[1]}"');
        return swapped;
      }
    }
    // Horizontal keyword followed by numeric => x=keyword, y=numeric.
    if (_isHorizKW(a) && _looksNumeric(b)) {
      final List<String> pair = <String>[a, b];
      _cachedParsedPosition[input] = pair;
      cssLogger.finer('[CSSPosition] Parsed background-position "$input" => x="${pair[0]}", y="${pair[1]}"');
      return pair;
    }
    // Numeric followed by vertical keyword => x=numeric, y=keyword.
    if (_looksNumeric(a) && _isVertKW(b)) {
      final List<String> pair = <String>[a, b];
      _cachedParsedPosition[input] = pair;
      cssLogger.finer('[CSSPosition] Parsed background-position "$input" => x="${pair[0]}", y="${pair[1]}"');
      return pair;
    }
  }
  if (tokens.isEmpty) {
    cssLogger.warning('[CSSPosition] Empty background-position input. Falling back to center center.');
    return _cachedParsedPosition[input] = [CENTER, CENTER];
  }


  String? hKeyword;
  String? vKeyword;
  String? hOffset;
  String? vOffset;
  String? lastAxis; // 'x' or 'y'

  for (int i = 0; i < tokens.length; i++) {
    final String t = tokens[i];
    if (t == LEFT || t == RIGHT) {
      hKeyword ??= t;
      lastAxis = 'x';
      continue;
    }
    if (t == TOP || t == BOTTOM) {
      vKeyword ??= t;
      lastAxis = 'y';
      continue;
    }
    if (t == CENTER) {
      // Assign CENTER to the axis that isn't specified yet; if both unset, prefer x first.
      if (hKeyword == null) {
        hKeyword = CENTER;
        lastAxis = 'x';
      } else if (vKeyword == null) {
        vKeyword = CENTER;
        lastAxis = 'y';
      } else {
        // Extra center token; ignore.
      }
      continue;
    }
    if (_looksNumeric(t)) {
      if (lastAxis == 'x') {
        if (hOffset == null) {
          hOffset = t;
        } else if (vOffset == null) {
          // Two-value syntax: second numeric goes to y.
          vOffset = t;
          lastAxis = 'y';
        }
      } else if (lastAxis == 'y') {
        if (vOffset == null) {
          vOffset = t;
        } else if (hOffset == null) {
          hOffset = t;
          lastAxis = 'x';
        }
      } else {
        // No prior axis keyword: first length -> x, second -> y.
        if (hOffset == null) {
          hOffset = t;
          lastAxis = 'x';
        } else if (vOffset == null) {
          vOffset = t;
          lastAxis = 'y';
        }
      }
      continue;
    }
    // Unknown token: ignore but log once.
    cssLogger.fine('[CSSPosition] Ignoring unknown token in background-position: "$t"');
  }

  // Defaults per spec when a side/offset is omitted.
  // If only a vertical keyword/offset provided, horizontal defaults to center.
  // If only a horizontal provided, vertical defaults to center.
  // If first token is a length/percentage only, treat as horizontal offset.
  if (hKeyword == null && hOffset == null) {
    if (vKeyword != null || vOffset != null) {
      hKeyword = CENTER;
    }
  }
  if (vKeyword == null && vOffset == null) {
    if (hKeyword != null || hOffset != null) {
      vKeyword = CENTER;
    }
  }

  // Compute final axis values.
  final String xValue = _axisToValue(hKeyword, hOffset, horizontal: true);
  final String yValue = _axisToValue(vKeyword, vOffset, horizontal: false);

  final List<String> result = <String>[xValue, yValue];
  _cachedParsedPosition[input] = result;

  cssLogger.finer('[CSSPosition] Parsed background-position "$input" => x="$xValue", y="$yValue"');
  return result;
}