', start: 8, end: 13},\r\n * // {name: 'match', value: '
an
', start: 13, end: 27},\r\n * // {name: 'right', value: '
', start: 27, end: 33},\r\n * // {name: 'between', value: ' example', start: 33, end: 41}\r\n * // ]\r\n *\r\n * // Omitting unneeded parts with null valueNames, and using escapeChar\r\n * str = '...{1}.\\\\{{function(x,y){return {y:x}}}';\r\n * XRegExp.matchRecursive(str, '{', '}', 'g', {\r\n * valueNames: ['literal', null, 'value', null],\r\n * escapeChar: '\\\\'\r\n * });\r\n * // -> [\r\n * // {name: 'literal', value: '...', start: 0, end: 3},\r\n * // {name: 'value', value: '1', start: 4, end: 5},\r\n * // {name: 'literal', value: '.\\\\{', start: 6, end: 9},\r\n * // {name: 'value', value: 'function(x,y){return {y:x}}', start: 10, end: 37}\r\n * // ]\r\n *\r\n * // Sticky mode via flag y\r\n * str = '<1><<<2>>><3>4<5>';\r\n * XRegExp.matchRecursive(str, '<', '>', 'gy');\r\n * // -> ['1', '<<2>>', '3']\r\n */\r\n XRegExp.matchRecursive = function(str, left, right, flags, options) {\r\n flags = flags || '';\r\n options = options || {};\r\n var global = flags.indexOf('g') > -1;\r\n var sticky = flags.indexOf('y') > -1;\r\n // Flag `y` is controlled internally\r\n var basicFlags = flags.replace(/y/g, '');\r\n var escapeChar = options.escapeChar;\r\n var vN = options.valueNames;\r\n var output = [];\r\n var openTokens = 0;\r\n var delimStart = 0;\r\n var delimEnd = 0;\r\n var lastOuterEnd = 0;\r\n var outerStart;\r\n var innerStart;\r\n var leftMatch;\r\n var rightMatch;\r\n var esc;\r\n left = XRegExp(left, basicFlags);\r\n right = XRegExp(right, basicFlags);\r\n\r\n if (escapeChar) {\r\n if (escapeChar.length > 1) {\r\n throw new Error('Cannot use more than one escape character');\r\n }\r\n escapeChar = XRegExp.escape(escapeChar);\r\n // Example of concatenated `esc` regex:\r\n // `escapeChar`: '%'\r\n // `left`: '<'\r\n // `right`: '>'\r\n // Regex is: /(?:%[\\S\\s]|(?:(?!<|>)[^%])+)+/\r\n esc = new RegExp(\r\n '(?:' + escapeChar + '[\\\\S\\\\s]|(?:(?!' +\r\n // Using `XRegExp.union` safely rewrites backreferences in `left` and `right`.\r\n // Intentionally not passing `basicFlags` to `XRegExp.union` since any syntax\r\n // transformation resulting from those flags was already applied to `left` and\r\n // `right` when they were passed through the XRegExp constructor above.\r\n XRegExp.union([left, right], '', {conjunction: 'or'}).source +\r\n ')[^' + escapeChar + '])+)+',\r\n // Flags `gy` not needed here\r\n flags.replace(/[^imu]+/g, '')\r\n );\r\n }\r\n\r\n while (true) {\r\n // If using an escape character, advance to the delimiter's next starting position,\r\n // skipping any escaped characters in between\r\n if (escapeChar) {\r\n delimEnd += (XRegExp.exec(str, esc, delimEnd, 'sticky') || [''])[0].length;\r\n }\r\n leftMatch = XRegExp.exec(str, left, delimEnd);\r\n rightMatch = XRegExp.exec(str, right, delimEnd);\r\n // Keep the leftmost match only\r\n if (leftMatch && rightMatch) {\r\n if (leftMatch.index <= rightMatch.index) {\r\n rightMatch = null;\r\n } else {\r\n leftMatch = null;\r\n }\r\n }\r\n // Paths (LM: leftMatch, RM: rightMatch, OT: openTokens):\r\n // LM | RM | OT | Result\r\n // 1 | 0 | 1 | loop\r\n // 1 | 0 | 0 | loop\r\n // 0 | 1 | 1 | loop\r\n // 0 | 1 | 0 | throw\r\n // 0 | 0 | 1 | throw\r\n // 0 | 0 | 0 | break\r\n // The paths above don't include the sticky mode special case. The loop ends after the\r\n // first completed match if not `global`.\r\n if (leftMatch || rightMatch) {\r\n delimStart = (leftMatch || rightMatch).index;\r\n delimEnd = delimStart + (leftMatch || rightMatch)[0].length;\r\n } else if (!openTokens) {\r\n break;\r\n }\r\n if (sticky && !openTokens && delimStart > lastOuterEnd) {\r\n break;\r\n }\r\n if (leftMatch) {\r\n if (!openTokens) {\r\n outerStart = delimStart;\r\n innerStart = delimEnd;\r\n }\r\n ++openTokens;\r\n } else if (rightMatch && openTokens) {\r\n if (!--openTokens) {\r\n if (vN) {\r\n if (vN[0] && outerStart > lastOuterEnd) {\r\n output.push(row(vN[0], str.slice(lastOuterEnd, outerStart), lastOuterEnd, outerStart));\r\n }\r\n if (vN[1]) {\r\n output.push(row(vN[1], str.slice(outerStart, innerStart), outerStart, innerStart));\r\n }\r\n if (vN[2]) {\r\n output.push(row(vN[2], str.slice(innerStart, delimStart), innerStart, delimStart));\r\n }\r\n if (vN[3]) {\r\n output.push(row(vN[3], str.slice(delimStart, delimEnd), delimStart, delimEnd));\r\n }\r\n } else {\r\n output.push(str.slice(innerStart, delimStart));\r\n }\r\n lastOuterEnd = delimEnd;\r\n if (!global) {\r\n break;\r\n }\r\n }\r\n } else {\r\n throw new Error('Unbalanced delimiter found in string');\r\n }\r\n // If the delimiter matched an empty string, avoid an infinite loop\r\n if (delimStart === delimEnd) {\r\n ++delimEnd;\r\n }\r\n }\r\n\r\n if (global && !sticky && vN && vN[0] && str.length > lastOuterEnd) {\r\n output.push(row(vN[0], str.slice(lastOuterEnd), lastOuterEnd, str.length));\r\n }\r\n\r\n return output;\r\n };\r\n\r\n};\r\n\n},{}],3:[function(require,module,exports){\n/*!\r\n * XRegExp Unicode Base 3.2.0\r\n *