diff --git a/CHANGELOG.md b/CHANGELOG.md index dddf276c821f..62b0f5a84243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix issue where some pseudo-element variants generated the wrong selector ([#10943](https://github.com/tailwindlabs/tailwindcss/pull/10943), [#10962](https://github.com/tailwindlabs/tailwindcss/pull/10962)) - Make font settings propagate into buttons, inputs, etc. ([#10940](https://github.com/tailwindlabs/tailwindcss/pull/10940)) +- Fix parsing of `theme()` inside `calc()` when there are no spaces around operators ([#11157](https://github.com/tailwindlabs/tailwindcss/pull/11157)) ### Added diff --git a/package-lock.json b/package-lock.json index ecdc2141e39c..4e61e7b51ccf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, @@ -27115,7 +27114,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "prettier": "^2.8.8", "resolve": "^1.22.2", "rimraf": "^5.0.0", diff --git a/package-lock.stable.json b/package-lock.stable.json index a80b9c55ca2c..4d422e6a27a3 100644 --- a/package-lock.stable.json +++ b/package-lock.stable.json @@ -29,7 +29,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, diff --git a/package.json b/package.json index e91533f0b0af..de13cc70b902 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, diff --git a/package.stable.json b/package.stable.json index 308740509d42..6022310d8ae1 100644 --- a/package.stable.json +++ b/package.stable.json @@ -87,7 +87,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, diff --git a/src/lib/evaluateTailwindFunctions.js b/src/lib/evaluateTailwindFunctions.js index 7e04ef428174..ff73f466c979 100644 --- a/src/lib/evaluateTailwindFunctions.js +++ b/src/lib/evaluateTailwindFunctions.js @@ -1,7 +1,7 @@ import dlv from 'dlv' import didYouMean from 'didyoumean' import transformThemeValue from '../util/transformThemeValue' -import parseValue from 'postcss-value-parser' +import parseValue from '../value-parser/index' import { normalizeScreens } from '../util/normalizeScreens' import buildMediaQuery from '../util/buildMediaQuery' import { toPath } from '../util/toPath' @@ -146,6 +146,9 @@ function resolveVNode(node, vNode, functions) { } function resolveFunctions(node, input, functions) { + let hasAnyFn = Object.keys(functions).some((fn) => input.includes(`${fn}(`)) + if (!hasAnyFn) return input + return parseValue(input) .walk((vNode) => { resolveVNode(node, vNode, functions) diff --git a/src/util/dataTypes.js b/src/util/dataTypes.js index 55cc005799f6..b4e07c689b54 100644 --- a/src/util/dataTypes.js +++ b/src/util/dataTypes.js @@ -49,10 +49,22 @@ export function normalize(value, isRoot = true) { value = value.trim() } - // Add spaces around operators inside math functions like calc() that do not follow an operator - // or '('. - value = value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => { + value = normalizeMathOperatorSpacing(value) + + return value +} + +/** + * Add spaces around operators inside math functions + * like calc() that do not follow an operator or '('. + * + * @param {string} value + * @returns {string} + */ +function normalizeMathOperatorSpacing(value) { + return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => { let vars = [] + return match .replace(/var\((--.+?)[,)]/g, (match, g1) => { vars.push(g1) @@ -61,8 +73,6 @@ export function normalize(value, isRoot = true) { .replace(/(-?\d*\.?\d(?!\b-\d.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, '$1 $2 ') .replace(placeholderRe, () => vars.shift()) }) - - return value } export function url(value) { diff --git a/src/value-parser/LICENSE b/src/value-parser/LICENSE new file mode 100644 index 000000000000..6dcaefcbed35 --- /dev/null +++ b/src/value-parser/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) Bogdan Chadkin + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/value-parser/README.md b/src/value-parser/README.md new file mode 100644 index 000000000000..ea9e20291d2e --- /dev/null +++ b/src/value-parser/README.md @@ -0,0 +1,3 @@ +# postcss-value-parser (forked + inlined) + +This is a customized version of of [PostCSS Value Parser](https://github.com/TrySound/postcss-value-parser) to fix some bugs around parsing CSS functions. diff --git a/src/value-parser/index.d.ts b/src/value-parser/index.d.ts new file mode 100644 index 000000000000..0c0c4b991123 --- /dev/null +++ b/src/value-parser/index.d.ts @@ -0,0 +1,177 @@ +declare namespace postcssValueParser { + interface BaseNode { + /** + * The offset, inclusive, inside the CSS value at which the node starts. + */ + sourceIndex: number + + /** + * The offset, exclusive, inside the CSS value at which the node ends. + */ + sourceEndIndex: number + + /** + * The node's characteristic value + */ + value: string + } + + interface ClosableNode { + /** + * Whether the parsed CSS value ended before the node was properly closed + */ + unclosed?: true + } + + interface AdjacentAwareNode { + /** + * The token at the start of the node + */ + before: string + + /** + * The token at the end of the node + */ + after: string + } + + interface CommentNode extends BaseNode, ClosableNode { + type: 'comment' + } + + interface DivNode extends BaseNode, AdjacentAwareNode { + type: 'div' + } + + interface FunctionNode extends BaseNode, ClosableNode, AdjacentAwareNode { + type: 'function' + + /** + * Nodes inside the function + */ + nodes: Node[] + } + + interface SpaceNode extends BaseNode { + type: 'space' + } + + interface StringNode extends BaseNode, ClosableNode { + type: 'string' + + /** + * The quote type delimiting the string + */ + quote: '"' | "'" + } + + interface UnicodeRangeNode extends BaseNode { + type: 'unicode-range' + } + + interface WordNode extends BaseNode { + type: 'word' + } + + /** + * Any node parsed from a CSS value + */ + type Node = + | CommentNode + | DivNode + | FunctionNode + | SpaceNode + | StringNode + | UnicodeRangeNode + | WordNode + + interface CustomStringifierCallback { + /** + * @param node The node to stringify + * @returns The serialized CSS representation of the node + */ + (nodes: Node): string | undefined + } + + interface WalkCallback { + /** + * @param node The currently visited node + * @param index The index of the node in the series of parsed nodes + * @param nodes The series of parsed nodes + * @returns Returning `false` will prevent traversal of descendant nodes (only applies if `bubble` was set to `true` in the `walk()` call) + */ + (node: Node, index: number, nodes: Node[]): void | boolean + } + + /** + * A CSS dimension, decomposed into its numeric and unit parts + */ + interface Dimension { + number: string + unit: string + } + + /** + * A wrapper around a parsed CSS value that allows for inspecting and walking nodes + */ + interface ParsedValue { + /** + * The series of parsed nodes + */ + nodes: Node[] + + /** + * Walk all parsed nodes, applying a callback + * + * @param callback A visitor callback that will be executed for each node + * @param bubble When set to `true`, walking will be done inside-out instead of outside-in + */ + walk(callback: WalkCallback, bubble?: boolean): this + } + + interface ValueParser { + /** + * Decompose a CSSĀ dimension into its numeric and unit part + * + * @param value The dimension to decompose + * @returns An object representing `number` and `unit` part of the dimension or `false` if the decomposing fails + */ + unit(value: string): Dimension | false + + /** + * Serialize a series of nodes into a CSS value + * + * @param nodes The nodes to stringify + * @param custom A custom stringifier callback + * @returns The generated CSS value + */ + stringify(nodes: Node | Node[], custom?: CustomStringifierCallback): string + + /** + * Walk a series of nodes, applying a callback + * + * @param nodes The nodes to walk + * @param callback A visitor callback that will be executed for each node + * @param bubble When set to `true`, walking will be done inside-out instead of outside-in + */ + walk(nodes: Node[], callback: WalkCallback, bubble?: boolean): void + + /** + * Parse a CSS value into a series of nodes to operate on + * + * @param value The value to parse + */ + new (value: string): ParsedValue + + /** + * Parse a CSS value into a series of nodes to operate on + * + * @param value The value to parse + */ + (value: string): ParsedValue + } +} + +declare const postcssValueParser: postcssValueParser.ValueParser + +export = postcssValueParser diff --git a/src/value-parser/index.js b/src/value-parser/index.js new file mode 100644 index 000000000000..5587ccfa3822 --- /dev/null +++ b/src/value-parser/index.js @@ -0,0 +1,28 @@ +var parse = require('./parse') +var walk = require('./walk') +var stringify = require('./stringify') + +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 = require('./unit') + +ValueParser.walk = walk + +ValueParser.stringify = stringify + +module.exports = ValueParser diff --git a/src/value-parser/parse.js b/src/value-parser/parse.js new file mode 100644 index 000000000000..445599664225 --- /dev/null +++ b/src/value-parser/parse.js @@ -0,0 +1,303 @@ +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 + prev.sourceEndIndex += token.length + } else if ( + code === comma || + code === colon || + (code === slash && + value.charCodeAt(next + 1) !== star && + (!parent || (parent && parent.type === 'function' && false))) + ) { + 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' && true) { + 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' && true) || + (code === slash && parent.type === 'function' && true) || + (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, + 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 +} diff --git a/src/value-parser/stringify.js b/src/value-parser/stringify.js new file mode 100644 index 000000000000..c95890610a67 --- /dev/null +++ b/src/value-parser/stringify.js @@ -0,0 +1,41 @@ +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(node.nodes, custom) + if (type !== 'function') { + return buf + } + return value + '(' + (node.before || '') + buf + (node.after || '') + (node.unclosed ? '' : ')') + } + return value +} + +function stringify(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) +} + +module.exports = stringify diff --git a/src/value-parser/unit.js b/src/value-parser/unit.js new file mode 100644 index 000000000000..42d6cd3a7265 --- /dev/null +++ b/src/value-parser/unit.js @@ -0,0 +1,118 @@ +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 +module.exports = 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), + } +} diff --git a/src/value-parser/walk.js b/src/value-parser/walk.js new file mode 100644 index 000000000000..dd20a439259a --- /dev/null +++ b/src/value-parser/walk.js @@ -0,0 +1,18 @@ +module.exports = 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) + } + } +} diff --git a/tests/evaluateTailwindFunctions.test.js b/tests/evaluateTailwindFunctions.test.js index e4085e03e3b4..34eb5adc0ab2 100644 --- a/tests/evaluateTailwindFunctions.test.js +++ b/tests/evaluateTailwindFunctions.test.js @@ -1383,5 +1383,36 @@ crosscheck(({ stable, oxide }) => { // 4. But we've not received any further logs about it expect().toHaveBeenWarnedWith(['invalid-theme-key-in-class']) }) + + test('it works mayhaps', async () => { + let input = css` + .test { + /* prettier-ignore */ + inset: calc(-1 * (2*theme("spacing.4"))); + /* prettier-ignore */ + padding: calc(-1 * (2* theme("spacing.4"))); + } + ` + + let output = css` + .test { + /* prettier-ignore */ + inset: calc(-1 * (2*1rem)); + /* prettier-ignore */ + padding: calc(-1 * (2* 1rem)); + } + ` + + return run(input, { + theme: { + spacing: { + 4: '1rem', + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) + }) }) }) diff --git a/tests/source-maps.test.js b/tests/source-maps.test.js index a19aea687bdd..e0839bf6ef59 100644 --- a/tests/source-maps.test.js +++ b/tests/source-maps.test.js @@ -260,145 +260,143 @@ crosscheck(({ stable, oxide }) => { '2:6-20 -> 304:2-12', '2:20 -> 305:0', '2:6 -> 307:0', - '2:20 -> 309:1', - '2:6 -> 310:0', - '2:6-20 -> 311:2-12', - '2:20 -> 312:0', - '2:6 -> 314:0', - '2:20 -> 316:1', - '2:6 -> 318:0', - '2:6-20 -> 319:2-18', - '2:20 -> 320:0', - '2:6 -> 322:0', - '2:20 -> 325:1', - '2:6 -> 327:0', - '2:6-20 -> 329:2-20', - '2:6-20 -> 330:2-24', - '2:20 -> 331:0', - '2:6 -> 333:0', - '2:20 -> 335:1', - '2:6 -> 337:0', - '2:6-20 -> 339:2-17', - '2:20 -> 340:0', + '2:6-20 -> 308:2-12', + '2:20 -> 309:0', + '2:6 -> 311:0', + '2:20 -> 313:1', + '2:6 -> 315:0', + '2:6-20 -> 316:2-18', + '2:20 -> 317:0', + '2:6 -> 319:0', + '2:20 -> 322:1', + '2:6 -> 324:0', + '2:6-20 -> 326:2-20', + '2:6-20 -> 327:2-24', + '2:20 -> 328:0', + '2:6 -> 330:0', + '2:20 -> 332:1', + '2:6 -> 334:0', + '2:6-20 -> 336:2-17', + '2:20 -> 337:0', + '2:6 -> 339:0', + '2:20 -> 341:1', '2:6 -> 342:0', - '2:20 -> 344:1', - '2:6 -> 345:0', - '2:6-20 -> 346:2-17', - '2:20 -> 347:0', - '2:6 -> 349:0', - '2:20 -> 353:1', - '2:6 -> 355:0', - '2:6-20 -> 363:2-24', - '2:6-20 -> 364:2-32', - '2:20 -> 365:0', - '2:6 -> 367:0', - '2:20 -> 369:1', - '2:6 -> 371:0', - '2:6-20 -> 373:2-17', - '2:6-20 -> 374:2-14', - '2:20 -> 375:0', - '2:6-20 -> 377:0-72', - '2:6 -> 378:0', - '2:6-20 -> 379:2-15', - '2:20 -> 380:0', - '2:6 -> 382:0', - '2:6-20 -> 383:2-26', - '2:6-20 -> 384:2-26', - '2:6-20 -> 385:2-21', - '2:6-20 -> 386:2-21', - '2:6-20 -> 387:2-16', - '2:6-20 -> 388:2-16', - '2:6-20 -> 389:2-16', - '2:6-20 -> 390:2-17', - '2:6-20 -> 391:2-17', - '2:6-20 -> 392:2-15', - '2:6-20 -> 393:2-15', - '2:6-20 -> 394:2-20', - '2:6-20 -> 395:2-40', - '2:6-20 -> 396:2-32', - '2:6-20 -> 397:2-31', - '2:6-20 -> 398:2-30', - '2:6-20 -> 399:2-17', - '2:6-20 -> 400:2-22', - '2:6-20 -> 401:2-24', - '2:6-20 -> 402:2-25', - '2:6-20 -> 403:2-26', - '2:6-20 -> 404:2-20', - '2:6-20 -> 405:2-29', - '2:6-20 -> 406:2-30', - '2:6-20 -> 407:2-40', - '2:6-20 -> 408:2-36', - '2:6-20 -> 409:2-29', - '2:6-20 -> 410:2-24', - '2:6-20 -> 411:2-32', - '2:6-20 -> 412:2-14', + '2:6-20 -> 343:2-17', + '2:20 -> 344:0', + '2:6 -> 346:0', + '2:20 -> 350:1', + '2:6 -> 352:0', + '2:6-20 -> 360:2-24', + '2:6-20 -> 361:2-32', + '2:20 -> 362:0', + '2:6 -> 364:0', + '2:20 -> 366:1', + '2:6 -> 368:0', + '2:6-20 -> 370:2-17', + '2:6-20 -> 371:2-14', + '2:20 -> 372:0', + '2:6-20 -> 374:0-72', + '2:6 -> 375:0', + '2:6-20 -> 376:2-15', + '2:20 -> 377:0', + '2:6 -> 379:0', + '2:6-20 -> 380:2-26', + '2:6-20 -> 381:2-26', + '2:6-20 -> 382:2-21', + '2:6-20 -> 383:2-21', + '2:6-20 -> 384:2-16', + '2:6-20 -> 385:2-16', + '2:6-20 -> 386:2-16', + '2:6-20 -> 387:2-17', + '2:6-20 -> 388:2-17', + '2:6-20 -> 389:2-15', + '2:6-20 -> 390:2-15', + '2:6-20 -> 391:2-20', + '2:6-20 -> 392:2-40', + '2:6-20 -> 393:2-32', + '2:6-20 -> 394:2-31', + '2:6-20 -> 395:2-30', + '2:6-20 -> 396:2-17', + '2:6-20 -> 397:2-22', + '2:6-20 -> 398:2-24', + '2:6-20 -> 399:2-25', + '2:6-20 -> 400:2-26', + '2:6-20 -> 401:2-20', + '2:6-20 -> 402:2-29', + '2:6-20 -> 403:2-30', + '2:6-20 -> 404:2-40', + '2:6-20 -> 405:2-36', + '2:6-20 -> 406:2-29', + '2:6-20 -> 407:2-24', + '2:6-20 -> 408:2-32', + '2:6-20 -> 409:2-14', + '2:6-20 -> 410:2-20', + '2:6-20 -> 411:2-18', + '2:6-20 -> 412:2-19', '2:6-20 -> 413:2-20', - '2:6-20 -> 414:2-18', - '2:6-20 -> 415:2-19', - '2:6-20 -> 416:2-20', - '2:6-20 -> 417:2-16', - '2:6-20 -> 418:2-18', - '2:6-20 -> 419:2-15', - '2:6-20 -> 420:2-21', - '2:6-20 -> 421:2-23', + '2:6-20 -> 414:2-16', + '2:6-20 -> 415:2-18', + '2:6-20 -> 416:2-15', + '2:6-20 -> 417:2-21', + '2:6-20 -> 418:2-23', + '2:6-20 -> 419:2-29', + '2:6-20 -> 420:2-27', + '2:6-20 -> 421:2-28', '2:6-20 -> 422:2-29', - '2:6-20 -> 423:2-27', - '2:6-20 -> 424:2-28', - '2:6-20 -> 425:2-29', - '2:6-20 -> 426:2-25', - '2:6-20 -> 427:2-26', - '2:6-20 -> 428:2-27', - '2:6 -> 429:2', - '2:20 -> 430:0', - '2:6 -> 432:0', - '2:6-20 -> 433:2-26', - '2:6-20 -> 434:2-26', - '2:6-20 -> 435:2-21', - '2:6-20 -> 436:2-21', - '2:6-20 -> 437:2-16', - '2:6-20 -> 438:2-16', - '2:6-20 -> 439:2-16', - '2:6-20 -> 440:2-17', - '2:6-20 -> 441:2-17', - '2:6-20 -> 442:2-15', - '2:6-20 -> 443:2-15', - '2:6-20 -> 444:2-20', - '2:6-20 -> 445:2-40', - '2:6-20 -> 446:2-32', - '2:6-20 -> 447:2-31', - '2:6-20 -> 448:2-30', - '2:6-20 -> 449:2-17', - '2:6-20 -> 450:2-22', - '2:6-20 -> 451:2-24', - '2:6-20 -> 452:2-25', - '2:6-20 -> 453:2-26', - '2:6-20 -> 454:2-20', - '2:6-20 -> 455:2-29', - '2:6-20 -> 456:2-30', - '2:6-20 -> 457:2-40', - '2:6-20 -> 458:2-36', - '2:6-20 -> 459:2-29', - '2:6-20 -> 460:2-24', - '2:6-20 -> 461:2-32', - '2:6-20 -> 462:2-14', + '2:6-20 -> 423:2-25', + '2:6-20 -> 424:2-26', + '2:6-20 -> 425:2-27', + '2:6 -> 426:2', + '2:20 -> 427:0', + '2:6 -> 429:0', + '2:6-20 -> 430:2-26', + '2:6-20 -> 431:2-26', + '2:6-20 -> 432:2-21', + '2:6-20 -> 433:2-21', + '2:6-20 -> 434:2-16', + '2:6-20 -> 435:2-16', + '2:6-20 -> 436:2-16', + '2:6-20 -> 437:2-17', + '2:6-20 -> 438:2-17', + '2:6-20 -> 439:2-15', + '2:6-20 -> 440:2-15', + '2:6-20 -> 441:2-20', + '2:6-20 -> 442:2-40', + '2:6-20 -> 443:2-32', + '2:6-20 -> 444:2-31', + '2:6-20 -> 445:2-30', + '2:6-20 -> 446:2-17', + '2:6-20 -> 447:2-22', + '2:6-20 -> 448:2-24', + '2:6-20 -> 449:2-25', + '2:6-20 -> 450:2-26', + '2:6-20 -> 451:2-20', + '2:6-20 -> 452:2-29', + '2:6-20 -> 453:2-30', + '2:6-20 -> 454:2-40', + '2:6-20 -> 455:2-36', + '2:6-20 -> 456:2-29', + '2:6-20 -> 457:2-24', + '2:6-20 -> 458:2-32', + '2:6-20 -> 459:2-14', + '2:6-20 -> 460:2-20', + '2:6-20 -> 461:2-18', + '2:6-20 -> 462:2-19', '2:6-20 -> 463:2-20', - '2:6-20 -> 464:2-18', - '2:6-20 -> 465:2-19', - '2:6-20 -> 466:2-20', - '2:6-20 -> 467:2-16', - '2:6-20 -> 468:2-18', - '2:6-20 -> 469:2-15', - '2:6-20 -> 470:2-21', - '2:6-20 -> 471:2-23', + '2:6-20 -> 464:2-16', + '2:6-20 -> 465:2-18', + '2:6-20 -> 466:2-15', + '2:6-20 -> 467:2-21', + '2:6-20 -> 468:2-23', + '2:6-20 -> 469:2-29', + '2:6-20 -> 470:2-27', + '2:6-20 -> 471:2-28', '2:6-20 -> 472:2-29', - '2:6-20 -> 473:2-27', - '2:6-20 -> 474:2-28', - '2:6-20 -> 475:2-29', - '2:6-20 -> 476:2-25', - '2:6-20 -> 477:2-26', - '2:6-20 -> 478:2-27', - '2:6 -> 479:2', - '2:20 -> 480:0', + '2:6-20 -> 473:2-25', + '2:6-20 -> 474:2-26', + '2:6-20 -> 475:2-27', + '2:6 -> 476:2', + '2:20 -> 477:0', ]) })