nl_tokens: / * Sizes Parser
pl_tokens: function parseSizes ( strValue ) { // (Percentage CSS lengths are not allowed in this case, to avoid confusion: // https://html.spec.whatwg.org/multipage/embedded-content.html#valid-source-size-list // CSS allows a single optional plus or minus sign: // http://www.w3.org/TR/CSS2/syndata.html#numbers // CSS is ASCII case-insensitive: // http://www.w3.org/TR/CSS2/syndata.html#characters ) // Spec allows exponential notation for <number> type: // http://dev.w3.org/csswg/css-values/#numbers var regexCssLengthWithUnits = / ^(?:[+-]?[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?(?:ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmin|vmax|vw)$ / i ; // (This is a quick and lenient test. Because of optional unlimited-depth internal // grouping parens and strict spacing rules, this could get very complicated.) var regexCssCalc = / ^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$ / i ; var i ; var unparsedSizesList ; var unparsedSizesListLength ; var unparsedSize ; var lastComponentValue ; var size ; // UTILITY FUNCTIONS //  (Toy CSS parser. The goals here are: //  1) expansive test coverage without the weight of a full CSS parser. //  2) Avoiding regex wherever convenient. //  Quick tests: http://jsfiddle.net/gtntL4gr/3/ //  Returns an array of arrays.) function parseComponentValues ( str ) { var chrctr ; var <mask> = "" ; var componentArray = [ ] ; var listArray = [ ] ; var parenDepth = 0 ; var pos = 0 ; var inComment = false ; function pushComponent ( ) { if ( component ) { componentArray . push ( component ) ; component = "" ; } } function pushComponentArray ( ) { if ( componentArray [ 0 ] ) { listArray . push ( componentArray ) ; componentArray = [ ] ; } } // (Loop forwards from the beginning of the string.) while ( true ) { chrctr = str . charAt ( pos ) ; if ( chrctr === "" ) { // ( End of string reached.) pushComponent ( ) ; pushComponentArray ( ) ; return listArray ; } else if ( inComment ) { if ( ( chrctr === "*" ) && ( str [ pos + 1 ] === "/" ) ) { // (At end of a comment.) inComment = false ; pos += 2 ; pushComponent ( ) ; continue ; } else { pos += 1 ; // (Skip all characters inside comments.) continue ; } } else if ( isSpace ( chrctr ) ) { // (If previous character in loop was also a space, or if // at the beginning of the string, do not add space char to // component.) if ( ( str . charAt ( pos - 1 ) && isSpace ( str . charAt ( pos - 1 ) ) ) || ! component ) { pos += 1 ; continue ; } else if ( parenDepth === 0 ) { pushComponent ( ) ; pos += 1 ; continue ; } else { // (Replace any space character with a plain space for legibility.) chrctr = " " ; } } else if ( chrctr === "(" ) { parenDepth += 1 ; } else if ( chrctr === ")" ) { parenDepth -= 1 ; } else if ( chrctr === "," ) { pushComponent ( ) ; pushComponentArray ( ) ; pos += 1 ; continue ; } else if ( ( chrctr === "/" ) && ( str . charAt ( pos + 1 ) === "*" ) ) { inComment = true ; pos += 2 ; continue ; } component = component + chrctr ; pos += 1 ; } } function isValidNonNegativeSourceSizeValue ( s ) { if ( regexCssLengthWithUnits . test ( s ) && ( parseFloat ( s ) >= 0 ) ) { return true ; } if ( regexCssCalc . test ( s ) ) { return true ; } // ( http://www.w3.org/TR/CSS2/syndata.html#numbers says: // "-0 is equivalent to 0 and is not a negative number." which means that // unitless zero and unitless negative zero must be accepted as special cases.) if ( ( s === "0" ) || ( s === "-0" ) || ( s === "+0" ) ) { return true ; } return false ; } // When asked to parse a sizes attribute from an element, parse a // comma-separated list of component values from the value of the element's // sizes attribute (or the empty string, if the attribute is absent), and let // unparsed sizes list be the result. // http://dev.w3.org/csswg/css-syntax/#parse-comma-separated-list-of-component-values unparsedSizesList = parseComponentValues ( strValue ) ; unparsedSizesListLength = unparsedSizesList . length ; // For each unparsed size in unparsed sizes list: for ( i = 0 ; i < unparsedSizesListLength ; i ++ ) { unparsedSize = unparsedSizesList [ i ] ; // 1. Remove all consecutive <whitespace-token>s from the end of unparsed size. // ( parseComponentValues() already omits spaces outside of parens. ) // If unparsed size is now empty, that is a parse error; continue to the next // iteration of this algorithm. // ( parseComponentValues() won't push an empty array. ) // 2. If the last component value in unparsed size is a valid non-negative // <source-size-value>, let size be its value and remove the component value // from unparsed size. Any CSS function other than the calc() function is // invalid. Otherwise, there is a parse error; continue to the next iteration // of this algorithm. // http://dev.w3.org/csswg/css-syntax/#parse-component-value lastComponentValue = unparsedSize [ unparsedSize . length - 1 ] ; if ( isValidNonNegativeSourceSizeValue ( lastComponentValue ) ) { size = lastComponentValue ; unparsedSize . pop ( ) ; } else { continue ; } // 3. Remove all consecutive <whitespace-token>s from the end of unparsed // size. If unparsed size is now empty, return size and exit this algorithm. // If this was not the last item in unparsed sizes list, that is a parse error. if ( unparsedSize . length === 0 ) { return size ; } // 4. Parse the remaining component values in unparsed size as a // <media-condition>. If it does not parse correctly, or it does parse // correctly but the <media-condition> evaluates to false, continue to the // next iteration of this algorithm. // (Parsing all possible compound media conditions in JS is heavy, complicated, // and the payoff is unclear. Is there ever an situation where the // media condition parses incorrectly but still somehow evaluates to true? // Can we just rely on the browser/polyfill to do it?) unparsedSize = unparsedSize . join ( " " ) ; if ( ! ( pf . matchesMedia ( unparsedSize ) ) ) { continue ; } // 5. Return size and exit this algorithm. return size ; } // If the above algorithm exhausts unparsed sizes list without returning a // size value, return 100vw. return "100vw" ; }