biblatex-csl-converter
Version:
Bibliography format converter: BibLaTeX, BibTeX, CSL-JSON, RIS, ENW, EndNote XML, Citavi, DOCX citations, ODT citations — parse, convert, and export with round-trip fidelity
1 lines • 627 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../src/const.ts","../src/edtf-parser.ts","../src/export/const.ts","../src/export/biblatex.ts","../node_modules/xregexp/src/xregexp.js","../node_modules/xregexp/src/addons/build.js","../node_modules/xregexp/tools/output/categories.js","../node_modules/xregexp/tools/output/properties.js","../node_modules/xregexp/tools/output/scripts.js","../node_modules/xregexp/src/index.js","../node_modules/xregexp/src/addons/matchrecursive.js","../node_modules/xregexp/src/addons/unicode-base.js","../node_modules/xregexp/src/addons/unicode-categories.js","../node_modules/xregexp/src/addons/unicode-properties.js","../node_modules/xregexp/src/addons/unicode-scripts.js","../src/export/csl/sentence-caser.ts","../src/export/csl/index.ts","../src/i18n/locales.ts","../src/i18n/index.ts","../src/import/const.ts","../src/import/group-parser.ts","../src/import/literal-parser.ts","../src/import/name-parser.ts","../src/import/tools.ts","../src/import/biblatex.ts","../src/import/citavi.ts","../src/import/citavi-xml.ts","../src/import/csl.ts","../src/import/docx-native.ts","../src/import/endnote.ts","../src/import/docx-citations.ts","../src/import/enw.ts","../src/import/nbib.ts","../src/import/odt-native.ts","../src/import/odt-citations.ts","../src/import/ris.ts","../src/import/sniffer.ts","../src/unescape-csl.ts"],"sourcesContent":[null,null,null,null,"/*!\n * XRegExp 5.1.2\n * <xregexp.com>\n * Steven Levithan (c) 2007-present MIT License\n */\n\n/**\n * XRegExp provides augmented, extensible regular expressions. You get additional regex syntax and\n * flags, beyond what browsers support natively. XRegExp is also a regex utility belt with tools to\n * make your client-side grepping simpler and more powerful, while freeing you from related\n * cross-browser inconsistencies.\n */\n\n// ==--------------------------==\n// Private stuff\n// ==--------------------------==\n\n// Property name used for extended regex instance data\nconst REGEX_DATA = 'xregexp';\n// Optional features that can be installed and uninstalled\nconst features = {\n astral: false,\n namespacing: true\n};\n// Storage for fixed/extended native methods\nconst fixed = {};\n// Storage for regexes cached by `XRegExp.cache`\nlet regexCache = Object.create(null);\n// Storage for pattern details cached by the `XRegExp` constructor\nlet patternCache = Object.create(null);\n// Storage for regex syntax tokens added internally or by `XRegExp.addToken`\nconst tokens = [];\n// Token scopes\nconst defaultScope = 'default';\nconst classScope = 'class';\n// Regexes that match native regex syntax, including octals\nconst nativeTokens = {\n // Any native multicharacter token in default scope, or any single character\n 'default': /\\\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\\d*|x[\\dA-Fa-f]{2}|u(?:[\\dA-Fa-f]{4}|{[\\dA-Fa-f]+})|c[A-Za-z]|[\\s\\S])|\\(\\?(?:[:=!]|<[=!])|[?*+]\\?|{\\d+(?:,\\d*)?}\\??|[\\s\\S]/,\n // Any native multicharacter token in character class scope, or any single character\n 'class': /\\\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\\dA-Fa-f]{2}|u(?:[\\dA-Fa-f]{4}|{[\\dA-Fa-f]+})|c[A-Za-z]|[\\s\\S])|[\\s\\S]/\n};\n// Any backreference or dollar-prefixed character in replacement strings\nconst replacementToken = /\\$(?:\\{([^\\}]+)\\}|<([^>]+)>|(\\d\\d?|[\\s\\S]?))/g;\n// Check for correct `exec` handling of nonparticipating capturing groups\nconst correctExecNpcg = /()??/.exec('')[1] === undefined;\n// Check for ES6 `flags` prop support\nconst hasFlagsProp = /x/.flags !== undefined;\n\nfunction hasNativeFlag(flag) {\n // Can't check based on the presence of properties/getters since browsers might support such\n // properties even when they don't support the corresponding flag in regex construction (tested\n // in Chrome 48, where `'unicode' in /x/` is true but trying to construct a regex with flag `u`\n // throws an error)\n let isSupported = true;\n try {\n // Can't use regex literals for testing even in a `try` because regex literals with\n // unsupported flags cause a compilation error in IE\n new RegExp('', flag);\n\n // Work around a broken/incomplete IE11 polyfill for sticky introduced in core-js 3.6.0\n if (flag === 'y') {\n // Using function to avoid babel transform to regex literal\n const gy = (() => 'gy')();\n const incompleteY = '.a'.replace(new RegExp('a', gy), '.') === '..';\n if (incompleteY) {\n isSupported = false;\n }\n }\n } catch (exception) {\n isSupported = false;\n }\n return isSupported;\n}\n// Check for ES2021 `d` flag support\nconst hasNativeD = hasNativeFlag('d');\n// Check for ES2018 `s` flag support\nconst hasNativeS = hasNativeFlag('s');\n// Check for ES6 `u` flag support\nconst hasNativeU = hasNativeFlag('u');\n// Check for ES6 `y` flag support\nconst hasNativeY = hasNativeFlag('y');\n// Tracker for known flags, including addon flags\nconst registeredFlags = {\n d: hasNativeD,\n g: true,\n i: true,\n m: true,\n s: hasNativeS,\n u: hasNativeU,\n y: hasNativeY\n};\n// Flags to remove when passing to native `RegExp` constructor\nconst nonnativeFlags = hasNativeS ? /[^dgimsuy]+/g : /[^dgimuy]+/g;\n\n/**\n * Attaches extended data and `XRegExp.prototype` properties to a regex object.\n *\n * @private\n * @param {RegExp} regex Regex to augment.\n * @param {Array} captureNames Array with capture names, or `null`.\n * @param {String} xSource XRegExp pattern used to generate `regex`, or `null` if N/A.\n * @param {String} xFlags XRegExp flags used to generate `regex`, or `null` if N/A.\n * @param {Boolean} [isInternalOnly=false] Whether the regex will be used only for internal\n * operations, and never exposed to users. For internal-only regexes, we can improve perf by\n * skipping some operations like attaching `XRegExp.prototype` properties.\n * @returns {!RegExp} Augmented regex.\n */\nfunction augment(regex, captureNames, xSource, xFlags, isInternalOnly) {\n regex[REGEX_DATA] = {\n captureNames\n };\n\n if (isInternalOnly) {\n return regex;\n }\n\n // Can't auto-inherit these since the XRegExp constructor returns a nonprimitive value\n if (regex.__proto__) {\n regex.__proto__ = XRegExp.prototype;\n } else {\n for (const p in XRegExp.prototype) {\n // An `XRegExp.prototype.hasOwnProperty(p)` check wouldn't be worth it here, since this\n // is performance sensitive, and enumerable `Object.prototype` or `RegExp.prototype`\n // extensions exist on `regex.prototype` anyway\n regex[p] = XRegExp.prototype[p];\n }\n }\n\n regex[REGEX_DATA].source = xSource;\n // Emulate the ES6 `flags` prop by ensuring flags are in alphabetical order\n regex[REGEX_DATA].flags = xFlags ? xFlags.split('').sort().join('') : xFlags;\n\n return regex;\n}\n\n/**\n * Removes any duplicate characters from the provided string.\n *\n * @private\n * @param {String} str String to remove duplicate characters from.\n * @returns {string} String with any duplicate characters removed.\n */\nfunction clipDuplicates(str) {\n return str.replace(/([\\s\\S])(?=[\\s\\S]*\\1)/g, '');\n}\n\n/**\n * Copies a regex object while preserving extended data and augmenting with `XRegExp.prototype`\n * properties. The copy has a fresh `lastIndex` property (set to zero). Allows adding and removing\n * flags g and y while copying the regex.\n *\n * @private\n * @param {RegExp} regex Regex to copy.\n * @param {Object} [options] Options object with optional properties:\n * - `addG` {Boolean} Add flag g while copying the regex.\n * - `addY` {Boolean} Add flag y while copying the regex.\n * - `removeG` {Boolean} Remove flag g while copying the regex.\n * - `removeY` {Boolean} Remove flag y while copying the regex.\n * - `isInternalOnly` {Boolean} Whether the copied regex will be used only for internal\n * operations, and never exposed to users. For internal-only regexes, we can improve perf by\n * skipping some operations like attaching `XRegExp.prototype` properties.\n * - `source` {String} Overrides `<regex>.source`, for special cases.\n * @returns {RegExp} Copy of the provided regex, possibly with modified flags.\n */\nfunction copyRegex(regex, options) {\n if (!XRegExp.isRegExp(regex)) {\n throw new TypeError('Type RegExp expected');\n }\n\n const xData = regex[REGEX_DATA] || {};\n let flags = getNativeFlags(regex);\n let flagsToAdd = '';\n let flagsToRemove = '';\n let xregexpSource = null;\n let xregexpFlags = null;\n\n options = options || {};\n\n if (options.removeG) {flagsToRemove += 'g';}\n if (options.removeY) {flagsToRemove += 'y';}\n if (flagsToRemove) {\n flags = flags.replace(new RegExp(`[${flagsToRemove}]+`, 'g'), '');\n }\n\n if (options.addG) {flagsToAdd += 'g';}\n if (options.addY) {flagsToAdd += 'y';}\n if (flagsToAdd) {\n flags = clipDuplicates(flags + flagsToAdd);\n }\n\n if (!options.isInternalOnly) {\n if (xData.source !== undefined) {\n xregexpSource = xData.source;\n }\n // null or undefined; don't want to add to `flags` if the previous value was null, since\n // that indicates we're not tracking original precompilation flags\n if (xData.flags != null) {\n // Flags are only added for non-internal regexes by `XRegExp.globalize`. Flags are never\n // removed for non-internal regexes, so don't need to handle it\n xregexpFlags = flagsToAdd ? clipDuplicates(xData.flags + flagsToAdd) : xData.flags;\n }\n }\n\n // Augment with `XRegExp.prototype` properties, but use the native `RegExp` constructor to avoid\n // searching for special tokens. That would be wrong for regexes constructed by `RegExp`, and\n // unnecessary for regexes constructed by `XRegExp` because the regex has already undergone the\n // translation to native regex syntax\n regex = augment(\n new RegExp(options.source || regex.source, flags),\n hasNamedCapture(regex) ? xData.captureNames.slice(0) : null,\n xregexpSource,\n xregexpFlags,\n options.isInternalOnly\n );\n\n return regex;\n}\n\n/**\n * Converts hexadecimal to decimal.\n *\n * @private\n * @param {String} hex\n * @returns {number}\n */\nfunction dec(hex) {\n return parseInt(hex, 16);\n}\n\n/**\n * Returns a pattern that can be used in a native RegExp in place of an ignorable token such as an\n * inline comment or whitespace with flag x. This is used directly as a token handler function\n * passed to `XRegExp.addToken`.\n *\n * @private\n * @param {String} match Match arg of `XRegExp.addToken` handler\n * @param {String} scope Scope arg of `XRegExp.addToken` handler\n * @param {String} flags Flags arg of `XRegExp.addToken` handler\n * @returns {string} Either '' or '(?:)', depending on which is needed in the context of the match.\n */\nfunction getContextualTokenSeparator(match, scope, flags) {\n const matchEndPos = match.index + match[0].length;\n const precedingChar = match.input[match.index - 1];\n const followingChar = match.input[matchEndPos];\n if (\n // No need to separate tokens if at the beginning or end of a group, before or after a\n // group, or before or after a `|`\n /^[()|]$/.test(precedingChar) ||\n /^[()|]$/.test(followingChar) ||\n // No need to separate tokens if at the beginning or end of the pattern\n match.index === 0 ||\n matchEndPos === match.input.length ||\n // No need to separate tokens if at the beginning of a noncapturing group or lookaround.\n // Looks only at the last 4 chars (at most) for perf when constructing long regexes.\n /\\(\\?(?:[:=!]|<[=!])$/.test(match.input.substring(match.index - 4, match.index)) ||\n // Avoid separating tokens when the following token is a quantifier\n isQuantifierNext(match.input, matchEndPos, flags)\n ) {\n return '';\n }\n // Keep tokens separated. This avoids e.g. inadvertedly changing `\\1 1` or `\\1(?#)1` to `\\11`.\n // This also ensures all tokens remain as discrete atoms, e.g. it prevents converting the\n // syntax error `(? :` into `(?:`.\n return '(?:)';\n}\n\n/**\n * Returns native `RegExp` flags used by a regex object.\n *\n * @private\n * @param {RegExp} regex Regex to check.\n * @returns {string} Native flags in use.\n */\nfunction getNativeFlags(regex) {\n return hasFlagsProp ?\n regex.flags :\n // Explicitly using `RegExp.prototype.toString` (rather than e.g. `String` or concatenation\n // with an empty string) allows this to continue working predictably when\n // `XRegExp.proptotype.toString` is overridden\n /\\/([a-z]*)$/i.exec(RegExp.prototype.toString.call(regex))[1];\n}\n\n/**\n * Determines whether a regex has extended instance data used to track capture names.\n *\n * @private\n * @param {RegExp} regex Regex to check.\n * @returns {boolean} Whether the regex uses named capture.\n */\nfunction hasNamedCapture(regex) {\n return !!(regex[REGEX_DATA] && regex[REGEX_DATA].captureNames);\n}\n\n/**\n * Converts decimal to hexadecimal.\n *\n * @private\n * @param {Number|String} dec\n * @returns {string}\n */\nfunction hex(dec) {\n return parseInt(dec, 10).toString(16);\n}\n\n/**\n * Checks whether the next nonignorable token after the specified position is a quantifier.\n *\n * @private\n * @param {String} pattern Pattern to search within.\n * @param {Number} pos Index in `pattern` to search at.\n * @param {String} flags Flags used by the pattern.\n * @returns {Boolean} Whether the next nonignorable token is a quantifier.\n */\nfunction isQuantifierNext(pattern, pos, flags) {\n const inlineCommentPattern = '\\\\(\\\\?#[^)]*\\\\)';\n const lineCommentPattern = '#[^#\\\\n]*';\n const quantifierPattern = '[?*+]|{\\\\d+(?:,\\\\d*)?}';\n const regex = flags.includes('x') ?\n // Ignore any leading whitespace, line comments, and inline comments\n new RegExp(`^(?:\\\\s|${lineCommentPattern}|${inlineCommentPattern})*(?:${quantifierPattern})`) :\n // Ignore any leading inline comments\n new RegExp(`^(?:${inlineCommentPattern})*(?:${quantifierPattern})`);\n return regex.test(pattern.slice(pos));\n}\n\n/**\n * Determines whether a value is of the specified type, by resolving its internal [[Class]].\n *\n * @private\n * @param {*} value Object to check.\n * @param {String} type Type to check for, in TitleCase.\n * @returns {boolean} Whether the object matches the type.\n */\nfunction isType(value, type) {\n return Object.prototype.toString.call(value) === `[object ${type}]`;\n}\n\n/**\n * Returns the object, or throws an error if it is `null` or `undefined`. This is used to follow\n * the ES5 abstract operation `ToObject`.\n *\n * @private\n * @param {*} value Object to check and return.\n * @returns {*} The provided object.\n */\nfunction nullThrows(value) {\n // null or undefined\n if (value == null) {\n throw new TypeError('Cannot convert null or undefined to object');\n }\n\n return value;\n}\n\n/**\n * Adds leading zeros if shorter than four characters. Used for fixed-length hexadecimal values.\n *\n * @private\n * @param {String} str\n * @returns {string}\n */\nfunction pad4(str) {\n while (str.length < 4) {\n str = `0${str}`;\n }\n return str;\n}\n\n/**\n * Checks for flag-related errors, and strips/applies flags in a leading mode modifier. Offloads\n * the flag preparation logic from the `XRegExp` constructor.\n *\n * @private\n * @param {String} pattern Regex pattern, possibly with a leading mode modifier.\n * @param {String} flags Any combination of flags.\n * @returns {!Object} Object with properties `pattern` and `flags`.\n */\nfunction prepareFlags(pattern, flags) {\n // Recent browsers throw on duplicate flags, so copy this behavior for nonnative flags\n if (clipDuplicates(flags) !== flags) {\n throw new SyntaxError(`Invalid duplicate regex flag ${flags}`);\n }\n\n // Strip and apply a leading mode modifier with any combination of flags except `dgy`\n pattern = pattern.replace(/^\\(\\?([\\w$]+)\\)/, ($0, $1) => {\n if (/[dgy]/.test($1)) {\n throw new SyntaxError(`Cannot use flags dgy in mode modifier ${$0}`);\n }\n // Allow duplicate flags within the mode modifier\n flags = clipDuplicates(flags + $1);\n return '';\n });\n\n // Throw on unknown native or nonnative flags\n for (const flag of flags) {\n if (!registeredFlags[flag]) {\n throw new SyntaxError(`Unknown regex flag ${flag}`);\n }\n }\n\n return {\n pattern,\n flags\n };\n}\n\n/**\n * Prepares an options object from the given value.\n *\n * @private\n * @param {String|Object} value Value to convert to an options object.\n * @returns {Object} Options object.\n */\nfunction prepareOptions(value) {\n const options = {};\n\n if (isType(value, 'String')) {\n XRegExp.forEach(value, /[^\\s,]+/, (match) => {\n options[match] = true;\n });\n\n return options;\n }\n\n return value;\n}\n\n/**\n * Registers a flag so it doesn't throw an 'unknown flag' error.\n *\n * @private\n * @param {String} flag Single-character flag to register.\n */\nfunction registerFlag(flag) {\n if (!/^[\\w$]$/.test(flag)) {\n throw new Error('Flag must be a single character A-Za-z0-9_$');\n }\n\n registeredFlags[flag] = true;\n}\n\n/**\n * Runs built-in and custom regex syntax tokens in reverse insertion order at the specified\n * position, until a match is found.\n *\n * @private\n * @param {String} pattern Original pattern from which an XRegExp object is being built.\n * @param {String} flags Flags being used to construct the regex.\n * @param {Number} pos Position to search for tokens within `pattern`.\n * @param {Number} scope Regex scope to apply: 'default' or 'class'.\n * @param {Object} context Context object to use for token handler functions.\n * @returns {Object} Object with properties `matchLength`, `output`, and `reparse`; or `null`.\n */\nfunction runTokens(pattern, flags, pos, scope, context) {\n let i = tokens.length;\n const leadChar = pattern[pos];\n let result = null;\n let match;\n let t;\n\n // Run in reverse insertion order\n while (i--) {\n t = tokens[i];\n if (\n (t.leadChar && t.leadChar !== leadChar) ||\n (t.scope !== scope && t.scope !== 'all') ||\n (t.flag && !flags.includes(t.flag))\n ) {\n continue;\n }\n\n match = XRegExp.exec(pattern, t.regex, pos, 'sticky');\n if (match) {\n result = {\n matchLength: match[0].length,\n output: t.handler.call(context, match, scope, flags),\n reparse: t.reparse\n };\n // Finished with token tests\n break;\n }\n }\n\n return result;\n}\n\n/**\n * Enables or disables implicit astral mode opt-in. When enabled, flag A is automatically added to\n * all new regexes created by XRegExp. This causes an error to be thrown when creating regexes if\n * the Unicode Base addon is not available, since flag A is registered by that addon.\n *\n * @private\n * @param {Boolean} on `true` to enable; `false` to disable.\n */\nfunction setAstral(on) {\n features.astral = on;\n}\n\n/**\n * Adds named capture groups to the `groups` property of match arrays. See here for details:\n * https://github.com/tc39/proposal-regexp-named-groups\n *\n * @private\n * @param {Boolean} on `true` to enable; `false` to disable.\n */\nfunction setNamespacing(on) {\n features.namespacing = on;\n}\n\n// ==--------------------------==\n// Constructor\n// ==--------------------------==\n\n/**\n * Creates an extended regular expression object for matching text with a pattern. Differs from a\n * native regular expression in that additional syntax and flags are supported. The returned object\n * is in fact a native `RegExp` and works with all native methods.\n *\n * @class XRegExp\n * @constructor\n * @param {String|RegExp} pattern Regex pattern string, or an existing regex object to copy.\n * @param {String} [flags] Any combination of flags.\n * Native flags:\n * - `d` - indices for capturing groups (ES2021)\n * - `g` - global\n * - `i` - ignore case\n * - `m` - multiline anchors\n * - `u` - unicode (ES6)\n * - `y` - sticky (Firefox 3+, ES6)\n * Additional XRegExp flags:\n * - `n` - named capture only\n * - `s` - dot matches all (aka singleline) - works even when not natively supported\n * - `x` - free-spacing and line comments (aka extended)\n * - `A` - 21-bit Unicode properties (aka astral) - requires the Unicode Base addon\n * Flags cannot be provided when constructing one `RegExp` from another.\n * @returns {RegExp} Extended regular expression object.\n * @example\n *\n * // With named capture and flag x\n * XRegExp(`(?<year> [0-9]{4} ) -? # year\n * (?<month> [0-9]{2} ) -? # month\n * (?<day> [0-9]{2} ) # day`, 'x');\n *\n * // Providing a regex object copies it. Native regexes are recompiled using native (not XRegExp)\n * // syntax. Copies maintain extended data, are augmented with `XRegExp.prototype` properties, and\n * // have fresh `lastIndex` properties (set to zero).\n * XRegExp(/regex/);\n */\nfunction XRegExp(pattern, flags) {\n if (XRegExp.isRegExp(pattern)) {\n if (flags !== undefined) {\n throw new TypeError('Cannot supply flags when copying a RegExp');\n }\n return copyRegex(pattern);\n }\n\n // Copy the argument behavior of `RegExp`\n pattern = pattern === undefined ? '' : String(pattern);\n flags = flags === undefined ? '' : String(flags);\n\n if (XRegExp.isInstalled('astral') && !flags.includes('A')) {\n // This causes an error to be thrown if the Unicode Base addon is not available\n flags += 'A';\n }\n\n if (!patternCache[pattern]) {\n patternCache[pattern] = {};\n }\n\n if (!patternCache[pattern][flags]) {\n const context = {\n hasNamedCapture: false,\n captureNames: []\n };\n let scope = defaultScope;\n let output = '';\n let pos = 0;\n let result;\n\n // Check for flag-related errors, and strip/apply flags in a leading mode modifier\n const applied = prepareFlags(pattern, flags);\n let appliedPattern = applied.pattern;\n const appliedFlags = applied.flags;\n\n // Use XRegExp's tokens to translate the pattern to a native regex pattern.\n // `appliedPattern.length` may change on each iteration if tokens use `reparse`\n while (pos < appliedPattern.length) {\n do {\n // Check for custom tokens at the current position\n result = runTokens(appliedPattern, appliedFlags, pos, scope, context);\n // If the matched token used the `reparse` option, splice its output into the\n // pattern before running tokens again at the same position\n if (result && result.reparse) {\n appliedPattern = appliedPattern.slice(0, pos) +\n result.output +\n appliedPattern.slice(pos + result.matchLength);\n }\n } while (result && result.reparse);\n\n if (result) {\n output += result.output;\n pos += (result.matchLength || 1);\n } else {\n // Get the native token at the current position\n const [token] = XRegExp.exec(appliedPattern, nativeTokens[scope], pos, 'sticky');\n output += token;\n pos += token.length;\n if (token === '[' && scope === defaultScope) {\n scope = classScope;\n } else if (token === ']' && scope === classScope) {\n scope = defaultScope;\n }\n }\n }\n\n patternCache[pattern][flags] = {\n // Use basic cleanup to collapse repeated empty groups like `(?:)(?:)` to `(?:)`. Empty\n // groups are sometimes inserted during regex transpilation in order to keep tokens\n // separated. However, more than one empty group in a row is never needed.\n pattern: output.replace(/(?:\\(\\?:\\))+/g, '(?:)'),\n // Strip all but native flags\n flags: appliedFlags.replace(nonnativeFlags, ''),\n // `context.captureNames` has an item for each capturing group, even if unnamed\n captures: context.hasNamedCapture ? context.captureNames : null\n };\n }\n\n const generated = patternCache[pattern][flags];\n return augment(\n new RegExp(generated.pattern, generated.flags),\n generated.captures,\n pattern,\n flags\n );\n}\n\n// Add `RegExp.prototype` to the prototype chain\nXRegExp.prototype = new RegExp();\n\n// ==--------------------------==\n// Public properties\n// ==--------------------------==\n\n/**\n * The XRegExp version number as a string containing three dot-separated parts. For example,\n * '2.0.0-beta-3'.\n *\n * @static\n * @memberOf XRegExp\n * @type String\n */\nXRegExp.version = '5.1.2';\n\n// ==--------------------------==\n// Public methods\n// ==--------------------------==\n\n// Intentionally undocumented; used in tests and addons\nXRegExp._clipDuplicates = clipDuplicates;\nXRegExp._hasNativeFlag = hasNativeFlag;\nXRegExp._dec = dec;\nXRegExp._hex = hex;\nXRegExp._pad4 = pad4;\n\n/**\n * Extends XRegExp syntax and allows custom flags. This is used internally and can be used to\n * create XRegExp addons. If more than one token can match the same string, the last added wins.\n *\n * @memberOf XRegExp\n * @param {RegExp} regex Regex object that matches the new token.\n * @param {Function} handler Function that returns a new pattern string (using native regex syntax)\n * to replace the matched token within all future XRegExp regexes. Has access to persistent\n * properties of the regex being built, through `this`. Invoked with three arguments:\n * - The match array, with named backreference properties.\n * - The regex scope where the match was found: 'default' or 'class'.\n * - The flags used by the regex, including any flags in a leading mode modifier.\n * The handler function becomes part of the XRegExp construction process, so be careful not to\n * construct XRegExps within the function or you will trigger infinite recursion.\n * @param {Object} [options] Options object with optional properties:\n * - `scope` {String} Scope where the token applies: 'default', 'class', or 'all'.\n * - `flag` {String} Single-character flag that triggers the token. This also registers the\n * flag, which prevents XRegExp from throwing an 'unknown flag' error when the flag is used.\n * - `optionalFlags` {String} Any custom flags checked for within the token `handler` that are\n * not required to trigger the token. This registers the flags, to prevent XRegExp from\n * throwing an 'unknown flag' error when any of the flags are used.\n * - `reparse` {Boolean} Whether the `handler` function's output should not be treated as\n * final, and instead be reparseable by other tokens (including the current token). Allows\n * token chaining or deferring.\n * - `leadChar` {String} Single character that occurs at the beginning of any successful match\n * of the token (not always applicable). This doesn't change the behavior of the token unless\n * you provide an erroneous value. However, providing it can increase the token's performance\n * since the token can be skipped at any positions where this character doesn't appear.\n * @example\n *\n * // Basic usage: Add \\a for the ALERT control code\n * XRegExp.addToken(\n * /\\\\a/,\n * () => '\\\\x07',\n * {scope: 'all'}\n * );\n * XRegExp('\\\\a[\\\\a-\\\\n]+').test('\\x07\\n\\x07'); // -> true\n *\n * // Add the U (ungreedy) flag from PCRE and RE2, which reverses greedy and lazy quantifiers.\n * // Since `scope` is not specified, it uses 'default' (i.e., transformations apply outside of\n * // character classes only)\n * XRegExp.addToken(\n * /([?*+]|{\\d+(?:,\\d*)?})(\\??)/,\n * (match) => `${match[1]}${match[2] ? '' : '?'}`,\n * {flag: 'U'}\n * );\n * XRegExp('a+', 'U').exec('aaa')[0]; // -> 'a'\n * XRegExp('a+?', 'U').exec('aaa')[0]; // -> 'aaa'\n */\nXRegExp.addToken = (regex, handler, options) => {\n options = options || {};\n let {optionalFlags} = options;\n\n if (options.flag) {\n registerFlag(options.flag);\n }\n\n if (optionalFlags) {\n optionalFlags = optionalFlags.split('');\n for (const flag of optionalFlags) {\n registerFlag(flag);\n }\n }\n\n // Add to the private list of syntax tokens\n tokens.push({\n regex: copyRegex(regex, {\n addG: true,\n addY: hasNativeY,\n isInternalOnly: true\n }),\n handler,\n scope: options.scope || defaultScope,\n flag: options.flag,\n reparse: options.reparse,\n leadChar: options.leadChar\n });\n\n // Reset the pattern cache used by the `XRegExp` constructor, since the same pattern and flags\n // might now produce different results\n XRegExp.cache.flush('patterns');\n};\n\n/**\n * Caches and returns the result of calling `XRegExp(pattern, flags)`. On any subsequent call with\n * the same pattern and flag combination, the cached copy of the regex is returned.\n *\n * @memberOf XRegExp\n * @param {String} pattern Regex pattern string.\n * @param {String} [flags] Any combination of XRegExp flags.\n * @returns {RegExp} Cached XRegExp object.\n * @example\n *\n * let match;\n * while (match = XRegExp.cache('.', 'gs').exec('abc')) {\n * // The regex is compiled once only\n * }\n */\nXRegExp.cache = (pattern, flags) => {\n if (!regexCache[pattern]) {\n regexCache[pattern] = {};\n }\n return regexCache[pattern][flags] || (\n regexCache[pattern][flags] = XRegExp(pattern, flags)\n );\n};\n\n// Intentionally undocumented; used in tests\nXRegExp.cache.flush = (cacheName) => {\n if (cacheName === 'patterns') {\n // Flush the pattern cache used by the `XRegExp` constructor\n patternCache = Object.create(null);\n } else {\n // Flush the regex cache populated by `XRegExp.cache`\n regexCache = Object.create(null);\n }\n};\n\n/**\n * Escapes any regular expression metacharacters, for use when matching literal strings. The result\n * can safely be used at any position within a regex that uses any flags.\n *\n * @memberOf XRegExp\n * @param {String} str String to escape.\n * @returns {string} String with regex metacharacters escaped.\n * @example\n *\n * XRegExp.escape('Escaped? <.>');\n * // -> 'Escaped\\?\\u0020<\\.>'\n */\n// Following are the contexts where each metacharacter needs to be escaped because it would\n// otherwise have a special meaning, change the meaning of surrounding characters, or cause an\n// error. Context 'default' means outside character classes only.\n// - `\\` - context: all\n// - `[()*+?.$|` - context: default\n// - `]` - context: default with flag u or if forming the end of a character class\n// - `{}` - context: default with flag u or if part of a valid/complete quantifier pattern\n// - `,` - context: default if in a position that causes an unescaped `{` to turn into a quantifier.\n// Ex: `/^a{1\\,2}$/` matches `'a{1,2}'`, but `/^a{1,2}$/` matches `'a'` or `'aa'`\n// - `#` and <whitespace> - context: default with flag x\n// - `^` - context: default, and context: class if it's the first character in the class\n// - `-` - context: class if part of a valid character class range\nXRegExp.escape = (str) => String(nullThrows(str)).\n // Escape most special chars with a backslash\n replace(/[\\\\\\[\\]{}()*+?.^$|]/g, '\\\\$&').\n // Convert to \\uNNNN for special chars that can't be escaped when used with ES6 flag `u`\n replace(/[\\s#\\-,]/g, (match) => `\\\\u${pad4(hex(match.charCodeAt(0)))}`);\n\n/**\n * Executes a regex search in a specified string. Returns a match array or `null`. If the provided\n * regex uses named capture, named capture properties are included on the match array's `groups`\n * property. Optional `pos` and `sticky` arguments specify the search start position, and whether\n * the match must start at the specified position only. The `lastIndex` property of the provided\n * regex is not used, but is updated for compatibility. Also fixes browser bugs compared to the\n * native `RegExp.prototype.exec` and can be used reliably cross-browser.\n *\n * @memberOf XRegExp\n * @param {String} str String to search.\n * @param {RegExp} regex Regex to search with.\n * @param {Number} [pos=0] Zero-based index at which to start the search.\n * @param {Boolean|String} [sticky=false] Whether the match must start at the specified position\n * only. The string `'sticky'` is accepted as an alternative to `true`.\n * @returns {Array} Match array with named capture properties on the `groups` object, or `null`. If\n * the `namespacing` feature is off, named capture properties are directly on the match array.\n * @example\n *\n * // Basic use, with named capturing group\n * let match = XRegExp.exec('U+2620', XRegExp('U\\\\+(?<hex>[0-9A-F]{4})'));\n * match.groups.hex; // -> '2620'\n *\n * // With pos and sticky, in a loop\n * let pos = 3, result = [], match;\n * while (match = XRegExp.exec('<1><2><3><4>5<6>', /<(\\d)>/, pos, 'sticky')) {\n * result.push(match[1]);\n * pos = match.index + match[0].length;\n * }\n * // result -> ['2', '3', '4']\n */\nXRegExp.exec = (str, regex, pos, sticky) => {\n let cacheKey = 'g';\n let addY = false;\n let fakeY = false;\n let match;\n\n addY = hasNativeY && !!(sticky || (regex.sticky && sticky !== false));\n if (addY) {\n cacheKey += 'y';\n } else if (sticky) {\n // Simulate sticky matching by appending an empty capture to the original regex. The\n // resulting regex will succeed no matter what at the current index (set with `lastIndex`),\n // and will not search the rest of the subject string. We'll know that the original regex\n // has failed if that last capture is `''` rather than `undefined` (i.e., if that last\n // capture participated in the match).\n fakeY = true;\n cacheKey += 'FakeY';\n }\n\n regex[REGEX_DATA] = regex[REGEX_DATA] || {};\n\n // Shares cached copies with `XRegExp.match`/`replace`\n const r2 = regex[REGEX_DATA][cacheKey] || (\n regex[REGEX_DATA][cacheKey] = copyRegex(regex, {\n addG: true,\n addY,\n source: fakeY ? `${regex.source}|()` : undefined,\n removeY: sticky === false,\n isInternalOnly: true\n })\n );\n\n pos = pos || 0;\n r2.lastIndex = pos;\n\n // Fixed `exec` required for `lastIndex` fix, named backreferences, etc.\n match = fixed.exec.call(r2, str);\n\n // Get rid of the capture added by the pseudo-sticky matcher if needed. An empty string means\n // the original regexp failed (see above).\n if (fakeY && match && match.pop() === '') {\n match = null;\n }\n\n if (regex.global) {\n regex.lastIndex = match ? r2.lastIndex : 0;\n }\n\n return match;\n};\n\n/**\n * Executes a provided function once per regex match. Searches always start at the beginning of the\n * string and continue until the end, regardless of the state of the regex's `global` property and\n * initial `lastIndex`.\n *\n * @memberOf XRegExp\n * @param {String} str String to search.\n * @param {RegExp} regex Regex to search with.\n * @param {Function} callback Function to execute for each match. Invoked with four arguments:\n * - The match array, with named backreference properties.\n * - The zero-based match index.\n * - The string being traversed.\n * - The regex object being used to traverse the string.\n * @example\n *\n * // Extracts every other digit from a string\n * const evens = [];\n * XRegExp.forEach('1a2345', /\\d/, (match, i) => {\n * if (i % 2) evens.push(+match[0]);\n * });\n * // evens -> [2, 4]\n */\nXRegExp.forEach = (str, regex, callback) => {\n let pos = 0;\n let i = -1;\n let match;\n\n while ((match = XRegExp.exec(str, regex, pos))) {\n // Because `regex` is provided to `callback`, the function could use the deprecated/\n // nonstandard `RegExp.prototype.compile` to mutate the regex. However, since `XRegExp.exec`\n // doesn't use `lastIndex` to set the search position, this can't lead to an infinite loop,\n // at least. Actually, because of the way `XRegExp.exec` caches globalized versions of\n // regexes, mutating the regex will not have any effect on the iteration or matched strings,\n // which is a nice side effect that brings extra safety.\n callback(match, ++i, str, regex);\n\n pos = match.index + (match[0].length || 1);\n }\n};\n\n/**\n * Copies a regex object and adds flag `g`. The copy maintains extended data, is augmented with\n * `XRegExp.prototype` properties, and has a fresh `lastIndex` property (set to zero). Native\n * regexes are not recompiled using XRegExp syntax.\n *\n * @memberOf XRegExp\n * @param {RegExp} regex Regex to globalize.\n * @returns {RegExp} Copy of the provided regex with flag `g` added.\n * @example\n *\n * const globalCopy = XRegExp.globalize(/regex/);\n * globalCopy.global; // -> true\n */\nXRegExp.globalize = (regex) => copyRegex(regex, {addG: true});\n\n/**\n * Installs optional features according to the specified options. Can be undone using\n * `XRegExp.uninstall`.\n *\n * @memberOf XRegExp\n * @param {Object|String} options Options object or string.\n * @example\n *\n * // With an options object\n * XRegExp.install({\n * // Enables support for astral code points in Unicode addons (implicitly sets flag A)\n * astral: true,\n *\n * // Adds named capture groups to the `groups` property of matches\n * namespacing: true\n * });\n *\n * // With an options string\n * XRegExp.install('astral namespacing');\n */\nXRegExp.install = (options) => {\n options = prepareOptions(options);\n\n if (!features.astral && options.astral) {\n setAstral(true);\n }\n\n if (!features.namespacing && options.namespacing) {\n setNamespacing(true);\n }\n};\n\n/**\n * Checks whether an individual optional feature is installed.\n *\n * @memberOf XRegExp\n * @param {String} feature Name of the feature to check. One of:\n * - `astral`\n * - `namespacing`\n * @returns {boolean} Whether the feature is installed.\n * @example\n *\n * XRegExp.isInstalled('astral');\n */\nXRegExp.isInstalled = (feature) => !!(features[feature]);\n\n/**\n * Returns `true` if an object is a regex; `false` if it isn't. This works correctly for regexes\n * created in another frame, when `instanceof` and `constructor` checks would fail.\n *\n * @memberOf XRegExp\n * @param {*} value Object to check.\n * @returns {boolean} Whether the object is a `RegExp` object.\n * @example\n *\n * XRegExp.isRegExp('string'); // -> false\n * XRegExp.isRegExp(/regex/i); // -> true\n * XRegExp.isRegExp(RegExp('^', 'm')); // -> true\n * XRegExp.isRegExp(XRegExp('(?s).')); // -> true\n */\nXRegExp.isRegExp = (value) => Object.prototype.toString.call(value) === '[object RegExp]';\n// Same as `isType(value, 'RegExp')`, but avoiding that function call here for perf since\n// `isRegExp` is used heavily by internals including regex construction\n\n/**\n * Returns the first matched string, or in global mode, an array containing all matched strings.\n * This is essentially a more convenient re-implementation of `String.prototype.match` that gives\n * the result types you actually want (string instead of `exec`-style array in match-first mode,\n * and an empty array instead of `null` when no matches are found in match-all mode). It also lets\n * you override flag g and ignore `lastIndex`, and fixes browser bugs.\n *\n * @memberOf XRegExp\n * @param {String} str String to search.\n * @param {RegExp} regex Regex to search with.\n * @param {String} [scope='one'] Use 'one' to return the first match as a string. Use 'all' to\n * return an array of all matched strings. If not explicitly specified and `regex` uses flag g,\n * `scope` is 'all'.\n * @returns {String|Array} In match-first mode: First match as a string, or `null`. In match-all\n * mode: Array of all matched strings, or an empty array.\n * @example\n *\n * // Match first\n * XRegExp.match('abc', /\\w/); // -> 'a'\n * XRegExp.match('abc', /\\w/g, 'one'); // -> 'a'\n * XRegExp.match('abc', /x/g, 'one'); // -> null\n *\n * // Match all\n * XRegExp.match('abc', /\\w/g); // -> ['a', 'b', 'c']\n * XRegExp.match('abc', /\\w/, 'all'); // -> ['a', 'b', 'c']\n * XRegExp.match('abc', /x/, 'all'); // -> []\n */\nXRegExp.match = (str, regex, scope) => {\n const global = (regex.global && scope !== 'one') || scope === 'all';\n const cacheKey = ((global ? 'g' : '') + (regex.sticky ? 'y' : '')) || 'noGY';\n\n regex[REGEX_DATA] = regex[REGEX_DATA] || {};\n\n // Shares cached copies with `XRegExp.exec`/`replace`\n const r2 = regex[REGEX_DATA][cacheKey] || (\n regex[REGEX_DATA][cacheKey] = copyRegex(regex, {\n addG: !!global,\n removeG: scope === 'one',\n isInternalOnly: true\n })\n );\n\n const result = String(nullThrows(str)).match(r2);\n\n if (regex.global) {\n regex.lastIndex = (\n (scope === 'one' && result) ?\n // Can't use `r2.lastIndex` since `r2` is nonglobal in this case\n (result.index + result[0].length) : 0\n );\n }\n\n return global ? (result || []) : (result && result[0]);\n};\n\n/**\n * Retrieves the matches from searching a string using a chain of regexes that successively search\n * within previous matches. The provided `chain` array can contain regexes and or objects with\n * `regex` and `backref` properties. When a backreference is specified, the named or numbered\n * backreference is passed forward to the next regex or returned.\n *\n * @memberOf XRegExp\n * @param {String} str String to search.\n * @param {Array} chain Regexes that each search for matches within preceding results.\n * @returns {Array} Matches by the last regex in the chain, or an empty array.\n * @example\n *\n * // Basic usage; matches numbers within <b> tags\n * XRegExp.matchChain('1 <b>2</b> 3 <b>4 a 56</b>', [\n * XRegExp('(?is)<b>.*?</b>'),\n * /\\d+/\n * ]);\n * // -> ['2', '4', '56']\n *\n * // Passing forward and returning specific backreferences\n * const html = `<a href=\"http://xregexp.com/api/\">XRegExp</a>\n * <a href=\"http://www.google.com/\">Google</a>`;\n * XRegExp.matchChain(html, [\n * {regex: /<a href=\"([^\"]+)\">/i, backref: 1},\n * {regex: XRegExp('(?i)^https?://(?<domain>[^/?#]+)'), backref: 'domain'}\n * ]);\n * // -> ['xregexp.com', 'www.google.com']\n */\nXRegExp.matchChain = (str, chain) => (function recurseChain(values, level) {\n const item = chain[level].regex ? chain[level] : {regex: chain[level]};\n const matches = [];\n\n function addMatch(match) {\n if (item.backref) {\n const ERR_UNDEFINED_GROUP = `Backreference to undefined group: ${item.backref}`;\n const isNamedBackref = isNaN(item.backref);\n\n if (isNamedBackref && XRegExp.isInstalled('namespacing')) {\n // `groups` has `null` as prototype, so using `in` instead of `hasOwnProperty`\n if (!(match.groups && item.backref in match.groups)) {\n throw new ReferenceError(ERR_UNDEFINED_GROUP);\n }\n } else if (!match.hasOwnProperty(item.backref)) {\n throw new ReferenceError(ERR_UNDEFINED_GROUP);\n }\n\n const backrefValue = isNamedBackref && XRegExp.isInstalled('namespacing') ?\n match.groups[item.backref] :\n match[item.backref];\n\n matches.push(backrefValue || '');\n } else {\n matches.push(match[0]);\n }\n }\n\n for (const value of values) {\n XRegExp.forEach(value, item.regex, addMatch);\n }\n\n return ((level === chain.length - 1) || !matches.length) ?\n matches :\n recurseChain(matches, level + 1);\n}([str], 0));\n\n/**\n * Returns a new string with one or all matches of a pattern replaced. The pattern can be a string\n * or regex, and the replacement can be a string or a function to be called for each match. To\n * perform a global search and replace, use the optional `scope` argument or include flag g if using\n * a regex. Replacement strings can use `$<n>` or `${n}` for named and numbered backreferences.\n * Replacement functions can use named backreferences via the last argument. Also fixes browser bugs\n * compared to the native `String.prototype.replace` and can be used reliably cross-browser.\n *\n * @memberOf XRegExp\n * @param {String} str String to search.\n * @param {RegExp|String} search Search pattern to be replaced.\n * @param {String|Function} replacement Replacement string or a function invoked to create it.\n * Replacement strings can include special replacement syntax:\n * - $$ - Inserts a literal $ character.\n * - $&, $0 - Inserts the matched substring.\n * - $` - Inserts the string that precedes the matched substring (left context).\n * - $' - Inserts the string that follows the matched substring (right context).\n * - $n, $nn - Where n/nn are digits referencing an existing capturing group, inserts\n * backreference n/nn.\n * - $<n>, ${n} - Where n is a name or any number of digits that reference an existing capturing\n * group, inserts backreference n.\n * Replacement functions are invoked with three or more arguments:\n * - args[0] - The matched substring (corresponds to `$&` above). If the `namespacing` feature\n * is off, named backreferences are accessible as properties of this argument.\n * - args[1..n] - One argument for each backreference (corresponding to `$1`, `$2`, etc. above).\n * If the regex has no capturing groups, no arguments appear in this position.\n * - args[n+1] - The zero-based index of the match within the entire search string.\n * - args[n+2] - The total string being searched.\n * - args[n+3] - If the the search pattern is a regex with named capturing groups, the last\n * argument is the groups object. Its keys are the backreference names and its values are the\n * backreference values. If the `namespacing` feature is off, this argument is not present.\n * @param {String} [scope] Use 'one' to replace the first match only, or 'all'. Defaults to 'one'.\n * Defaults to 'all' if using a regex with flag g.\n * @returns {String} New string with one or all matches replaced.\n * @example\n *\n * // Regex search, using named backreferences in replacement string\n * const name = XRegExp('(?<first>\\\\w+) (?<last>\\\\w+)');\n * XRegExp.replace('John Smith', name, '$<last>, $<first>');\n * // -> 'Smith, John'\n *\n * // Regex search, using named backreferences in replacement function\n * XRegExp.replace('John Smith', name, (...args) => {\n * const groups = args[args.length - 1];\n * return `${groups.last}, ${groups.first}`;\n * });\n * // -> 'Smith, John'\n *\n * // String search, with replace-all\n * XRegExp.replace('RegExp builds RegExps', 'RegExp', 'XRegExp', 'all');\n * // -> 'XRegExp builds XRegExps'\n */\nXRegExp.replace = (str, search, replacement, scope) => {\n const isRegex = XRegExp.isRegExp(search);\n const global = (search.global && scope !== 'one') || scope === 'all';\n const cacheKey = ((global ? 'g' : '') + (search.sticky ? 'y' : '')) || 'noGY';\n let s2 = search;\n\n if (isRegex) {\n search[REGEX_DATA] = search[REGEX_DATA] || {};\n\n // Shares cached copies with `XRegExp.exec`/`match`. Since a copy is used, `search`'s\n // `lastIndex` isn't updated *during* replacement iterations\n s2 = search[REGEX_DATA][cacheKey] || (\n search[REGEX_DATA][cacheKey] = copyRegex(search, {\n addG: !!global,\n removeG: scope === 'one',\n isInternalOnly: true\n })\n );\n } else if (global) {\n s2 = new RegExp(XRegExp.escape(String(search)), 'g');\n }\n\n // Fixed `replace` required for named backreferences, etc.\n const result = fixed.replace.call(nullThrows(str), s2, replacement);\n\n if (isRegex && search.global) {\n // Fixes IE, Safari bug (last tested IE 9, Safari 5.1)\n search.lastIndex = 0;\n }\n\n return result;\n};\n\n/**\n * Performs batch processing of string replacements. Used like `XRegExp.replace`, but accepts an\n * array of replacement details. Later replacements operate on the output of earlier replacements.\n * Replacement details are accepted as an array with a regex or string to search for, the\n * replacement string or function, and an optional scope of 'one' or 'all'. Uses the XRegExp\n * replacement text syntax, which supports named backreference properties via `$<name>` or\n * `${name}`.\n *\n * @memberOf XRegExp\n * @param {String} str String to search.\n * @param {Array} replacements Array of replacement detail arrays.\n * @returns {String} New string with all