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.
547 lines
12 KiB
547 lines
12 KiB
'use strict'; |
|
|
|
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$1 = "+".charCodeAt(0); |
|
var isUnicodeRange = /^[a-f0-9?-]+$/i; |
|
|
|
var parse$1 = 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; |
|
prev.sourceEndIndex += token.length; |
|
} 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, |
|
sourceEndIndex: next, |
|
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); |
|
token.sourceEndIndex = token.unclosed ? next : next + 1; |
|
tokens.push(token); |
|
pos = next + 1; |
|
code = value.charCodeAt(pos); |
|
|
|
// Comments |
|
} else if (code === slash && value.charCodeAt(pos + 1) === star) { |
|
next = value.indexOf("*/", pos); |
|
|
|
token = { |
|
type: "comment", |
|
sourceIndex: pos, |
|
sourceEndIndex: next + 2 |
|
}; |
|
|
|
if (next === -1) { |
|
token.unclosed = true; |
|
next = value.length; |
|
token.sourceEndIndex = next; |
|
} |
|
|
|
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, |
|
sourceEndIndex: pos + token.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, |
|
sourceEndIndex: pos + token.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, |
|
sourceEndIndex: whitespacePos + 1, |
|
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, |
|
sourceEndIndex: next, |
|
value: value.slice(whitespacePos + 1, next) |
|
}); |
|
} else { |
|
token.after = value.slice(whitespacePos + 1, next); |
|
token.sourceEndIndex = next; |
|
} |
|
} else { |
|
token.after = ""; |
|
token.nodes = []; |
|
} |
|
pos = next + 1; |
|
token.sourceEndIndex = token.unclosed ? next : pos; |
|
code = value.charCodeAt(pos); |
|
tokens.push(token); |
|
} else { |
|
balanced += 1; |
|
token.after = ""; |
|
token.sourceEndIndex = pos + 1; |
|
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; |
|
parent.sourceEndIndex += after.length; |
|
after = ""; |
|
balanced -= 1; |
|
stack[stack.length - 1].sourceEndIndex = pos; |
|
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$1 === token.charCodeAt(1) && |
|
isUnicodeRange.test(token.slice(2)) |
|
) { |
|
tokens.push({ |
|
type: "unicode-range", |
|
sourceIndex: pos, |
|
sourceEndIndex: next, |
|
value: token |
|
}); |
|
} else { |
|
tokens.push({ |
|
type: "word", |
|
sourceIndex: pos, |
|
sourceEndIndex: next, |
|
value: token |
|
}); |
|
} |
|
|
|
pos = next; |
|
} |
|
} |
|
|
|
for (pos = stack.length - 1; pos; pos -= 1) { |
|
stack[pos].unclosed = true; |
|
stack[pos].sourceEndIndex = value.length; |
|
} |
|
|
|
return stack[0].nodes; |
|
}; |
|
|
|
var walk$1 = function walk(nodes, cb, bubble) { |
|
var i, max, node, result; |
|
|
|
for (i = 0, max = nodes.length; i < max; i += 1) { |
|
node = nodes[i]; |
|
if (!bubble) { |
|
result = cb(node, i, nodes); |
|
} |
|
|
|
if ( |
|
result !== false && |
|
node.type === "function" && |
|
Array.isArray(node.nodes) |
|
) { |
|
walk(node.nodes, cb, bubble); |
|
} |
|
|
|
if (bubble) { |
|
cb(node, i, nodes); |
|
} |
|
} |
|
}; |
|
|
|
function stringifyNode(node, custom) { |
|
var type = node.type; |
|
var value = node.value; |
|
var buf; |
|
var customResult; |
|
|
|
if (custom && (customResult = custom(node)) !== undefined) { |
|
return customResult; |
|
} else if (type === "word" || type === "space") { |
|
return value; |
|
} else if (type === "string") { |
|
buf = node.quote || ""; |
|
return buf + value + (node.unclosed ? "" : buf); |
|
} else if (type === "comment") { |
|
return "/*" + value + (node.unclosed ? "" : "*/"); |
|
} else if (type === "div") { |
|
return (node.before || "") + value + (node.after || ""); |
|
} else if (Array.isArray(node.nodes)) { |
|
buf = stringify$1(node.nodes, custom); |
|
if (type !== "function") { |
|
return buf; |
|
} |
|
return ( |
|
value + |
|
"(" + |
|
(node.before || "") + |
|
buf + |
|
(node.after || "") + |
|
(node.unclosed ? "" : ")") |
|
); |
|
} |
|
return value; |
|
} |
|
|
|
function stringify$1(nodes, custom) { |
|
var result, i; |
|
|
|
if (Array.isArray(nodes)) { |
|
result = ""; |
|
for (i = nodes.length - 1; ~i; i -= 1) { |
|
result = stringifyNode(nodes[i], custom) + result; |
|
} |
|
return result; |
|
} |
|
return stringifyNode(nodes, custom); |
|
} |
|
|
|
var stringify_1 = stringify$1; |
|
|
|
var minus = "-".charCodeAt(0); |
|
var plus = "+".charCodeAt(0); |
|
var dot = ".".charCodeAt(0); |
|
var exp = "e".charCodeAt(0); |
|
var EXP = "E".charCodeAt(0); |
|
|
|
// Check if three code points would start a number |
|
// https://www.w3.org/TR/css-syntax-3/#starts-with-a-number |
|
function likeNumber(value) { |
|
var code = value.charCodeAt(0); |
|
var nextCode; |
|
|
|
if (code === plus || code === minus) { |
|
nextCode = value.charCodeAt(1); |
|
|
|
if (nextCode >= 48 && nextCode <= 57) { |
|
return true; |
|
} |
|
|
|
var nextNextCode = value.charCodeAt(2); |
|
|
|
if (nextCode === dot && nextNextCode >= 48 && nextNextCode <= 57) { |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
if (code === dot) { |
|
nextCode = value.charCodeAt(1); |
|
|
|
if (nextCode >= 48 && nextCode <= 57) { |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
if (code >= 48 && code <= 57) { |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
// Consume a number |
|
// https://www.w3.org/TR/css-syntax-3/#consume-number |
|
var unit = function(value) { |
|
var pos = 0; |
|
var length = value.length; |
|
var code; |
|
var nextCode; |
|
var nextNextCode; |
|
|
|
if (length === 0 || !likeNumber(value)) { |
|
return false; |
|
} |
|
|
|
code = value.charCodeAt(pos); |
|
|
|
if (code === plus || code === minus) { |
|
pos++; |
|
} |
|
|
|
while (pos < length) { |
|
code = value.charCodeAt(pos); |
|
|
|
if (code < 48 || code > 57) { |
|
break; |
|
} |
|
|
|
pos += 1; |
|
} |
|
|
|
code = value.charCodeAt(pos); |
|
nextCode = value.charCodeAt(pos + 1); |
|
|
|
if (code === dot && nextCode >= 48 && nextCode <= 57) { |
|
pos += 2; |
|
|
|
while (pos < length) { |
|
code = value.charCodeAt(pos); |
|
|
|
if (code < 48 || code > 57) { |
|
break; |
|
} |
|
|
|
pos += 1; |
|
} |
|
} |
|
|
|
code = value.charCodeAt(pos); |
|
nextCode = value.charCodeAt(pos + 1); |
|
nextNextCode = value.charCodeAt(pos + 2); |
|
|
|
if ( |
|
(code === exp || code === EXP) && |
|
((nextCode >= 48 && nextCode <= 57) || |
|
((nextCode === plus || nextCode === minus) && |
|
nextNextCode >= 48 && |
|
nextNextCode <= 57)) |
|
) { |
|
pos += nextCode === plus || nextCode === minus ? 3 : 2; |
|
|
|
while (pos < length) { |
|
code = value.charCodeAt(pos); |
|
|
|
if (code < 48 || code > 57) { |
|
break; |
|
} |
|
|
|
pos += 1; |
|
} |
|
} |
|
|
|
return { |
|
number: value.slice(0, pos), |
|
unit: value.slice(pos) |
|
}; |
|
}; |
|
|
|
var parse = parse$1; |
|
var walk = walk$1; |
|
var stringify = stringify_1; |
|
|
|
function ValueParser(value) { |
|
if (this instanceof ValueParser) { |
|
this.nodes = parse(value); |
|
return this; |
|
} |
|
return new ValueParser(value); |
|
} |
|
|
|
ValueParser.prototype.toString = function() { |
|
return Array.isArray(this.nodes) ? stringify(this.nodes) : ""; |
|
}; |
|
|
|
ValueParser.prototype.walk = function(cb, bubble) { |
|
walk(this.nodes, cb, bubble); |
|
return this; |
|
}; |
|
|
|
ValueParser.unit = unit; |
|
|
|
ValueParser.walk = walk; |
|
|
|
ValueParser.stringify = stringify; |
|
|
|
var lib = ValueParser; |
|
|
|
exports.lib = lib;
|
|
|