import MediaQueryItem from './MediaQueryItem.js'; import MediaQueryTypeEnum from './MediaQueryTypeEnum.js'; import IBrowserWindow from '../window/IBrowserWindow.js'; /** * Media query RegExp. * * Group 1: "not", "only", "all", "screen", "print". * Group 2: Rule. * Group 3: Rule end paranthesis (must be present if no value). * Group 4: Comma (,). * Group 5: "or", "and". */ const MEDIA_QUERY_REGEXP = /(not|only|all|screen|print)|\(([^\)]+)(\)){0,1}|(,)| +(or|and) +/g; /** * Check if resolution RegExp. */ const IS_RESOLUTION_REGEXP = /[<>]/; /** * Resolution RegExp. * * Group 1: First resolution value. * Group 2: First resolution operator. * Group 3: Resolution type. * Group 4: Second resolution operator. * Group 5: Second resolution value. */ const RESOLUTION_REGEXP = /(?:([0-9]+[a-z]+) *(<|<=|>|=>)){0,1} *(width|height) *(?:(<|<=|>|=>) *([0-9]+[a-z]+)){0,1}/; /** * Utility for parsing a query string. */ export default class MediaQueryParser { /** * Parses a media query string. * * @param options Options. * @param options.ownerWindow Owner window. * @param options.mediaQuery Media query string. * @param [options.rootFontSize] Root font size. * @returns Media query items. */ public static parse(options: { ownerWindow: IBrowserWindow; mediaQuery: string; rootFontSize?: string | number | null; }): MediaQueryItem[] { let currentMediaQueryItem: MediaQueryItem = new MediaQueryItem({ ownerWindow: options.ownerWindow, rootFontSize: options.rootFontSize }); const mediaQueryItems: MediaQueryItem[] = [currentMediaQueryItem]; const regexp = new RegExp(MEDIA_QUERY_REGEXP); let match: RegExpExecArray | null = null; while ((match = regexp.exec(options.mediaQuery.toLowerCase()))) { if (match[4] === ',' || match[5] === 'or') { currentMediaQueryItem = new MediaQueryItem({ ownerWindow: options.ownerWindow, rootFontSize: options.rootFontSize }); mediaQueryItems.push(currentMediaQueryItem); } else if (match[1] === 'all' || match[1] === 'screen' || match[1] === 'print') { currentMediaQueryItem.mediaTypes.push(match[1]); } else if (match[1] === 'not') { currentMediaQueryItem.not = true; } else if (match[2]) { const resolutionMatch = IS_RESOLUTION_REGEXP.test(match[2]) ? match[2].match(RESOLUTION_REGEXP) : null; if (resolutionMatch && (resolutionMatch[1] || resolutionMatch[5])) { currentMediaQueryItem.ranges.push({ before: resolutionMatch[1] ? { value: resolutionMatch[1], operator: resolutionMatch[2] } : null, type: resolutionMatch[3], after: resolutionMatch[5] ? { value: resolutionMatch[5], operator: resolutionMatch[4] } : null }); } else { const [name, value] = match[2].split(':'); const trimmedValue = value ? value.trim() : null; if (!trimmedValue && !match[3]) { return [ new MediaQueryItem({ ownerWindow: options.ownerWindow, rootFontSize: options.rootFontSize, not: true, mediaTypes: [MediaQueryTypeEnum.all] }) ]; } currentMediaQueryItem.rules.push({ name: name.trim(), value: trimmedValue }); } } } return mediaQueryItems; } }