parsePositionShorthand static method
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;
}