You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							304 lines
						
					
					
						
							7.4 KiB
						
					
					
				
			
		
		
	
	
							304 lines
						
					
					
						
							7.4 KiB
						
					
					
				var openParentheses = "(".charCodeAt(0); | 
						|
var closeParentheses = ")".charCodeAt(0); | 
						|
var singleQuote = "'".charCodeAt(0); | 
						|
var doubleQuote = '"'.charCodeAt(0); | 
						|
var backslash = "\\".charCodeAt(0); | 
						|
var slash = "/".charCodeAt(0); | 
						|
var comma = ",".charCodeAt(0); | 
						|
var colon = ":".charCodeAt(0); | 
						|
var star = "*".charCodeAt(0); | 
						|
var uLower = "u".charCodeAt(0); | 
						|
var uUpper = "U".charCodeAt(0); | 
						|
var plus = "+".charCodeAt(0); | 
						|
var isUnicodeRange = /^[a-f0-9?-]+$/i; | 
						|
 | 
						|
module.exports = function(input) { | 
						|
  var tokens = []; | 
						|
  var value = input; | 
						|
 | 
						|
  var next, | 
						|
    quote, | 
						|
    prev, | 
						|
    token, | 
						|
    escape, | 
						|
    escapePos, | 
						|
    whitespacePos, | 
						|
    parenthesesOpenPos; | 
						|
  var pos = 0; | 
						|
  var code = value.charCodeAt(pos); | 
						|
  var max = value.length; | 
						|
  var stack = [{ nodes: tokens }]; | 
						|
  var balanced = 0; | 
						|
  var parent; | 
						|
 | 
						|
  var name = ""; | 
						|
  var before = ""; | 
						|
  var after = ""; | 
						|
 | 
						|
  while (pos < max) { | 
						|
    // Whitespaces | 
						|
    if (code <= 32) { | 
						|
      next = pos; | 
						|
      do { | 
						|
        next += 1; | 
						|
        code = value.charCodeAt(next); | 
						|
      } while (code <= 32); | 
						|
      token = value.slice(pos, next); | 
						|
 | 
						|
      prev = tokens[tokens.length - 1]; | 
						|
      if (code === closeParentheses && balanced) { | 
						|
        after = token; | 
						|
      } else if (prev && prev.type === "div") { | 
						|
        prev.after = token; | 
						|
      } else if ( | 
						|
        code === comma || | 
						|
        code === colon || | 
						|
        (code === slash && | 
						|
          value.charCodeAt(next + 1) !== star && | 
						|
          (!parent || | 
						|
            (parent && parent.type === "function" && parent.value !== "calc"))) | 
						|
      ) { | 
						|
        before = token; | 
						|
      } else { | 
						|
        tokens.push({ | 
						|
          type: "space", | 
						|
          sourceIndex: pos, | 
						|
          value: token | 
						|
        }); | 
						|
      } | 
						|
 | 
						|
      pos = next; | 
						|
 | 
						|
      // Quotes | 
						|
    } else if (code === singleQuote || code === doubleQuote) { | 
						|
      next = pos; | 
						|
      quote = code === singleQuote ? "'" : '"'; | 
						|
      token = { | 
						|
        type: "string", | 
						|
        sourceIndex: pos, | 
						|
        quote: quote | 
						|
      }; | 
						|
      do { | 
						|
        escape = false; | 
						|
        next = value.indexOf(quote, next + 1); | 
						|
        if (~next) { | 
						|
          escapePos = next; | 
						|
          while (value.charCodeAt(escapePos - 1) === backslash) { | 
						|
            escapePos -= 1; | 
						|
            escape = !escape; | 
						|
          } | 
						|
        } else { | 
						|
          value += quote; | 
						|
          next = value.length - 1; | 
						|
          token.unclosed = true; | 
						|
        } | 
						|
      } while (escape); | 
						|
      token.value = value.slice(pos + 1, next); | 
						|
 | 
						|
      tokens.push(token); | 
						|
      pos = next + 1; | 
						|
      code = value.charCodeAt(pos); | 
						|
 | 
						|
      // Comments | 
						|
    } else if (code === slash && value.charCodeAt(pos + 1) === star) { | 
						|
      token = { | 
						|
        type: "comment", | 
						|
        sourceIndex: pos | 
						|
      }; | 
						|
 | 
						|
      next = value.indexOf("*/", pos); | 
						|
      if (next === -1) { | 
						|
        token.unclosed = true; | 
						|
        next = value.length; | 
						|
      } | 
						|
 | 
						|
      token.value = value.slice(pos + 2, next); | 
						|
      tokens.push(token); | 
						|
 | 
						|
      pos = next + 2; | 
						|
      code = value.charCodeAt(pos); | 
						|
 | 
						|
      // Operation within calc | 
						|
    } else if ( | 
						|
      (code === slash || code === star) && | 
						|
      parent && | 
						|
      parent.type === "function" && | 
						|
      parent.value === "calc" | 
						|
    ) { | 
						|
      token = value[pos]; | 
						|
      tokens.push({ | 
						|
        type: "word", | 
						|
        sourceIndex: pos - before.length, | 
						|
        value: token | 
						|
      }); | 
						|
      pos += 1; | 
						|
      code = value.charCodeAt(pos); | 
						|
 | 
						|
      // Dividers | 
						|
    } else if (code === slash || code === comma || code === colon) { | 
						|
      token = value[pos]; | 
						|
 | 
						|
      tokens.push({ | 
						|
        type: "div", | 
						|
        sourceIndex: pos - before.length, | 
						|
        value: token, | 
						|
        before: before, | 
						|
        after: "" | 
						|
      }); | 
						|
      before = ""; | 
						|
 | 
						|
      pos += 1; | 
						|
      code = value.charCodeAt(pos); | 
						|
 | 
						|
      // Open parentheses | 
						|
    } else if (openParentheses === code) { | 
						|
      // Whitespaces after open parentheses | 
						|
      next = pos; | 
						|
      do { | 
						|
        next += 1; | 
						|
        code = value.charCodeAt(next); | 
						|
      } while (code <= 32); | 
						|
      parenthesesOpenPos = pos; | 
						|
      token = { | 
						|
        type: "function", | 
						|
        sourceIndex: pos - name.length, | 
						|
        value: name, | 
						|
        before: value.slice(parenthesesOpenPos + 1, next) | 
						|
      }; | 
						|
      pos = next; | 
						|
 | 
						|
      if (name === "url" && code !== singleQuote && code !== doubleQuote) { | 
						|
        next -= 1; | 
						|
        do { | 
						|
          escape = false; | 
						|
          next = value.indexOf(")", next + 1); | 
						|
          if (~next) { | 
						|
            escapePos = next; | 
						|
            while (value.charCodeAt(escapePos - 1) === backslash) { | 
						|
              escapePos -= 1; | 
						|
              escape = !escape; | 
						|
            } | 
						|
          } else { | 
						|
            value += ")"; | 
						|
            next = value.length - 1; | 
						|
            token.unclosed = true; | 
						|
          } | 
						|
        } while (escape); | 
						|
        // Whitespaces before closed | 
						|
        whitespacePos = next; | 
						|
        do { | 
						|
          whitespacePos -= 1; | 
						|
          code = value.charCodeAt(whitespacePos); | 
						|
        } while (code <= 32); | 
						|
        if (parenthesesOpenPos < whitespacePos) { | 
						|
          if (pos !== whitespacePos + 1) { | 
						|
            token.nodes = [ | 
						|
              { | 
						|
                type: "word", | 
						|
                sourceIndex: pos, | 
						|
                value: value.slice(pos, whitespacePos + 1) | 
						|
              } | 
						|
            ]; | 
						|
          } else { | 
						|
            token.nodes = []; | 
						|
          } | 
						|
          if (token.unclosed && whitespacePos + 1 !== next) { | 
						|
            token.after = ""; | 
						|
            token.nodes.push({ | 
						|
              type: "space", | 
						|
              sourceIndex: whitespacePos + 1, | 
						|
              value: value.slice(whitespacePos + 1, next) | 
						|
            }); | 
						|
          } else { | 
						|
            token.after = value.slice(whitespacePos + 1, next); | 
						|
          } | 
						|
        } else { | 
						|
          token.after = ""; | 
						|
          token.nodes = []; | 
						|
        } | 
						|
        pos = next + 1; | 
						|
        code = value.charCodeAt(pos); | 
						|
        tokens.push(token); | 
						|
      } else { | 
						|
        balanced += 1; | 
						|
        token.after = ""; | 
						|
        tokens.push(token); | 
						|
        stack.push(token); | 
						|
        tokens = token.nodes = []; | 
						|
        parent = token; | 
						|
      } | 
						|
      name = ""; | 
						|
 | 
						|
      // Close parentheses | 
						|
    } else if (closeParentheses === code && balanced) { | 
						|
      pos += 1; | 
						|
      code = value.charCodeAt(pos); | 
						|
 | 
						|
      parent.after = after; | 
						|
      after = ""; | 
						|
      balanced -= 1; | 
						|
      stack.pop(); | 
						|
      parent = stack[balanced]; | 
						|
      tokens = parent.nodes; | 
						|
 | 
						|
      // Words | 
						|
    } else { | 
						|
      next = pos; | 
						|
      do { | 
						|
        if (code === backslash) { | 
						|
          next += 1; | 
						|
        } | 
						|
        next += 1; | 
						|
        code = value.charCodeAt(next); | 
						|
      } while ( | 
						|
        next < max && | 
						|
        !( | 
						|
          code <= 32 || | 
						|
          code === singleQuote || | 
						|
          code === doubleQuote || | 
						|
          code === comma || | 
						|
          code === colon || | 
						|
          code === slash || | 
						|
          code === openParentheses || | 
						|
          (code === star && | 
						|
            parent && | 
						|
            parent.type === "function" && | 
						|
            parent.value === "calc") || | 
						|
          (code === slash && | 
						|
            parent.type === "function" && | 
						|
            parent.value === "calc") || | 
						|
          (code === closeParentheses && balanced) | 
						|
        ) | 
						|
      ); | 
						|
      token = value.slice(pos, next); | 
						|
 | 
						|
      if (openParentheses === code) { | 
						|
        name = token; | 
						|
      } else if ( | 
						|
        (uLower === token.charCodeAt(0) || uUpper === token.charCodeAt(0)) && | 
						|
        plus === token.charCodeAt(1) && | 
						|
        isUnicodeRange.test(token.slice(2)) | 
						|
      ) { | 
						|
        tokens.push({ | 
						|
          type: "unicode-range", | 
						|
          sourceIndex: pos, | 
						|
          value: token | 
						|
        }); | 
						|
      } else { | 
						|
        tokens.push({ | 
						|
          type: "word", | 
						|
          sourceIndex: pos, | 
						|
          value: token | 
						|
        }); | 
						|
      } | 
						|
 | 
						|
      pos = next; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  for (pos = stack.length - 1; pos; pos -= 1) { | 
						|
    stack[pos].unclosed = true; | 
						|
  } | 
						|
 | 
						|
  return stack[0].nodes; | 
						|
};
 | 
						|
 |