Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2770 (2024-04-13 16:12:13)

feat: Further improve Boolean help messages

# Description

Provide more meaningful information to help users trouble-shoot any problems with Boolean filters.

*Note: more improvements are coming so I am not updating the documentation yet.*

1. When there is an error parsing a Boolean filter, show the user which sub-expressions have any problems that need fixing. (See 'Screenshots' heading below...)
2. When there is an error parsing any statement, show any continuation lines, and placeholders, to aid in locating the error in the query.
3. Add some sample problem queries to the test vault, to inspect the results of this PR.
    - See `resources/sample_vaults/Tasks-Demo/Filters/Boolean Combinations.md`
    - The new descriptive text may be useful in updating the docs for Boolean filters.

## Motivation and Context

Try to make it easier still for users to diagnose errors with:

- multi-line filters in general,
- and Boolean filters in particular.

## How has this been tested?

- Automated tests
- Exploratory testing

## Screenshots (if appropriate)

Consider the error output generated for this line:

`( filter by function ! 'NON_TASK,CANCELLED'.includes(task.status.type) ) OR ( filter by function const date = task.due.moment; return date ? !date.isValid() : false; ) OR ( filter by function task.due.moment?.isSameOrBefore(moment(), 'day') || false ) OR ( filter by function task.urgency.toFixed(2) === 1.95.toFixed(2) ) OR ( filter by function (!task.isRecurring) && task.originalMarkdown.includes('🔁') ) OR ( filter by function task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase() ) OR ( filter by function const taskDate = task.due.moment; const now = moment(); return taskDate?.isSame(now, 'day') || ( !taskDate && task.heading?.includes(now.format('YYYY-MM-DD')) ) || false ) OR ( filter by function const wanted = '#context/home'; return task.heading?.includes(wanted) || task.tags.find( (tag) => tag === wanted ) && true || false; )`

The error message will contain a list of the sub-expressions extracted.

Before this PR, that list looked like this:

```text
Where the sub-expressions in the simplified line are:
    'f1': 'filter by function ! 'NON_TASK,CANCELLED'.includes(task.status.type'
    'f2': 'filter by function const date = task.due.moment; return date ? !date.isValid() : false;'
    'f3': 'filter by function task.due.moment?.isSameOrBefore(moment(), 'day') || false'
    'f4': 'filter by function task.urgency.toFixed(2) === 1.95.toFixed(2'
    'f5': 'filter by function (!task.isRecurring) && task.originalMarkdown.includes('🔁''
    'f6': 'filter by function task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase('
    'f7': 'filter by function const taskDate = task.due.moment; const now = moment(); return taskDate?.isSame(now, 'day') || ( !taskDate && task.heading?.includes(now.format('YYYY-MM-DD')) ) || false'
    'f8': 'filter by function const wanted = '#context/home'; return task.heading?.includes(wanted) || task.tags.find( (tag) => tag === wanted ) && true || false;'
```

After this PR, the list looks like this, where the `ERROR` and `OK` comments should help identify the source of any problems:

```text
Where the sub-expressions in the simplified line are:
    'f1': 'filter by function ! 'NON_TASK,CANCELLED'.includes(task.status.type'
        => ERROR:
           Error: Failed parsing expression "! 'NON_TASK,CANCELLED'.includes(task.status.type".
           The error message was:
           "SyntaxError: missing ) after argument list"
    'f2': 'filter by function const date = task.due.moment; return date ? !date.isValid() : false;'
        => OK
    'f3': 'filter by function task.due.moment?.isSameOrBefore(moment(), 'day') || false'
        => OK
    'f4': 'filter by function task.urgency.toFixed(2) === 1.95.toFixed(2'
        => ERROR:
           Error: Failed parsing expression "task.urgency.toFixed(2) === 1.95.toFixed(2".
           The error message was:
           "SyntaxError: missing ) after argument list"
    'f5': 'filter by function (!task.isRecurring) && task.originalMarkdown.includes('🔁''
        => ERROR:
           Error: Failed parsing expression "(!task.isRecurring) && task.originalMarkdown.includes('🔁'".
           The error message was:
           "SyntaxError: missing ) after argument list"
    'f6': 'filter by function task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase('
        => ERROR:
           Error: Failed parsing expression "task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase(".
           The error message was:
           "SyntaxError: Unexpected token '}'"
    'f7': 'filter by function const taskDate = task.due.moment; const now = moment(); return taskDate?.isSame(now, 'day') || ( !taskDate && task.heading?.includes(now.format('YYYY-MM-DD')) ) || false'
        => OK
    'f8': 'filter by function const wanted = '#context/home'; return task.heading?.includes(wanted) || task.tags.find( (tag) => tag === wanted ) && true || false;'
        => OK
```

## Types of changes

Changes visible to users:

- [x] **New feature** (prefix: `feat` - non-breaking change which adds functionality)
- [ ] **Documentation** (prefix: `docs` - improvements to any documentation content **for users**)
- [x] **Sample vault** (prefix: `vault` - improvements to the [Tasks-Demo sample vault](https://github.com/obsidian-tasks-group/obsidian-tasks/tree/main/resources/sample_vaults/Tasks-Demo))

Internal changes:

- [x] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke tests)

## Checklist

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change requires a change to the documentation.
- [ ] I have [updated the documentation](https://publish.obsidian.md/tasks-contributing/Documentation/About+Documentation) accordingly.
    - *Will be done later*
- [x] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

- [x] My contribution follow this project's [contributing guide](https://publish.obsidian.md/tasks-contributing)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)



Patch:
diff --git a/resources/sample_vaults/Tasks-Demo/.obsidian/community-plugins.json b/resources/sample_vaults/Tasks-Demo/.obsidian/community-plugins.json
index 480176ad..43e3b8da 100644
--- a/resources/sample_vaults/Tasks-Demo/.obsidian/community-plugins.json
+++ b/resources/sample_vaults/Tasks-Demo/.obsidian/community-plugins.json
@@ -9,6 +9,5 @@
   "quickadd",
   "obsidian42-brat",
   "hot-reload",
-  "obsidian-theme-design-utilities",
-  "number-headings-obsidian"
+  "obsidian-theme-design-utilities"
 ]
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/data.json b/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/data.json
deleted file mode 100644
index 098b2739..00000000
--- a/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/data.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "skipTopLevel": true,
-  "firstLevel": 1,
-  "maxLevel": 6,
-  "styleLevel1": "1",
-  "styleLevelOther": "1",
-  "auto": false,
-  "separator": "",
-  "contents": "",
-  "skipHeadings": "",
-  "startAt": "",
-  "off": false
-}
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/main.js b/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/main.js
deleted file mode 100644
index e49aad84..00000000
--- a/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/main.js
+++ /dev/null
@@ -1,1136 +0,0 @@
-/*
-THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
-if you want to view the source visit the plugins github repository
-*/
-
-'use strict';
-
-var obsidian = require('obsidian');
-
-/******************************************************************************
-Copyright (c) Microsoft Corporation.
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-PERFORMANCE OF THIS SOFTWARE.
-***************************************************************************** */
-
-function __awaiter(thisArg, _arguments, P, generator) {
-    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
-    return new (P || (P = Promise))(function (resolve, reject) {
-        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
-        step((generator = generator.apply(thisArg, _arguments || [])).next());
-    });
-}
-
-typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
-    var e = new Error(message);
-    return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
-};
-
-function getActiveView(app) {
-    const activeView = app.workspace.getActiveViewOfType(obsidian.MarkdownView);
-    return activeView !== null && activeView !== void 0 ? activeView : undefined;
-}
-function isViewActive(app) {
-    const activeView = getActiveView(app);
-    if (activeView && activeView.file)
-        return true;
-    return false;
-}
-function getViewMetadata(app) {
-    const activeView = getActiveView(app);
-    if (activeView && activeView.file) {
-        const data = app.metadataCache.getFileCache(activeView.file) || {};
-        return data;
-    }
-    return undefined;
-}
-function getViewInfo(app) {
-    const activeView = getActiveView(app);
-    const data = getViewMetadata(app);
-    const editor = activeView ? activeView.editor : undefined;
-    if (activeView && data && editor) {
-        return {
-            activeView, data, editor
-        };
-    }
-    return undefined;
-}
-
-const roman_map = {
-  M: 1000,
-  CM: 900,
-  D: 500,
-  CD: 400,
-  C: 100,
-  XC: 90,
-  L: 50,
-  XL: 40,
-  X: 10,
-  IX: 9,
-  V: 5,
-  IV: 4,
-  I: 1
-};
-
-const allChars = Object.keys(roman_map);
-const allNumerals = Object.values(roman_map);
-const romanPattern =
-  /^(M{1,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|M{0,4}(CM|C?D|D?C{1,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|M{0,4}(CM|CD|D?C{0,3})(XC|X?L|L?X{1,3})(IX|IV|V?I{0,3})|M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|I?V|V?I{1,3}))$/;
-
-const romanize = (decimal) => {
-  if (
-    decimal <= 0 ||
-    typeof decimal !== 'number' ||
-    Math.floor(decimal) !== decimal
-  ) {
-    throw new Error('requires an unsigned integer')
-  }
-  if (decimal >= 4000) {
-    throw new Error('requires max value of less than 3999 or less')
-  }
-  let roman = '';
-  for (let i = 0; i < allChars.length; i++) {
-    while (decimal >= allNumerals[i]) {
-      decimal -= allNumerals[i];
-      roman += allChars[i];
-    }
-  }
-  return roman
-};
-
-const deromanize = (romanStr) => {
-  if (typeof romanStr !== 'string') {
-    throw new Error('requires a string')
-  }
-  if (!romanPattern.test(romanStr)) {
-    throw new Error('requires valid roman numeral string')
-  }
-  let romanString = romanStr.toUpperCase();
-  let arabic = 0;
-  let iteration = romanString.length;
-  while (iteration--) {
-    let cumulative = roman_map[romanString[iteration]];
-    if (cumulative < roman_map[romanString[iteration + 1]]) {
-      arabic -= cumulative;
-    } else {
-      arabic += cumulative;
-    }
-  }
-  return arabic
-};
-
-var romans = {
-  deromanize,
-  romanize,
-  allChars,
-  allNumerals
-};
-
-// Validates the string using a regex to ensure is is a valid arabic numbering value
-function isValidArabicNumberingValueString(s) {
-    const regex = /^[0-9]+$/;
-    return regex.test(s);
-}
-// Validates the string using a regex to ensure is is a valid alphabet numbering value
-function isValidAlphabetNumberingValueString(s) {
-    const regex = /^[A-Z]$/;
-    return regex.test(s);
-}
-// Validates the string using a regex to ensure is is a valid roman numbering value
-function isValidRomanNumberingValueString(s) {
-    const regex = /^[0IVXLCDM]+$/; // This includes zero for zeroth testing
-    return regex.test(s);
-}
-function printableNumberingToken(t) {
-    switch (t.style) {
-        case '1':
-            return t.value.toString();
-        case 'A':
-            return t.value;
-        case 'I':
-            return t.value;
-    }
-}
-function zerothNumberingTokenInStyle(style) {
-    switch (style) {
-        case '1':
-            return { style: '1', value: 0 };
-        case 'A':
-            return { style: 'A', value: 'Z' };
-        case 'I':
-            return { style: 'I', value: '0' };
-    }
-}
-function firstNumberingTokenInStyle(style) {
-    switch (style) {
-        case '1':
-            return { style: '1', value: 1 };
-        case 'A':
-            return { style: 'A', value: 'A' };
-        case 'I':
-            return { style: 'I', value: 'I' };
-    }
-}
-function nextNumberingToken(t) {
-    switch (t.style) {
-        case '1':
-            return { style: '1', value: t.value + 1 };
-        case 'A':
-            if (t.value === 'Z')
-                return { style: 'A', value: 'A' };
-            else
-                return { style: 'A', value: String.fromCharCode(t.value.charCodeAt(0) + 1) };
-        case 'I':
-            if (t.value === '0')
-                return { style: 'I', value: 'I' };
-            else
-                return { style: 'I', value: romans.romanize(romans.deromanize(t.value) + 1) };
-    }
-}
-function previousNumberingToken(t) {
-    switch (t.style) {
-        case '1':
-            return { style: '1', value: t.value - 1 };
-        case 'A':
-            if (t.value === 'A')
-                return { style: 'A', value: 'Z' };
-            else
-                return { style: 'A', value: String.fromCharCode(t.value.charCodeAt(0) - 1) };
-        case 'I':
-            if (t.value === 'I')
-                return { style: 'I', value: '0' };
-            else
-                return { style: 'I', value: romans.romanize(romans.deromanize(t.value) - 1) };
-    }
-}
-function makeNumberingString(numberingStack) {
-    let numberingString = '';
-    for (let i = 0; i < numberingStack.length; i++) {
-        if (i === 0) {
-            numberingString += ' ';
-        }
-        else {
-            numberingString += '.';
-        }
-        numberingString += printableNumberingToken(numberingStack[i]);
-    }
-    return numberingString;
-}
-function startAtOrZerothInStyle(startAtSettingString, style) {
-    if (startAtSettingString === '')
-        return zerothNumberingTokenInStyle(style);
-    let firstNumberingTokenFromSetting;
-    switch (style) {
-        case '1':
-            if (!isValidArabicNumberingValueString(startAtSettingString))
-                return zerothNumberingTokenInStyle(style);
-            firstNumberingTokenFromSetting = { style: '1', value: parseInt(startAtSettingString) };
-            break;
-        case 'A':
-            if (!isValidAlphabetNumberingValueString(startAtSettingString))
-                return zerothNumberingTokenInStyle(style);
-            firstNumberingTokenFromSetting = { style: 'A', value: startAtSettingString };
-            break;
-        case 'I':
-            if (!isValidRomanNumberingValueString(startAtSettingString))
-                return zerothNumberingTokenInStyle(style);
-            firstNumberingTokenFromSetting = { style: 'I', value: startAtSettingString };
-            break;
-    }
-    // Convert the first numbering token to a zeroth numbering token
-    return previousNumberingToken(firstNumberingTokenFromSetting);
-}
-
-const DEFAULT_SETTINGS = {
-    skipTopLevel: false,
-    firstLevel: 1,
-    maxLevel: 6,
-    styleLevel1: '1',
-    styleLevelOther: '1',
-    auto: false,
-    separator: '',
-    contents: '',
-    skipHeadings: '',
-    startAt: '',
-    off: false
-};
-function isValidNumberingStyleString(s) {
-    if (s === 'A' || s === '1' || s === 'I')
-        return true;
-    return false;
-}
-function isValidNumberingValueString(s) {
-    if (s === '' || isValidArabicNumberingValueString(s) || isValidAlphabetNumberingValueString(s) || isValidRomanNumberingValueString(s))
-        return true;
-    return false;
-}
-function isValidFlag(f) {
-    if (f === true || f === false)
-        return true;
-    return false;
-}
-function isValidFirstOrMaxLevel(x) {
-    if (typeof x === 'number' && x >= 1 && x <= 6)
-        return true;
-    return false;
-}
-function isValidSeparator(x) {
-    return typeof x === 'string' &&
-        (x === '' ||
-            x === ':' || x === ' :' ||
-            x === '.' || x === ' .' ||
-            x === '-' || x === ' -' ||
-            x === '—' || x === ' —' || /* em-dash */
-            x === ')' || x === ' )');
-}
-function isValidBlockIdSetting(x) {
-    if (typeof x === 'string' && (x === '' || x.startsWith('^')))
-        return true;
-    return false;
-}
-function isNonEmptyBlockId(x) {
-    if (x.length > 2 && x.startsWith('^'))
-        return true;
-    return false;
-}
-
-function createSupportFlagsFromSettings(styleLevel1, styleLevelOther) {
-    return {
-        alphabet: styleLevel1 === 'A' || styleLevelOther === 'A',
-        roman: styleLevel1 === 'I' || styleLevelOther === 'I'
-    };
-}
-// Get the regex for the header string, based on the support flags. The generated regex is used to find the range of the header prefix.
-// The regex is generated dynamically, because the regex is different depending on the support flags.
-function getRegexForHeaderString(flags) {
-    if (flags.alphabet && flags.roman) {
-        // Regex to match the heading prefix, including the space after the hash(es), but not the heading text
-        return /^\s{0,4}#+( )?([0-9]+\.|[A-Z]\.|[IVXLCDM]+\.)*([0-9]+|[A-Z]|[IVXLCDM]+)?( )?[)—:.-]?( )+/g;
-    }
-    else if (!flags.alphabet && flags.roman) {
-        // Regex to match the heading prefix, including the space after the hash(es), but not the heading text
-        return /^\s{0,4}#+( )?([0-9]+\.|[IVXLCDM]+\.)*([0-9]+|[IVXLCDM]+)?( )?[)—:.-]?( )+/g;
-    }
-    else if (flags.alphabet && !flags.roman) {
-        // Regex to match the heading prefix, including the space after the hash(es), but not the heading text
-        return /^\s{0,4}#+( )?([0-9]+\.|[A-Z]\.)*([0-9]+|[A-Z])?( )?[)—:.-]?( )+/g;
-    }
-    else if (!flags.alphabet && !flags.roman) {
-        // Regex to match the heading prefix, including the space after the hash(es), but not the heading text
-        return /^\s{0,4}#+( )?([0-9]+\.)*([0-9]+)?( )?[)—:.-]?( )+/g;
-    }
-    throw new Error('Unexpected combination of support flags');
-}
-// Find the range of the heading prefix, including the space after any numbering, but not the heading text
-function findRangeInHeaderString(lineText, lineNumber, flags) {
-    const regex = getRegexForHeaderString(flags);
-    if (!lineText)
-        return undefined;
-    const matches = lineText.match(regex);
-    if (matches && matches.length !== 1) {
-        // eslint-disable-next-line no-console
-        console.log("Unexpected heading format: '" + lineText + "'");
-        return undefined;
-    }
-    const match = matches ? matches[0] : '';
-    const from = {
-        line: lineNumber,
-        ch: 0
-    };
-    const to = {
-        line: lineNumber,
-        ch: match.length
-    };
-    return { from, to };
-}
-function updateSettingsFromFrontMatterFormatPart(part, settings) {
-    // Parse the separator
-    let partWithoutSeparator = part;
-    const potentialTwoCharSeparator = part.slice(-2);
-    if (isValidSeparator(potentialTwoCharSeparator)) {
-        settings.separator = potentialTwoCharSeparator;
-        partWithoutSeparator = part.slice(0, -2);
-    }
-    else {
-        const potentialOneCharSeparator = part.slice(-1);
-        if (isValidSeparator(potentialOneCharSeparator)) {
-            settings.separator = potentialOneCharSeparator;
-            partWithoutSeparator = part.slice(0, -1);
-        }
-        else {
-            settings.separator = '';
-        }
-    }
-    // Parse the numbering style
-    const descriptors = partWithoutSeparator.split('.');
-    let firstNumberedDescriptor = 0;
-    // Handle the case where the first descriptor is an underscore
-    if (descriptors.length > 1 && descriptors[0] === '_') {
-        // The first descriptor is an instruction to skip top levels, so skip them
-        settings.skipTopLevel = true;
-        firstNumberedDescriptor = 1;
-    }
-    else {
-        settings.skipTopLevel = false;
-    }
-    if (descriptors.length - firstNumberedDescriptor >= 2) {
-        const styleLevel1 = descriptors[firstNumberedDescriptor];
-        if (isValidNumberingStyleString(styleLevel1)) {
-            settings.styleLevel1 = styleLevel1;
-        }
-        const styleLevelOther = descriptors[firstNumberedDescriptor + 1];
-        if (isValidNumberingStyleString(styleLevelOther)) {
-            settings.styleLevelOther = styleLevelOther;
-        }
-    }
-    return settings;
-}
-
-const AUTO_PART_KEY = 'auto';
-const FIRST_LEVEL_PART_KEY = 'first-level';
-const MAX_LEVEL_PART_KEY = 'max';
-const CONTENTS_PART_KEY = 'contents';
-const SKIP_PART_KEY = 'skip';
-const START_AT_PART_KEY = 'start-at';
-const OFF_PART_KEY = 'off';
-function parseCompactFrontMatterSettings(fm) {
-    const entry = obsidian.parseFrontMatterEntry(fm, 'number headings');
-    if (entry) {
-        const entryString = String(entry);
-        const parts = entryString.split(',');
-        let settings = Object.assign({}, DEFAULT_SETTINGS);
-        for (const part of parts) {
-            const trimmedPart = part.trim();
-            if (trimmedPart.length === 0)
-                continue;
-            if (trimmedPart === OFF_PART_KEY) {
-                // Parse off part
-                settings.off = true;
-            }
-            else if (trimmedPart === AUTO_PART_KEY) {
-                // Parse auto numbering part
-                settings.auto = true;
-            }
-            else if (trimmedPart.startsWith(FIRST_LEVEL_PART_KEY)) {
-                // Parse first level part
-                const nstring = trimmedPart.substring(FIRST_LEVEL_PART_KEY.length + 1);
-                const n = parseInt(nstring);
-                if (isValidFirstOrMaxLevel(n)) {
-                    settings.firstLevel = n;
-                }
-            }
-            else if (trimmedPart.startsWith(MAX_LEVEL_PART_KEY)) {
-                // Parse max level part
-                const nstring = trimmedPart.substring(MAX_LEVEL_PART_KEY.length + 1);
-                const n = parseInt(nstring);
-                if (isValidFirstOrMaxLevel(n)) {
-                    settings.maxLevel = n;
-                }
-            }
-            else if (trimmedPart.startsWith(START_AT_PART_KEY)) {
-                // Parse "start at" part
-                const value = trimmedPart.substring(START_AT_PART_KEY.length + 1);
-                if (isValidNumberingValueString(value)) {
-                    settings.startAt = value;
-                }
-            }
-            else if (trimmedPart.startsWith(CONTENTS_PART_KEY)) {
-                if (trimmedPart.length <= CONTENTS_PART_KEY.length + 1)
-                    continue;
-                // Parse contents heading part
-                const tocHeadingBlockIdName = trimmedPart.substring(CONTENTS_PART_KEY.length + 1);
-                if (isValidBlockIdSetting(tocHeadingBlockIdName)) {
-                    settings.contents = tocHeadingBlockIdName;
-                }
-            }
-            else if (trimmedPart.startsWith(SKIP_PART_KEY)) {
-                if (trimmedPart.length <= SKIP_PART_KEY.length + 1)
-                    continue;
-                // Parse skip heading part
-                const skipHeadingBlockIdName = trimmedPart.substring(SKIP_PART_KEY.length + 1);
-                if (isValidBlockIdSetting(skipHeadingBlockIdName)) {
-                    settings.skipHeadings = skipHeadingBlockIdName;
-                }
-            }
-            else {
-                // Parse formatting part
-                settings = updateSettingsFromFrontMatterFormatPart(trimmedPart, settings);
-            }
-        }
-        return settings;
-    }
-    return undefined;
-}
-const getFrontMatterSettingsOrAlternative = ({ frontmatter }, alternativeSettings) => {
-    var _a, _b, _c, _d, _e;
-    if (frontmatter !== undefined) {
-        const decompactedSettings = parseCompactFrontMatterSettings(frontmatter);
-        if (decompactedSettings !== undefined)
-            return decompactedSettings;
-        // NOTE: Everything below is for backwards compatibility only
-        const skipTopLevelEntry = (_a = obsidian.parseFrontMatterEntry(frontmatter, 'number-headings-skip-top-level')) !== null && _a !== void 0 ? _a : obsidian.parseFrontMatterEntry(frontmatter, 'header-numbering-skip-top-level');
-        const skipTopLevel = isValidFlag(skipTopLevelEntry) ? skipTopLevelEntry : alternativeSettings.skipTopLevel;
-        const maxLevelEntry = (_b = obsidian.parseFrontMatterEntry(frontmatter, 'number-headings-max-level')) !== null && _b !== void 0 ? _b : obsidian.parseFrontMatterEntry(frontmatter, 'header-numbering-max-level');
-        const maxLevel = isValidFirstOrMaxLevel(maxLevelEntry) ? maxLevelEntry : alternativeSettings.maxLevel;
-        const styleLevel1Entry = String((_c = obsidian.parseFrontMatterEntry(frontmatter, 'number-headings-style-level-1')) !== null && _c !== void 0 ? _c : obsidian.parseFrontMatterEntry(frontmatter, 'header-numbering-style-level-1'));
-        const styleLevel1 = isValidNumberingStyleString(styleLevel1Entry) ? styleLevel1Entry : alternativeSettings.styleLevel1;
-        const styleLevelOtherEntry = String((_d = obsidian.parseFrontMatterEntry(frontmatter, 'number-headings-style-level-other')) !== null && _d !== void 0 ? _d : obsidian.parseFrontMatterEntry(frontmatter, 'header-numbering-style-level-other'));
-        const styleLevelOther = isValidNumberingStyleString(styleLevelOtherEntry) ? styleLevelOtherEntry : alternativeSettings.styleLevelOther;
-        const autoEntry = (_e = obsidian.parseFrontMatterEntry(frontmatter, 'number-headings-auto')) !== null && _e !== void 0 ? _e : obsidian.parseFrontMatterEntry(frontmatter, 'header-numbering-auto');
-        const auto = isValidFlag(autoEntry) ? autoEntry : alternativeSettings.auto;
-        return Object.assign(Object.assign({}, alternativeSettings), { skipTopLevel, maxLevel, styleLevel1, styleLevelOther, auto });
-    }
-    else {
-        return alternativeSettings;
-    }
-};
-function settingsToCompactFrontMatterValue(settings) {
-    if (settings.off)
-        return OFF_PART_KEY;
-    const autoPart = settings.auto ? 'auto, ' : '';
-    const firstLevelPart = `first-level ${settings.firstLevel}, `;
-    const maxPart = `max ${settings.maxLevel}, `;
-    const contentsPart = settings.contents && settings.contents.length > 0 ? `contents ${settings.contents}, ` : '';
-    const skipHeadingsPart = settings.skipHeadings && settings.skipHeadings.length > 0 ? `skip ${settings.skipHeadings}, ` : '';
-    const skipTopLevelString = settings.skipTopLevel ? '_.' : '';
-    const stylePart = `${skipTopLevelString}${settings.styleLevel1}.${settings.styleLevelOther}${settings.separator}`;
-    const startAtPart = settings.startAt !== '' ? `start-at ${settings.startAt}, ` : '';
-    return autoPart + firstLevelPart + maxPart + contentsPart + skipHeadingsPart + startAtPart + stylePart;
-}
-const saveSettingsToFrontMatter = (fileManager, file, settings) => {
-    fileManager.processFrontMatter(file, frontmatter => {
-        const v = settingsToCompactFrontMatterValue(settings);
-        frontmatter['number headings'] = v;
-    });
-};
-
-class NumberingDoneModal extends obsidian.Modal {
-    constructor(app, config) {
-        super(app);
-        this.config = config;
-    }
-    onOpen() {
-        const { contentEl, titleEl } = this;
-        titleEl.setText('Number Headings - Successfully Completed');
-        contentEl.createEl('div', { text: this.config.message });
-        contentEl.createEl('pre', { text: this.config.preformattedMessage });
-        contentEl.createEl('div', { text: "Do you want to save these settings in the document's front matter?", cls: 'number-headings-question' });
-        const containerForButtons = contentEl.createEl('div', { cls: 'number-headings-button-container' });
-        const noButton = containerForButtons.createEl('button', {});
-        noButton.setText('No');
-        noButton.onClickEvent((ev) => {
-            this.close();
-            return ev;
-        });
-        const yesButton = containerForButtons.createEl('button', {});
-        yesButton.setText('Yes, save settings in document');
-        yesButton.onClickEvent((ev) => {
-            this.config.saveSettingsCallback(false);
-            this.close();
-            return ev;
-        });
-        const yesAndAutoButton = containerForButtons.createEl('button', {});
-        yesAndAutoButton.setText('Yes, save settings in document, and automatically number');
-        yesAndAutoButton.onClickEvent((ev) => {
-            this.config.saveSettingsCallback(true);
-            this.close();
-            return ev;
-        });
-    }
-    onClose() {
-        const { contentEl, titleEl } = this;
-        contentEl.empty();
-        titleEl.empty();
-    }
-}
-function showNumberingDoneMessage(app, settings) {
-    const saveSettingsCallback = (shouldAddAutoFlag) => {
-        const tweakedSettings = Object.assign({}, settings);
-        if (shouldAddAutoFlag)
-            tweakedSettings.auto = true;
-        const file = app.workspace.getActiveFile();
-        if (file) {
-            saveSettingsToFrontMatter(app.fileManager, file, tweakedSettings);
-        }
-    };
-    const config = {
-        message: `Successfully updated all heading numbers in the document, using the settings below. 
-      See settings panel to change how headings are numbered, or use front matter
-      (see settings panel).`,
-        preformattedMessage: `Skip top heading level: ${settings.skipTopLevel}
-First heading level: ${settings.firstLevel}
-Start numbering first heading at: ${settings.startAt}
-Maximum heading level: ${settings.maxLevel}
-Style for level 1 headings: ${settings.styleLevel1}
-Style for lower level headings (below level 1): ${settings.styleLevelOther}
-Separator: ${settings.separator}
-Table of Contents Anchor: ${settings.contents}
-Skip Headings Anchor: ${settings.skipHeadings}`,
-        saveSettingsCallback
-    };
-    const leaf = app.workspace.activeLeaf;
-    if (leaf) {
-        new NumberingDoneModal(app, config).open();
-    }
-}
-
-const TOC_LIST_ITEM_BULLET = '-';
-function makeHeadingHashString(editor, heading) {
-    const regex = /^\s{0,4}#+/g;
-    const headingLineString = editor.getLine(heading.position.start.line);
-    if (!headingLineString)
-        return undefined;
-    const matches = headingLineString.match(regex);
-    if (!matches)
-        return undefined;
-    if (matches.length !== 1) {
-        // eslint-disable-next-line no-console
-        console.log("Unexpected heading format: '" + headingLineString + "'");
-        return undefined;
-    }
-    const match = matches[0];
-    return match.trimLeft();
-}
-function findHeadingPrefixRange(editor, heading, flags) {
-    const lineNumber = heading.position.start.line;
-    const lineText = editor.getLine(lineNumber);
-    return findRangeInHeaderString(lineText, lineNumber, flags);
-}
-function cleanHeadingTextForToc(htext) {
-    if (htext.contains('^')) {
-        const x = htext.split('^');
-        if (x.length > 1) {
-            return x[0].trim();
-        }
-    }
-    return htext.trim();
-}
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-function createTocEntry(h, settings, initialHeadingLevel) {
-    const text = h.heading;
-    const cleanText = cleanHeadingTextForToc(text);
-    let bulletIndent = '';
-    const startLevel = initialHeadingLevel;
-    for (let i = startLevel; i < h.level; i++) {
-        bulletIndent += '\t';
-    }
-    const entryLink = `[[#${text}|${cleanText}]]`;
-    return bulletIndent + TOC_LIST_ITEM_BULLET + ' ' + entryLink;
-}
-// Replace a range, but only if there is a change in text, to prevent poluting the undo stack
-function replaceRangeEconomically(editor, changes, range, text) {
-    const previousText = editor.getRange(range.from, range.to);
-    if (previousText !== text) {
-        changes.push({
-            text: text,
-            from: range.from,
-            to: range.to
-        });
-    }
-}
-const updateHeadingNumbering = (viewInfo, settings) => {
-    var _a;
-    if (!viewInfo)
-        return;
-    const headings = (_a = viewInfo.data.headings) !== null && _a !== void 0 ? _a : [];
-    const editor = viewInfo.editor;
-    const supportFlags = createSupportFlagsFromSettings(settings.styleLevel1, settings.styleLevelOther);
-    let previousLevel = 1;
-    let numberingStack = [startAtOrZerothInStyle(settings.startAt, settings.styleLevel1)];
-    if (settings.firstLevel > 1) {
-        previousLevel = settings.firstLevel;
-    }
-    else if (settings.skipTopLevel) {
-        previousLevel = 2;
-    }
-    const changes = [];
-    for (const heading of headings) {
-        // Update the numbering stack based on the level and previous level
-        const level = heading.level;
-        // Handle skipped & ignored levels.
-        if ((settings.firstLevel > level) || (settings.skipTopLevel && level === 1)) {
-            // Resets the numbering when a level is skipped.
-            // Note: This leaves headings as they are, allowing people to have numbers at the start of
-            // ignored headings.
-            numberingStack = [startAtOrZerothInStyle(settings.startAt, settings.styleLevel1)];
-            if (settings.firstLevel > 1) {
-                previousLevel = settings.firstLevel;
-            }
-            else if (settings.skipTopLevel) {
-                previousLevel = 2;
-            }
-            continue;
-        }
-        // Handle skipped headings
-        if (settings.skipHeadings.length > 0) {
-            if (heading.heading.endsWith(settings.skipHeadings)) {
-                continue;
-            }
-        }
-        // Adjust numbering stack
-        if (level === previousLevel) {
-            const x = numberingStack.pop();
-            if (x !== undefined) {
-                numberingStack.push(nextNumberingToken(x));
-            }
-        }
-        else if (level < previousLevel) {
-            for (let i = previousLevel; i > level; i--) {
-                numberingStack.pop();
-            }
-            const x = numberingStack.pop();
-            if (x !== undefined) {
-                numberingStack.push(nextNumberingToken(x));
-            }
-        }
-        else if (level > previousLevel) {
-            for (let i = previousLevel; i < level; i++) {
-                numberingStack.push(firstNumberingTokenInStyle(settings.styleLevelOther));
-            }
-        }
-        // Set the previous level to this level for the next iteration
-        previousLevel = level;
-        if (level > settings.maxLevel) {
-            // If we are above the max level, just don't number it
-            continue;
-        }
-        // Find the range to replace, and then do it
-        const prefixRange = findHeadingPrefixRange(editor, heading, supportFlags);
-        if (prefixRange === undefined)
-            return;
-        const headingHashString = makeHeadingHashString(editor, heading);
-        if (headingHashString === undefined)
-            return;
-        const prefixString = makeNumberingString(numberingStack);
-        replaceRangeEconomically(editor, changes, prefixRange, headingHashString + prefixString + settings.separator + ' ');
-    }
-    // Execute the transaction to make all the changes at once
-    if (changes.length > 0) {
-        // eslint-disable-next-line no-console
-        console.log('Number Headings Plugin: Applying headings numbering changes:', changes.length);
-        editor.transaction({
-            changes: changes
-        });
-    }
-};
-const updateTableOfContents = (viewInfo, settings) => {
-    var _a;
-    if (!viewInfo)
-        return;
-    const headings = (_a = viewInfo.data.headings) !== null && _a !== void 0 ? _a : [];
-    const editor = viewInfo.editor;
-    if (!isNonEmptyBlockId(settings.contents))
-        return;
-    let tocHeading;
-    let tocBuilder = '\n';
-    const changes = [];
-    // In case headings start above level 1, we don't want to indent the bullets too much
-    let initialHeadingLevel = 1;
-    if (headings.length > 0) {
-        initialHeadingLevel = headings[0].level;
-    }
-    for (const heading of headings) {
-        // ORDERING: Important to find the TOC heading before skipping skipped headings, since that is for numbering
-        // Find the TOC heading
-        if (heading.heading.endsWith(settings.contents)) {
-            tocHeading = heading;
-        }
-        /* This code lets us skip TOC lines for skipped headings, but doesn't work well with first-level setting
-        if ((settings.skipTopLevel && heading.level === 1) || (heading.level > settings.maxLevel)) {
-          continue
-        }
-        */
-        const tocEntry = createTocEntry(heading, settings, initialHeadingLevel);
-        tocBuilder += tocEntry + '\n';
-    }
-    // Insert the generated table of contents
-    if (tocHeading) {
-        const from = {
-            line: tocHeading.position.start.line + 1,
-            ch: 0
-        };
-        // Find the end of the TOC section
-        const startingLine = tocHeading.position.start.line + 1;
-        let endingLine = startingLine;
-        let foundList = false;
-        const lastLineInEditor = editor.lastLine();
-        for (;; endingLine++) {
-            const line = editor.getLine(endingLine);
-            if (line === undefined || endingLine > lastLineInEditor) {
-                // Reached end of file, insert at the start of the TOC section
-                endingLine = startingLine;
-                break;
-            }
-            const trimmedLineText = line.trimStart();
-            if (foundList) {
-                if (!trimmedLineText.startsWith(TOC_LIST_ITEM_BULLET))
-                    break;
-                if (trimmedLineText.startsWith('#'))
-                    break;
-            }
-            else {
-                if (trimmedLineText.startsWith(TOC_LIST_ITEM_BULLET)) {
-                    foundList = true;
-                }
-                else if (trimmedLineText.startsWith('#')) {
-                    // Reached the next heading without finding existing TOC list, insert at the start of the TOC section
-                    endingLine = startingLine;
-                    break;
-                }
-                else {
-                    continue;
-                }
-            }
-        }
-        if (tocBuilder === '\n') {
-            tocBuilder = '';
-        }
-        const to = {
-            line: endingLine,
-            ch: 0
-        };
-        const range = { from, to };
-        replaceRangeEconomically(editor, changes, range, tocBuilder);
-    }
-    // Execute the transaction to make all the changes at once
-    if (changes.length > 0) {
-        // eslint-disable-next-line no-console
-        console.log('Number Headings Plugin: Applying table of contents changes:', changes.length);
-        editor.transaction({
-            changes: changes
-        });
-    }
-};
-const removeHeadingNumbering = (viewInfo) => {
-    var _a;
-    if (!viewInfo)
-        return;
-    const headings = (_a = viewInfo.data.headings) !== null && _a !== void 0 ? _a : [];
-    const editor = viewInfo.editor;
-    const changes = [];
-    for (const heading of headings) {
-        const prefixRange = findHeadingPrefixRange(editor, heading, { alphabet: true, roman: true });
-        if (prefixRange === undefined)
-            return;
-        const headingHashString = makeHeadingHashString(editor, heading);
-        if (headingHashString === undefined)
-            return;
-        replaceRangeEconomically(editor, changes, prefixRange, headingHashString + ' ');
-    }
-    if (changes.length > 0) {
-        editor.transaction({
-            changes: changes
-        });
-    }
-};
-
-class NumberHeadingsPluginSettingTab extends obsidian.PluginSettingTab {
-    constructor(app, plugin) {
-        super(app, plugin);
-        this.plugin = plugin;
-    }
-    display() {
-        const { containerEl } = this;
-        containerEl.empty();
-        containerEl.createEl('h2', { text: 'Number Headings - Settings' });
-        containerEl.createEl('div', { text: 'To add numbering to your document, bring up the command window (on Mac, type CMD+P), and then type "Number Headings" to see a list of available commands.' });
-        containerEl.createEl('br', {});
-        containerEl.createEl('div', { text: 'If the document has front matter defined with the below settings, the project-wide settings defined on this screen will be ignored. You can define front matter like this:' });
-        containerEl.createEl('pre', {
-            text: `    ---
-    alias:
-    - Example Alias
-    tags:
-    - example-tag
-    number headings: first-level 1, start-at 2, max 6, 1.1, auto, contents ^toc
-    ---`
-        });
-        containerEl.createEl('div', {
-            text: `
-      The 'number headings' front matter key is used to store numbering settings specific to the file. There are four possible options
-      in the value to the right of the colon, separated by commas.
-    `
-        });
-        const ul = containerEl.createEl('ul', {});
-        const li0 = ul.createEl('li', {});
-        li0.createEl('b', { text: 'Automatic numbering' });
-        li0.createEl('span', { text: ': If \'auto\' appears, the document will be automatically numbered.' });
-        const li1 = ul.createEl('li', {});
-        li1.createEl('b', { text: 'First level to number' });
-        li1.createEl('span', { text: ': If \'first-level 2\' appears, the numbering will start at the second level' });
-        const li2 = ul.createEl('li', {});
-        li2.createEl('b', { text: 'Start numbering first heading at' });
-        li2.createEl('span', { text: ': If \'start-at C\' appears, the numbering of the first level will start at C, instead of A' });
-        const li3 = ul.createEl('li', {});
-        li3.createEl('b', { text: 'Maximum level to number' });
-        li3.createEl('span', { text: ': If \'max 6\' appears, the headings above level 6 will be skipped.' });
-        const li4 = ul.createEl('li', {});
-        li4.createEl('b', { text: 'Table of contents anchor' });
-        li4.createEl('span', { text: ': If \'contents ^toc\' appears, the heading that ends with the anchor ^toc will have a table of contents inserted beneath it.' });
-        const li41 = ul.createEl('li', {});
-        li41.createEl('b', { text: 'Skip headings anchor' });
-        li41.createEl('span', { text: ': If \'skip ^skipped\' appears, the heading that ends with the anchor ^skipped will not be numbered.' });
-        const li5 = ul.createEl('li', {});
-        li5.createEl('b', { text: 'Numbering style' });
-        li5.createEl('span', {
-            text: `:
-      A style text like '1.1', 'A.1', or '_.1.1' tells the plugin how to format the headings.
-      If a style string ends with '.' (a dot), ':' (a colon), '-' (a dash), '—' (an emdash), or ')' (a right parenthesis), the heading numbers will be separated from the heading title
-      with that symbol.`
-        });
-        const ul3 = li5.createEl('ul', {});
-        ul3.createEl('li', {
-            text: `      
-      For example, '1.1' means both top level and other headings will be numbered starting from '1'.
-    `
-        });
-        ul3.createEl('li', {
-            text: `      
-      For example, 'A.1' means top level headings will be numbered starting from 'A'.
-    `
-        });
-        ul3.createEl('li', {
-            text: `      
-      For example, '_.A.1' means top level headings will NOT be numbered, but the next levels will be numbered with letters and numbers.
-    `
-        });
-        ul3.createEl('li', {
-            text: `      
-      For example, '1.1:' means headings will look like '## 2.4: Example Heading'
-    `
-        });
-        ul3.createEl('li', {
-            text: `      
-      For example, 'A.1-' means headings will look like '## B.5- Example Heading'
-    `
-        });
-        ul3.createEl('li', {
-            text: `      
-      For example, 'I.A —' means headings will look like '## IV.A — Example Heading' (with Roman numerals)
-    `
-        });
-        const li100 = ul.createEl('li', {});
-        li100.createEl('b', { text: 'Numbering off' });
-        li100.createEl('span', { text: ': If \'off\' appears, the document will not be numbered.' });
-        new obsidian.Setting(containerEl)
-            .setName('Skip top heading level')
-            .setDesc('If selected, numbering will not be applied to the top heading level.')
-            .addToggle(toggle => toggle
-            .setValue(this.plugin.settings.skipTopLevel)
-            .setTooltip('Skip top heading level')
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.skipTopLevel = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('First heading level')
-            .setDesc('First heading level to number.')
-            .addSlider(slider => slider
-            .setLimits(1, 6, 1)
-            .setValue(this.plugin.settings.firstLevel)
-            .setDynamicTooltip()
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.firstLevel = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('Start numbering at')
-            .setDesc('Start numbering the first heading level from this value.')
-            .addText(text => text
-            .setValue(this.plugin.settings.startAt)
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.startAt = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('Maximum heading level')
-            .setDesc('Maximum heading level to number.')
-            .addSlider(slider => slider
-            .setLimits(1, 6, 1)
-            .setValue(this.plugin.settings.maxLevel)
-            .setDynamicTooltip()
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.maxLevel = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('Style for level 1 headings')
-            .setDesc('Defines the numbering style for level one headings. Valid values are 1 (for numbers) or A (for capital letters) or I (for Roman numerals).')
-            .addText(text => text
-            .setValue(this.plugin.settings.styleLevel1)
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.styleLevel1 = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('Style for lower level headings (below level 1)')
-            .setDesc('Defines the numbering style for headings below level one. Valid values are 1 (for numbers) or A (for capital letters) or I (for Roman numerals).')
-            .addText(text => text
-            .setValue(this.plugin.settings.styleLevelOther)
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.styleLevelOther = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('Automatic numbering')
-            .setDesc('Turns on automatic numbering of documents.')
-            .addToggle(toggle => toggle
-            .setValue(this.plugin.settings.auto)
-            .setTooltip('Turn on automatic numbering')
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.auto = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('Separator style')
-            .setDesc('Defines the separator style between the heading number and the heading text. Valid values are : (colon) or . (dot) or - (dash) or — (emdash) or ) (a right parenthesis). You can also leave it blank for no separator, or have a space before the separator.')
-            .addText(text => text
-            .setValue(this.plugin.settings.separator)
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.separator = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('Table of Contents Anchor')
-            .setDesc('Anchor which labels the header where a table of contents should be inserted. The anchor should be added at the end of a header. For example, ^toc.')
-            .addText(text => text
-            .setValue(this.plugin.settings.contents)
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.contents = value;
-            yield this.plugin.saveSettings();
-        })));
-        new obsidian.Setting(containerEl)
-            .setName('Skip Headings Anchor')
-            .setDesc('Anchor which labels the headers that should not be numbered. The anchor should be added at the end of a header. For example, ^skipped.')
-            .addText(text => text
-            .setValue(this.plugin.settings.skipHeadings)
-            .onChange((value) => __awaiter(this, void 0, void 0, function* () {
-            this.plugin.settings.skipHeadings = value;
-            yield this.plugin.saveSettings();
-        })));
-    }
-}
-class NumberHeadingsPlugin extends obsidian.Plugin {
-    onload() {
-        return __awaiter(this, void 0, void 0, function* () {
-            // eslint-disable-next-line no-console
-            console.info('Loading Number Headings Plugin, version ' + this.manifest.version);
-            yield this.loadSettings();
-            this.addCommand({
-                id: 'number-headings-with-options',
-                name: 'Number all headings in document (and show options)',
-                checkCallback: (checking) => {
-                    if (checking)
-                        return isViewActive(this.app);
-                    const viewInfo = getViewInfo(this.app);
-                    if (viewInfo) {
-                        const settings = getFrontMatterSettingsOrAlternative(viewInfo.data, this.settings);
-                        if (settings.off)
-                            return false;
-                        updateHeadingNumbering(viewInfo, settings);
-                        setTimeout(() => {
-                            // HACK: This must happen after a timeout so that there is time for the editor transaction to complete
-                            const postNumberingViewInfo = getViewInfo(this.app);
-                            updateTableOfContents(postNumberingViewInfo, settings);
-                        }, 3000);
-                        showNumberingDoneMessage(this.app, settings);
-                    }
-                    return false;
-                }
-            });
-            this.addCommand({
-                id: 'number-headings',
-                name: 'Number all headings in document',
-                checkCallback: (checking) => {
-                    if (checking)
-                        return isViewActive(this.app);
-                    const viewInfo = getViewInfo(this.app);
-                    if (viewInfo) {
-                        const settings = getFrontMatterSettingsOrAlternative(viewInfo.data, this.settings);
-                        if (settings.off)
-                            return false;
-                        updateHeadingNumbering(viewInfo, settings);
-                        setTimeout(() => {
-                            // HACK: This must happen after a timeout so that there is time for the editor transaction to complete
-                            const postNumberingViewInfo = getViewInfo(this.app);
-                            updateTableOfContents(postNumberingViewInfo, settings);
-                        }, 3000);
-                        // NOTE: The line below is intentionally commented out, since this command is the same as
-                        //       the above command, except for this line
-                        // showNumberingDoneMessage(this.app, settings, viewInfo)
-                    }
-                    return false;
-                }
-            });
-            this.addCommand({
-                id: 'remove-number-headings',
-                name: 'Remove numbering from all headings in document',
-                checkCallback: (checking) => {
-                    if (checking)
-                        return isViewActive(this.app);
-                    const viewInfo = getViewInfo(this.app);
-                    removeHeadingNumbering(viewInfo);
-                    return true;
-                }
-            });
-            this.addCommand({
-                id: 'save-settings-to-front-matter',
-                name: 'Save settings to front matter',
-                checkCallback: (checking) => {
-                    if (checking)
-                        return isViewActive(this.app);
-                    const viewInfo = getViewInfo(this.app);
-                    const file = this.app.workspace.getActiveFile();
-                    if (viewInfo && file) {
-                        const settings = getFrontMatterSettingsOrAlternative(viewInfo.data, this.settings);
-                        saveSettingsToFrontMatter(this.app.fileManager, file, settings);
-                    }
-                    return false;
-                }
-            });
-            this.addSettingTab(new NumberHeadingsPluginSettingTab(this.app, this));
-            this.registerInterval(window.setInterval(() => {
-                const viewInfo = getViewInfo(this.app);
-                if (viewInfo) {
-                    const settings = getFrontMatterSettingsOrAlternative(viewInfo.data, this.settings);
-                    if (settings.off)
-                        return;
-                    if (settings.auto) {
-                        updateHeadingNumbering(viewInfo, settings);
-                        setTimeout(() => {
-                            // HACK: This must happen after a timeout so that there is time for the editor transaction to complete
-                            const postNumberingViewInfo = getViewInfo(this.app);
-                            updateTableOfContents(postNumberingViewInfo, settings);
-                        }, 3000);
-                        // eslint-disable-next-line no-console
-                        console.log('Number Headings Plugin: Automatically numbered document');
-                    }
-                }
-            }, 10 * 1000));
-        });
-    }
-    loadSettings() {
-        return __awaiter(this, void 0, void 0, function* () {
-            this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
-        });
-    }
-    saveSettings() {
-        return __awaiter(this, void 0, void 0, function* () {
-            yield this.saveData(this.settings);
-        });
-    }
-}
-
-module.exports = NumberHeadingsPlugin;
-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInNyYy9hY3RpdmVWaWV3SGVscGVycy50cyIsIm5vZGVfbW9kdWxlcy9yb21hbnMvcm9tYW5zLmpzIiwic3JjL251bWJlcmluZ1Rva2Vucy50cyIsInNyYy9zZXR0aW5nc1R5cGVzLnRzIiwic3JjL3RleHRQcm9jZXNzaW5nLnRzIiwic3JjL2Zyb250TWF0dGVyLnRzIiwic3JjL21lc3NhZ2VzLnRzIiwic3JjL251bWJlcmluZy50cyIsInNyYy9tYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uXHJcblxyXG5QZXJtaXNzaW9uIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBhbmQvb3IgZGlzdHJpYnV0ZSB0aGlzIHNvZnR3YXJlIGZvciBhbnlcclxucHVycG9zZSB3aXRoIG9yIHdpdGhvdXQgZmVlIGlzIGhlcmVieSBncmFudGVkLlxyXG5cclxuVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiBBTkQgVEhFIEFVVEhPUiBESVNDTEFJTVMgQUxMIFdBUlJBTlRJRVMgV0lUSFxyXG5SRUdBUkQgVE8gVEhJUyBTT0ZUV0FSRSBJTkNMVURJTkcgQUxMIElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFlcclxuQU5EIEZJVE5FU1MuIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1IgQkUgTElBQkxFIEZPUiBBTlkgU1BFQ0lBTCwgRElSRUNULFxyXG5JTkRJUkVDVCwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIE9SIEFOWSBEQU1BR0VTIFdIQVRTT0VWRVIgUkVTVUxUSU5HIEZST01cclxuTE9TUyBPRiBVU0UsIERBVEEgT1IgUFJPRklUUywgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIE5FR0xJR0VOQ0UgT1JcclxuT1RIRVIgVE9SVElPVVMgQUNUSU9OLCBBUklTSU5HIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFVTRSBPUlxyXG5QRVJGT1JNQU5DRSBPRiBUSElTIFNPRlRXQVJFLlxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiAqL1xyXG4vKiBnbG9iYWwgUmVmbGVjdCwgUHJvbWlzZSwgU3VwcHJlc3NlZEVycm9yLCBTeW1ib2wgKi9cclxuXHJcbnZhciBleHRlbmRTdGF0aWNzID0gZnVuY3Rpb24oZCwgYikge1xyXG4gICAgZXh0ZW5kU3RhdGljcyA9IE9iamVjdC5zZXRQcm90b3R5cGVPZiB8fFxyXG4gICAgICAgICh7IF9fcHJvdG9fXzogW10gfSBpbnN0YW5jZW9mIEFycmF5ICYmIGZ1bmN0aW9uIChkLCBiKSB7IGQuX19wcm90b19fID0gYjsgfSkgfHxcclxuICAgICAgICBmdW5jdGlvbiAoZCwgYikgeyBmb3IgKHZhciBwIGluIGIpIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoYiwgcCkpIGRbcF0gPSBiW3BdOyB9O1xyXG4gICAgcmV0dXJuIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbn07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19leHRlbmRzKGQsIGIpIHtcclxuICAgIGlmICh0eXBlb2YgYiAhPT0gXCJmdW5jdGlvblwiICYmIGIgIT09IG51bGwpXHJcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkNsYXNzIGV4dGVuZHMgdmFsdWUgXCIgKyBTdHJpbmcoYikgKyBcIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsXCIpO1xyXG4gICAgZXh0ZW5kU3RhdGljcyhkLCBiKTtcclxuICAgIGZ1bmN0aW9uIF9fKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gZDsgfVxyXG4gICAgZC5wcm90b3R5cGUgPSBiID09PSBudWxsID8gT2JqZWN0LmNyZWF0ZShiKSA6IChfXy5wcm90b3R5cGUgPSBiLnByb3RvdHlwZSwgbmV3IF9fKCkpO1xyXG59XHJcblxyXG5leHBvcnQgdmFyIF9fYXNzaWduID0gZnVuY3Rpb24oKSB7XHJcbiAgICBfX2Fzc2lnbiA9IE9iamVjdC5hc3NpZ24gfHwgZnVuY3Rpb24gX19hc3NpZ24odCkge1xyXG4gICAgICAgIGZvciAodmFyIHMsIGkgPSAxLCBuID0gYXJndW1lbnRzLmxlbmd0aDsgaSA8IG47IGkrKykge1xyXG4gICAgICAgICAgICBzID0gYXJndW1lbnRzW2ldO1xyXG4gICAgICAgICAgICBmb3IgKHZhciBwIGluIHMpIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocywgcCkpIHRbcF0gPSBzW3BdO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gdDtcclxuICAgIH1cclxuICAgIHJldHVybiBfX2Fzc2lnbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19yZXN0KHMsIGUpIHtcclxuICAgIHZhciB0ID0ge307XHJcbiAgICBmb3IgKHZhciBwIGluIHMpIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocywgcCkgJiYgZS5pbmRleE9mKHApIDwgMClcclxuICAgICAgICB0W3BdID0gc1twXTtcclxuICAgIGlmIChzICE9IG51bGwgJiYgdHlwZW9mIE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMgPT09IFwiZnVuY3Rpb25cIilcclxuICAgICAgICBmb3IgKHZhciBpID0gMCwgcCA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMocyk7IGkgPCBwLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgICAgIGlmIChlLmluZGV4T2YocFtpXSkgPCAwICYmIE9iamVjdC5wcm90b3R5cGUucHJvcGVydHlJc0VudW1lcmFibGUuY2FsbChzLCBwW2ldKSlcclxuICAgICAgICAgICAgICAgIHRbcFtpXV0gPSBzW3BbaV1dO1xyXG4gICAgICAgIH1cclxuICAgIHJldHVybiB0O1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19kZWNvcmF0ZShkZWNvcmF0b3JzLCB0YXJnZXQsIGtleSwgZGVzYykge1xyXG4gICAgdmFyIGMgPSBhcmd1bWVudHMubGVuZ3RoLCByID0gYyA8IDMgPyB0YXJnZXQgOiBkZXNjID09PSBudWxsID8gZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodGFyZ2V0LCBrZXkpIDogZGVzYywgZDtcclxuICAgIGlmICh0eXBlb2YgUmVmbGVjdCA9PT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSA9PT0gXCJmdW5jdGlvblwiKSByID0gUmVmbGVjdC5kZWNvcmF0ZShkZWNvcmF0b3JzLCB0YXJnZXQsIGtleSwgZGVzYyk7XHJcbiAgICBlbHNlIGZvciAodmFyIGkgPSBkZWNvcmF0b3JzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSBpZiAoZCA9IGRlY29yYXRvcnNbaV0pIHIgPSAoYyA8IDMgPyBkKHIpIDogYyA+IDMgPyBkKHRhcmdldCwga2V5LCByKSA6IGQodGFyZ2V0LCBrZXkpKSB8fCByO1xyXG4gICAgcmV0dXJuIGMgPiAzICYmIHIgJiYgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwga2V5LCByKSwgcjtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcGFyYW0ocGFyYW1JbmRleCwgZGVjb3JhdG9yKSB7XHJcbiAgICByZXR1cm4gZnVuY3Rpb24gKHRhcmdldCwga2V5KSB7IGRlY29yYXRvcih0YXJnZXQsIGtleSwgcGFyYW1JbmRleCk7IH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZXNEZWNvcmF0ZShjdG9yLCBkZXNjcmlwdG9ySW4sIGRlY29yYXRvcnMsIGNvbnRleHRJbiwgaW5pdGlhbGl6ZXJzLCBleHRyYUluaXRpYWxpemVycykge1xyXG4gICAgZnVuY3Rpb24gYWNjZXB0KGYpIHsgaWYgKGYgIT09IHZvaWQgMCAmJiB0eXBlb2YgZiAhPT0gXCJmdW5jdGlvblwiKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiRnVuY3Rpb24gZXhwZWN0ZWRcIik7IHJldHVybiBmOyB9XHJcbiAgICB2YXIga2luZCA9IGNvbnRleHRJbi5raW5kLCBrZXkgPSBraW5kID09PSBcImdldHRlclwiID8gXCJnZXRcIiA6IGtpbmQgPT09IFwic2V0dGVyXCIgPyBcInNldFwiIDogXCJ2YWx1ZVwiO1xyXG4gICAgdmFyIHRhcmdldCA9ICFkZXNjcmlwdG9ySW4gJiYgY3RvciA/IGNvbnRleHRJbltcInN0YXRpY1wiXSA/IGN0b3IgOiBjdG9yLnByb3RvdHlwZSA6IG51bGw7XHJcbiAgICB2YXIgZGVzY3JpcHRvciA9IGRlc2NyaXB0b3JJbiB8fCAodGFyZ2V0ID8gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0YXJnZXQsIGNvbnRleHRJbi5uYW1lKSA6IHt9KTtcclxuICAgIHZhciBfLCBkb25lID0gZmFsc2U7XHJcbiAgICBmb3IgKHZhciBpID0gZGVjb3JhdG9ycy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG4gICAgICAgIHZhciBjb250ZXh0ID0ge307XHJcbiAgICAgICAgZm9yICh2YXIgcCBpbiBjb250ZXh0SW4pIGNvbnRleHRbcF0gPSBwID09PSBcImFjY2Vzc1wiID8ge30gOiBjb250ZXh0SW5bcF07XHJcbiAgICAgICAgZm9yICh2YXIgcCBpbiBjb250ZXh0SW4uYWNjZXNzKSBjb250ZXh0LmFjY2Vzc1twXSA9IGNvbnRleHRJbi5hY2Nlc3NbcF07XHJcbiAgICAgICAgY29udGV4dC5hZGRJbml0aWFsaXplciA9IGZ1bmN0aW9uIChmKSB7IGlmIChkb25lKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IGFkZCBpbml0aWFsaXplcnMgYWZ0ZXIgZGVjb3JhdGlvbiBoYXMgY29tcGxldGVkXCIpOyBleHRyYUluaXRpYWxpemVycy5wdXNoKGFjY2VwdChmIHx8IG51bGwpKTsgfTtcclxuICAgICAgICB2YXIgcmVzdWx0ID0gKDAsIGRlY29yYXRvcnNbaV0pKGtpbmQgPT09IFwiYWNjZXNzb3JcIiA/IHsgZ2V0OiBkZXNjcmlwdG9yLmdldCwgc2V0OiBkZXNjcmlwdG9yLnNldCB9IDogZGVzY3JpcHRvcltrZXldLCBjb250ZXh0KTtcclxuICAgICAgICBpZiAoa2luZCA9PT0gXCJhY2Nlc3NvclwiKSB7XHJcbiAgICAgICAgICAgIGlmIChyZXN1bHQgPT09IHZvaWQgMCkgY29udGludWU7XHJcbiAgICAgICAgICAgIGlmIChyZXN1bHQgPT09IG51bGwgfHwgdHlwZW9mIHJlc3VsdCAhPT0gXCJvYmplY3RcIikgdGhyb3cgbmV3IFR5cGVFcnJvcihcIk9iamVjdCBleHBlY3RlZFwiKTtcclxuICAgICAgICAgICAgaWYgKF8gPSBhY2NlcHQocmVzdWx0LmdldCkpIGRlc2NyaXB0b3IuZ2V0ID0gXztcclxuICAgICAgICAgICAgaWYgKF8gPSBhY2NlcHQocmVzdWx0LnNldCkpIGRlc2NyaXB0b3Iuc2V0ID0gXztcclxuICAgICAgICAgICAgaWYgKF8gPSBhY2NlcHQocmVzdWx0LmluaXQpKSBpbml0aWFsaXplcnMudW5zaGlmdChfKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSBpZiAoXyA9IGFjY2VwdChyZXN1bHQpKSB7XHJcbiAgICAgICAgICAgIGlmIChraW5kID09PSBcImZpZWxkXCIpIGluaXRpYWxpemVycy51bnNoaWZ0KF8pO1xyXG4gICAgICAgICAgICBlbHNlIGRlc2NyaXB0b3Jba2V5XSA9IF87XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgaWYgKHRhcmdldCkgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgY29udGV4dEluLm5hbWUsIGRlc2NyaXB0b3IpO1xyXG4gICAgZG9uZSA9IHRydWU7XHJcbn07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19ydW5Jbml0aWFsaXplcnModGhpc0FyZywgaW5pdGlhbGl6ZXJzLCB2YWx1ZSkge1xyXG4gICAgdmFyIHVzZVZhbHVlID0gYXJndW1lbnRzLmxlbmd0aCA+IDI7XHJcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGluaXRpYWxpemVycy5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgIHZhbHVlID0gdXNlVmFsdWUgPyBpbml0aWFsaXplcnNbaV0uY2FsbCh0aGlzQXJnLCB2YWx1ZSkgOiBpbml0aWFsaXplcnNbaV0uY2FsbCh0aGlzQXJnKTtcclxuICAgIH1cclxuICAgIHJldHVybiB1c2VWYWx1ZSA/IHZhbHVlIDogdm9pZCAwO1xyXG59O1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcHJvcEtleSh4KSB7XHJcbiAgICByZXR1cm4gdHlwZW9mIHggPT09IFwic3ltYm9sXCIgPyB4IDogXCJcIi5jb25jYXQoeCk7XHJcbn07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19zZXRGdW5jdGlvbk5hbWUoZiwgbmFtZSwgcHJlZml4KSB7XHJcbiAgICBpZiAodHlwZW9mIG5hbWUgPT09IFwic3ltYm9sXCIpIG5hbWUgPSBuYW1lLmRlc2NyaXB0aW9uID8gXCJbXCIuY29uY2F0KG5hbWUuZGVzY3JpcHRpb24sIFwiXVwiKSA6IFwiXCI7XHJcbiAgICByZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGYsIFwibmFtZVwiLCB7IGNvbmZpZ3VyYWJsZTogdHJ1ZSwgdmFsdWU6IHByZWZpeCA/IFwiXCIuY29uY2F0KHByZWZpeCwgXCIgXCIsIG5hbWUpIDogbmFtZSB9KTtcclxufTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX21ldGFkYXRhKG1ldGFkYXRhS2V5LCBtZXRhZGF0YVZhbHVlKSB7XHJcbiAgICBpZiAodHlwZW9mIFJlZmxlY3QgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIFJlZmxlY3QubWV0YWRhdGEgPT09IFwiZnVuY3Rpb25cIikgcmV0dXJuIFJlZmxlY3QubWV0YWRhdGEobWV0YWRhdGFLZXksIG1ldGFkYXRhVmFsdWUpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hd2FpdGVyKHRoaXNBcmcsIF9hcmd1bWVudHMsIFAsIGdlbmVyYXRvcikge1xyXG4gICAgZnVuY3Rpb24gYWRvcHQodmFsdWUpIHsgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgUCA/IHZhbHVlIDogbmV3IFAoZnVuY3Rpb24gKHJlc29sdmUpIHsgcmVzb2x2ZSh2YWx1ZSk7IH0pOyB9XHJcbiAgICByZXR1cm4gbmV3IChQIHx8IChQID0gUHJvbWlzZSkpKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcclxuICAgICAgICBmdW5jdGlvbiBmdWxmaWxsZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3IubmV4dCh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gcmVqZWN0ZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3JbXCJ0aHJvd1wiXSh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gc3RlcChyZXN1bHQpIHsgcmVzdWx0LmRvbmUgPyByZXNvbHZlKHJlc3VsdC52YWx1ZSkgOiBhZG9wdChyZXN1bHQudmFsdWUpLnRoZW4oZnVsZmlsbGVkLCByZWplY3RlZCk7IH1cclxuICAgICAgICBzdGVwKChnZW5lcmF0b3IgPSBnZW5lcmF0b3IuYXBwbHkodGhpc0FyZywgX2FyZ3VtZW50cyB8fCBbXSkpLm5leHQoKSk7XHJcbiAgICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZ2VuZXJhdG9yKHRoaXNBcmcsIGJvZHkpIHtcclxuICAgIHZhciBfID0geyBsYWJlbDogMCwgc2VudDogZnVuY3Rpb24oKSB7IGlmICh0WzBdICYgMSkgdGhyb3cgdFsxXTsgcmV0dXJuIHRbMV07IH0sIHRyeXM6IFtdLCBvcHM6IFtdIH0sIGYsIHksIHQsIGc7XHJcbiAgICByZXR1cm4gZyA9IHsgbmV4dDogdmVyYigwKSwgXCJ0aHJvd1wiOiB2ZXJiKDEpLCBcInJldHVyblwiOiB2ZXJiKDIpIH0sIHR5cGVvZiBTeW1ib2wgPT09IFwiZnVuY3Rpb25cIiAmJiAoZ1tTeW1ib2wuaXRlcmF0b3JdID0gZnVuY3Rpb24oKSB7IHJldHVybiB0aGlzOyB9KSwgZztcclxuICAgIGZ1bmN0aW9uIHZlcmIobikgeyByZXR1cm4gZnVuY3Rpb24gKHYpIHsgcmV0dXJuIHN0ZXAoW24sIHZdKTsgfTsgfVxyXG4gICAgZnVuY3Rpb24gc3RlcChvcCkge1xyXG4gICAgICAgIGlmIChmKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiR2VuZXJhdG9yIGlzIGFscmVhZHkgZXhlY3V0aW5nLlwiKTtcclxuICAgICAgICB3aGlsZSAoZyAmJiAoZyA9IDAsIG9wWzBdICYmIChfID0gMCkpLCBfKSB0cnkge1xyXG4gICAgICAgICAgICBpZiAoZiA9IDEsIHkgJiYgKHQgPSBvcFswXSAmIDIgPyB5W1wicmV0dXJuXCJdIDogb3BbMF0gPyB5W1widGhyb3dcIl0gfHwgKCh0ID0geVtcInJldHVyblwiXSkgJiYgdC5jYWxsKHkpLCAwKSA6IHkubmV4dCkgJiYgISh0ID0gdC5jYWxsKHksIG9wWzFdKSkuZG9uZSkgcmV0dXJuIHQ7XHJcbiAgICAgICAgICAgIGlmICh5ID0gMCwgdCkgb3AgPSBbb3BbMF0gJiAyLCB0LnZhbHVlXTtcclxuICAgICAgICAgICAgc3dpdGNoIChvcFswXSkge1xyXG4gICAgICAgICAgICAgICAgY2FzZSAwOiBjYXNlIDE6IHQgPSBvcDsgYnJlYWs7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDQ6IF8ubGFiZWwrKzsgcmV0dXJuIHsgdmFsdWU6IG9wWzFdLCBkb25lOiBmYWxzZSB9O1xyXG4gICAgICAgICAgICAgICAgY2FzZSA1OiBfLmxhYmVsKys7IHkgPSBvcFsxXTsgb3AgPSBbMF07IGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgY2FzZSA3OiBvcCA9IF8ub3BzLnBvcCgpOyBfLnRyeXMucG9wKCk7IGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcclxuICAgICAgICAgICAgICAgICAgICBpZiAoISh0ID0gXy50cnlzLCB0ID0gdC5sZW5ndGggPiAwICYmIHRbdC5sZW5ndGggLSAxXSkgJiYgKG9wWzBdID09PSA2IHx8IG9wWzBdID09PSAyKSkgeyBfID0gMDsgY29udGludWU7IH1cclxuICAgICAgICAgICAgICAgICAgICBpZiAob3BbMF0gPT09IDMgJiYgKCF0IHx8IChvcFsxXSA+IHRbMF0gJiYgb3BbMV0gPCB0WzNdKSkpIHsgXy5sYWJlbCA9IG9wWzFdOyBicmVhazsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGlmIChvcFswXSA9PT0gNiAmJiBfLmxhYmVsIDwgdFsxXSkgeyBfLmxhYmVsID0gdFsxXTsgdCA9IG9wOyBicmVhazsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGlmICh0ICYmIF8ubGFiZWwgPCB0WzJdKSB7IF8ubGFiZWwgPSB0WzJdOyBfLm9wcy5wdXNoKG9wKTsgYnJlYWs7IH1cclxuICAgICAgICAgICAgICAgICAgICBpZiAodFsyXSkgXy5vcHMucG9wKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgXy50cnlzLnBvcCgpOyBjb250aW51ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBvcCA9IGJvZHkuY2FsbCh0aGlzQXJnLCBfKTtcclxuICAgICAgICB9IGNhdGNoIChlKSB7IG9wID0gWzYsIGVdOyB5ID0gMDsgfSBmaW5hbGx5IHsgZiA9IHQgPSAwOyB9XHJcbiAgICAgICAgaWYgKG9wWzBdICYgNSkgdGhyb3cgb3BbMV07IHJldHVybiB7IHZhbHVlOiBvcFswXSA/IG9wWzFdIDogdm9pZCAwLCBkb25lOiB0cnVlIH07XHJcbiAgICB9XHJcbn1cclxuXHJcbmV4cG9ydCB2YXIgX19jcmVhdGVCaW5kaW5nID0gT2JqZWN0LmNyZWF0ZSA/IChmdW5jdGlvbihvLCBtLCBrLCBrMikge1xyXG4gICAgaWYgKGsyID09PSB1bmRlZmluZWQpIGsyID0gaztcclxuICAgIHZhciBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihtLCBrKTtcclxuICAgIGlmICghZGVzYyB8fCAoXCJnZXRcIiBpbiBkZXNjID8gIW0uX19lc01vZHVsZSA6IGRlc2Mud3JpdGFibGUgfHwgZGVzYy5jb25maWd1cmFibGUpKSB7XHJcbiAgICAgICAgZGVzYyA9IHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBmdW5jdGlvbigpIHsgcmV0dXJuIG1ba107IH0gfTtcclxuICAgIH1cclxuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShvLCBrMiwgZGVzYyk7XHJcbn0pIDogKGZ1bmN0aW9uKG8sIG0sIGssIGsyKSB7XHJcbiAgICBpZiAoazIgPT09IHVuZGVmaW5lZCkgazIgPSBrO1xyXG4gICAgb1trMl0gPSBtW2tdO1xyXG59KTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2V4cG9ydFN0YXIobSwgbykge1xyXG4gICAgZm9yICh2YXIgcCBpbiBtKSBpZiAocCAhPT0gXCJkZWZhdWx0XCIgJiYgIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvLCBwKSkgX19jcmVhdGVCaW5kaW5nKG8sIG0sIHApO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX192YWx1ZXMobykge1xyXG4gICAgdmFyIHMgPSB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgU3ltYm9sLml0ZXJhdG9yLCBtID0gcyAmJiBvW3NdLCBpID0gMDtcclxuICAgIGlmIChtKSByZXR1cm4gbS5jYWxsKG8pO1xyXG4gICAgaWYgKG8gJiYgdHlwZW9mIG8ubGVuZ3RoID09PSBcIm51bWJlclwiKSByZXR1cm4ge1xyXG4gICAgICAgIG5leHQ6IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgaWYgKG8gJiYgaSA+PSBvLmxlbmd0aCkgbyA9IHZvaWQgMDtcclxuICAgICAgICAgICAgcmV0dXJuIHsgdmFsdWU6IG8gJiYgb1tpKytdLCBkb25lOiAhbyB9O1xyXG4gICAgICAgIH1cclxuICAgIH07XHJcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKHMgPyBcIk9iamVjdCBpcyBub3QgaXRlcmFibGUuXCIgOiBcIlN5bWJvbC5pdGVyYXRvciBpcyBub3QgZGVmaW5lZC5cIik7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3JlYWQobywgbikge1xyXG4gICAgdmFyIG0gPSB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgb1tTeW1ib2wuaXRlcmF0b3JdO1xyXG4gICAgaWYgKCFtKSByZXR1cm4gbztcclxuICAgIHZhciBpID0gbS5jYWxsKG8pLCByLCBhciA9IFtdLCBlO1xyXG4gICAgdHJ5IHtcclxuICAgICAgICB3aGlsZSAoKG4gPT09IHZvaWQgMCB8fCBuLS0gPiAwKSAmJiAhKHIgPSBpLm5leHQoKSkuZG9uZSkgYXIucHVzaChyLnZhbHVlKTtcclxuICAgIH1cclxuICAgIGNhdGNoIChlcnJvcikgeyBlID0geyBlcnJvcjogZXJyb3IgfTsgfVxyXG4gICAgZmluYWxseSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgaWYgKHIgJiYgIXIuZG9uZSAmJiAobSA9IGlbXCJyZXR1cm5cIl0pKSBtLmNhbGwoaSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGZpbmFsbHkgeyBpZiAoZSkgdGhyb3cgZS5lcnJvcjsgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIGFyO1xyXG59XHJcblxyXG4vKiogQGRlcHJlY2F0ZWQgKi9cclxuZXhwb3J0IGZ1bmN0aW9uIF9fc3ByZWFkKCkge1xyXG4gICAgZm9yICh2YXIgYXIgPSBbXSwgaSA9IDA7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspXHJcbiAgICAgICAgYXIgPSBhci5jb25jYXQoX19yZWFkKGFyZ3VtZW50c1tpXSkpO1xyXG4gICAgcmV0dXJuIGFyO1xyXG59XHJcblxyXG4vKiogQGRlcHJlY2F0ZWQgKi9cclxuZXhwb3J0IGZ1bmN0aW9uIF9fc3ByZWFkQXJyYXlzKCkge1xyXG4gICAgZm9yICh2YXIgcyA9IDAsIGkgPSAwLCBpbCA9IGFyZ3VtZW50cy5sZW5ndGg7IGkgPCBpbDsgaSsrKSBzICs9IGFyZ3VtZW50c1tpXS5sZW5ndGg7XHJcbiAgICBmb3IgKHZhciByID0gQXJyYXkocyksIGsgPSAwLCBpID0gMDsgaSA8IGlsOyBpKyspXHJcbiAgICAgICAgZm9yICh2YXIgYSA9IGFyZ3VtZW50c1tpXSwgaiA9IDAsIGpsID0gYS5sZW5ndGg7IGogPCBqbDsgaisrLCBrKyspXHJcbiAgICAgICAgICAgIHJba10gPSBhW2pdO1xyXG4gICAgcmV0dXJuIHI7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3NwcmVhZEFycmF5KHRvLCBmcm9tLCBwYWNrKSB7XHJcbiAgICBpZiAocGFjayB8fCBhcmd1bWVudHMubGVuZ3RoID09PSAyKSBmb3IgKHZhciBpID0gMCwgbCA9IGZyb20ubGVuZ3RoLCBhcjsgaSA8IGw7IGkrKykge1xyXG4gICAgICAgIGlmIChhciB8fCAhKGkgaW4gZnJvbSkpIHtcclxuICAgICAgICAgICAgaWYgKCFhcikgYXIgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChmcm9tLCAwLCBpKTtcclxuICAgICAgICAgICAgYXJbaV0gPSBmcm9tW2ldO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiB0by5jb25jYXQoYXIgfHwgQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoZnJvbSkpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hd2FpdCh2KSB7XHJcbiAgICByZXR1cm4gdGhpcyBpbnN0YW5jZW9mIF9fYXdhaXQgPyAodGhpcy52ID0gdiwgdGhpcykgOiBuZXcgX19hd2FpdCh2KTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fYXN5bmNHZW5lcmF0b3IodGhpc0FyZywgX2FyZ3VtZW50cywgZ2VuZXJhdG9yKSB7XHJcbiAgICBpZiAoIVN5bWJvbC5hc3luY0l0ZXJhdG9yKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiU3ltYm9sLmFzeW5jSXRlcmF0b3IgaXMgbm90IGRlZmluZWQuXCIpO1xyXG4gICAgdmFyIGcgPSBnZW5lcmF0b3IuYXBwbHkodGhpc0FyZywgX2FyZ3VtZW50cyB8fCBbXSksIGksIHEgPSBbXTtcclxuICAgIHJldHVybiBpID0ge30sIHZlcmIoXCJuZXh0XCIpLCB2ZXJiKFwidGhyb3dcIiksIHZlcmIoXCJyZXR1cm5cIiksIGlbU3ltYm9sLmFzeW5jSXRlcmF0b3JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfSwgaTtcclxuICAgIGZ1bmN0aW9uIHZlcmIobikgeyBpZiAoZ1tuXSkgaVtuXSA9IGZ1bmN0aW9uICh2KSB7IHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbiAoYSwgYikgeyBxLnB1c2goW24sIHYsIGEsIGJdKSA+IDEgfHwgcmVzdW1lKG4sIHYpOyB9KTsgfTsgfVxyXG4gICAgZnVuY3Rpb24gcmVzdW1lKG4sIHYpIHsgdHJ5IHsgc3RlcChnW25dKHYpKTsgfSBjYXRjaCAoZSkgeyBzZXR0bGUocVswXVszXSwgZSk7IH0gfVxyXG4gICAgZnVuY3Rpb24gc3RlcChyKSB7IHIudmFsdWUgaW5zdGFuY2VvZiBfX2F3YWl0ID8gUHJvbWlzZS5yZXNvbHZlKHIudmFsdWUudikudGhlbihmdWxmaWxsLCByZWplY3QpIDogc2V0dGxlKHFbMF1bMl0sIHIpOyB9XHJcbiAgICBmdW5jdGlvbiBmdWxmaWxsKHZhbHVlKSB7IHJlc3VtZShcIm5leHRcIiwgdmFsdWUpOyB9XHJcbiAgICBmdW5jdGlvbiByZWplY3QodmFsdWUpIHsgcmVzdW1lKFwidGhyb3dcIiwgdmFsdWUpOyB9XHJcbiAgICBmdW5jdGlvbiBzZXR0bGUoZiwgdikgeyBpZiAoZih2KSwgcS5zaGlmdCgpLCBxLmxlbmd0aCkgcmVzdW1lKHFbMF1bMF0sIHFbMF1bMV0pOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2FzeW5jRGVsZWdhdG9yKG8pIHtcclxuICAgIHZhciBpLCBwO1xyXG4gICAgcmV0dXJuIGkgPSB7fSwgdmVyYihcIm5leHRcIiksIHZlcmIoXCJ0aHJvd1wiLCBmdW5jdGlvbiAoZSkgeyB0aHJvdyBlOyB9KSwgdmVyYihcInJldHVyblwiKSwgaVtTeW1ib2wuaXRlcmF0b3JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfSwgaTtcclxuICAgIGZ1bmN0aW9uIHZlcmIobiwgZikgeyBpW25dID0gb1tuXSA/IGZ1bmN0aW9uICh2KSB7IHJldHVybiAocCA9ICFwKSA/IHsgdmFsdWU6IF9fYXdhaXQob1tuXSh2KSksIGRvbmU6IGZhbHNlIH0gOiBmID8gZih2KSA6IHY7IH0gOiBmOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2FzeW5jVmFsdWVzKG8pIHtcclxuICAgIGlmICghU3ltYm9sLmFzeW5jSXRlcmF0b3IpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJTeW1ib2wuYXN5bmNJdGVyYXRvciBpcyBub3QgZGVmaW5lZC5cIik7XHJcbiAgICB2YXIgbSA9IG9bU3ltYm9sLmFzeW5jSXRlcmF0b3JdLCBpO1xyXG4gICAgcmV0dXJuIG0gPyBtLmNhbGwobykgOiAobyA9IHR5cGVvZiBfX3ZhbHVlcyA9PT0gXCJmdW5jdGlvblwiID8gX192YWx1ZXMobykgOiBvW1N5bWJvbC5pdGVyYXRvcl0oKSwgaSA9IHt9LCB2ZXJiKFwibmV4dFwiKSwgdmVyYihcInRocm93XCIpLCB2ZXJiKFwicmV0dXJuXCIpLCBpW1N5bWJvbC5hc3luY0l0ZXJhdG9yXSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXM7IH0sIGkpO1xyXG4gICAgZnVuY3Rpb24gdmVyYihuKSB7IGlbbl0gPSBvW25dICYmIGZ1bmN0aW9uICh2KSB7IHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7IHYgPSBvW25dKHYpLCBzZXR0bGUocmVzb2x2ZSwgcmVqZWN0LCB2LmRvbmUsIHYudmFsdWUpOyB9KTsgfTsgfVxyXG4gICAgZnVuY3Rpb24gc2V0dGxlKHJlc29sdmUsIHJlamVjdCwgZCwgdikgeyBQcm9taXNlLnJlc29sdmUodikudGhlbihmdW5jdGlvbih2KSB7IHJlc29sdmUoeyB2YWx1ZTogdiwgZG9uZTogZCB9KTsgfSwgcmVqZWN0KTsgfVxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19tYWtlVGVtcGxhdGVPYmplY3QoY29va2VkLCByYXcpIHtcclxuICAgIGlmIChPYmplY3QuZGVmaW5lUHJvcGVydHkpIHsgT2JqZWN0LmRlZmluZVByb3BlcnR5KGNvb2tlZCwgXCJyYXdcIiwgeyB2YWx1ZTogcmF3IH0pOyB9IGVsc2UgeyBjb29rZWQucmF3ID0gcmF3OyB9XHJcbiAgICByZXR1cm4gY29va2VkO1xyXG59O1xyXG5cclxudmFyIF9fc2V0TW9kdWxlRGVmYXVsdCA9IE9iamVjdC5jcmVhdGUgPyAoZnVuY3Rpb24obywgdikge1xyXG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KG8sIFwiZGVmYXVsdFwiLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2IH0pO1xyXG59KSA6IGZ1bmN0aW9uKG8sIHYpIHtcclxuICAgIG9bXCJkZWZhdWx0XCJdID0gdjtcclxufTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2ltcG9ydFN0YXIobW9kKSB7XHJcbiAgICBpZiAobW9kICYmIG1vZC5fX2VzTW9kdWxlKSByZXR1cm4gbW9kO1xyXG4gICAgdmFyIHJlc3VsdCA9IHt9O1xyXG4gICAgaWYgKG1vZCAhPSBudWxsKSBmb3IgKHZhciBrIGluIG1vZCkgaWYgKGsgIT09IFwiZGVmYXVsdFwiICYmIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChtb2QsIGspKSBfX2NyZWF0ZUJpbmRpbmcocmVzdWx0LCBtb2QsIGspO1xyXG4gICAgX19zZXRNb2R1bGVEZWZhdWx0KHJlc3VsdCwgbW9kKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2ltcG9ydERlZmF1bHQobW9kKSB7XHJcbiAgICByZXR1cm4gKG1vZCAmJiBtb2QuX19lc01vZHVsZSkgPyBtb2QgOiB7IGRlZmF1bHQ6IG1vZCB9O1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19jbGFzc1ByaXZhdGVGaWVsZEdldChyZWNlaXZlciwgc3RhdGUsIGtpbmQsIGYpIHtcclxuICAgIGlmIChraW5kID09PSBcImFcIiAmJiAhZikgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlByaXZhdGUgYWNjZXNzb3Igd2FzIGRlZmluZWQgd2l0aG91dCBhIGdldHRlclwiKTtcclxuICAgIGlmICh0eXBlb2Ygc3RhdGUgPT09IFwiZnVuY3Rpb25cIiA/IHJlY2VpdmVyICE9PSBzdGF0ZSB8fCAhZiA6ICFzdGF0ZS5oYXMocmVjZWl2ZXIpKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IHJlYWQgcHJpdmF0ZSBtZW1iZXIgZnJvbSBhbiBvYmplY3Qgd2hvc2UgY2xhc3MgZGlkIG5vdCBkZWNsYXJlIGl0XCIpO1xyXG4gICAgcmV0dXJuIGtpbmQgPT09IFwibVwiID8gZiA6IGtpbmQgPT09IFwiYVwiID8gZi5jYWxsKHJlY2VpdmVyKSA6IGYgPyBmLnZhbHVlIDogc3RhdGUuZ2V0KHJlY2VpdmVyKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fY2xhc3NQcml2YXRlRmllbGRTZXQocmVjZWl2ZXIsIHN0YXRlLCB2YWx1ZSwga2luZCwgZikge1xyXG4gICAgaWYgKGtpbmQgPT09IFwibVwiKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiUHJpdmF0ZSBtZXRob2QgaXMgbm90IHdyaXRhYmxlXCIpO1xyXG4gICAgaWYgKGtpbmQgPT09IFwiYVwiICYmICFmKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiUHJpdmF0ZSBhY2Nlc3NvciB3YXMgZGVmaW5lZCB3aXRob3V0IGEgc2V0dGVyXCIpO1xyXG4gICAgaWYgKHR5cGVvZiBzdGF0ZSA9PT0gXCJmdW5jdGlvblwiID8gcmVjZWl2ZXIgIT09IHN0YXRlIHx8ICFmIDogIXN0YXRlLmhhcyhyZWNlaXZlcikpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3Qgd3JpdGUgcHJpdmF0ZSBtZW1iZXIgdG8gYW4gb2JqZWN0IHdob3NlIGNsYXNzIGRpZCBub3QgZGVjbGFyZSBpdFwiKTtcclxuICAgIHJldHVybiAoa2luZCA9PT0gXCJhXCIgPyBmLmNhbGwocmVjZWl2ZXIsIHZhbHVlKSA6IGYgPyBmLnZhbHVlID0gdmFsdWUgOiBzdGF0ZS5zZXQocmVjZWl2ZXIsIHZhbHVlKSksIHZhbHVlO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19jbGFzc1ByaXZhdGVGaWVsZEluKHN0YXRlLCByZWNlaXZlcikge1xyXG4gICAgaWYgKHJlY2VpdmVyID09PSBudWxsIHx8ICh0eXBlb2YgcmVjZWl2ZXIgIT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIHJlY2VpdmVyICE9PSBcImZ1bmN0aW9uXCIpKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IHVzZSAnaW4nIG9wZXJhdG9yIG9uIG5vbi1vYmplY3RcIik7XHJcbiAgICByZXR1cm4gdHlwZW9mIHN0YXRlID09PSBcImZ1bmN0aW9uXCIgPyByZWNlaXZlciA9PT0gc3RhdGUgOiBzdGF0ZS5oYXMocmVjZWl2ZXIpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hZGREaXNwb3NhYmxlUmVzb3VyY2UoZW52LCB2YWx1ZSwgYXN5bmMpIHtcclxuICAgIGlmICh2YWx1ZSAhPT0gbnVsbCAmJiB2YWx1ZSAhPT0gdm9pZCAwKSB7XHJcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgdmFsdWUgIT09IFwiZnVuY3Rpb25cIikgdGhyb3cgbmV3IFR5cGVFcnJvcihcIk9iamVjdCBleHBlY3RlZC5cIik7XHJcbiAgICAgICAgdmFyIGRpc3Bvc2U7XHJcbiAgICAgICAgaWYgKGFzeW5jKSB7XHJcbiAgICAgICAgICAgIGlmICghU3ltYm9sLmFzeW5jRGlzcG9zZSkgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN5bWJvbC5hc3luY0Rpc3Bvc2UgaXMgbm90IGRlZmluZWQuXCIpO1xyXG4gICAgICAgICAgICBkaXNwb3NlID0gdmFsdWVbU3ltYm9sLmFzeW5jRGlzcG9zZV07XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChkaXNwb3NlID09PSB2b2lkIDApIHtcclxuICAgICAgICAgICAgaWYgKCFTeW1ib2wuZGlzcG9zZSkgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN5bWJvbC5kaXNwb3NlIGlzIG5vdCBkZWZpbmVkLlwiKTtcclxuICAgICAgICAgICAgZGlzcG9zZSA9IHZhbHVlW1N5bWJvbC5kaXNwb3NlXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKHR5cGVvZiBkaXNwb3NlICE9PSBcImZ1bmN0aW9uXCIpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJPYmplY3Qgbm90IGRpc3Bvc2FibGUuXCIpO1xyXG4gICAgICAgIGVudi5zdGFjay5wdXNoKHsgdmFsdWU6IHZhbHVlLCBkaXNwb3NlOiBkaXNwb3NlLCBhc3luYzogYXN5bmMgfSk7XHJcbiAgICB9XHJcbiAgICBlbHNlIGlmIChhc3luYykge1xyXG4gICAgICAgIGVudi5zdGFjay5wdXNoKHsgYXN5bmM6IHRydWUgfSk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gdmFsdWU7XHJcbn1cclxuXHJcbnZhciBfU3VwcHJlc3NlZEVycm9yID0gdHlwZW9mIFN1cHByZXNzZWRFcnJvciA9PT0gXCJmdW5jdGlvblwiID8gU3VwcHJlc3NlZEVycm9yIDogZnVuY3Rpb24gKGVycm9yLCBzdXBwcmVzc2VkLCBtZXNzYWdlKSB7XHJcbiAgICB2YXIgZSA9IG5ldyBFcnJvcihtZXNzYWdlKTtcclxuICAgIHJldHVybiBlLm5hbWUgPSBcIlN1cHByZXNzZWRFcnJvclwiLCBlLmVycm9yID0gZXJyb3IsIGUuc3VwcHJlc3NlZCA9IHN1cHByZXNzZWQsIGU7XHJcbn07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19kaXNwb3NlUmVzb3VyY2VzKGVudikge1xyXG4gICAgZnVuY3Rpb24gZmFpbChlKSB7XHJcbiAgICAgICAgZW52LmVycm9yID0gZW52Lmhhc0Vycm9yID8gbmV3IF9TdXBwcmVzc2VkRXJyb3IoZSwgZW52LmVycm9yLCBcIkFuIGVycm9yIHdhcyBzdXBwcmVzc2VkIGR1cmluZyBkaXNwb3NhbC5cIikgOiBlO1xyXG4gICAgICAgIGVudi5oYXNFcnJvciA9IHRydWU7XHJcbiAgICB9XHJcbiAgICBmdW5jdGlvbiBuZXh0KCkge1xyXG4gICAgICAgIHdoaWxlIChlbnYuc3RhY2subGVuZ3RoKSB7XHJcbiAgICAgICAgICAgIHZhciByZWMgPSBlbnYuc3RhY2sucG9wKCk7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICB2YXIgcmVzdWx0ID0gcmVjLmRpc3Bvc2UgJiYgcmVjLmRpc3Bvc2UuY2FsbChyZWMudmFsdWUpO1xyXG4gICAgICAgICAgICAgICAgaWYgKHJlYy5hc3luYykgcmV0dXJuIFByb21pc2UucmVzb2x2ZShyZXN1bHQpLnRoZW4obmV4dCwgZnVuY3Rpb24oZSkgeyBmYWlsKGUpOyByZXR1cm4gbmV4dCgpOyB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBjYXRjaCAoZSkge1xyXG4gICAgICAgICAgICAgICAgZmFpbChlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoZW52Lmhhc0Vycm9yKSB0aHJvdyBlbnYuZXJyb3I7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgICBfX2V4dGVuZHM6IF9fZXh0ZW5kcyxcclxuICAgIF9fYXNzaWduOiBfX2Fzc2lnbixcclxuICAgIF9fcmVzdDogX19yZXN0LFxyXG4gICAgX19kZWNvcmF0ZTogX19kZWNvcmF0ZSxcclxuICAgIF9fcGFyYW06IF9fcGFyYW0sXHJcbiAgICBfX21ldGFkYXRhOiBfX21ldGFkYXRhLFxyXG4gICAgX19hd2FpdGVyOiBfX2F3YWl0ZXIsXHJcbiAgICBfX2dlbmVyYXRvcjogX19nZW5lcmF0b3IsXHJcbiAgICBfX2NyZWF0ZUJpbmRpbmc6IF9fY3JlYXRlQmluZGluZyxcclxuICAgIF9fZXhwb3J0U3RhcjogX19leHBvcnRTdGFyLFxyXG4gICAgX192YWx1ZXM6IF9fdmFsdWVzLFxyXG4gICAgX19yZWFkOiBfX3JlYWQsXHJcbiAgICBfX3NwcmVhZDogX19zcHJlYWQsXHJcbiAgICBfX3NwcmVhZEFycmF5czogX19zcHJlYWRBcnJheXMsXHJcbiAgICBfX3NwcmVhZEFycmF5OiBfX3NwcmVhZEFycmF5LFxyXG4gICAgX19hd2FpdDogX19hd2FpdCxcclxuICAgIF9fYXN5bmNHZW5lcmF0b3I6IF9fYXN5bmNHZW5lcmF0b3IsXHJcbiAgICBfX2FzeW5jRGVsZWdhdG9yOiBfX2FzeW5jRGVsZWdhdG9yLFxyXG4gICAgX19hc3luY1ZhbHVlczogX19hc3luY1ZhbHVlcyxcclxuICAgIF9fbWFrZVRlbXBsYXRlT2JqZWN0OiBfX21ha2VUZW1wbGF0ZU9iamVjdCxcclxuICAgIF9faW1wb3J0U3RhcjogX19pbXBvcnRTdGFyLFxyXG4gICAgX19pbXBvcnREZWZhdWx0OiBfX2ltcG9ydERlZmF1bHQsXHJcbiAgICBfX2NsYXNzUHJpdmF0ZUZpZWxkR2V0OiBfX2NsYXNzUHJpdmF0ZUZpZWxkR2V0LFxyXG4gICAgX19jbGFzc1ByaXZhdGVGaWVsZFNldDogX19jbGFzc1ByaXZhdGVGaWVsZFNldCxcclxuICAgIF9fY2xhc3NQcml2YXRlRmllbGRJbjogX19jbGFzc1ByaXZhdGVGaWVsZEluLFxyXG4gICAgX19hZGREaXNwb3NhYmxlUmVzb3VyY2U6IF9fYWRkRGlzcG9zYWJsZVJlc291cmNlLFxyXG4gICAgX19kaXNwb3NlUmVzb3VyY2VzOiBfX2Rpc3Bvc2VSZXNvdXJjZXMsXHJcbn07XHJcbiIsImltcG9ydCB7IEFwcCwgQ2FjaGVkTWV0YWRhdGEsIEVkaXRvciwgTWFya2Rvd25WaWV3IH0gZnJvbSAnb2JzaWRpYW4nXG5cbmZ1bmN0aW9uIGdldEFjdGl2ZVZpZXcoYXBwOiBBcHApOiBNYXJrZG93blZpZXcgfCB1bmRlZmluZWQge1xuICBjb25zdCBhY3RpdmVWaWV3ID0gYXBwLndvcmtzcGFjZS5nZXRBY3RpdmVWaWV3T2ZUeXBlKE1hcmtkb3duVmlldylcbiAgcmV0dXJuIGFjdGl2ZVZpZXcgPz8gdW5kZWZpbmVkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1ZpZXdBY3RpdmUoYXBwOiBBcHApOiBib29sZWFuIHtcbiAgY29uc3QgYWN0aXZlVmlldyA9IGdldEFjdGl2ZVZpZXcoYXBwKVxuICBpZiAoYWN0aXZlVmlldyAmJiBhY3RpdmVWaWV3LmZpbGUpIHJldHVybiB0cnVlXG4gIHJldHVybiBmYWxzZVxufVxuXG5mdW5jdGlvbiBnZXRWaWV3TWV0YWRhdGEoYXBwOiBBcHApOiBDYWNoZWRNZXRhZGF0YSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGFjdGl2ZVZpZXcgPSBnZXRBY3RpdmVWaWV3KGFwcClcbiAgaWYgKGFjdGl2ZVZpZXcgJiYgYWN0aXZlVmlldy5maWxlKSB7XG4gICAgY29uc3QgZGF0YSA9IGFwcC5tZXRhZGF0YUNhY2hlLmdldEZpbGVDYWNoZShhY3RpdmVWaWV3LmZpbGUpIHx8IHt9XG4gICAgcmV0dXJuIGRhdGFcbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVmlld0luZm8ge1xuICBhY3RpdmVWaWV3OiBNYXJrZG93blZpZXdcbiAgZGF0YTogQ2FjaGVkTWV0YWRhdGFcbiAgZWRpdG9yOiBFZGl0b3Jcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFZpZXdJbmZvKGFwcDogQXBwKTogVmlld0luZm8gfCB1bmRlZmluZWQge1xuICBjb25zdCBhY3RpdmVWaWV3ID0gZ2V0QWN0aXZlVmlldyhhcHApXG4gIGNvbnN0IGRhdGEgPSBnZXRWaWV3TWV0YWRhdGEoYXBwKVxuICBjb25zdCBlZGl0b3IgPSBhY3RpdmVWaWV3ID8gYWN0aXZlVmlldy5lZGl0b3IgOiB1bmRlZmluZWRcblxuICBpZiAoYWN0aXZlVmlldyAmJiBkYXRhICYmIGVkaXRvcikge1xuICAgIHJldHVybiB7XG4gICAgICBhY3RpdmVWaWV3LCBkYXRhLCBlZGl0b3JcbiAgICB9XG4gIH1cblxuICByZXR1cm4gdW5kZWZpbmVkXG59XG4iLCJjb25zdCByb21hbl9tYXAgPSB7XG4gIE06IDEwMDAsXG4gIENNOiA5MDAsXG4gIEQ6IDUwMCxcbiAgQ0Q6IDQwMCxcbiAgQzogMTAwLFxuICBYQzogOTAsXG4gIEw6IDUwLFxuICBYTDogNDAsXG4gIFg6IDEwLFxuICBJWDogOSxcbiAgVjogNSxcbiAgSVY6IDQsXG4gIEk6IDFcbn1cblxuY29uc3QgYWxsQ2hhcnMgPSBPYmplY3Qua2V5cyhyb21hbl9tYXApXG5jb25zdCBhbGxOdW1lcmFscyA9IE9iamVjdC52YWx1ZXMocm9tYW5fbWFwKVxuY29uc3Qgcm9tYW5QYXR0ZXJuID1cbiAgL14oTXsxLDR9KENNfENEfEQ/Q3swLDN9KShYQ3xYTHxMP1h7MCwzfSkoSVh8SVZ8Vj9JezAsM30pfE17MCw0fShDTXxDP0R8RD9DezEsM30pKFhDfFhMfEw/WHswLDN9KShJWHxJVnxWP0l7MCwzfSl8TXswLDR9KENNfENEfEQ/Q3swLDN9KShYQ3xYP0x8TD9YezEsM30pKElYfElWfFY/SXswLDN9KXxNezAsNH0oQ018Q0R8RD9DezAsM30pKFhDfFhMfEw/WHswLDN9KShJWHxJP1Z8Vj9JezEsM30pKSQvXG5cbmNvbnN0IHJvbWFuaXplID0gKGRlY2ltYWwpID0+IHtcbiAgaWYgKFxuICAgIGRlY2ltYWwgPD0gMCB8fFxuICAgIHR5cGVvZiBkZWNpbWFsICE9PSAnbnVtYmVyJyB8fFxuICAgIE1hdGguZmxvb3IoZGVjaW1hbCkgIT09IGRlY2ltYWxcbiAgKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdyZXF1aXJlcyBhbiB1bnNpZ25lZCBpbnRlZ2VyJylcbiAgfVxuICBpZiAoZGVjaW1hbCA+PSA0MDAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdyZXF1aXJlcyBtYXggdmFsdWUgb2YgbGVzcyB0aGFuIDM5OTkgb3IgbGVzcycpXG4gIH1cbiAgbGV0IHJvbWFuID0gJydcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBhbGxDaGFycy5sZW5ndGg7IGkrKykge1xuICAgIHdoaWxlIChkZWNpbWFsID49IGFsbE51bWVyYWxzW2ldKSB7XG4gICAgICBkZWNpbWFsIC09IGFsbE51bWVyYWxzW2ldXG4gICAgICByb21hbiArPSBhbGxDaGFyc1tpXVxuICAgIH1cbiAgfVxuICByZXR1cm4gcm9tYW5cbn1cblxuY29uc3QgZGVyb21hbml6ZSA9IChyb21hblN0cikgPT4ge1xuICBpZiAodHlwZW9mIHJvbWFuU3RyICE9PSAnc3RyaW5nJykge1xuICAgIHRocm93IG5ldyBFcnJvcigncmVxdWlyZXMgYSBzdHJpbmcnKVxuICB9XG4gIGlmICghcm9tYW5QYXR0ZXJuLnRlc3Qocm9tYW5TdHIpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdyZXF1aXJlcyB2YWxpZCByb21hbiBudW1lcmFsIHN0cmluZycpXG4gIH1cbiAgbGV0IHJvbWFuU3RyaW5nID0gcm9tYW5TdHIudG9VcHBlckNhc2UoKVxuICBsZXQgYXJhYmljID0gMFxuICBsZXQgaXRlcmF0aW9uID0gcm9tYW5TdHJpbmcubGVuZ3RoXG4gIHdoaWxlIChpdGVyYXRpb24tLSkge1xuICAgIGxldCBjdW11bGF0aXZlID0gcm9tYW5fbWFwW3JvbWFuU3RyaW5nW2l0ZXJhdGlvbl1dXG4gICAgaWYgKGN1bXVsYXRpdmUgPCByb21hbl9tYXBbcm9tYW5TdHJpbmdbaXRlcmF0aW9uICsgMV1dKSB7XG4gICAgICBhcmFiaWMgLT0gY3VtdWxhdGl2ZVxuICAgIH0gZWxzZSB7XG4gICAgICBhcmFiaWMgKz0gY3VtdWxhdGl2ZVxuICAgIH1cbiAgfVxuICByZXR1cm4gYXJhYmljXG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBkZXJvbWFuaXplLFxuICByb21hbml6ZSxcbiAgYWxsQ2hhcnMsXG4gIGFsbE51bWVyYWxzXG59XG4iLCJpbXBvcnQgeyBkZXJvbWFuaXplLCByb21hbml6ZSB9IGZyb20gJ3JvbWFucydcblxuZXhwb3J0IHR5cGUgTnVtYmVyaW5nVG9rZW4gPSBOdW1iZXJpbmdUb2tlbkFyYWJpYyB8IE51bWJlcmluZ1Rva2VuQWxwaGFiZXQgfCBOdW1iZXJpbmdUb2tlblJvbWFuXG5leHBvcnQgdHlwZSBOdW1iZXJpbmdUb2tlbkFyYWJpYyA9IHtcbiAgc3R5bGU6ICcxJ1xuICB2YWx1ZTogbnVtYmVyXG59XG5leHBvcnQgdHlwZSBOdW1iZXJpbmdUb2tlbkFscGhhYmV0ID0ge1xuICBzdHlsZTogJ0EnXG4gIHZhbHVlOiBzdHJpbmdcbn1cbmV4cG9ydCB0eXBlIE51bWJlcmluZ1Rva2VuUm9tYW4gPSB7XG4gIHN0eWxlOiAnSSdcbiAgdmFsdWU6IHN0cmluZ1xufVxuZXhwb3J0IHR5cGUgTnVtYmVyaW5nU3R5bGUgPSAnMScgfCAnQScgfCAnSSdcbmV4cG9ydCB0eXBlIE51bWJlcmluZ1ZhbHVlID0gbnVtYmVyIHwgc3RyaW5nXG5cbi8vIFZhbGlkYXRlcyB0aGUgc3RyaW5nIHVzaW5nIGEgcmVnZXggdG8gZW5zdXJlIGlzIGlzIGEgdmFsaWQgYXJhYmljIG51bWJlcmluZyB2YWx1ZVxuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWRBcmFiaWNOdW1iZXJpbmdWYWx1ZVN0cmluZyhzOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3QgcmVnZXggPSAvXlswLTldKyQvXG4gIHJldHVybiByZWdleC50ZXN0KHMpXG59XG5cbi8vIFZhbGlkYXRlcyB0aGUgc3RyaW5nIHVzaW5nIGEgcmVnZXggdG8gZW5zdXJlIGlzIGlzIGEgdmFsaWQgYWxwaGFiZXQgbnVtYmVyaW5nIHZhbHVlXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZEFscGhhYmV0TnVtYmVyaW5nVmFsdWVTdHJpbmcoczogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGNvbnN0IHJlZ2V4ID0gL15bQS1aXSQvXG4gIHJldHVybiByZWdleC50ZXN0KHMpXG59XG5cbi8vIFZhbGlkYXRlcyB0aGUgc3RyaW5nIHVzaW5nIGEgcmVnZXggdG8gZW5zdXJlIGlzIGlzIGEgdmFsaWQgcm9tYW4gbnVtYmVyaW5nIHZhbHVlXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZFJvbWFuTnVtYmVyaW5nVmFsdWVTdHJpbmcoczogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGNvbnN0IHJlZ2V4ID0gL15bMElWWExDRE1dKyQvIC8vIFRoaXMgaW5jbHVkZXMgemVybyBmb3IgemVyb3RoIHRlc3RpbmdcbiAgcmV0dXJuIHJlZ2V4LnRlc3Qocylcbn1cblxuZnVuY3Rpb24gcHJpbnRhYmxlTnVtYmVyaW5nVG9rZW4odDogTnVtYmVyaW5nVG9rZW4pOiBzdHJpbmcge1xuICBzd2l0Y2ggKHQuc3R5bGUpIHtcbiAgICBjYXNlICcxJzpcbiAgICAgIHJldHVybiB0LnZhbHVlLnRvU3RyaW5nKClcbiAgICBjYXNlICdBJzpcbiAgICAgIHJldHVybiB0LnZhbHVlXG4gICAgY2FzZSAnSSc6XG4gICAgICByZXR1cm4gdC52YWx1ZVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB6ZXJvdGhOdW1iZXJpbmdUb2tlbkluU3R5bGUoc3R5bGU6IE51bWJlcmluZ1N0eWxlKTogTnVtYmVyaW5nVG9rZW4ge1xuICBzd2l0Y2ggKHN0eWxlKSB7XG4gICAgY2FzZSAnMSc6XG4gICAgICByZXR1cm4geyBzdHlsZTogJzEnLCB2YWx1ZTogMCB9XG4gICAgY2FzZSAnQSc6XG4gICAgICByZXR1cm4geyBzdHlsZTogJ0EnLCB2YWx1ZTogJ1onIH1cbiAgICBjYXNlICdJJzpcbiAgICAgIHJldHVybiB7IHN0eWxlOiAnSScsIHZhbHVlOiAnMCcgfVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmaXJzdE51bWJlcmluZ1Rva2VuSW5TdHlsZShzdHlsZTogTnVtYmVyaW5nU3R5bGUpOiBOdW1iZXJpbmdUb2tlbiB7XG4gIHN3aXRjaCAoc3R5bGUpIHtcbiAgICBjYXNlICcxJzpcbiAgICAgIHJldHVybiB7IHN0eWxlOiAnMScsIHZhbHVlOiAxIH1cbiAgICBjYXNlICdBJzpcbiAgICAgIHJldHVybiB7IHN0eWxlOiAnQScsIHZhbHVlOiAnQScgfVxuICAgIGNhc2UgJ0knOlxuICAgICAgcmV0dXJuIHsgc3R5bGU6ICdJJywgdmFsdWU6ICdJJyB9XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG5leHROdW1iZXJpbmdUb2tlbih0OiBOdW1iZXJpbmdUb2tlbik6IE51bWJlcmluZ1Rva2VuIHtcbiAgc3dpdGNoICh0LnN0eWxlKSB7XG4gICAgY2FzZSAnMSc6XG4gICAgICByZXR1cm4geyBzdHlsZTogJzEnLCB2YWx1ZTogdC52YWx1ZSArIDEgfVxuICAgIGNhc2UgJ0EnOlxuICAgICAgaWYgKHQudmFsdWUgPT09ICdaJykgcmV0dXJuIHsgc3R5bGU6ICdBJywgdmFsdWU6ICdBJyB9XG4gICAgICBlbHNlIHJldHVybiB7IHN0eWxlOiAnQScsIHZhbHVlOiBTdHJpbmcuZnJvbUNoYXJDb2RlKHQudmFsdWUuY2hhckNvZGVBdCgwKSArIDEpIH1cbiAgICBjYXNlICdJJzpcbiAgICAgIGlmICh0LnZhbHVlID09PSAnMCcpIHJldHVybiB7IHN0eWxlOiAnSScsIHZhbHVlOiAnSScgfVxuICAgICAgZWxzZSByZXR1cm4geyBzdHlsZTogJ0knLCB2YWx1ZTogcm9tYW5pemUoZGVyb21hbml6ZSh0LnZhbHVlKSArIDEpIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcHJldmlvdXNOdW1iZXJpbmdUb2tlbih0OiBOdW1iZXJpbmdUb2tlbik6IE51bWJlcmluZ1Rva2VuIHtcbiAgc3dpdGNoICh0LnN0eWxlKSB7XG4gICAgY2FzZSAnMSc6XG4gICAgICByZXR1cm4geyBzdHlsZTogJzEnLCB2YWx1ZTogdC52YWx1ZSAtIDEgfVxuICAgIGNhc2UgJ0EnOlxuICAgICAgaWYgKHQudmFsdWUgPT09ICdBJykgcmV0dXJuIHsgc3R5bGU6ICdBJywgdmFsdWU6ICdaJyB9XG4gICAgICBlbHNlIHJldHVybiB7IHN0eWxlOiAnQScsIHZhbHVlOiBTdHJpbmcuZnJvbUNoYXJDb2RlKHQudmFsdWUuY2hhckNvZGVBdCgwKSAtIDEpIH1cbiAgICBjYXNlICdJJzpcbiAgICAgIGlmICh0LnZhbHVlID09PSAnSScpIHJldHVybiB7IHN0eWxlOiAnSScsIHZhbHVlOiAnMCcgfVxuICAgICAgZWxzZSByZXR1cm4geyBzdHlsZTogJ0knLCB2YWx1ZTogcm9tYW5pemUoZGVyb21hbml6ZSh0LnZhbHVlKSAtIDEpIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gbWFrZU51bWJlcmluZ1N0cmluZyhudW1iZXJpbmdTdGFjazogTnVtYmVyaW5nVG9rZW5bXSk6IHN0cmluZyB7XG4gIGxldCBudW1iZXJpbmdTdHJpbmcgPSAnJ1xuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbnVtYmVyaW5nU3RhY2subGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoaSA9PT0gMCkge1xuICAgICAgbnVtYmVyaW5nU3RyaW5nICs9ICcgJ1xuICAgIH0gZWxzZSB7XG4gICAgICBudW1iZXJpbmdTdHJpbmcgKz0gJy4nXG4gICAgfVxuICAgIG51bWJlcmluZ1N0cmluZyArPSBwcmludGFibGVOdW1iZXJpbmdUb2tlbihudW1iZXJpbmdTdGFja1tpXSlcbiAgfVxuXG4gIHJldHVybiBudW1iZXJpbmdTdHJpbmdcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHN0YXJ0QXRPclplcm90aEluU3R5bGUoc3RhcnRBdFNldHRpbmdTdHJpbmc6IHN0cmluZywgc3R5bGU6IE51bWJlcmluZ1N0eWxlKTogTnVtYmVyaW5nVG9rZW4ge1xuICBpZiAoc3RhcnRBdFNldHRpbmdTdHJpbmcgPT09ICcnKSByZXR1cm4gemVyb3RoTnVtYmVyaW5nVG9rZW5JblN0eWxlKHN0eWxlKVxuXG4gIGxldCBmaXJzdE51bWJlcmluZ1Rva2VuRnJvbVNldHRpbmc6IE51bWJlcmluZ1Rva2VuXG5cbiAgc3dpdGNoIChzdHlsZSkge1xuICAgIGNhc2UgJzEnOlxuICAgICAgaWYgKCFpc1ZhbGlkQXJhYmljTnVtYmVyaW5nVmFsdWVTdHJpbmcoc3RhcnRBdFNldHRpbmdTdHJpbmcpKSByZXR1cm4gemVyb3RoTnVtYmVyaW5nVG9rZW5JblN0eWxlKHN0eWxlKVxuXG4gICAgICBmaXJzdE51bWJlcmluZ1Rva2VuRnJvbVNldHRpbmcgPSB7IHN0eWxlOiAnMScsIHZhbHVlOiBwYXJzZUludChzdGFydEF0U2V0dGluZ1N0cmluZykgfVxuICAgICAgYnJlYWtcbiAgICBjYXNlICdBJzpcbiAgICAgIGlmICghaXNWYWxpZEFscGhhYmV0TnVtYmVyaW5nVmFsdWVTdHJpbmcoc3RhcnRBdFNldHRpbmdTdHJpbmcpKSByZXR1cm4gemVyb3RoTnVtYmVyaW5nVG9rZW5JblN0eWxlKHN0eWxlKVxuXG4gICAgICBmaXJzdE51bWJlcmluZ1Rva2VuRnJvbVNldHRpbmcgPSB7IHN0eWxlOiAnQScsIHZhbHVlOiBzdGFydEF0U2V0dGluZ1N0cmluZyB9XG4gICAgICBicmVha1xuICAgIGNhc2UgJ0knOlxuICAgICAgaWYgKCFpc1ZhbGlkUm9tYW5OdW1iZXJpbmdWYWx1ZVN0cmluZyhzdGFydEF0U2V0dGluZ1N0cmluZykpIHJldHVybiB6ZXJvdGhOdW1iZXJpbmdUb2tlbkluU3R5bGUoc3R5bGUpXG4gICAgICBmaXJzdE51bWJlcmluZ1Rva2VuRnJvbVNldHRpbmcgPSB7IHN0eWxlOiAnSScsIHZhbHVlOiBzdGFydEF0U2V0dGluZ1N0cmluZyB9XG4gICAgICBicmVha1xuICB9XG5cbiAgLy8gQ29udmVydCB0aGUgZmlyc3QgbnVtYmVyaW5nIHRva2VuIHRvIGEgemVyb3RoIG51bWJlcmluZyB0b2tlblxuICByZXR1cm4gcHJldmlvdXNOdW1iZXJpbmdUb2tlbihmaXJzdE51bWJlcmluZ1Rva2VuRnJvbVNldHRpbmcpXG59XG4iLCJpbXBvcnQgeyBpc1ZhbGlkQWxwaGFiZXROdW1iZXJpbmdWYWx1ZVN0cmluZywgaXNWYWxpZEFyYWJpY051bWJlcmluZ1ZhbHVlU3RyaW5nLCBpc1ZhbGlkUm9tYW5OdW1iZXJpbmdWYWx1ZVN0cmluZywgTnVtYmVyaW5nU3R5bGUgfSBmcm9tICcuL251bWJlcmluZ1Rva2VucydcblxuZXhwb3J0IGludGVyZmFjZSBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzIHtcbiAgc2tpcFRvcExldmVsOiBib29sZWFuLFxuICBmaXJzdExldmVsOiBudW1iZXIsXG4gIG1heExldmVsOiBudW1iZXIsXG4gIHN0eWxlTGV2ZWwxOiBOdW1iZXJpbmdTdHlsZSxcbiAgc3R5bGVMZXZlbE90aGVyOiBOdW1iZXJpbmdTdHlsZSxcbiAgYXV0bzogYm9vbGVhbixcbiAgc2VwYXJhdG9yOiBzdHJpbmcsXG4gIGNvbnRlbnRzOiBzdHJpbmcsXG4gIHNraXBIZWFkaW5nczogc3RyaW5nLFxuICBzdGFydEF0OiBzdHJpbmcsXG4gIG9mZjogYm9vbGVhblxufVxuXG5leHBvcnQgY29uc3QgREVGQVVMVF9TRVRUSU5HUzogUmVhZG9ubHk8TnVtYmVySGVhZGluZ3NQbHVnaW5TZXR0aW5ncz4gPSB7XG4gIHNraXBUb3BMZXZlbDogZmFsc2UsXG4gIGZpcnN0TGV2ZWw6IDEsXG4gIG1heExldmVsOiA2LFxuICBzdHlsZUxldmVsMTogJzEnLFxuICBzdHlsZUxldmVsT3RoZXI6ICcxJyxcbiAgYXV0bzogZmFsc2UsXG4gIHNlcGFyYXRvcjogJycsXG4gIGNvbnRlbnRzOiAnJyxcbiAgc2tpcEhlYWRpbmdzOiAnJyxcbiAgc3RhcnRBdDogJycsXG4gIG9mZjogZmFsc2Vcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWROdW1iZXJpbmdTdHlsZVN0cmluZyhzOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgaWYgKHMgPT09ICdBJyB8fCBzID09PSAnMScgfHwgcyA9PT0gJ0knKSByZXR1cm4gdHJ1ZVxuICByZXR1cm4gZmFsc2Vcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWROdW1iZXJpbmdWYWx1ZVN0cmluZyhzOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgaWYgKHMgPT09ICcnIHx8IGlzVmFsaWRBcmFiaWNOdW1iZXJpbmdWYWx1ZVN0cmluZyhzKSB8fCBpc1ZhbGlkQWxwaGFiZXROdW1iZXJpbmdWYWx1ZVN0cmluZyhzKSB8fCBpc1ZhbGlkUm9tYW5OdW1iZXJpbmdWYWx1ZVN0cmluZyhzKSkgcmV0dXJuIHRydWVcbiAgcmV0dXJuIGZhbHNlXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1ZhbGlkRmxhZyhmOiB1bmtub3duKTogYm9vbGVhbiB7XG4gIGlmIChmID09PSB0cnVlIHx8IGYgPT09IGZhbHNlKSByZXR1cm4gdHJ1ZVxuICByZXR1cm4gZmFsc2Vcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWRGaXJzdE9yTWF4TGV2ZWwoeDogdW5rbm93bik6IGJvb2xlYW4ge1xuICBpZiAodHlwZW9mIHggPT09ICdudW1iZXInICYmIHggPj0gMSAmJiB4IDw9IDYpIHJldHVybiB0cnVlXG4gIHJldHVybiBmYWxzZVxufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZFNlcGFyYXRvcih4OiB1bmtub3duKTogYm9vbGVhbiB7XG4gIHJldHVybiB0eXBlb2YgeCA9PT0gJ3N0cmluZycgJiZcbiAgICAoXG4gICAgICB4ID09PSAnJyB8fFxuICAgICAgeCA9PT0gJzonIHx8IHggPT09ICcgOicgfHxcbiAgICAgIHggPT09ICcuJyB8fCB4ID09PSAnIC4nIHx8XG4gICAgICB4ID09PSAnLScgfHwgeCA9PT0gJyAtJyB8fFxuICAgICAgeCA9PT0gJ+KAlCcgfHwgeCA9PT0gJyDigJQnIHx8IC8qIGVtLWRhc2ggKi9cbiAgICAgIHggPT09ICcpJyB8fCB4ID09PSAnICknXG4gICAgKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZEJsb2NrSWRTZXR0aW5nKHg6IHVua25vd24pOiBib29sZWFuIHtcbiAgaWYgKHR5cGVvZiB4ID09PSAnc3RyaW5nJyAmJiAoeCA9PT0gJycgfHwgeC5zdGFydHNXaXRoKCdeJykpKSByZXR1cm4gdHJ1ZVxuICByZXR1cm4gZmFsc2Vcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzTm9uRW1wdHlCbG9ja0lkKHg6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBpZiAoeC5sZW5ndGggPiAyICYmIHguc3RhcnRzV2l0aCgnXicpKSByZXR1cm4gdHJ1ZVxuICByZXR1cm4gZmFsc2Vcbn1cbiIsImltcG9ydCB7IEVkaXRvclJhbmdlIH0gZnJvbSAnb2JzaWRpYW4nXG5pbXBvcnQgeyBOdW1iZXJpbmdTdHlsZSB9IGZyb20gJy4vbnVtYmVyaW5nVG9rZW5zJ1xuaW1wb3J0IHsgaXNWYWxpZE51bWJlcmluZ1N0eWxlU3RyaW5nLCBpc1ZhbGlkU2VwYXJhdG9yLCBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5nc1R5cGVzJ1xuXG5leHBvcnQgdHlwZSBTdXBwb3J0RmxhZ3MgPSB7XG4gIGFscGhhYmV0OiBib29sZWFuLFxuICByb21hbjogYm9vbGVhbixcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVN1cHBvcnRGbGFnc0Zyb21TZXR0aW5ncyhzdHlsZUxldmVsMTogc3RyaW5nLCBzdHlsZUxldmVsT3RoZXI6IHN0cmluZyk6IFN1cHBvcnRGbGFncyB7XG4gIHJldHVybiB7XG4gICAgYWxwaGFiZXQ6IHN0eWxlTGV2ZWwxID09PSAnQScgfHwgc3R5bGVMZXZlbE90aGVyID09PSAnQScsXG4gICAgcm9tYW46IHN0eWxlTGV2ZWwxID09PSAnSScgfHwgc3R5bGVMZXZlbE90aGVyID09PSAnSSdcbiAgfVxufVxuXG4vLyBHZXQgdGhlIHJlZ2V4IGZvciB0aGUgaGVhZGVyIHN0cmluZywgYmFzZWQgb24gdGhlIHN1cHBvcnQgZmxhZ3MuIFRoZSBnZW5lcmF0ZWQgcmVnZXggaXMgdXNlZCB0byBmaW5kIHRoZSByYW5nZSBvZiB0aGUgaGVhZGVyIHByZWZpeC5cbi8vIFRoZSByZWdleCBpcyBnZW5lcmF0ZWQgZHluYW1pY2FsbHksIGJlY2F1c2UgdGhlIHJlZ2V4IGlzIGRpZmZlcmVudCBkZXBlbmRpbmcgb24gdGhlIHN1cHBvcnQgZmxhZ3MuXG5mdW5jdGlvbiBnZXRSZWdleEZvckhlYWRlclN0cmluZyhmbGFnczogU3VwcG9ydEZsYWdzKTogUmVnRXhwIHtcbiAgaWYgKGZsYWdzLmFscGhhYmV0ICYmIGZsYWdzLnJvbWFuKSB7XG4gICAgLy8gUmVnZXggdG8gbWF0Y2ggdGhlIGhlYWRpbmcgcHJlZml4LCBpbmNsdWRpbmcgdGhlIHNwYWNlIGFmdGVyIHRoZSBoYXNoKGVzKSwgYnV0IG5vdCB0aGUgaGVhZGluZyB0ZXh0XG4gICAgcmV0dXJuIC9eXFxzezAsNH0jKyggKT8oWzAtOV0rXFwufFtBLVpdXFwufFtJVlhMQ0RNXStcXC4pKihbMC05XSt8W0EtWl18W0lWWExDRE1dKyk/KCApP1sp4oCUOi4tXT8oICkrL2dcbiAgfSBlbHNlIGlmICghZmxhZ3MuYWxwaGFiZXQgJiYgZmxhZ3Mucm9tYW4pIHtcbiAgICAvLyBSZWdleCB0byBtYXRjaCB0aGUgaGVhZGluZyBwcmVmaXgsIGluY2x1ZGluZyB0aGUgc3BhY2UgYWZ0ZXIgdGhlIGhhc2goZXMpLCBidXQgbm90IHRoZSBoZWFkaW5nIHRleHRcbiAgICByZXR1cm4gL15cXHN7MCw0fSMrKCApPyhbMC05XStcXC58W0lWWExDRE1dK1xcLikqKFswLTldK3xbSVZYTENETV0rKT8oICk/WynigJQ6Li1dPyggKSsvZ1xuICB9IGVsc2UgaWYgKGZsYWdzLmFscGhhYmV0ICYmICFmbGFncy5yb21hbikge1xuICAgIC8vIFJlZ2V4IHRvIG1hdGNoIHRoZSBoZWFkaW5nIHByZWZpeCwgaW5jbHVkaW5nIHRoZSBzcGFjZSBhZnRlciB0aGUgaGFzaChlcyksIGJ1dCBub3QgdGhlIGhlYWRpbmcgdGV4dFxuICAgIHJldHVybiAvXlxcc3swLDR9IysoICk/KFswLTldK1xcLnxbQS1aXVxcLikqKFswLTldK3xbQS1aXSk/KCApP1sp4oCUOi4tXT8oICkrL2dcbiAgfSBlbHNlIGlmICghZmxhZ3MuYWxwaGFiZXQgJiYgIWZsYWdzLnJvbWFuKSB7XG4gICAgLy8gUmVnZXggdG8gbWF0Y2ggdGhlIGhlYWRpbmcgcHJlZml4LCBpbmNsdWRpbmcgdGhlIHNwYWNlIGFmdGVyIHRoZSBoYXNoKGVzKSwgYnV0IG5vdCB0aGUgaGVhZGluZyB0ZXh0XG4gICAgcmV0dXJuIC9eXFxzezAsNH0jKyggKT8oWzAtOV0rXFwuKSooWzAtOV0rKT8oICk/WynigJQ6Li1dPyggKSsvZ1xuICB9XG5cbiAgdGhyb3cgbmV3IEVycm9yKCdVbmV4cGVjdGVkIGNvbWJpbmF0aW9uIG9mIHN1cHBvcnQgZmxhZ3MnKVxufVxuXG4vLyBGaW5kIHRoZSByYW5nZSBvZiB0aGUgaGVhZGluZyBwcmVmaXgsIGluY2x1ZGluZyB0aGUgc3BhY2UgYWZ0ZXIgYW55IG51bWJlcmluZywgYnV0IG5vdCB0aGUgaGVhZGluZyB0ZXh0XG5leHBvcnQgZnVuY3Rpb24gZmluZFJhbmdlSW5IZWFkZXJTdHJpbmcobGluZVRleHQ6IHN0cmluZywgbGluZU51bWJlcjogbnVtYmVyLCBmbGFnczogU3VwcG9ydEZsYWdzKTogRWRpdG9yUmFuZ2UgfCB1bmRlZmluZWQge1xuICBjb25zdCByZWdleCA9IGdldFJlZ2V4Rm9ySGVhZGVyU3RyaW5nKGZsYWdzKVxuXG4gIGlmICghbGluZVRleHQpIHJldHVybiB1bmRlZmluZWRcblxuICBjb25zdCBtYXRjaGVzID0gbGluZVRleHQubWF0Y2gocmVnZXgpXG5cbiAgaWYgKG1hdGNoZXMgJiYgbWF0Y2hlcy5sZW5ndGggIT09IDEpIHtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgIGNvbnNvbGUubG9nKFwiVW5leHBlY3RlZCBoZWFkaW5nIGZvcm1hdDogJ1wiICsgbGluZVRleHQgKyBcIidcIilcbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH1cblxuICBjb25zdCBtYXRjaCA9IG1hdGNoZXMgPyBtYXRjaGVzWzBdIDogJydcblxuICBjb25zdCBmcm9tID0ge1xuICAgIGxpbmU6IGxpbmVOdW1iZXIsXG4gICAgY2g6IDBcbiAgfVxuICBjb25zdCB0byA9IHtcbiAgICBsaW5lOiBsaW5lTnVtYmVyLFxuICAgIGNoOiBtYXRjaC5sZW5ndGhcbiAgfVxuXG4gIHJldHVybiB7IGZyb20sIHRvIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwZGF0ZVNldHRpbmdzRnJvbUZyb250TWF0dGVyRm9ybWF0UGFydChwYXJ0OiBzdHJpbmcsIHNldHRpbmdzOiBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzKTogTnVtYmVySGVhZGluZ3NQbHVnaW5TZXR0aW5ncyB7XG4gIC8vIFBhcnNlIHRoZSBzZXBhcmF0b3JcbiAgbGV0IHBhcnRXaXRob3V0U2VwYXJhdG9yID0gcGFydFxuICBjb25zdCBwb3RlbnRpYWxUd29DaGFyU2VwYXJhdG9yID0gcGFydC5zbGljZSgtMilcbiAgaWYgKGlzVmFsaWRTZXBhcmF0b3IocG90ZW50aWFsVHdvQ2hhclNlcGFyYXRvcikpIHtcbiAgICBzZXR0aW5ncy5zZXBhcmF0b3IgPSBwb3RlbnRpYWxUd29DaGFyU2VwYXJhdG9yXG4gICAgcGFydFdpdGhvdXRTZXBhcmF0b3IgPSBwYXJ0LnNsaWNlKDAsIC0yKVxuICB9IGVsc2Uge1xuICAgIGNvbnN0IHBvdGVudGlhbE9uZUNoYXJTZXBhcmF0b3IgPSBwYXJ0LnNsaWNlKC0xKVxuICAgIGlmIChpc1ZhbGlkU2VwYXJhdG9yKHBvdGVudGlhbE9uZUNoYXJTZXBhcmF0b3IpKSB7XG4gICAgICBzZXR0aW5ncy5zZXBhcmF0b3IgPSBwb3RlbnRpYWxPbmVDaGFyU2VwYXJhdG9yXG4gICAgICBwYXJ0V2l0aG91dFNlcGFyYXRvciA9IHBhcnQuc2xpY2UoMCwgLTEpXG4gICAgfSBlbHNlIHtcbiAgICAgIHNldHRpbmdzLnNlcGFyYXRvciA9ICcnXG4gICAgfVxuICB9XG5cbiAgLy8gUGFyc2UgdGhlIG51bWJlcmluZyBzdHlsZVxuICBjb25zdCBkZXNjcmlwdG9ycyA9IHBhcnRXaXRob3V0U2VwYXJhdG9yLnNwbGl0KCcuJylcbiAgbGV0IGZpcnN0TnVtYmVyZWREZXNjcmlwdG9yID0gMFxuXG4gIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgZmlyc3QgZGVzY3JpcHRvciBpcyBhbiB1bmRlcnNjb3JlXG4gIGlmIChkZXNjcmlwdG9ycy5sZW5ndGggPiAxICYmIGRlc2NyaXB0b3JzWzBdID09PSAnXycpIHtcbiAgICAvLyBUaGUgZmlyc3QgZGVzY3JpcHRvciBpcyBhbiBpbnN0cnVjdGlvbiB0byBza2lwIHRvcCBsZXZlbHMsIHNvIHNraXAgdGhlbVxuICAgIHNldHRpbmdzLnNraXBUb3BMZXZlbCA9IHRydWVcbiAgICBmaXJzdE51bWJlcmVkRGVzY3JpcHRvciA9IDFcbiAgfSBlbHNlIHtcbiAgICBzZXR0aW5ncy5za2lwVG9wTGV2ZWwgPSBmYWxzZVxuICB9XG5cbiAgaWYgKGRlc2NyaXB0b3JzLmxlbmd0aCAtIGZpcnN0TnVtYmVyZWREZXNjcmlwdG9yID49IDIpIHtcbiAgICBjb25zdCBzdHlsZUxldmVsMSA9IGRlc2NyaXB0b3JzW2ZpcnN0TnVtYmVyZWREZXNjcmlwdG9yXVxuICAgIGlmIChpc1ZhbGlkTnVtYmVyaW5nU3R5bGVTdHJpbmcoc3R5bGVMZXZlbDEpKSB7XG4gICAgICBzZXR0aW5ncy5zdHlsZUxldmVsMSA9IHN0eWxlTGV2ZWwxIGFzIE51bWJlcmluZ1N0eWxlXG4gICAgfVxuICAgIGNvbnN0IHN0eWxlTGV2ZWxPdGhlciA9IGRlc2NyaXB0b3JzW2ZpcnN0TnVtYmVyZWREZXNjcmlwdG9yICsgMV1cbiAgICBpZiAoaXNWYWxpZE51bWJlcmluZ1N0eWxlU3RyaW5nKHN0eWxlTGV2ZWxPdGhlcikpIHtcbiAgICAgIHNldHRpbmdzLnN0eWxlTGV2ZWxPdGhlciA9IHN0eWxlTGV2ZWxPdGhlciBhcyBOdW1iZXJpbmdTdHlsZVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBzZXR0aW5nc1xufVxuIiwiaW1wb3J0IHsgQ2FjaGVkTWV0YWRhdGEsIEZpbGVNYW5hZ2VyLCBGcm9udE1hdHRlckNhY2hlLCBURmlsZSwgcGFyc2VGcm9udE1hdHRlckVudHJ5IH0gZnJvbSAnb2JzaWRpYW4nXG5pbXBvcnQgeyBOdW1iZXJpbmdTdHlsZSB9IGZyb20gJy4vbnVtYmVyaW5nVG9rZW5zJ1xuaW1wb3J0IHsgREVGQVVMVF9TRVRUSU5HUywgTnVtYmVySGVhZGluZ3NQbHVnaW5TZXR0aW5ncywgaXNWYWxpZEJsb2NrSWRTZXR0aW5nLCBpc1ZhbGlkRmlyc3RPck1heExldmVsLCBpc1ZhbGlkRmxhZywgaXNWYWxpZE51bWJlcmluZ1N0eWxlU3RyaW5nLCBpc1ZhbGlkTnVtYmVyaW5nVmFsdWVTdHJpbmcgfSBmcm9tICcuL3NldHRpbmdzVHlwZXMnXG5pbXBvcnQgeyB1cGRhdGVTZXR0aW5nc0Zyb21Gcm9udE1hdHRlckZvcm1hdFBhcnQgfSBmcm9tICcuL3RleHRQcm9jZXNzaW5nJ1xuXG5jb25zdCBBVVRPX1BBUlRfS0VZID0gJ2F1dG8nXG5jb25zdCBGSVJTVF9MRVZFTF9QQVJUX0tFWSA9ICdmaXJzdC1sZXZlbCdcbmNvbnN0IE1BWF9MRVZFTF9QQVJUX0tFWSA9ICdtYXgnXG5jb25zdCBDT05URU5UU19QQVJUX0tFWSA9ICdjb250ZW50cydcbmNvbnN0IFNLSVBfUEFSVF9LRVkgPSAnc2tpcCdcbmNvbnN0IFNUQVJUX0FUX1BBUlRfS0VZID0gJ3N0YXJ0LWF0J1xuY29uc3QgT0ZGX1BBUlRfS0VZID0gJ29mZidcblxuZnVuY3Rpb24gcGFyc2VDb21wYWN0RnJvbnRNYXR0ZXJTZXR0aW5ncyhmbTogRnJvbnRNYXR0ZXJDYWNoZSk6IE51bWJlckhlYWRpbmdzUGx1Z2luU2V0dGluZ3MgfCB1bmRlZmluZWQge1xuICBjb25zdCBlbnRyeSA9IHBhcnNlRnJvbnRNYXR0ZXJFbnRyeShmbSwgJ251bWJlciBoZWFkaW5ncycpXG4gIGlmIChlbnRyeSkge1xuICAgIGNvbnN0IGVudHJ5U3RyaW5nID0gU3RyaW5nKGVudHJ5KVxuICAgIGNvbnN0IHBhcnRzID0gZW50cnlTdHJpbmcuc3BsaXQoJywnKVxuICAgIGxldCBzZXR0aW5nczogTnVtYmVySGVhZGluZ3NQbHVnaW5TZXR0aW5ncyA9IHsgLi4uREVGQVVMVF9TRVRUSU5HUyB9XG5cbiAgICBmb3IgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcbiAgICAgIGNvbnN0IHRyaW1tZWRQYXJ0ID0gcGFydC50cmltKClcbiAgICAgIGlmICh0cmltbWVkUGFydC5sZW5ndGggPT09IDApIGNvbnRpbnVlXG5cbiAgICAgIGlmICh0cmltbWVkUGFydCA9PT0gT0ZGX1BBUlRfS0VZKSB7XG4gICAgICAgIC8vIFBhcnNlIG9mZiBwYXJ0XG4gICAgICAgIHNldHRpbmdzLm9mZiA9IHRydWVcbiAgICAgIH0gZWxzZSBpZiAodHJpbW1lZFBhcnQgPT09IEFVVE9fUEFSVF9LRVkpIHtcbiAgICAgICAgLy8gUGFyc2UgYXV0byBudW1iZXJpbmcgcGFydFxuICAgICAgICBzZXR0aW5ncy5hdXRvID0gdHJ1ZVxuICAgICAgfSBlbHNlIGlmICh0cmltbWVkUGFydC5zdGFydHNXaXRoKEZJUlNUX0xFVkVMX1BBUlRfS0VZKSkge1xuICAgICAgICAvLyBQYXJzZSBmaXJzdCBsZXZlbCBwYXJ0XG4gICAgICAgIGNvbnN0IG5zdHJpbmcgPSB0cmltbWVkUGFydC5zdWJzdHJpbmcoRklSU1RfTEVWRUxfUEFSVF9LRVkubGVuZ3RoICsgMSlcbiAgICAgICAgY29uc3QgbiA9IHBhcnNlSW50KG5zdHJpbmcpXG4gICAgICAgIGlmIChpc1ZhbGlkRmlyc3RPck1heExldmVsKG4pKSB7XG4gICAgICAgICAgc2V0dGluZ3MuZmlyc3RMZXZlbCA9IG5cbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICh0cmltbWVkUGFydC5zdGFydHNXaXRoKE1BWF9MRVZFTF9QQVJUX0tFWSkpIHtcbiAgICAgICAgLy8gUGFyc2UgbWF4IGxldmVsIHBhcnRcbiAgICAgICAgY29uc3QgbnN0cmluZyA9IHRyaW1tZWRQYXJ0LnN1YnN0cmluZyhNQVhfTEVWRUxfUEFSVF9LRVkubGVuZ3RoICsgMSlcbiAgICAgICAgY29uc3QgbiA9IHBhcnNlSW50KG5zdHJpbmcpXG4gICAgICAgIGlmIChpc1ZhbGlkRmlyc3RPck1heExldmVsKG4pKSB7XG4gICAgICAgICAgc2V0dGluZ3MubWF4TGV2ZWwgPSBuXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAodHJpbW1lZFBhcnQuc3RhcnRzV2l0aChTVEFSVF9BVF9QQVJUX0tFWSkpIHtcbiAgICAgICAgLy8gUGFyc2UgXCJzdGFydCBhdFwiIHBhcnRcbiAgICAgICAgY29uc3QgdmFsdWUgPSB0cmltbWVkUGFydC5zdWJzdHJpbmcoU1RBUlRfQVRfUEFSVF9LRVkubGVuZ3RoICsgMSlcbiAgICAgICAgaWYgKGlzVmFsaWROdW1iZXJpbmdWYWx1ZVN0cmluZyh2YWx1ZSkpIHtcbiAgICAgICAgICBzZXR0aW5ncy5zdGFydEF0ID0gdmFsdWVcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICh0cmltbWVkUGFydC5zdGFydHNXaXRoKENPTlRFTlRTX1BBUlRfS0VZKSkge1xuICAgICAgICBpZiAodHJpbW1lZFBhcnQubGVuZ3RoIDw9IENPTlRFTlRTX1BBUlRfS0VZLmxlbmd0aCArIDEpIGNvbnRpbnVlXG4gICAgICAgIC8vIFBhcnNlIGNvbnRlbnRzIGhlYWRpbmcgcGFydFxuICAgICAgICBjb25zdCB0b2NIZWFkaW5nQmxvY2tJZE5hbWUgPSB0cmltbWVkUGFydC5zdWJzdHJpbmcoQ09OVEVOVFNfUEFSVF9LRVkubGVuZ3RoICsgMSlcbiAgICAgICAgaWYgKGlzVmFsaWRCbG9ja0lkU2V0dGluZyh0b2NIZWFkaW5nQmxvY2tJZE5hbWUpKSB7XG4gICAgICAgICAgc2V0dGluZ3MuY29udGVudHMgPSB0b2NIZWFkaW5nQmxvY2tJZE5hbWVcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICh0cmltbWVkUGFydC5zdGFydHNXaXRoKFNLSVBfUEFSVF9LRVkpKSB7XG4gICAgICAgIGlmICh0cmltbWVkUGFydC5sZW5ndGggPD0gU0tJUF9QQVJUX0tFWS5sZW5ndGggKyAxKSBjb250aW51ZVxuICAgICAgICAvLyBQYXJzZSBza2lwIGhlYWRpbmcgcGFydFxuICAgICAgICBjb25zdCBza2lwSGVhZGluZ0Jsb2NrSWROYW1lID0gdHJpbW1lZFBhcnQuc3Vic3RyaW5nKFNLSVBfUEFSVF9LRVkubGVuZ3RoICsgMSlcbiAgICAgICAgaWYgKGlzVmFsaWRCbG9ja0lkU2V0dGluZyhza2lwSGVhZGluZ0Jsb2NrSWROYW1lKSkge1xuICAgICAgICAgIHNldHRpbmdzLnNraXBIZWFkaW5ncyA9IHNraXBIZWFkaW5nQmxvY2tJZE5hbWVcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gUGFyc2UgZm9ybWF0dGluZyBwYXJ0XG4gICAgICAgIHNldHRpbmdzID0gdXBkYXRlU2V0dGluZ3NGcm9tRnJvbnRNYXR0ZXJGb3JtYXRQYXJ0KHRyaW1tZWRQYXJ0LCBzZXR0aW5ncylcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gc2V0dGluZ3NcbiAgfVxuXG4gIHJldHVybiB1bmRlZmluZWRcbn1cblxuZXhwb3J0IGNvbnN0IGdldEZyb250TWF0dGVyU2V0dGluZ3NPckFsdGVybmF0aXZlID0gKFxuICB7IGZyb250bWF0dGVyIH06IENhY2hlZE1ldGFkYXRhLFxuICBhbHRlcm5hdGl2ZVNldHRpbmdzOiBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzXG4pOiBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzID0+IHtcbiAgaWYgKGZyb250bWF0dGVyICE9PSB1bmRlZmluZWQpIHtcbiAgICBjb25zdCBkZWNvbXBhY3RlZFNldHRpbmdzID0gcGFyc2VDb21wYWN0RnJvbnRNYXR0ZXJTZXR0aW5ncyhmcm9udG1hdHRlcilcbiAgICBpZiAoZGVjb21wYWN0ZWRTZXR0aW5ncyAhPT0gdW5kZWZpbmVkKSByZXR1cm4gZGVjb21wYWN0ZWRTZXR0aW5nc1xuXG4gICAgLy8gTk9URTogRXZlcnl0aGluZyBiZWxvdyBpcyBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkgb25seVxuXG4gICAgY29uc3Qgc2tpcFRvcExldmVsRW50cnkgPSBwYXJzZUZyb250TWF0dGVyRW50cnkoZnJvbnRtYXR0ZXIsICdudW1iZXItaGVhZGluZ3Mtc2tpcC10b3AtbGV2ZWwnKSA/PyBwYXJzZUZyb250TWF0dGVyRW50cnkoZnJvbnRtYXR0ZXIsICdoZWFkZXItbnVtYmVyaW5nLXNraXAtdG9wLWxldmVsJylcbiAgICBjb25zdCBza2lwVG9wTGV2ZWwgPSBpc1ZhbGlkRmxhZyhza2lwVG9wTGV2ZWxFbnRyeSkgPyBza2lwVG9wTGV2ZWxFbnRyeSA6IGFsdGVybmF0aXZlU2V0dGluZ3Muc2tpcFRvcExldmVsXG5cbiAgICBjb25zdCBtYXhMZXZlbEVudHJ5ID0gcGFyc2VGcm9udE1hdHRlckVudHJ5KGZyb250bWF0dGVyLCAnbnVtYmVyLWhlYWRpbmdzLW1heC1sZXZlbCcpID8/IHBhcnNlRnJvbnRNYXR0ZXJFbnRyeShmcm9udG1hdHRlciwgJ2hlYWRlci1udW1iZXJpbmctbWF4LWxldmVsJylcbiAgICBjb25zdCBtYXhMZXZlbCA9IGlzVmFsaWRGaXJzdE9yTWF4TGV2ZWwobWF4TGV2ZWxFbnRyeSkgPyBtYXhMZXZlbEVudHJ5IDogYWx0ZXJuYXRpdmVTZXR0aW5ncy5tYXhMZXZlbFxuXG4gICAgY29uc3Qgc3R5bGVMZXZlbDFFbnRyeSA9IFN0cmluZyhcbiAgICAgIHBhcnNlRnJvbnRNYXR0ZXJFbnRyeShmcm9udG1hdHRlciwgJ251bWJlci1oZWFkaW5ncy1zdHlsZS1sZXZlbC0xJykgPz9cbiAgICAgIHBhcnNlRnJvbnRNYXR0ZXJFbnRyeShmcm9udG1hdHRlciwgJ2hlYWRlci1udW1iZXJpbmctc3R5bGUtbGV2ZWwtMScpXG4gICAgKVxuICAgIGNvbnN0IHN0eWxlTGV2ZWwxOiBOdW1iZXJpbmdTdHlsZSA9IGlzVmFsaWROdW1iZXJpbmdTdHlsZVN0cmluZyhzdHlsZUxldmVsMUVudHJ5KSA/IHN0eWxlTGV2ZWwxRW50cnkgYXMgTnVtYmVyaW5nU3R5bGUgOiBhbHRlcm5hdGl2ZVNldHRpbmdzLnN0eWxlTGV2ZWwxXG5cbiAgICBjb25zdCBzdHlsZUxldmVsT3RoZXJFbnRyeSA9IFN0cmluZyhcbiAgICAgIHBhcnNlRnJvbnRNYXR0ZXJFbnRyeShmcm9udG1hdHRlciwgJ251bWJlci1oZWFkaW5ncy1zdHlsZS1sZXZlbC1vdGhlcicpID8/XG4gICAgICBwYXJzZUZyb250TWF0dGVyRW50cnkoZnJvbnRtYXR0ZXIsICdoZWFkZXItbnVtYmVyaW5nLXN0eWxlLWxldmVsLW90aGVyJylcbiAgICApXG4gICAgY29uc3Qgc3R5bGVMZXZlbE90aGVyOiBOdW1iZXJpbmdTdHlsZSA9IGlzVmFsaWROdW1iZXJpbmdTdHlsZVN0cmluZyhzdHlsZUxldmVsT3RoZXJFbnRyeSkgPyBzdHlsZUxldmVsT3RoZXJFbnRyeSBhcyBOdW1iZXJpbmdTdHlsZSA6IGFsdGVybmF0aXZlU2V0dGluZ3Muc3R5bGVMZXZlbE90aGVyXG5cbiAgICBjb25zdCBhdXRvRW50cnkgPSBwYXJzZUZyb250TWF0dGVyRW50cnkoZnJvbnRtYXR0ZXIsICdudW1iZXItaGVhZGluZ3MtYXV0bycpID8/IHBhcnNlRnJvbnRNYXR0ZXJFbnRyeShmcm9udG1hdHRlciwgJ2hlYWRlci1udW1iZXJpbmctYXV0bycpXG4gICAgY29uc3QgYXV0byA9IGlzVmFsaWRGbGFnKGF1dG9FbnRyeSkgPyBhdXRvRW50cnkgOiBhbHRlcm5hdGl2ZVNldHRpbmdzLmF1dG9cblxuICAgIHJldHVybiB7IC4uLmFsdGVybmF0aXZlU2V0dGluZ3MsIHNraXBUb3BMZXZlbCwgbWF4TGV2ZWwsIHN0eWxlTGV2ZWwxLCBzdHlsZUxldmVsT3RoZXIsIGF1dG8gfVxuICB9IGVsc2Uge1xuICAgIHJldHVybiBhbHRlcm5hdGl2ZVNldHRpbmdzXG4gIH1cbn1cblxuZnVuY3Rpb24gc2V0dGluZ3NUb0NvbXBhY3RGcm9udE1hdHRlclZhbHVlKHNldHRpbmdzOiBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzKTogc3RyaW5nIHtcbiAgaWYgKHNldHRpbmdzLm9mZikgcmV0dXJuIE9GRl9QQVJUX0tFWVxuXG4gIGNvbnN0IGF1dG9QYXJ0ID0gc2V0dGluZ3MuYXV0byA/ICdhdXRvLCAnIDogJydcbiAgY29uc3QgZmlyc3RMZXZlbFBhcnQgPSBgZmlyc3QtbGV2ZWwgJHtzZXR0aW5ncy5maXJzdExldmVsfSwgYFxuICBjb25zdCBtYXhQYXJ0ID0gYG1heCAke3NldHRpbmdzLm1heExldmVsfSwgYFxuICBjb25zdCBjb250ZW50c1BhcnQgPSBzZXR0aW5ncy5jb250ZW50cyAmJiBzZXR0aW5ncy5jb250ZW50cy5sZW5ndGggPiAwID8gYGNvbnRlbnRzICR7c2V0dGluZ3MuY29udGVudHN9LCBgIDogJydcbiAgY29uc3Qgc2tpcEhlYWRpbmdzUGFydCA9IHNldHRpbmdzLnNraXBIZWFkaW5ncyAmJiBzZXR0aW5ncy5za2lwSGVhZGluZ3MubGVuZ3RoID4gMCA/IGBza2lwICR7c2V0dGluZ3Muc2tpcEhlYWRpbmdzfSwgYCA6ICcnXG4gIGNvbnN0IHNraXBUb3BMZXZlbFN0cmluZyA9IHNldHRpbmdzLnNraXBUb3BMZXZlbCA/ICdfLicgOiAnJ1xuICBjb25zdCBzdHlsZVBhcnQgPSBgJHtza2lwVG9wTGV2ZWxTdHJpbmd9JHtzZXR0aW5ncy5zdHlsZUxldmVsMX0uJHtzZXR0aW5ncy5zdHlsZUxldmVsT3RoZXJ9JHtzZXR0aW5ncy5zZXBhcmF0b3J9YFxuICBjb25zdCBzdGFydEF0UGFydCA9IHNldHRpbmdzLnN0YXJ0QXQgIT09ICcnID8gYHN0YXJ0LWF0ICR7c2V0dGluZ3Muc3RhcnRBdH0sIGAgOiAnJ1xuICByZXR1cm4gYXV0b1BhcnQgKyBmaXJzdExldmVsUGFydCArIG1heFBhcnQgKyBjb250ZW50c1BhcnQgKyBza2lwSGVhZGluZ3NQYXJ0ICsgc3RhcnRBdFBhcnQgKyBzdHlsZVBhcnRcbn1cblxuZXhwb3J0IGNvbnN0IHNhdmVTZXR0aW5nc1RvRnJvbnRNYXR0ZXIgPSAoXG4gIGZpbGVNYW5hZ2VyOiBGaWxlTWFuYWdlcixcbiAgZmlsZTogVEZpbGUsXG4gIHNldHRpbmdzOiBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzXG4pOiB2b2lkID0+IHtcbiAgZmlsZU1hbmFnZXIucHJvY2Vzc0Zyb250TWF0dGVyKGZpbGUsIGZyb250bWF0dGVyID0+IHtcbiAgICBjb25zdCB2OiBzdHJpbmcgPSBzZXR0aW5nc1RvQ29tcGFjdEZyb250TWF0dGVyVmFsdWUoc2V0dGluZ3MpXG4gICAgZnJvbnRtYXR0ZXJbJ251bWJlciBoZWFkaW5ncyddID0gdlxuICB9KVxufVxuIiwiaW1wb3J0IHsgQXBwLCBNb2RhbCB9IGZyb20gJ29ic2lkaWFuJ1xuaW1wb3J0IHsgc2F2ZVNldHRpbmdzVG9Gcm9udE1hdHRlciB9IGZyb20gJy4vZnJvbnRNYXR0ZXInXG5pbXBvcnQgeyBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5nc1R5cGVzJ1xuXG5leHBvcnQgaW50ZXJmYWNlIE51bWJlcmluZ0RvbmVDb25maWcge1xuICBtZXNzYWdlOiBzdHJpbmdcbiAgcHJlZm9ybWF0dGVkTWVzc2FnZTogc3RyaW5nXG4gIHNhdmVTZXR0aW5nc0NhbGxiYWNrOiAoc2hvdWxkQWRkQXV0b0ZsYWc6IGJvb2xlYW4pID0+IHZvaWRcbn1cblxuY2xhc3MgTnVtYmVyaW5nRG9uZU1vZGFsIGV4dGVuZHMgTW9kYWwge1xuICBjb25maWc6IE51bWJlcmluZ0RvbmVDb25maWdcblxuICBjb25zdHJ1Y3RvcihhcHA6IEFwcCwgY29uZmlnOiBOdW1iZXJpbmdEb25lQ29uZmlnKSB7XG4gICAgc3VwZXIoYXBwKVxuICAgIHRoaXMuY29uZmlnID0gY29uZmlnXG4gIH1cblxuICBvbk9wZW4oKTogdm9pZCB7XG4gICAgY29uc3QgeyBjb250ZW50RWwsIHRpdGxlRWwgfSA9IHRoaXNcbiAgICB0aXRsZUVsLnNldFRleHQoJ051bWJlciBIZWFkaW5ncyAtIFN1Y2Nlc3NmdWxseSBDb21wbGV0ZWQnKVxuXG4gICAgY29udGVudEVsLmNyZWF0ZUVsKCdkaXYnLCB7IHRleHQ6IHRoaXMuY29uZmlnLm1lc3NhZ2UgfSlcbiAgICBjb250ZW50RWwuY3JlYXRlRWwoJ3ByZScsIHsgdGV4dDogdGhpcy5jb25maWcucHJlZm9ybWF0dGVkTWVzc2FnZSB9KVxuXG4gICAgY29udGVudEVsLmNyZWF0ZUVsKCdkaXYnLCB7IHRleHQ6IFwiRG8geW91IHdhbnQgdG8gc2F2ZSB0aGVzZSBzZXR0aW5ncyBpbiB0aGUgZG9jdW1lbnQncyBmcm9udCBtYXR0ZXI/XCIsIGNsczogJ251bWJlci1oZWFkaW5ncy1xdWVzdGlvbicgfSlcblxuICAgIGNvbnN0IGNvbnRhaW5lckZvckJ1dHRvbnMgPSBjb250ZW50RWwuY3JlYXRlRWwoJ2RpdicsIHsgY2xzOiAnbnVtYmVyLWhlYWRpbmdzLWJ1dHRvbi1jb250YWluZXInIH0pXG5cbiAgICBjb25zdCBub0J1dHRvbiA9IGNvbnRhaW5lckZvckJ1dHRvbnMuY3JlYXRlRWwoJ2J1dHRvbicsIHt9KVxuICAgIG5vQnV0dG9uLnNldFRleHQoJ05vJylcbiAgICBub0J1dHRvbi5vbkNsaWNrRXZlbnQoKGV2OiBNb3VzZUV2ZW50KSA9PiB7XG4gICAgICB0aGlzLmNsb3NlKClcbiAgICAgIHJldHVybiBldlxuICAgIH0pXG5cbiAgICBjb25zdCB5ZXNCdXR0b24gPSBjb250YWluZXJGb3JCdXR0b25zLmNyZWF0ZUVsKCdidXR0b24nLCB7fSlcbiAgICB5ZXNCdXR0b24uc2V0VGV4dCgnWWVzLCBzYXZlIHNldHRpbmdzIGluIGRvY3VtZW50JylcbiAgICB5ZXNCdXR0b24ub25DbGlja0V2ZW50KChldjogTW91c2VFdmVudCkgPT4ge1xuICAgICAgdGhpcy5jb25maWcuc2F2ZVNldHRpbmdzQ2FsbGJhY2soZmFsc2UpXG4gICAgICB0aGlzLmNsb3NlKClcbiAgICAgIHJldHVybiBldlxuICAgIH0pXG5cbiAgICBjb25zdCB5ZXNBbmRBdXRvQnV0dG9uID0gY29udGFpbmVyRm9yQnV0dG9ucy5jcmVhdGVFbCgnYnV0dG9uJywge30pXG4gICAgeWVzQW5kQXV0b0J1dHRvbi5zZXRUZXh0KCdZZXMsIHNhdmUgc2V0dGluZ3MgaW4gZG9jdW1lbnQsIGFuZCBhdXRvbWF0aWNhbGx5IG51bWJlcicpXG4gICAgeWVzQW5kQXV0b0J1dHRvbi5vbkNsaWNrRXZlbnQoKGV2OiBNb3VzZUV2ZW50KSA9PiB7XG4gICAgICB0aGlzLmNvbmZpZy5zYXZlU2V0dGluZ3NDYWxsYmFjayh0cnVlKVxuICAgICAgdGhpcy5jbG9zZSgpXG4gICAgICByZXR1cm4gZXZcbiAgICB9KVxuICB9XG5cbiAgb25DbG9zZSgpOiB2b2lkIHtcbiAgICBjb25zdCB7IGNvbnRlbnRFbCwgdGl0bGVFbCB9ID0gdGhpc1xuICAgIGNvbnRlbnRFbC5lbXB0eSgpXG4gICAgdGl0bGVFbC5lbXB0eSgpXG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNob3dOdW1iZXJpbmdEb25lTWVzc2FnZShhcHA6IEFwcCwgc2V0dGluZ3M6IE51bWJlckhlYWRpbmdzUGx1Z2luU2V0dGluZ3MpOiB2b2lkIHtcbiAgY29uc3Qgc2F2ZVNldHRpbmdzQ2FsbGJhY2sgPSAoc2hvdWxkQWRkQXV0b0ZsYWc6IGJvb2xlYW4pOiB2b2lkID0+IHtcbiAgICBjb25zdCB0d2Vha2VkU2V0dGluZ3MgPSB7IC4uLnNldHRpbmdzIH1cbiAgICBpZiAoc2hvdWxkQWRkQXV0b0ZsYWcpIHR3ZWFrZWRTZXR0aW5ncy5hdXRvID0gdHJ1ZVxuICAgIGNvbnN0IGZpbGUgPSBhcHAud29ya3NwYWNlLmdldEFjdGl2ZUZpbGUoKVxuICAgIGlmIChmaWxlKSB7XG4gICAgICBzYXZlU2V0dGluZ3NUb0Zyb250TWF0dGVyKGFwcC5maWxlTWFuYWdlciwgZmlsZSwgdHdlYWtlZFNldHRpbmdzKVxuICAgIH1cbiAgfVxuICBjb25zdCBjb25maWc6IE51bWJlcmluZ0RvbmVDb25maWcgPSB7XG4gICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIGFsbCBoZWFkaW5nIG51bWJlcnMgaW4gdGhlIGRvY3VtZW50LCB1c2luZyB0aGUgc2V0dGluZ3MgYmVsb3cuIFxuICAgICAgU2VlIHNldHRpbmdzIHBhbmVsIHRvIGNoYW5nZSBob3cgaGVhZGluZ3MgYXJlIG51bWJlcmVkLCBvciB1c2UgZnJvbnQgbWF0dGVyXG4gICAgICAoc2VlIHNldHRpbmdzIHBhbmVsKS5gLFxuICAgIHByZWZvcm1hdHRlZE1lc3NhZ2U6IGBTa2lwIHRvcCBoZWFkaW5nIGxldmVsOiAke3NldHRpbmdzLnNraXBUb3BMZXZlbH1cbkZpcnN0IGhlYWRpbmcgbGV2ZWw6ICR7c2V0dGluZ3MuZmlyc3RMZXZlbH1cblN0YXJ0IG51bWJlcmluZyBmaXJzdCBoZWFkaW5nIGF0OiAke3NldHRpbmdzLnN0YXJ0QXR9XG5NYXhpbXVtIGhlYWRpbmcgbGV2ZWw6ICR7c2V0dGluZ3MubWF4TGV2ZWx9XG5TdHlsZSBmb3IgbGV2ZWwgMSBoZWFkaW5nczogJHtzZXR0aW5ncy5zdHlsZUxldmVsMX1cblN0eWxlIGZvciBsb3dlciBsZXZlbCBoZWFkaW5ncyAoYmVsb3cgbGV2ZWwgMSk6ICR7c2V0dGluZ3Muc3R5bGVMZXZlbE90aGVyfVxuU2VwYXJhdG9yOiAke3NldHRpbmdzLnNlcGFyYXRvcn1cblRhYmxlIG9mIENvbnRlbnRzIEFuY2hvcjogJHtzZXR0aW5ncy5jb250ZW50c31cblNraXAgSGVhZGluZ3MgQW5jaG9yOiAke3NldHRpbmdzLnNraXBIZWFkaW5nc31gLFxuICAgIHNhdmVTZXR0aW5nc0NhbGxiYWNrXG4gIH1cblxuICBjb25zdCBsZWFmID0gYXBwLndvcmtzcGFjZS5hY3RpdmVMZWFmXG4gIGlmIChsZWFmKSB7XG4gICAgbmV3IE51bWJlcmluZ0RvbmVNb2RhbChhcHAsIGNvbmZpZykub3BlbigpXG4gIH1cbn1cbiIsImltcG9ydCB7IEVkaXRvciwgRWRpdG9yQ2hhbmdlLCBFZGl0b3JSYW5nZSwgSGVhZGluZ0NhY2hlIH0gZnJvbSAnb2JzaWRpYW4nXG5pbXBvcnQgeyBWaWV3SW5mbyB9IGZyb20gJy4vYWN0aXZlVmlld0hlbHBlcnMnXG5pbXBvcnQgeyBOdW1iZXJpbmdUb2tlbiwgZmlyc3ROdW1iZXJpbmdUb2tlbkluU3R5bGUsIG1ha2VOdW1iZXJpbmdTdHJpbmcsIG5leHROdW1iZXJpbmdUb2tlbiwgc3RhcnRBdE9yWmVyb3RoSW5TdHlsZSB9IGZyb20gJy4vbnVtYmVyaW5nVG9rZW5zJ1xuaW1wb3J0IHsgTnVtYmVySGVhZGluZ3NQbHVnaW5TZXR0aW5ncywgaXNOb25FbXB0eUJsb2NrSWQgfSBmcm9tICcuL3NldHRpbmdzVHlwZXMnXG5pbXBvcnQgeyBTdXBwb3J0RmxhZ3MsIGNyZWF0ZVN1cHBvcnRGbGFnc0Zyb21TZXR0aW5ncywgZmluZFJhbmdlSW5IZWFkZXJTdHJpbmcgfSBmcm9tICcuL3RleHRQcm9jZXNzaW5nJ1xuXG5jb25zdCBUT0NfTElTVF9JVEVNX0JVTExFVCA9ICctJ1xuXG5mdW5jdGlvbiBtYWtlSGVhZGluZ0hhc2hTdHJpbmcoZWRpdG9yOiBFZGl0b3IsIGhlYWRpbmc6IEhlYWRpbmdDYWNoZSk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHJlZ2V4ID0gL15cXHN7MCw0fSMrL2dcbiAgY29uc3QgaGVhZGluZ0xpbmVTdHJpbmcgPSBlZGl0b3IuZ2V0TGluZShoZWFkaW5nLnBvc2l0aW9uLnN0YXJ0LmxpbmUpXG4gIGlmICghaGVhZGluZ0xpbmVTdHJpbmcpIHJldHVybiB1bmRlZmluZWRcblxuICBjb25zdCBtYXRjaGVzID0gaGVhZGluZ0xpbmVTdHJpbmcubWF0Y2gocmVnZXgpXG4gIGlmICghbWF0Y2hlcykgcmV0dXJuIHVuZGVmaW5lZFxuXG4gIGlmIChtYXRjaGVzLmxlbmd0aCAhPT0gMSkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgY29uc29sZS5sb2coXCJVbmV4cGVjdGVkIGhlYWRpbmcgZm9ybWF0OiAnXCIgKyBoZWFkaW5nTGluZVN0cmluZyArIFwiJ1wiKVxuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxuXG4gIGNvbnN0IG1hdGNoID0gbWF0Y2hlc1swXVxuICByZXR1cm4gbWF0Y2gudHJpbUxlZnQoKVxufVxuXG5mdW5jdGlvbiBmaW5kSGVhZGluZ1ByZWZpeFJhbmdlKGVkaXRvcjogRWRpdG9yLCBoZWFkaW5nOiBIZWFkaW5nQ2FjaGUsIGZsYWdzOiBTdXBwb3J0RmxhZ3MpOiBFZGl0b3JSYW5nZSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGxpbmVOdW1iZXIgPSBoZWFkaW5nLnBvc2l0aW9uLnN0YXJ0LmxpbmVcbiAgY29uc3QgbGluZVRleHQgPSBlZGl0b3IuZ2V0TGluZShsaW5lTnVtYmVyKVxuICByZXR1cm4gZmluZFJhbmdlSW5IZWFkZXJTdHJpbmcobGluZVRleHQsIGxpbmVOdW1iZXIsIGZsYWdzKVxufVxuXG5mdW5jdGlvbiBjbGVhbkhlYWRpbmdUZXh0Rm9yVG9jKGh0ZXh0OiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoaHRleHQuY29udGFpbnMoJ14nKSkge1xuICAgIGNvbnN0IHggPSBodGV4dC5zcGxpdCgnXicpXG4gICAgaWYgKHgubGVuZ3RoID4gMSkge1xuICAgICAgcmV0dXJuIHhbMF0udHJpbSgpXG4gICAgfVxuICB9XG4gIHJldHVybiBodGV4dC50cmltKClcbn1cblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby11bnVzZWQtdmFyc1xuZnVuY3Rpb24gY3JlYXRlVG9jRW50cnkoaDogSGVhZGluZ0NhY2hlLCBzZXR0aW5nczogTnVtYmVySGVhZGluZ3NQbHVnaW5TZXR0aW5ncywgaW5pdGlhbEhlYWRpbmdMZXZlbDogbnVtYmVyKTogc3RyaW5nIHtcbiAgY29uc3QgdGV4dCA9IGguaGVhZGluZ1xuICBjb25zdCBjbGVhblRleHQgPSBjbGVhbkhlYWRpbmdUZXh0Rm9yVG9jKHRleHQpXG5cbiAgbGV0IGJ1bGxldEluZGVudCA9ICcnXG4gIGNvbnN0IHN0YXJ0TGV2ZWwgPSBpbml0aWFsSGVhZGluZ0xldmVsXG4gIGZvciAobGV0IGkgPSBzdGFydExldmVsOyBpIDwgaC5sZXZlbDsgaSsrKSB7XG4gICAgYnVsbGV0SW5kZW50ICs9ICdcXHQnXG4gIH1cblxuICBjb25zdCBlbnRyeUxpbmsgPSBgW1sjJHt0ZXh0fXwke2NsZWFuVGV4dH1dXWBcblxuICByZXR1cm4gYnVsbGV0SW5kZW50ICsgVE9DX0xJU1RfSVRFTV9CVUxMRVQgKyAnICcgKyBlbnRyeUxpbmtcbn1cblxuLy8gUmVwbGFjZSBhIHJhbmdlLCBidXQgb25seSBpZiB0aGVyZSBpcyBhIGNoYW5nZSBpbiB0ZXh0LCB0byBwcmV2ZW50IHBvbHV0aW5nIHRoZSB1bmRvIHN0YWNrXG5mdW5jdGlvbiByZXBsYWNlUmFuZ2VFY29ub21pY2FsbHkoZWRpdG9yOiBFZGl0b3IsIGNoYW5nZXM6IEVkaXRvckNoYW5nZVtdLCByYW5nZTogRWRpdG9yUmFuZ2UsIHRleHQ6IHN0cmluZyk6IHZvaWQge1xuICBjb25zdCBwcmV2aW91c1RleHQgPSBlZGl0b3IuZ2V0UmFuZ2UocmFuZ2UuZnJvbSwgcmFuZ2UudG8pXG5cbiAgaWYgKHByZXZpb3VzVGV4dCAhPT0gdGV4dCkge1xuICAgIGNoYW5nZXMucHVzaCh7XG4gICAgICB0ZXh0OiB0ZXh0LFxuICAgICAgZnJvbTogcmFuZ2UuZnJvbSxcbiAgICAgIHRvOiByYW5nZS50b1xuICAgIH0pXG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IHVwZGF0ZUhlYWRpbmdOdW1iZXJpbmcgPSAoXG4gIHZpZXdJbmZvOiBWaWV3SW5mbyB8IHVuZGVmaW5lZCxcbiAgc2V0dGluZ3M6IE51bWJlckhlYWRpbmdzUGx1Z2luU2V0dGluZ3Ncbik6IHZvaWQgPT4ge1xuICBpZiAoIXZpZXdJbmZvKSByZXR1cm5cbiAgY29uc3QgaGVhZGluZ3MgPSB2aWV3SW5mby5kYXRhLmhlYWRpbmdzID8/IFtdXG4gIGNvbnN0IGVkaXRvciA9IHZpZXdJbmZvLmVkaXRvclxuICBjb25zdCBzdXBwb3J0RmxhZ3MgPSBjcmVhdGVTdXBwb3J0RmxhZ3NGcm9tU2V0dGluZ3Moc2V0dGluZ3Muc3R5bGVMZXZlbDEsIHNldHRpbmdzLnN0eWxlTGV2ZWxPdGhlcilcblxuICBsZXQgcHJldmlvdXNMZXZlbCA9IDFcblxuICBsZXQgbnVtYmVyaW5nU3RhY2s6IE51bWJlcmluZ1Rva2VuW10gPSBbc3RhcnRBdE9yWmVyb3RoSW5TdHlsZShzZXR0aW5ncy5zdGFydEF0LCBzZXR0aW5ncy5zdHlsZUxldmVsMSldXG5cbiAgaWYgKHNldHRpbmdzLmZpcnN0TGV2ZWwgPiAxKSB7XG4gICAgcHJldmlvdXNMZXZlbCA9IHNldHRpbmdzLmZpcnN0TGV2ZWxcbiAgfSBlbHNlIGlmIChzZXR0aW5ncy5za2lwVG9wTGV2ZWwpIHtcbiAgICBwcmV2aW91c0xldmVsID0gMlxuICB9XG5cbiAgY29uc3QgY2hhbmdlczogRWRpdG9yQ2hhbmdlW10gPSBbXVxuXG4gIGZvciAoY29uc3QgaGVhZGluZyBvZiBoZWFkaW5ncykge1xuICAgIC8vIFVwZGF0ZSB0aGUgbnVtYmVyaW5nIHN0YWNrIGJhc2VkIG9uIHRoZSBsZXZlbCBhbmQgcHJldmlvdXMgbGV2ZWxcblxuICAgIGNvbnN0IGxldmVsID0gaGVhZGluZy5sZXZlbFxuXG4gICAgLy8gSGFuZGxlIHNraXBwZWQgJiBpZ25vcmVkIGxldmVscy5cbiAgICBpZiAoKHNldHRpbmdzLmZpcnN0TGV2ZWwgPiBsZXZlbCkgfHwgKHNldHRpbmdzLnNraXBUb3BMZXZlbCAmJiBsZXZlbCA9PT0gMSkpIHtcbiAgICAgIC8vIFJlc2V0cyB0aGUgbnVtYmVyaW5nIHdoZW4gYSBsZXZlbCBpcyBza2lwcGVkLlxuICAgICAgLy8gTm90ZTogVGhpcyBsZWF2ZXMgaGVhZGluZ3MgYXMgdGhleSBhcmUsIGFsbG93aW5nIHBlb3BsZSB0byBoYXZlIG51bWJlcnMgYXQgdGhlIHN0YXJ0IG9mXG4gICAgICAvLyBpZ25vcmVkIGhlYWRpbmdzLlxuXG4gICAgICBudW1iZXJpbmdTdGFjayA9IFtzdGFydEF0T3JaZXJvdGhJblN0eWxlKHNldHRpbmdzLnN0YXJ0QXQsIHNldHRpbmdzLnN0eWxlTGV2ZWwxKV1cblxuICAgICAgaWYgKHNldHRpbmdzLmZpcnN0TGV2ZWwgPiAxKSB7XG4gICAgICAgIHByZXZpb3VzTGV2ZWwgPSBzZXR0aW5ncy5maXJzdExldmVsXG4gICAgICB9IGVsc2UgaWYgKHNldHRpbmdzLnNraXBUb3BMZXZlbCkge1xuICAgICAgICBwcmV2aW91c0xldmVsID0gMlxuICAgICAgfVxuICAgICAgY29udGludWVcbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgc2tpcHBlZCBoZWFkaW5nc1xuICAgIGlmIChzZXR0aW5ncy5za2lwSGVhZGluZ3MubGVuZ3RoID4gMCkge1xuICAgICAgaWYgKGhlYWRpbmcuaGVhZGluZy5lbmRzV2l0aChzZXR0aW5ncy5za2lwSGVhZGluZ3MpKSB7XG4gICAgICAgIGNvbnRpbnVlXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQWRqdXN0IG51bWJlcmluZyBzdGFja1xuICAgIGlmIChsZXZlbCA9PT0gcHJldmlvdXNMZXZlbCkge1xuICAgICAgY29uc3QgeCA9IG51bWJlcmluZ1N0YWNrLnBvcCgpXG4gICAgICBpZiAoeCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIG51bWJlcmluZ1N0YWNrLnB1c2gobmV4dE51bWJlcmluZ1Rva2VuKHgpKVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAobGV2ZWwgPCBwcmV2aW91c0xldmVsKSB7XG4gICAgICBmb3IgKGxldCBpID0gcHJldmlvdXNMZXZlbDsgaSA+IGxldmVsOyBpLS0pIHtcbiAgICAgICAgbnVtYmVyaW5nU3RhY2sucG9wKClcbiAgICAgIH1cbiAgICAgIGNvbnN0IHggPSBudW1iZXJpbmdTdGFjay5wb3AoKVxuICAgICAgaWYgKHggIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBudW1iZXJpbmdTdGFjay5wdXNoKG5leHROdW1iZXJpbmdUb2tlbih4KSlcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGxldmVsID4gcHJldmlvdXNMZXZlbCkge1xuICAgICAgZm9yIChsZXQgaSA9IHByZXZpb3VzTGV2ZWw7IGkgPCBsZXZlbDsgaSsrKSB7XG4gICAgICAgIG51bWJlcmluZ1N0YWNrLnB1c2goZmlyc3ROdW1iZXJpbmdUb2tlbkluU3R5bGUoc2V0dGluZ3Muc3R5bGVMZXZlbE90aGVyKSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBTZXQgdGhlIHByZXZpb3VzIGxldmVsIHRvIHRoaXMgbGV2ZWwgZm9yIHRoZSBuZXh0IGl0ZXJhdGlvblxuICAgIHByZXZpb3VzTGV2ZWwgPSBsZXZlbFxuXG4gICAgaWYgKGxldmVsID4gc2V0dGluZ3MubWF4TGV2ZWwpIHtcbiAgICAgIC8vIElmIHdlIGFyZSBhYm92ZSB0aGUgbWF4IGxldmVsLCBqdXN0IGRvbid0IG51bWJlciBpdFxuICAgICAgY29udGludWVcbiAgICB9XG5cbiAgICAvLyBGaW5kIHRoZSByYW5nZSB0byByZXBsYWNlLCBhbmQgdGhlbiBkbyBpdFxuICAgIGNvbnN0IHByZWZpeFJhbmdlID0gZmluZEhlYWRpbmdQcmVmaXhSYW5nZShlZGl0b3IsIGhlYWRpbmcsIHN1cHBvcnRGbGFncylcbiAgICBpZiAocHJlZml4UmFuZ2UgPT09IHVuZGVmaW5lZCkgcmV0dXJuXG4gICAgY29uc3QgaGVhZGluZ0hhc2hTdHJpbmcgPSBtYWtlSGVhZGluZ0hhc2hTdHJpbmcoZWRpdG9yLCBoZWFkaW5nKVxuICAgIGlmIChoZWFkaW5nSGFzaFN0cmluZyA9PT0gdW5kZWZpbmVkKSByZXR1cm5cbiAgICBjb25zdCBwcmVmaXhTdHJpbmcgPSBtYWtlTnVtYmVyaW5nU3RyaW5nKG51bWJlcmluZ1N0YWNrKVxuICAgIHJlcGxhY2VSYW5nZUVjb25vbWljYWxseShlZGl0b3IsIGNoYW5nZXMsIHByZWZpeFJhbmdlLCBoZWFkaW5nSGFzaFN0cmluZyArIHByZWZpeFN0cmluZyArIHNldHRpbmdzLnNlcGFyYXRvciArICcgJylcbiAgfVxuXG4gIC8vIEV4ZWN1dGUgdGhlIHRyYW5zYWN0aW9uIHRvIG1ha2UgYWxsIHRoZSBjaGFuZ2VzIGF0IG9uY2VcbiAgaWYgKGNoYW5nZXMubGVuZ3RoID4gMCkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgY29uc29sZS5sb2coJ051bWJlciBIZWFkaW5ncyBQbHVnaW46IEFwcGx5aW5nIGhlYWRpbmdzIG51bWJlcmluZyBjaGFuZ2VzOicsIGNoYW5nZXMubGVuZ3RoKVxuICAgIGVkaXRvci50cmFuc2FjdGlvbih7XG4gICAgICBjaGFuZ2VzOiBjaGFuZ2VzXG4gICAgfSlcbiAgfVxufVxuXG5leHBvcnQgY29uc3QgdXBkYXRlVGFibGVPZkNvbnRlbnRzID0gKFxuICB2aWV3SW5mbzogVmlld0luZm8gfCB1bmRlZmluZWQsXG4gIHNldHRpbmdzOiBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdzXG4pOiB2b2lkID0+IHtcbiAgaWYgKCF2aWV3SW5mbykgcmV0dXJuXG4gIGNvbnN0IGhlYWRpbmdzID0gdmlld0luZm8uZGF0YS5oZWFkaW5ncyA/PyBbXVxuICBjb25zdCBlZGl0b3IgPSB2aWV3SW5mby5lZGl0b3JcblxuICBpZiAoIWlzTm9uRW1wdHlCbG9ja0lkKHNldHRpbmdzLmNvbnRlbnRzKSkgcmV0dXJuXG5cbiAgbGV0IHRvY0hlYWRpbmc6IEhlYWRpbmdDYWNoZSB8IHVuZGVmaW5lZFxuICBsZXQgdG9jQnVpbGRlciA9ICdcXG4nXG4gIGNvbnN0IGNoYW5nZXM6IEVkaXRvckNoYW5nZVtdID0gW11cblxuICAvLyBJbiBjYXNlIGhlYWRpbmdzIHN0YXJ0IGFib3ZlIGxldmVsIDEsIHdlIGRvbid0IHdhbnQgdG8gaW5kZW50IHRoZSBidWxsZXRzIHRvbyBtdWNoXG4gIGxldCBpbml0aWFsSGVhZGluZ0xldmVsID0gMVxuICBpZiAoaGVhZGluZ3MubGVuZ3RoID4gMCkge1xuICAgIGluaXRpYWxIZWFkaW5nTGV2ZWwgPSBoZWFkaW5nc1swXS5sZXZlbFxuICB9XG5cbiAgZm9yIChjb25zdCBoZWFkaW5nIG9mIGhlYWRpbmdzKSB7XG4gICAgLy8gT1JERVJJTkc6IEltcG9ydGFudCB0byBmaW5kIHRoZSBUT0MgaGVhZGluZyBiZWZvcmUgc2tpcHBpbmcgc2tpcHBlZCBoZWFkaW5ncywgc2luY2UgdGhhdCBpcyBmb3IgbnVtYmVyaW5nXG5cbiAgICAvLyBGaW5kIHRoZSBUT0MgaGVhZGluZ1xuICAgIGlmIChoZWFkaW5nLmhlYWRpbmcuZW5kc1dpdGgoc2V0dGluZ3MuY29udGVudHMpKSB7XG4gICAgICB0b2NIZWFkaW5nID0gaGVhZGluZ1xuICAgIH1cblxuICAgIC8qIFRoaXMgY29kZSBsZXRzIHVzIHNraXAgVE9DIGxpbmVzIGZvciBza2lwcGVkIGhlYWRpbmdzLCBidXQgZG9lc24ndCB3b3JrIHdlbGwgd2l0aCBmaXJzdC1sZXZlbCBzZXR0aW5nXG4gICAgaWYgKChzZXR0aW5ncy5za2lwVG9wTGV2ZWwgJiYgaGVhZGluZy5sZXZlbCA9PT0gMSkgfHwgKGhlYWRpbmcubGV2ZWwgPiBzZXR0aW5ncy5tYXhMZXZlbCkpIHtcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuICAgICovXG5cbiAgICBjb25zdCB0b2NFbnRyeSA9IGNyZWF0ZVRvY0VudHJ5KGhlYWRpbmcsIHNldHRpbmdzLCBpbml0aWFsSGVhZGluZ0xldmVsKVxuICAgIHRvY0J1aWxkZXIgKz0gdG9jRW50cnkgKyAnXFxuJ1xuICB9XG5cbiAgLy8gSW5zZXJ0IHRoZSBnZW5lcmF0ZWQgdGFibGUgb2YgY29udGVudHNcbiAgaWYgKHRvY0hlYWRpbmcpIHtcbiAgICBjb25zdCBmcm9tID0ge1xuICAgICAgbGluZTogdG9jSGVhZGluZy5wb3NpdGlvbi5zdGFydC5saW5lICsgMSxcbiAgICAgIGNoOiAwXG4gICAgfVxuXG4gICAgLy8gRmluZCB0aGUgZW5kIG9mIHRoZSBUT0Mgc2VjdGlvblxuICAgIGNvbnN0IHN0YXJ0aW5nTGluZSA9IHRvY0hlYWRpbmcucG9zaXRpb24uc3RhcnQubGluZSArIDFcbiAgICBsZXQgZW5kaW5nTGluZSA9IHN0YXJ0aW5nTGluZVxuICAgIGxldCBmb3VuZExpc3QgPSBmYWxzZVxuICAgIGNvbnN0IGxhc3RMaW5lSW5FZGl0b3IgPSBlZGl0b3IubGFzdExpbmUoKVxuICAgIGZvciAoOyA7IGVuZGluZ0xpbmUrKykge1xuICAgICAgY29uc3QgbGluZSA9IGVkaXRvci5nZXRMaW5lKGVuZGluZ0xpbmUpXG4gICAgICBpZiAobGluZSA9PT0gdW5kZWZpbmVkIHx8IGVuZGluZ0xpbmUgPiBsYXN0TGluZUluRWRpdG9yKSB7XG4gICAgICAgIC8vIFJlYWNoZWQgZW5kIG9mIGZpbGUsIGluc2VydCBhdCB0aGUgc3RhcnQgb2YgdGhlIFRPQyBzZWN0aW9uXG4gICAgICAgIGVuZGluZ0xpbmUgPSBzdGFydGluZ0xpbmVcbiAgICAgICAgYnJlYWtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHRyaW1tZWRMaW5lVGV4dCA9IGxpbmUudHJpbVN0YXJ0KClcbiAgICAgIGlmIChmb3VuZExpc3QpIHtcbiAgICAgICAgaWYgKCF0cmltbWVkTGluZVRleHQuc3RhcnRzV2l0aChUT0NfTElTVF9JVEVNX0JVTExFVCkpIGJyZWFrXG4gICAgICAgIGlmICh0cmltbWVkTGluZVRleHQuc3RhcnRzV2l0aCgnIycpKSBicmVha1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHRyaW1tZWRMaW5lVGV4dC5zdGFydHNXaXRoKFRPQ19MSVNUX0lURU1fQlVMTEVUKSkge1xuICAgICAgICAgIGZvdW5kTGlzdCA9IHRydWVcbiAgICAgICAgfSBlbHNlIGlmICh0cmltbWVkTGluZVRleHQuc3RhcnRzV2l0aCgnIycpKSB7XG4gICAgICAgICAgLy8gUmVhY2hlZCB0aGUgbmV4dCBoZWFkaW5nIHdpdGhvdXQgZmluZGluZyBleGlzdGluZyBUT0MgbGlzdCwgaW5zZXJ0IGF0IHRoZSBzdGFydCBvZiB0aGUgVE9DIHNlY3Rpb25cbiAgICAgICAgICBlbmRpbmdMaW5lID0gc3RhcnRpbmdMaW5lXG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb250aW51ZVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHRvY0J1aWxkZXIgPT09ICdcXG4nKSB7XG4gICAgICB0b2NCdWlsZGVyID0gJydcbiAgICB9XG5cbiAgICBjb25zdCB0byA9IHtcbiAgICAgIGxpbmU6IGVuZGluZ0xpbmUsXG4gICAgICBjaDogMFxuICAgIH1cbiAgICBjb25zdCByYW5nZSA9IHsgZnJvbSwgdG8gfVxuICAgIHJlcGxhY2VSYW5nZUVjb25vbWljYWxseShlZGl0b3IsIGNoYW5nZXMsIHJhbmdlLCB0b2NCdWlsZGVyKVxuICB9XG5cbiAgLy8gRXhlY3V0ZSB0aGUgdHJhbnNhY3Rpb24gdG8gbWFrZSBhbGwgdGhlIGNoYW5nZXMgYXQgb25jZVxuICBpZiAoY2hhbmdlcy5sZW5ndGggPiAwKSB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICBjb25zb2xlLmxvZygnTnVtYmVyIEhlYWRpbmdzIFBsdWdpbjogQXBwbHlpbmcgdGFibGUgb2YgY29udGVudHMgY2hhbmdlczonLCBjaGFuZ2VzLmxlbmd0aClcbiAgICBlZGl0b3IudHJhbnNhY3Rpb24oe1xuICAgICAgY2hhbmdlczogY2hhbmdlc1xuICAgIH0pXG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IHJlbW92ZUhlYWRpbmdOdW1iZXJpbmcgPSAoXG4gIHZpZXdJbmZvOiBWaWV3SW5mbyB8IHVuZGVmaW5lZFxuKTogdm9pZCA9PiB7XG4gIGlmICghdmlld0luZm8pIHJldHVyblxuICBjb25zdCBoZWFkaW5ncyA9IHZpZXdJbmZvLmRhdGEuaGVhZGluZ3MgPz8gW11cbiAgY29uc3QgZWRpdG9yID0gdmlld0luZm8uZWRpdG9yXG5cbiAgY29uc3QgY2hhbmdlczogRWRpdG9yQ2hhbmdlW10gPSBbXVxuXG4gIGZvciAoY29uc3QgaGVhZGluZyBvZiBoZWFkaW5ncykge1xuICAgIGNvbnN0IHByZWZpeFJhbmdlID0gZmluZEhlYWRpbmdQcmVmaXhSYW5nZShlZGl0b3IsIGhlYWRpbmcsIHsgYWxwaGFiZXQ6IHRydWUsIHJvbWFuOiB0cnVlIH0pXG4gICAgaWYgKHByZWZpeFJhbmdlID09PSB1bmRlZmluZWQpIHJldHVyblxuICAgIGNvbnN0IGhlYWRpbmdIYXNoU3RyaW5nID0gbWFrZUhlYWRpbmdIYXNoU3RyaW5nKGVkaXRvciwgaGVhZGluZylcbiAgICBpZiAoaGVhZGluZ0hhc2hTdHJpbmcgPT09IHVuZGVmaW5lZCkgcmV0dXJuXG4gICAgcmVwbGFjZVJhbmdlRWNvbm9taWNhbGx5KGVkaXRvciwgY2hhbmdlcywgcHJlZml4UmFuZ2UsIGhlYWRpbmdIYXNoU3RyaW5nICsgJyAnKVxuICB9XG5cbiAgaWYgKGNoYW5nZXMubGVuZ3RoID4gMCkge1xuICAgIGVkaXRvci50cmFuc2FjdGlvbih7XG4gICAgICBjaGFuZ2VzOiBjaGFuZ2VzXG4gICAgfSlcbiAgfVxufVxuIiwiaW1wb3J0IHsgQXBwLCBQbHVnaW4sIFBsdWdpblNldHRpbmdUYWIsIFNldHRpbmcgfSBmcm9tICdvYnNpZGlhbidcclxuaW1wb3J0IHsgZ2V0Vmlld0luZm8sIGlzVmlld0FjdGl2ZSB9IGZyb20gJy4vYWN0aXZlVmlld0hlbHBlcnMnXHJcbmltcG9ydCB7IGdldEZyb250TWF0dGVyU2V0dGluZ3NPckFsdGVybmF0aXZlLCBzYXZlU2V0dGluZ3NUb0Zyb250TWF0dGVyIH0gZnJvbSAnLi9mcm9udE1hdHRlcidcclxuaW1wb3J0IHsgc2hvd051bWJlcmluZ0RvbmVNZXNzYWdlIH0gZnJvbSAnLi9tZXNzYWdlcydcclxuaW1wb3J0IHsgcmVtb3ZlSGVhZGluZ051bWJlcmluZywgdXBkYXRlSGVhZGluZ051bWJlcmluZywgdXBkYXRlVGFibGVPZkNvbnRlbnRzIH0gZnJvbSAnLi9udW1iZXJpbmcnXHJcbmltcG9ydCB7IE51bWJlcmluZ1N0eWxlIH0gZnJvbSAnLi9udW1iZXJpbmdUb2tlbnMnXHJcbmltcG9ydCB7IERFRkFVTFRfU0VUVElOR1MsIE51bWJlckhlYWRpbmdzUGx1Z2luU2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzVHlwZXMnXHJcblxyXG5jbGFzcyBOdW1iZXJIZWFkaW5nc1BsdWdpblNldHRpbmdUYWIgZXh0ZW5kcyBQbHVnaW5TZXR0aW5nVGFiIHtcclxuICBwbHVnaW46IE51bWJlckhlYWRpbmdzUGx1Z2luXHJcblxyXG4gIGNvbnN0cnVjdG9yKGFwcDogQXBwLCBwbHVnaW46IE51bWJlckhlYWRpbmdzUGx1Z2luKSB7XHJcbiAgICBzdXBlcihhcHAsIHBsdWdpbilcclxuICAgIHRoaXMucGx1Z2luID0gcGx1Z2luXHJcbiAgfVxyXG5cclxuICBkaXNwbGF5KCk6IHZvaWQge1xyXG4gICAgY29uc3QgeyBjb250YWluZXJFbCB9ID0gdGhpc1xyXG5cclxuICAgIGNvbnRhaW5lckVsLmVtcHR5KClcclxuXHJcbiAgICBjb250YWluZXJFbC5jcmVhdGVFbCgnaDInLCB7IHRleHQ6ICdOdW1iZXIgSGVhZGluZ3MgLSBTZXR0aW5ncycgfSlcclxuXHJcbiAgICBjb250YWluZXJFbC5jcmVhdGVFbCgnZGl2JywgeyB0ZXh0OiAnVG8gYWRkIG51bWJlcmluZyB0byB5b3VyIGRvY3VtZW50LCBicmluZyB1cCB0aGUgY29tbWFuZCB3aW5kb3cgKG9uIE1hYywgdHlwZSBDTUQrUCksIGFuZCB0aGVuIHR5cGUgXCJOdW1iZXIgSGVhZGluZ3NcIiB0byBzZWUgYSBsaXN0IG9mIGF2YWlsYWJsZSBjb21tYW5kcy4nIH0pXHJcblxyXG4gICAgY29udGFpbmVyRWwuY3JlYXRlRWwoJ2JyJywge30pXHJcblxyXG4gICAgY29udGFpbmVyRWwuY3JlYXRlRWwoJ2RpdicsIHsgdGV4dDogJ0lmIHRoZSBkb2N1bWVudCBoYXMgZnJvbnQgbWF0dGVyIGRlZmluZWQgd2l0aCB0aGUgYmVsb3cgc2V0dGluZ3MsIHRoZSBwcm9qZWN0LXdpZGUgc2V0dGluZ3MgZGVmaW5lZCBvbiB0aGlzIHNjcmVlbiB3aWxsIGJlIGlnbm9yZWQuIFlvdSBjYW4gZGVmaW5lIGZyb250IG1hdHRlciBsaWtlIHRoaXM6JyB9KVxyXG5cclxuICAgIGNvbnRhaW5lckVsLmNyZWF0ZUVsKCdwcmUnLCB7XHJcbiAgICAgIHRleHQ6IGAgICAgLS0tXHJcbiAgICBhbGlhczpcclxuICAgIC0gRXhhbXBsZSBBbGlhc1xyXG4gICAgdGFnczpcclxuICAgIC0gZXhhbXBsZS10YWdcclxuICAgIG51bWJlciBoZWFkaW5nczogZmlyc3QtbGV2ZWwgMSwgc3RhcnQtYXQgMiwgbWF4IDYsIDEuMSwgYXV0bywgY29udGVudHMgXnRvY1xyXG4gICAgLS0tYFxyXG4gICAgfSlcclxuXHJcbiAgICBjb250YWluZXJFbC5jcmVhdGVFbCgnZGl2Jywge1xyXG4gICAgICB0ZXh0OiBgXHJcbiAgICAgIFRoZSAnbnVtYmVyIGhlYWRpbmdzJyBmcm9udCBtYXR0ZXIga2V5IGlzIHVzZWQgdG8gc3RvcmUgbnVtYmVyaW5nIHNldHRpbmdzIHNwZWNpZmljIHRvIHRoZSBmaWxlLiBUaGVyZSBhcmUgZm91ciBwb3NzaWJsZSBvcHRpb25zXHJcbiAgICAgIGluIHRoZSB2YWx1ZSB0byB0aGUgcmlnaHQgb2YgdGhlIGNvbG9uLCBzZXBhcmF0ZWQgYnkgY29tbWFzLlxyXG4gICAgYFxyXG4gICAgfSlcclxuXHJcbiAgICBjb25zdCB1bCA9IGNvbnRhaW5lckVsLmNyZWF0ZUVsKCd1bCcsIHt9KVxyXG5cclxuICAgIGNvbnN0IGxpMCA9IHVsLmNyZWF0ZUVsKCdsaScsIHt9KVxyXG4gICAgbGkwLmNyZWF0ZUVsKCdiJywgeyB0ZXh0OiAnQXV0b21hdGljIG51bWJlcmluZycgfSlcclxuICAgIGxpMC5jcmVhdGVFbCgnc3BhbicsIHsgdGV4dDogJzogSWYgXFwnYXV0b1xcJyBhcHBlYXJzLCB0aGUgZG9jdW1lbnQgd2lsbCBiZSBhdXRvbWF0aWNhbGx5IG51bWJlcmVkLicgfSlcclxuXHJcbiAgICBjb25zdCBsaTEgPSB1bC5jcmVhdGVFbCgnbGknLCB7fSlcclxuICAgIGxpMS5jcmVhdGVFbCgnYicsIHsgdGV4dDogJ0ZpcnN0IGxldmVsIHRvIG51bWJlcicgfSlcclxuICAgIGxpMS5jcmVhdGVFbCgnc3BhbicsIHsgdGV4dDogJzogSWYgXFwnZmlyc3QtbGV2ZWwgMlxcJyBhcHBlYXJzLCB0aGUgbnVtYmVyaW5nIHdpbGwgc3RhcnQgYXQgdGhlIHNlY29uZCBsZXZlbCcgfSlcclxuXHJcbiAgICBjb25zdCBsaTIgPSB1bC5jcmVhdGVFbCgnbGknLCB7fSlcclxuICAgIGxpMi5jcmVhdGVFbCgnYicsIHsgdGV4dDogJ1N0YXJ0IG51bWJlcmluZyBmaXJzdCBoZWFkaW5nIGF0JyB9KVxyXG4gICAgbGkyLmNyZWF0ZUVsKCdzcGFuJywgeyB0ZXh0OiAnOiBJZiBcXCdzdGFydC1hdCBDXFwnIGFwcGVhcnMsIHRoZSBudW1iZXJpbmcgb2YgdGhlIGZpcnN0IGxldmVsIHdpbGwgc3RhcnQgYXQgQywgaW5zdGVhZCBvZiBBJyB9KVxyXG5cclxuICAgIGNvbnN0IGxpMyA9IHVsLmNyZWF0ZUVsKCdsaScsIHt9KVxyXG4gICAgbGkzLmNyZWF0ZUVsKCdiJywgeyB0ZXh0OiAnTWF4aW11bSBsZXZlbCB0byBudW1iZXInIH0pXHJcbiAgICBsaTMuY3JlYXRlRWwoJ3NwYW4nLCB7IHRleHQ6ICc6IElmIFxcJ21heCA2XFwnIGFwcGVhcnMsIHRoZSBoZWFkaW5ncyBhYm92ZSBsZXZlbCA2IHdpbGwgYmUgc2tpcHBlZC4nIH0pXHJcblxyXG4gICAgY29uc3QgbGk0ID0gdWwuY3JlYXRlRWwoJ2xpJywge30pXHJcbiAgICBsaTQuY3JlYXRlRWwoJ2InLCB7IHRleHQ6ICdUYWJsZSBvZiBjb250ZW50cyBhbmNob3InIH0pXHJcbiAgICBsaTQuY3JlYXRlRWwoJ3NwYW4nLCB7IHRleHQ6ICc6IElmIFxcJ2NvbnRlbnRzIF50b2NcXCcgYXBwZWFycywgdGhlIGhlYWRpbmcgdGhhdCBlbmRzIHdpdGggdGhlIGFuY2hvciBedG9jIHdpbGwgaGF2ZSBhIHRhYmxlIG9mIGNvbnRlbnRzIGluc2VydGVkIGJlbmVhdGggaXQuJyB9KVxyXG5cclxuICAgIGNvbnN0IGxpNDEgPSB1bC5jcmVhdGVFbCgnbGknLCB7fSlcclxuICAgIGxpNDEuY3JlYXRlRWwoJ2InLCB7IHRleHQ6ICdTa2lwIGhlYWRpbmdzIGFuY2hvcicgfSlcclxuICAgIGxpNDEuY3JlYXRlRWwoJ3NwYW4nLCB7IHRleHQ6ICc6IElmIFxcJ3NraXAgXnNraXBwZWRcXCcgYXBwZWFycywgdGhlIGhlYWRpbmcgdGhhdCBlbmRzIHdpdGggdGhlIGFuY2hvciBec2tpcHBlZCB3aWxsIG5vdCBiZSBudW1iZXJlZC4nIH0pXHJcblxyXG4gICAgY29uc3QgbGk1ID0gdWwuY3JlYXRlRWwoJ2xpJywge30pXHJcbiAgICBsaTUuY3JlYXRlRWwoJ2InLCB7IHRleHQ6ICdOdW1iZXJpbmcgc3R5bGUnIH0pXHJcbiAgICBsaTUuY3JlYXRlRWwoJ3NwYW4nLCB7XHJcbiAgICAgIHRleHQ6IGA6XHJcbiAgICAgIEEgc3R5bGUgdGV4dCBsaWtlICcxLjEnLCAnQS4xJywgb3IgJ18uMS4xJyB0ZWxscyB0aGUgcGx1Z2luIGhvdyB0byBmb3JtYXQgdGhlIGhlYWRpbmdzLlxyXG4gICAgICBJZiBhIHN0eWxlIHN0cmluZyBlbmRzIHdpdGggJy4nIChhIGRvdCksICc6JyAoYSBjb2xvbiksICctJyAoYSBkYXNoKSwgJ+KAlCcgKGFuIGVtZGFzaCksIG9yICcpJyAoYSByaWdodCBwYXJlbnRoZXNpcyksIHRoZSBoZWFkaW5nIG51bWJlcnMgd2lsbCBiZSBzZXBhcmF0ZWQgZnJvbSB0aGUgaGVhZGluZyB0aXRsZVxyXG4gICAgICB3aXRoIHRoYXQgc3ltYm9sLmBcclxuICAgIH0pXHJcblxyXG4gICAgY29uc3QgdWwzID0gbGk1LmNyZWF0ZUVsKCd1bCcsIHt9KVxyXG4gICAgdWwzLmNyZWF0ZUVsKCdsaScsIHtcclxuICAgICAgdGV4dDogYCAgICAgIFxyXG4gICAgICBGb3IgZXhhbXBsZSwgJzEuMScgbWVhbnMgYm90aCB0b3AgbGV2ZWwgYW5kIG90aGVyIGhlYWRpbmdzIHdpbGwgYmUgbnVtYmVyZWQgc3RhcnRpbmcgZnJvbSAnMScuXHJcbiAgICBgXHJcbiAgICB9KVxyXG4gICAgdWwzLmNyZWF0ZUVsKCdsaScsIHtcclxuICAgICAgdGV4dDogYCAgICAgIFxyXG4gICAgICBGb3IgZXhhbXBsZSwgJ0EuMScgbWVhbnMgdG9wIGxldmVsIGhlYWRpbmdzIHdpbGwgYmUgbnVtYmVyZWQgc3RhcnRpbmcgZnJvbSAnQScuXHJcbiAgICBgXHJcbiAgICB9KVxyXG4gICAgdWwzLmNyZWF0ZUVsKCdsaScsIHtcclxuICAgICAgdGV4dDogYCAgICAgIFxyXG4gICAgICBGb3IgZXhhbXBsZSwgJ18uQS4xJyBtZWFucyB0b3AgbGV2ZWwgaGVhZGluZ3Mgd2lsbCBOT1QgYmUgbnVtYmVyZWQsIGJ1dCB0aGUgbmV4dCBsZXZlbHMgd2lsbCBiZSBudW1iZXJlZCB3aXRoIGxldHRlcnMgYW5kIG51bWJlcnMuXHJcbiAgICBgXHJcbiAgICB9KVxyXG4gICAgdWwzLmNyZWF0ZUVsKCdsaScsIHtcclxuICAgICAgdGV4dDogYCAgICAgIFxyXG4gICAgICBGb3IgZXhhbXBsZSwgJzEuMTonIG1lYW5zIGhlYWRpbmdzIHdpbGwgbG9vayBsaWtlICcjIyAyLjQ6IEV4YW1wbGUgSGVhZGluZydcclxuICAgIGBcclxuICAgIH0pXHJcbiAgICB1bDMuY3JlYXRlRWwoJ2xpJywge1xyXG4gICAgICB0ZXh0OiBgICAgICAgXHJcbiAgICAgIEZvciBleGFtcGxlLCAnQS4xLScgbWVhbnMgaGVhZGluZ3Mgd2lsbCBsb29rIGxpa2UgJyMjIEIuNS0gRXhhbXBsZSBIZWFkaW5nJ1xyXG4gICAgYFxyXG4gICAgfSlcclxuICAgIHVsMy5jcmVhdGVFbCgnbGknLCB7XHJcbiAgICAgIHRleHQ6IGAgICAgICBcclxuICAgICAgRm9yIGV4YW1wbGUsICdJLkEg4oCUJyBtZWFucyBoZWFkaW5ncyB3aWxsIGxvb2sgbGlrZSAnIyMgSVYuQSDigJQgRXhhbXBsZSBIZWFkaW5nJyAod2l0aCBSb21hbiBudW1lcmFscylcclxuICAgIGBcclxuICAgIH0pXHJcblxyXG4gICAgY29uc3QgbGkxMDAgPSB1bC5jcmVhdGVFbCgnbGknLCB7fSlcclxuICAgIGxpMTAwLmNyZWF0ZUVsKCdiJywgeyB0ZXh0OiAnTnVtYmVyaW5nIG9mZicgfSlcclxuICAgIGxpMTAwLmNyZWF0ZUVsKCdzcGFuJywgeyB0ZXh0OiAnOiBJZiBcXCdvZmZcXCcgYXBwZWFycywgdGhlIGRvY3VtZW50IHdpbGwgbm90IGJlIG51bWJlcmVkLicgfSlcclxuXHJcbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcclxuICAgICAgLnNldE5hbWUoJ1NraXAgdG9wIGhlYWRpbmcgbGV2ZWwnKVxyXG4gICAgICAuc2V0RGVzYygnSWYgc2VsZWN0ZWQsIG51bWJlcmluZyB3aWxsIG5vdCBiZSBhcHBsaWVkIHRvIHRoZSB0b3AgaGVhZGluZyBsZXZlbC4nKVxyXG4gICAgICAuYWRkVG9nZ2xlKHRvZ2dsZSA9PiB0b2dnbGVcclxuICAgICAgICAuc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3Muc2tpcFRvcExldmVsKVxyXG4gICAgICAgIC5zZXRUb29sdGlwKCdTa2lwIHRvcCBoZWFkaW5nIGxldmVsJylcclxuICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XHJcbiAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5za2lwVG9wTGV2ZWwgPSB2YWx1ZVxyXG4gICAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKClcclxuICAgICAgICB9KSlcclxuXHJcbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcclxuICAgICAgLnNldE5hbWUoJ0ZpcnN0IGhlYWRpbmcgbGV2ZWwnKVxyXG4gICAgICAuc2V0RGVzYygnRmlyc3QgaGVhZGluZyBsZXZlbCB0byBudW1iZXIuJylcclxuICAgICAgLmFkZFNsaWRlcihzbGlkZXIgPT4gc2xpZGVyXHJcbiAgICAgICAgLnNldExpbWl0cygxLCA2LCAxKVxyXG4gICAgICAgIC5zZXRWYWx1ZSh0aGlzLnBsdWdpbi5zZXR0aW5ncy5maXJzdExldmVsKVxyXG4gICAgICAgIC5zZXREeW5hbWljVG9vbHRpcCgpXHJcbiAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xyXG4gICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3MuZmlyc3RMZXZlbCA9IHZhbHVlXHJcbiAgICAgICAgICBhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKVxyXG4gICAgICAgIH0pKVxyXG5cclxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxyXG4gICAgICAuc2V0TmFtZSgnU3RhcnQgbnVtYmVyaW5nIGF0JylcclxuICAgICAgLnNldERlc2MoJ1N0YXJ0IG51bWJlcmluZyB0aGUgZmlyc3QgaGVhZGluZyBsZXZlbCBmcm9tIHRoaXMgdmFsdWUuJylcclxuICAgICAgLmFkZFRleHQodGV4dCA9PiB0ZXh0XHJcbiAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLnN0YXJ0QXQpXHJcbiAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xyXG4gICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3Muc3RhcnRBdCA9IHZhbHVlXHJcbiAgICAgICAgICBhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKVxyXG4gICAgICAgIH0pKVxyXG5cclxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxyXG4gICAgICAuc2V0TmFtZSgnTWF4aW11bSBoZWFkaW5nIGxldmVsJylcclxuICAgICAgLnNldERlc2MoJ01heGltdW0gaGVhZGluZyBsZXZlbCB0byBudW1iZXIuJylcclxuICAgICAgLmFkZFNsaWRlcihzbGlkZXIgPT4gc2xpZGVyXHJcbiAgICAgICAgLnNldExpbWl0cygxLCA2LCAxKVxyXG4gICAgICAgIC5zZXRWYWx1ZSh0aGlzLnBsdWdpbi5zZXR0aW5ncy5tYXhMZXZlbClcclxuICAgICAgICAuc2V0RHluYW1pY1Rvb2x0aXAoKVxyXG4gICAgICAgIC5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcclxuICAgICAgICAgIHRoaXMucGx1Z2luLnNldHRpbmdzLm1heExldmVsID0gdmFsdWVcclxuICAgICAgICAgIGF3YWl0IHRoaXMucGx1Z2luLnNhdmVTZXR0aW5ncygpXHJcbiAgICAgICAgfSkpXHJcblxyXG4gICAgbmV3IFNldHRpbmcoY29udGFpbmVyRWwpXHJcbiAgICAgIC5zZXROYW1lKCdTdHlsZSBmb3IgbGV2ZWwgMSBoZWFkaW5ncycpXHJcbiAgICAgIC5zZXREZXNjKCdEZWZpbmVzIHRoZSBudW1iZXJpbmcgc3R5bGUgZm9yIGxldmVsIG9uZSBoZWFkaW5ncy4gVmFsaWQgdmFsdWVzIGFyZSAxIChmb3IgbnVtYmVycykgb3IgQSAoZm9yIGNhcGl0YWwgbGV0dGVycykgb3IgSSAoZm9yIFJvbWFuIG51bWVyYWxzKS4nKVxyXG4gICAgICAuYWRkVGV4dCh0ZXh0ID0+IHRleHRcclxuICAgICAgICAuc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3Muc3R5bGVMZXZlbDEpXHJcbiAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xyXG4gICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3Muc3R5bGVMZXZlbDEgPSB2YWx1ZSBhcyBOdW1iZXJpbmdTdHlsZVxyXG4gICAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKClcclxuICAgICAgICB9KSlcclxuXHJcbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcclxuICAgICAgLnNldE5hbWUoJ1N0eWxlIGZvciBsb3dlciBsZXZlbCBoZWFkaW5ncyAoYmVsb3cgbGV2ZWwgMSknKVxyXG4gICAgICAuc2V0RGVzYygnRGVmaW5lcyB0aGUgbnVtYmVyaW5nIHN0eWxlIGZvciBoZWFkaW5ncyBiZWxvdyBsZXZlbCBvbmUuIFZhbGlkIHZhbHVlcyBhcmUgMSAoZm9yIG51bWJlcnMpIG9yIEEgKGZvciBjYXBpdGFsIGxldHRlcnMpIG9yIEkgKGZvciBSb21hbiBudW1lcmFscykuJylcclxuICAgICAgLmFkZFRleHQodGV4dCA9PiB0ZXh0XHJcbiAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLnN0eWxlTGV2ZWxPdGhlcilcclxuICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XHJcbiAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5zdHlsZUxldmVsT3RoZXIgPSB2YWx1ZSBhcyBOdW1iZXJpbmdTdHlsZVxyXG4gICAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKClcclxuICAgICAgICB9KSlcclxuXHJcbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcclxuICAgICAgLnNldE5hbWUoJ0F1dG9tYXRpYyBudW1iZXJpbmcnKVxyXG4gICAgICAuc2V0RGVzYygnVHVybnMgb24gYXV0b21hdGljIG51bWJlcmluZyBvZiBkb2N1bWVudHMuJylcclxuICAgICAgLmFkZFRvZ2dsZSh0b2dnbGUgPT4gdG9nZ2xlXHJcbiAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLmF1dG8pXHJcbiAgICAgICAgLnNldFRvb2x0aXAoJ1R1cm4gb24gYXV0b21hdGljIG51bWJlcmluZycpXHJcbiAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xyXG4gICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3MuYXV0byA9IHZhbHVlXHJcbiAgICAgICAgICBhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKVxyXG4gICAgICAgIH0pKVxyXG5cclxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxyXG4gICAgICAuc2V0TmFtZSgnU2VwYXJhdG9yIHN0eWxlJylcclxuICAgICAgLnNldERlc2MoJ0RlZmluZXMgdGhlIHNlcGFyYXRvciBzdHlsZSBiZXR3ZWVuIHRoZSBoZWFkaW5nIG51bWJlciBhbmQgdGhlIGhlYWRpbmcgdGV4dC4gVmFsaWQgdmFsdWVzIGFyZSA6IChjb2xvbikgb3IgLiAoZG90KSBvciAtIChkYXNoKSBvciDigJQgKGVtZGFzaCkgb3IgKSAoYSByaWdodCBwYXJlbnRoZXNpcykuIFlvdSBjYW4gYWxzbyBsZWF2ZSBpdCBibGFuayBmb3Igbm8gc2VwYXJhdG9yLCBvciBoYXZlIGEgc3BhY2UgYmVmb3JlIHRoZSBzZXBhcmF0b3IuJylcclxuICAgICAgLmFkZFRleHQodGV4dCA9PiB0ZXh0XHJcbiAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLnNlcGFyYXRvcilcclxuICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XHJcbiAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5zZXBhcmF0b3IgPSB2YWx1ZVxyXG4gICAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKClcclxuICAgICAgICB9KSlcclxuXHJcbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcclxuICAgICAgLnNldE5hbWUoJ1RhYmxlIG9mIENvbnRlbnRzIEFuY2hvcicpXHJcbiAgICAgIC5zZXREZXNjKCdBbmNob3Igd2hpY2ggbGFiZWxzIHRoZSBoZWFkZXIgd2hlcmUgYSB0YWJsZSBvZiBjb250ZW50cyBzaG91bGQgYmUgaW5zZXJ0ZWQuIFRoZSBhbmNob3Igc2hvdWxkIGJlIGFkZGVkIGF0IHRoZSBlbmQgb2YgYSBoZWFkZXIuIEZvciBleGFtcGxlLCBedG9jLicpXHJcbiAgICAgIC5hZGRUZXh0KHRleHQgPT4gdGV4dFxyXG4gICAgICAgIC5zZXRWYWx1ZSh0aGlzLnBsdWdpbi5zZXR0aW5ncy5jb250ZW50cylcclxuICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XHJcbiAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5jb250ZW50cyA9IHZhbHVlXHJcbiAgICAgICAgICBhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKVxyXG4gICAgICAgIH0pKVxyXG5cclxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxyXG4gICAgICAuc2V0TmFtZSgnU2tpcCBIZWFkaW5ncyBBbmNob3InKVxyXG4gICAgICAuc2V0RGVzYygnQW5jaG9yIHdoaWNoIGxhYmVscyB0aGUgaGVhZGVycyB0aGF0IHNob3VsZCBub3QgYmUgbnVtYmVyZWQuIFRoZSBhbmNob3Igc2hvdWxkIGJlIGFkZGVkIGF0IHRoZSBlbmQgb2YgYSBoZWFkZXIuIEZvciBleGFtcGxlLCBec2tpcHBlZC4nKVxyXG4gICAgICAuYWRkVGV4dCh0ZXh0ID0+IHRleHRcclxuICAgICAgICAuc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3Muc2tpcEhlYWRpbmdzKVxyXG4gICAgICAgIC5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcclxuICAgICAgICAgIHRoaXMucGx1Z2luLnNldHRpbmdzLnNraXBIZWFkaW5ncyA9IHZhbHVlXHJcbiAgICAgICAgICBhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKVxyXG4gICAgICAgIH0pKVxyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgTnVtYmVySGVhZGluZ3NQbHVnaW4gZXh0ZW5kcyBQbHVnaW4ge1xyXG4gIHNldHRpbmdzITogTnVtYmVySGVhZGluZ3NQbHVnaW5TZXR0aW5nc1xyXG5cclxuICBhc3luYyBvbmxvYWQoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxyXG4gICAgY29uc29sZS5pbmZvKCdMb2FkaW5nIE51bWJlciBIZWFkaW5ncyBQbHVnaW4sIHZlcnNpb24gJyArIHRoaXMubWFuaWZlc3QudmVyc2lvbilcclxuXHJcbiAgICBhd2FpdCB0aGlzLmxvYWRTZXR0aW5ncygpXHJcblxyXG4gICAgdGhpcy5hZGRDb21tYW5kKHtcclxuICAgICAgaWQ6ICdudW1iZXItaGVhZGluZ3Mtd2l0aC1vcHRpb25zJyxcclxuICAgICAgbmFtZTogJ051bWJlciBhbGwgaGVhZGluZ3MgaW4gZG9jdW1lbnQgKGFuZCBzaG93IG9wdGlvbnMpJyxcclxuICAgICAgY2hlY2tDYWxsYmFjazogKGNoZWNraW5nOiBib29sZWFuKSA9PiB7XHJcbiAgICAgICAgaWYgKGNoZWNraW5nKSByZXR1cm4gaXNWaWV3QWN0aXZlKHRoaXMuYXBwKVxyXG5cclxuICAgICAgICBjb25zdCB2aWV3SW5mbyA9IGdldFZpZXdJbmZvKHRoaXMuYXBwKVxyXG4gICAgICAgIGlmICh2aWV3SW5mbykge1xyXG4gICAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBnZXRGcm9udE1hdHRlclNldHRpbmdzT3JBbHRlcm5hdGl2ZSh2aWV3SW5mby5kYXRhLCB0aGlzLnNldHRpbmdzKVxyXG4gICAgICAgICAgaWYgKHNldHRpbmdzLm9mZikgcmV0dXJuIGZhbHNlXHJcblxyXG4gICAgICAgICAgdXBkYXRlSGVhZGluZ051bWJlcmluZyh2aWV3SW5mbywgc2V0dGluZ3MpXHJcbiAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgLy8gSEFDSzogVGhpcyBtdXN0IGhhcHBlbiBhZnRlciBhIHRpbWVvdXQgc28gdGhhdCB0aGVyZSBpcyB0aW1lIGZvciB0aGUgZWRpdG9yIHRyYW5zYWN0aW9uIHRvIGNvbXBsZXRlXHJcbiAgICAgICAgICAgIGNvbnN0IHBvc3ROdW1iZXJpbmdWaWV3SW5mbyA9IGdldFZpZXdJbmZvKHRoaXMuYXBwKVxyXG4gICAgICAgICAgICB1cGRhdGVUYWJsZU9mQ29udGVudHMocG9zdE51bWJlcmluZ1ZpZXdJbmZvLCBzZXR0aW5ncylcclxuICAgICAgICAgIH0sIDMwMDApXHJcblxyXG4gICAgICAgICAgc2hvd051bWJlcmluZ0RvbmVNZXNzYWdlKHRoaXMuYXBwLCBzZXR0aW5ncylcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiBmYWxzZVxyXG4gICAgICB9XHJcbiAgICB9KVxyXG5cclxuICAgIHRoaXMuYWRkQ29tbWFuZCh7XHJcbiAgICAgIGlkOiAnbnVtYmVyLWhlYWRpbmdzJyxcclxuICAgICAgbmFtZTogJ051bWJlciBhbGwgaGVhZGluZ3MgaW4gZG9jdW1lbnQnLFxyXG4gICAgICBjaGVja0NhbGxiYWNrOiAoY2hlY2tpbmc6IGJvb2xlYW4pID0+IHtcclxuICAgICAgICBpZiAoY2hlY2tpbmcpIHJldHVybiBpc1ZpZXdBY3RpdmUodGhpcy5hcHApXHJcblxyXG4gICAgICAgIGNvbnN0IHZpZXdJbmZvID0gZ2V0Vmlld0luZm8odGhpcy5hcHApXHJcbiAgICAgICAgaWYgKHZpZXdJbmZvKSB7XHJcbiAgICAgICAgICBjb25zdCBzZXR0aW5ncyA9IGdldEZyb250TWF0dGVyU2V0dGluZ3NPckFsdGVybmF0aXZlKHZpZXdJbmZvLmRhdGEsIHRoaXMuc2V0dGluZ3MpXHJcbiAgICAgICAgICBpZiAoc2V0dGluZ3Mub2ZmKSByZXR1cm4gZmFsc2VcclxuXHJcbiAgICAgICAgICB1cGRhdGVIZWFkaW5nTnVtYmVyaW5nKHZpZXdJbmZvLCBzZXR0aW5ncylcclxuICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICAgICAgICAvLyBIQUNLOiBUaGlzIG11c3QgaGFwcGVuIGFmdGVyIGEgdGltZW91dCBzbyB0aGF0IHRoZXJlIGlzIHRpbWUgZm9yIHRoZSBlZGl0b3IgdHJhbnNhY3Rpb24gdG8gY29tcGxldGVcclxuICAgICAgICAgICAgY29uc3QgcG9zdE51bWJlcmluZ1ZpZXdJbmZvID0gZ2V0Vmlld0luZm8odGhpcy5hcHApXHJcbiAgICAgICAgICAgIHVwZGF0ZVRhYmxlT2ZDb250ZW50cyhwb3N0TnVtYmVyaW5nVmlld0luZm8sIHNldHRpbmdzKVxyXG4gICAgICAgICAgfSwgMzAwMClcclxuXHJcbiAgICAgICAgICAvLyBOT1RFOiBUaGUgbGluZSBiZWxvdyBpcyBpbnRlbnRpb25hbGx5IGNvbW1lbnRlZCBvdXQsIHNpbmNlIHRoaXMgY29tbWFuZCBpcyB0aGUgc2FtZSBhc1xyXG4gICAgICAgICAgLy8gICAgICAgdGhlIGFib3ZlIGNvbW1hbmQsIGV4Y2VwdCBmb3IgdGhpcyBsaW5lXHJcbiAgICAgICAgICAvLyBzaG93TnVtYmVyaW5nRG9uZU1lc3NhZ2UodGhpcy5hcHAsIHNldHRpbmdzLCB2aWV3SW5mbylcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiBmYWxzZVxyXG4gICAgICB9XHJcbiAgICB9KVxyXG5cclxuICAgIHRoaXMuYWRkQ29tbWFuZCh7XHJcbiAgICAgIGlkOiAncmVtb3ZlLW51bWJlci1oZWFkaW5ncycsXHJcbiAgICAgIG5hbWU6ICdSZW1vdmUgbnVtYmVyaW5nIGZyb20gYWxsIGhlYWRpbmdzIGluIGRvY3VtZW50JyxcclxuICAgICAgY2hlY2tDYWxsYmFjazogKGNoZWNraW5nOiBib29sZWFuKSA9PiB7XHJcbiAgICAgICAgaWYgKGNoZWNraW5nKSByZXR1cm4gaXNWaWV3QWN0aXZlKHRoaXMuYXBwKVxyXG5cclxuICAgICAgICBjb25zdCB2aWV3SW5mbyA9IGdldFZpZXdJbmZvKHRoaXMuYXBwKVxyXG4gICAgICAgIHJlbW92ZUhlYWRpbmdOdW1iZXJpbmcodmlld0luZm8pXHJcblxyXG4gICAgICAgIHJldHVybiB0cnVlXHJcbiAgICAgIH1cclxuICAgIH0pXHJcblxyXG4gICAgdGhpcy5hZGRDb21tYW5kKHtcclxuICAgICAgaWQ6ICdzYXZlLXNldHRpbmdzLXRvLWZyb250LW1hdHRlcicsXHJcbiAgICAgIG5hbWU6ICdTYXZlIHNldHRpbmdzIHRvIGZyb250IG1hdHRlcicsXHJcbiAgICAgIGNoZWNrQ2FsbGJhY2s6IChjaGVja2luZzogYm9vbGVhbikgPT4ge1xyXG4gICAgICAgIGlmIChjaGVja2luZykgcmV0dXJuIGlzVmlld0FjdGl2ZSh0aGlzLmFwcClcclxuXHJcbiAgICAgICAgY29uc3Qgdmlld0luZm8gPSBnZXRWaWV3SW5mbyh0aGlzLmFwcClcclxuICAgICAgICBjb25zdCBmaWxlID0gdGhpcy5hcHAud29ya3NwYWNlLmdldEFjdGl2ZUZpbGUoKVxyXG4gICAgICAgIGlmICh2aWV3SW5mbyAmJiBmaWxlKSB7XHJcbiAgICAgICAgICBjb25zdCBzZXR0aW5ncyA9IGdldEZyb250TWF0dGVyU2V0dGluZ3NPckFsdGVybmF0aXZlKHZpZXdJbmZvLmRhdGEsIHRoaXMuc2V0dGluZ3MpXHJcbiAgICAgICAgICBzYXZlU2V0dGluZ3NUb0Zyb250TWF0dGVyKHRoaXMuYXBwLmZpbGVNYW5hZ2VyLCBmaWxlLCBzZXR0aW5ncylcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiBmYWxzZVxyXG4gICAgICB9XHJcbiAgICB9KVxyXG5cclxuICAgIHRoaXMuYWRkU2V0dGluZ1RhYihuZXcgTnVtYmVySGVhZGluZ3NQbHVnaW5TZXR0aW5nVGFiKHRoaXMuYXBwLCB0aGlzKSlcclxuXHJcbiAgICB0aGlzLnJlZ2lzdGVySW50ZXJ2YWwod2luZG93LnNldEludGVydmFsKCgpID0+IHtcclxuICAgICAgY29uc3Qgdmlld0luZm8gPSBnZXRWaWV3SW5mbyh0aGlzLmFwcClcclxuICAgICAgaWYgKHZpZXdJbmZvKSB7XHJcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBnZXRGcm9udE1hdHRlclNldHRpbmdzT3JBbHRlcm5hdGl2ZSh2aWV3SW5mby5kYXRhLCB0aGlzLnNldHRpbmdzKVxyXG5cclxuICAgICAgICBpZiAoc2V0dGluZ3Mub2ZmKSByZXR1cm5cclxuXHJcbiAgICAgICAgaWYgKHNldHRpbmdzLmF1dG8pIHtcclxuICAgICAgICAgIHVwZGF0ZUhlYWRpbmdOdW1iZXJpbmcodmlld0luZm8sIHNldHRpbmdzKVxyXG4gICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgICAgICAgIC8vIEhBQ0s6IFRoaXMgbXVzdCBoYXBwZW4gYWZ0ZXIgYSB0aW1lb3V0IHNvIHRoYXQgdGhlcmUgaXMgdGltZSBmb3IgdGhlIGVkaXRvciB0cmFuc2FjdGlvbiB0byBjb21wbGV0ZVxyXG4gICAgICAgICAgICBjb25zdCBwb3N0TnVtYmVyaW5nVmlld0luZm8gPSBnZXRWaWV3SW5mbyh0aGlzLmFwcClcclxuICAgICAgICAgICAgdXBkYXRlVGFibGVPZkNvbnRlbnRzKHBvc3ROdW1iZXJpbmdWaWV3SW5mbywgc2V0dGluZ3MpXHJcbiAgICAgICAgICB9LCAzMDAwKVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcclxuICAgICAgICAgIGNvbnNvbGUubG9nKCdOdW1iZXIgSGVhZGluZ3MgUGx1Z2luOiBBdXRvbWF0aWNhbGx5IG51bWJlcmVkIGRvY3VtZW50JylcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sIDEwICogMTAwMCkpXHJcbiAgfVxyXG5cclxuICBhc3luYyBsb2FkU2V0dGluZ3MoKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICB0aGlzLnNldHRpbmdzID0gT2JqZWN0LmFzc2lnbih7fSwgREVGQVVMVF9TRVRUSU5HUywgYXdhaXQgdGhpcy5sb2FkRGF0YSgpKVxyXG4gIH1cclxuXHJcbiAgYXN5bmMgc2F2ZVNldHRpbmdzKCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgYXdhaXQgdGhpcy5zYXZlRGF0YSh0aGlzLnNldHRpbmdzKVxyXG4gIH1cclxufVxyXG4iXSwibmFtZXMiOlsiTWFya2Rvd25WaWV3Iiwicm9tYW5pemUiLCJkZXJvbWFuaXplIiwicGFyc2VGcm9udE1hdHRlckVudHJ5IiwiTW9kYWwiLCJQbHVnaW5TZXR0aW5nVGFiIiwiU2V0dGluZyIsIlBsdWdpbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQW9HQTtBQUNPLFNBQVMsU0FBUyxDQUFDLE9BQU8sRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRTtBQUM3RCxJQUFJLFNBQVMsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLE9BQU8sS0FBSyxZQUFZLENBQUMsR0FBRyxLQUFLLEdBQUcsSUFBSSxDQUFDLENBQUMsVUFBVSxPQUFPLEVBQUUsRUFBRSxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRTtBQUNoSCxJQUFJLE9BQU8sS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLE9BQU8sQ0FBQyxFQUFFLFVBQVUsT0FBTyxFQUFFLE1BQU0sRUFBRTtBQUMvRCxRQUFRLFNBQVMsU0FBUyxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFL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
diff --git a/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/manifest.json b/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/manifest.json
deleted file mode 100644
index c7cd633a..00000000
--- a/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/manifest.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-	"id": "number-headings-obsidian",
-	"name": "Number Headings",
-	"version": "1.16.0",
-	"minAppVersion": "1.4.0",
-	"description": "Automatically number or re-number headings in an Obsidian document",
-	"author": "Kevin Albrecht (onlyafly@gmail.com)",
-	"authorUrl": "https://www.kevinalbrecht.com",
-	"isDesktopOnly": false
-}
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/styles.css b/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/styles.css
deleted file mode 100644
index 12a666b1..00000000
--- a/resources/sample_vaults/Tasks-Demo/.obsidian/plugins/number-headings-obsidian/styles.css
+++ /dev/null
@@ -1,8 +0,0 @@
-div.number-headings-button-container > button {
-  font-weight: normal;
-}
-
-div.number-headings-question {
-  font-weight: bold;
-  margin-bottom: 10px;
-}
\ No newline at end of file
diff --git a/resources/sample_vaults/Tasks-Demo/Filters/Boolean Combinations.md b/resources/sample_vaults/Tasks-Demo/Filters/Boolean Combinations.md
index 0a4a5409..e97adc90 100644
--- a/resources/sample_vaults/Tasks-Demo/Filters/Boolean Combinations.md
+++ b/resources/sample_vaults/Tasks-Demo/Filters/Boolean Combinations.md
@@ -25,14 +25,20 @@ Full documentation is available: see [Combining Filters](https://publish.obsidia
 ### AND - XX and YY
 
 ```tasks
-not done
+ignore global query
+explain
+hide backlinks
+
 (tags includes #XX) AND (tags includes #YY)
 ```
 
 ### AND - XX and YY and ZZ
 
 ```tasks
-not done
+ignore global query
+explain
+hide backlinks
+
 (tags includes #XX) AND (tags includes #YY) AND (tags includes #ZZ)
 ```
 
@@ -41,7 +47,10 @@ not done
 ### OR - XX OR YY
 
 ```tasks
-not done
+ignore global query
+explain
+hide backlinks
+
 (tags includes #XX) OR (tags includes #YY)
 ```
 
@@ -52,7 +61,10 @@ not done
 ### XOR
 
 ```tasks
-not done
+ignore global query
+explain
+hide backlinks
+
 (tags includes #XX) XOR (tags includes #YY)
 ```
 
@@ -62,7 +74,10 @@ Note the inclusion of 'task 7'. Combining multiple `XOR` does not give the expec
 See [this question and its answers](https://electronics.stackexchange.com/questions/93713/how-is-an-xor-with-more-than-2-inputs-supposed-to-work).
 
 ```tasks
-not done
+ignore global query
+explain
+hide backlinks
+
 (tags includes #XX) XOR (tags includes #YY) XOR (tags includes #ZZ)
 ```
 
@@ -71,14 +86,140 @@ not done
 Matching only one of 3 filters - version 1:
 
 ```tasks
-not done
+ignore global query
+explain
+hide backlinks
+
 ( (tags includes #XX) AND (tags does not include #YY) AND (tags does not include #ZZ) ) OR ( (tags includes #YY) AND (tags does not include #XX) AND (tags does not include #ZZ) ) OR ( (tags includes #ZZ) AND (tags does not include #XX) AND (tags does not include #YY) )
 ```
 
 Matching only one of 3 filters - version 2:
 
 ```tasks
-not done
+ignore global query
+explain
+hide backlinks
+
 (tags includes #XX) XOR (tags includes #YY) XOR (tags includes #ZZ)
 NOT ( (tags includes #XX) AND (tags includes #YY) AND (tags includes #ZZ) )
 ```
+
+## Error Cases
+
+These examples demonstrate the error messages for various problem scenarios.
+
+### Extra delimiter in user query
+
+The Boolean line ends with a stray extra `)`.
+
+This is just an invalid query, and the extra `)` needs to be removed.
+
+This can be spotted in the error output by looking at the delimiters on the `simplified line` in the output:
+
+```tasks
+ignore global query
+explain
+hide backlinks
+
+(tags includes #XX) AND (tags includes #YY))
+```
+
+### Missing delimiter at end of user query
+
+Here, the final `)` is missing from the end of the Boolean line.
+
+Tasks checks that first and last character on Boolean lines, to ensure that consistent delimiters are used.
+
+In this particular case, the error message is not overly helpful.
+
+```tasks
+ignore global query
+explain
+hide backlinks
+
+(tags includes #XX) AND (tags includes #YY
+```
+
+### Delimiter swallowed by Tasks' parsing code
+
+#### The Problem
+
+In this example, each of the filters ends with a `)`, which is also the closing delimiter character in this Boolean line.
+
+Looking at the error message shows the error `SyntaxError: missing ) after argument list`.
+
+Basically, it was really hard to come up with an algorithm that allowed brackets inside filters, and allowed nested brackets in the Boolean logic.
+
+We have settled on an algorithm that greedily matches all delimeters just around the operators (`AND`, `OR` etc)
+
+This situation is most common with `filter by function`, as often JavaScript functions are then called.
+
+Read on for two different ways to fix this problem.
+
+```tasks
+ignore global query
+explain
+hide backlinks
+
+(filter by function task.tags.join(',').toUpperCase().includes('#XX')) AND \
+(filter by function task.tags.join(',').toUpperCase().includes('#YY')) AND \
+(filter by function task.tags.join(',').toUpperCase().includes('#ZZ'))
+```
+
+#### Workaround 1: use a different delimiter
+
+The following delimiter characters are available:
+
+- `(....)`
+- `[....]`
+- `{....}`
+- `"...."`
+
+We can choose any one of those delimiters sets, so long as we use the same delimiters for all sub-expressions on the line.
+
+Here, we adjust the expression to use `[....]` instead of `(....)`:
+
+```tasks
+ignore global query
+explain
+hide backlinks
+
+[filter by function task.tags.join(',').toUpperCase().includes('#XX')] AND \
+[filter by function task.tags.join(',').toUpperCase().includes('#YY')] AND \
+[filter by function task.tags.join(',').toUpperCase().includes('#ZZ')]
+```
+
+#### Workaround 2: add semicolons to filter by function
+
+Our other option for fixing the error is to add semicolons (`;`) at the end of each sub-expression, to put non-space character between the `)` in the `filter by function` expression and the `) AND`:
+
+```tasks
+ignore global query
+explain
+hide backlinks
+
+(filter by function task.tags.join(',').toUpperCase().includes('#XX'); ) AND \
+(filter by function task.tags.join(',').toUpperCase().includes('#YY'); ) AND \
+(filter by function task.tags.join(',').toUpperCase().includes('#ZZ'); )
+```
+
+#### Workaround 3: port the Boolean logic to JavaScript
+
+We can instead migrate the Boolean operators to JavaScript:
+
+- `AND` -> `&&`
+- `OR` -> `||`
+- `NOT` -> `!`
+
+Like this:
+
+```tasks
+ignore global query
+explain
+hide backlinks
+
+filter by function \
+    task.tags.join(',').toUpperCase().includes('#XX') && \
+    task.tags.join(',').toUpperCase().includes('#YY') && \
+    task.tags.join(',').toUpperCase().includes('#ZZ')
+```
diff --git a/resources/sample_vaults/Tasks-Demo/Other Plugins/Dataview/Parent-Child relationships.md b/resources/sample_vaults/Tasks-Demo/Other Plugins/Dataview/Parent-Child relationships.md
deleted file mode 100644
index 97fe93f2..00000000
--- a/resources/sample_vaults/Tasks-Demo/Other Plugins/Dataview/Parent-Child relationships.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# Parent-Child relationships
-
-## 1 Not completed tasks
-
-- [ ] #task lonely task
-
-- [ ] #task parent task
-  - [ ] #task child task 1
-    - non task grand-child
-  - [ ] #task child task 2
-  - non task child 3
-
-  - [ ] #task Lonely indented task
-
-### 1.1 H3 heading
-
-- non task parent
-  - [ ] #task another lonely indented task
-
-- [ ] #task task grand-parent
-  - non task intermediate parent
-    - [ ] #task child task under non task intermediate parent
-      - non task sub child
-
-## 2 Completed tasks
-
-- [x] #task parent task ✅ 2024-04-13
-  - [x] #task child task 1 ✅ 2024-04-13
-    - non task grand-child
-  - [x] #task child task 2 ✅ 2024-04-13
-  - non task child 3
-
-  - [x] #task Lonely indented task ✅ 2024-04-13
-
-### 2.1 H3 heading
-
-- non task parent
-  - [ ] #task another lonely indented task
-
-## 3 Partially completed tasks (child completed)
-
-- [ ] #task parent task
-  - [ ] #task child task 1
-    - non task grand-child
-  - [x] #task child task 2 ✅ 2024-04-13
-  - non task child 3
-
-  - [ ] #task Lonely indented task
-
-### 3.1 H3 heading
-
-- non task parent
-  - [ ] #task another lonely indented task
-
-## 4 Partially completed tasks (parent completed)
-
-- [x] #task parent task ✅ 2024-04-13
-  - [ ] #task child task 1
-    - non task grand-child
-  - [ ] #task child task 2
-  - non task child 3
-
-  - [ ] #task Lonely indented task
-
-### 4.1 H3 heading
-
-- non task parent
-  - [ ] #task another lonely indented task
-
-## 5 Dataview queries
-
-### 5.1 Completed
-
-```dataview
-TASK
-FROM "Other Plugins/Dataview/Parent-Child relationships"
-WHERE completed
-GROUP BY meta(section).subpath
-```
-
-### 5.2 Fully completed
-
-```dataview
-TASK
-FROM "Other Plugins/Dataview/Parent-Child relationships"
-WHERE fullyCompleted
-GROUP BY meta(section).subpath
-```
-
-### 5.3 Not completed
-
-```dataview
-TASK
-FROM "Other Plugins/Dataview/Parent-Child relationships"
-WHERE !completed
-GROUP BY meta(section).subpath
-```
-
-### 5.4 Not fully completed
-
-```dataview
-TASK
-FROM "Other Plugins/Dataview/Parent-Child relationships"
-WHERE !fullyCompleted
-GROUP BY meta(section).subpath
-```
diff --git a/src/Query/Filter/BooleanField.ts b/src/Query/Filter/BooleanField.ts
index eca72cbb..5d28da0b 100644
--- a/src/Query/Filter/BooleanField.ts
+++ b/src/Query/Filter/BooleanField.ts
@@ -1,5 +1,5 @@
-import { parse as boonParse } from 'boon-js';
 import type { PostfixExpression } from 'boon-js';
+import { parse as boonParse } from 'boon-js';
 import type { Token } from 'boon-js/lib/types';
 
 import { parseFilter } from '../FilterParser';
@@ -262,11 +262,7 @@ export class BooleanField extends Field {
      */
     private helpMessage(line: string, errorMessage: string, parseResult: BooleanPreprocessorResult) {
         const filters: { [key: string]: string } = parseResult.filters;
-        const expressions = Object.entries(filters)
-            .map(([key, value]) => {
-                return `    '${key}': '${value}'`;
-            })
-            .join('\n');
+        const expressions = this.stringifySubExpressionsForErrorMessage(filters);
 
         const simpleMessage = this.helpMessageFromSimpleError(line, errorMessage);
         const fullMessage = `${simpleMessage}
@@ -283,6 +279,33 @@ For help, see:
         return FilterOrErrorMessage.fromError(line, fullMessage);
     }
 
+    private stringifySubExpressionsForErrorMessage(filters: { [p: string]: string }) {
+        return Object.entries(filters)
+            .map(([placeholder, line]) => {
+                // Tell the user whether the sub-expression is valid, to work out which ones need fixing.
+                return `    '${placeholder}': '${line}'
+        => ${this.stringifySubExpressionStatus(line)}`;
+            })
+            .join('\n');
+    }
+
+    private stringifySubExpressionStatus(line: string) {
+        const parsedField = parseFilter(line);
+        if (!parsedField) {
+            return 'ERROR:\n           do not understand query';
+        }
+        if (parsedField.error) {
+            // In case the error message has more than one line, we split it in to separate lines,
+            // apply some standard/tidy indentation, and join the lines again:
+            const formattedFilterStatus = parsedField.error
+                .split('\n')
+                .map((line) => line.trim())
+                .join('\n           ');
+            return `ERROR:\n           ${formattedFilterStatus}`;
+        }
+        return 'OK';
+    }
+
     private helpMessageFromSimpleError(line: string, errorMessage: string) {
         return `Could not interpret the following instruction as a Boolean combination:
     ${line}
diff --git a/src/Query/Query.ts b/src/Query/Query.ts
index c63a75fc..7e835706 100644
--- a/src/Query/Query.ts
+++ b/src/Query/Query.ts
@@ -17,7 +17,7 @@ import { continueLines } from './Scanner';
 import { SearchInfo } from './SearchInfo';
 import { Sort } from './Sort/Sort';
 import type { Sorter } from './Sort/Sorter';
-import type { Statement } from './Statement';
+import { Statement } from './Statement';
 
 export class Query implements IQuery {
     /** Note: source is the raw source, before expanding any placeholders */
@@ -74,7 +74,7 @@ export class Query implements IQuery {
                     message = 'Unknown error';
                 }
 
-                this.setError(message, line);
+                this.setError(message, statement);
                 return;
             }
         });
@@ -114,7 +114,7 @@ export class Query implements IQuery {
             case this.parseFilter(line, statement):
                 break;
             default:
-                this.setError('do not understand query', line);
+                this.setError('do not understand query', statement);
         }
     }
 
@@ -240,9 +240,16 @@ ${source}`;
         return this._error;
     }
 
-    private setError(message: string, line: string) {
-        this._error = `${message}
-Problem line: "${line}"`;
+    private setError(message: string, statement: Statement) {
+        if (statement.allLinesIdentical()) {
+            this._error = `${message}
+Problem line: "${statement.rawInstruction}"`;
+        } else {
+            this._error = `${message}
+Problem statement:
+${statement.explainStatement('    ')}
+`;
+        }
     }
 
     public get ignoreGlobalQuery(): boolean {
@@ -331,7 +338,7 @@ Problem line: "${line}"`;
                     this._taskLayoutOptions.setVisibility(TaskLayoutComponent.DependsOn, !hide);
                     break;
                 default:
-                    this.setError('do not understand hide/show option', line);
+                    this.setError('do not understand hide/show option', new Statement(line, line));
             }
         }
     }
@@ -346,7 +353,7 @@ Problem line: "${line}"`;
 
                 this._filters.push(filterOrError.filter);
             } else {
-                this.setError(filterOrError.error ?? 'Unknown error', line);
+                this.setError(filterOrError.error ?? 'Unknown error', statement);
             }
             return true;
         }
@@ -356,7 +363,7 @@ Problem line: "${line}"`;
     private parseLimit(line: string): void {
         const limitMatch = line.match(this.limitRegexp);
         if (limitMatch === null) {
-            this.setError('do not understand query limit', line);
+            this.setError('do not understand query limit', new Statement(line, line));
             return;
         }
 
diff --git a/src/Query/Statement.ts b/src/Query/Statement.ts
index 027861b8..a79ae1e2 100644
--- a/src/Query/Statement.ts
+++ b/src/Query/Statement.ts
@@ -81,4 +81,11 @@ ${indent}${nextLine}`;
 
         return result;
     }
+
+    public allLinesIdentical() {
+        return (
+            this._rawInstruction === this._anyContinuationLinesRemoved &&
+            this._rawInstruction === this._anyPlaceholdersExpanded
+        );
+    }
 }


Test patch:
diff --git a/tests/Query/Filter/BooleanField.test.boolean_query_-_exhaustive_tests_explain.approved.txt b/tests/Query/Filter/BooleanField.test.boolean_query_-_exhaustive_tests_explain.approved.txt
index a9c9de9a..8da5cb8e 100644
--- a/tests/Query/Filter/BooleanField.test.boolean_query_-_exhaustive_tests_explain.approved.txt
+++ b/tests/Query/Filter/BooleanField.test.boolean_query_-_exhaustive_tests_explain.approved.txt
@@ -383,11 +383,35 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'filter by function task.description.includes('a''
+        => ERROR:
+           Error: Failed parsing expression "task.description.includes('a'".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
     'f2': 'filter by function task.description.includes('b''
+        => ERROR:
+           Error: Failed parsing expression "task.description.includes('b'".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
     'f3': 'filter by function task.description.includes('c''
+        => ERROR:
+           Error: Failed parsing expression "task.description.includes('c'".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
     'f4': 'filter by function task.description.includes('d''
+        => ERROR:
+           Error: Failed parsing expression "task.description.includes('d'".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
     'f5': 'filter by function task.description.includes('e''
+        => ERROR:
+           Error: Failed parsing expression "task.description.includes('e'".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
     'f6': 'filter by function task.description.includes('f''
+        => ERROR:
+           Error: Failed parsing expression "task.description.includes('f'".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -469,13 +493,33 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'filter by function ! 'NON_TASK,CANCELLED'.includes(task.status.type'
+        => ERROR:
+           Error: Failed parsing expression "! 'NON_TASK,CANCELLED'.includes(task.status.type".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
     'f2': 'filter by function const date = task.due.moment; return date ? !date.isValid() : false;'
+        => OK
     'f3': 'filter by function task.due.moment?.isSameOrBefore(moment(), 'day') || false'
+        => OK
     'f4': 'filter by function task.urgency.toFixed(2) === 1.95.toFixed(2'
+        => ERROR:
+           Error: Failed parsing expression "task.urgency.toFixed(2) === 1.95.toFixed(2".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
     'f5': 'filter by function (!task.isRecurring) && task.originalMarkdown.includes('🔁''
+        => ERROR:
+           Error: Failed parsing expression "(!task.isRecurring) && task.originalMarkdown.includes('🔁'".
+           The error message was:
+           "SyntaxError: missing ) after argument list"
     'f6': 'filter by function task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase('
+        => ERROR:
+           Error: Failed parsing expression "task.file.path.toLocaleLowerCase() === 'TASKS RELEASES/4.1.0 RELEASE.MD'.toLocaleLowerCase(".
+           The error message was:
+           "SyntaxError: Unexpected token '}'"
     'f7': 'filter by function const taskDate = task.due.moment; const now = moment(); return taskDate?.isSame(now, 'day') || ( !taskDate && task.heading?.includes(now.format('YYYY-MM-DD')) ) || false'
+        => OK
     'f8': 'filter by function const wanted = '#context/home'; return task.heading?.includes(wanted) || task.tags.find( (tag) => tag === wanted ) && true || false;'
+        => OK
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -2236,7 +2280,9 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'path includes ()some example('
+        => OK
     'f2': 'path includes ((some example'
+        => OK
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -2273,7 +2319,9 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'path includes (some example'
+        => OK
     'f2': 'path includes )some example('
+        => OK
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -2297,7 +2345,9 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'path includes )some example('
+        => OK
     'f2': 'path includes (some example'
+        => OK
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -3083,6 +3133,12 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'AND (description includes d1'
+        => ERROR:
+           Could not interpret the following instruction as a Boolean combination:
+           AND (description includes d1
+           
+           The error message is:
+           All filters in a Boolean instruction must be inside one of these pairs of delimiter characters: (...) or [...] or {...} or "...". Combinations of those delimiters are no longer supported.
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -3106,6 +3162,8 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'happens before blahblahblah'
+        => ERROR:
+           do not understand happens date
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -3194,6 +3252,8 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'description blahblah d1'
+        => ERROR:
+           do not understand query
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -3229,6 +3289,8 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'happens before blahblahblah'
+        => ERROR:
+           do not understand happens date
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
@@ -3276,6 +3338,12 @@ The instruction was converted to the following simplified line:
 
 Where the sub-expressions in the simplified line are:
     'f1': 'OR (description includes d1'
+        => ERROR:
+           Could not interpret the following instruction as a Boolean combination:
+           OR (description includes d1
+           
+           The error message is:
+           All filters in a Boolean instruction must be inside one of these pairs of delimiter characters: (...) or [...] or {...} or "...". Combinations of those delimiters are no longer supported.
 
 For help, see:
     https://publish.obsidian.md/tasks/Queries/Combining+Filters
diff --git a/tests/Query/Query.test.ts b/tests/Query/Query.test.ts
index f29fcb60..5ddd0b4d 100644
--- a/tests/Query/Query.test.ts
+++ b/tests/Query/Query.test.ts
@@ -535,7 +535,7 @@ describe('Query parsing', () => {
 
     describe('should include instruction in parsing error messages', () => {
         function getQueryError(source: string) {
-            return new Query(source).error;
+            return new Query(source, 'Example Path.md').error;
         }
 
         it('for invalid regular expression filter', () => {
@@ -609,6 +609,73 @@ Problem line: "${source}"`);
             expect(getQueryError(sourceUpperCase)).toEqual(`do not understand query
 Problem line: "${sourceUpperCase}"`);
         });
+
+        it('for instruction with continuation characters and placeholders', () => {
+            const source = `spaghetti includes \\
+    {{query.file.path}}`;
+
+            expect(getQueryError(source)).toEqual(`do not understand query
+Problem statement:
+    spaghetti includes \\
+        {{query.file.path}}
+     =>
+    spaghetti includes {{query.file.path}} =>
+    spaghetti includes Example Path.md
+`);
+        });
+
+        it('for boolean with filter-by-function closing ) swallowed by Boolean parsing', () => {
+            const source = "( filter by function task.originalMarkdown.includes('hello') )";
+            expect(getQueryError(source)).toMatchInlineSnapshot(`
+                "Could not interpret the following instruction as a Boolean combination:
+                    ( filter by function task.originalMarkdown.includes('hello') )
+
+                The error message is:
+                    malformed boolean query -- Invalid token (check the documentation for guidelines)
+
+                The instruction was converted to the following simplified line:
+                    ( f1) )
+
+                Where the sub-expressions in the simplified line are:
+                    'f1': 'filter by function task.originalMarkdown.includes('hello''
+                        => ERROR:
+                           Error: Failed parsing expression "task.originalMarkdown.includes('hello'".
+                           The error message was:
+                           "SyntaxError: missing ) after argument list"
+
+                For help, see:
+                    https://publish.obsidian.md/tasks/Queries/Combining+Filters
+
+                Problem line: "( filter by function task.originalMarkdown.includes('hello') )""
+            `);
+        });
+
+        it('for boolean with unknown instruction', () => {
+            const source = '( filename includesx {{query.file.filenameWithoutExtension}} )';
+            expect(getQueryError(source)).toMatchInlineSnapshot(`
+                "Could not interpret the following instruction as a Boolean combination:
+                    ( filename includesx Example Path )
+
+                The error message is:
+                    couldn't parse sub-expression 'filename includesx Example Path'
+
+                The instruction was converted to the following simplified line:
+                    ( f1 )
+
+                Where the sub-expressions in the simplified line are:
+                    'f1': 'filename includesx Example Path'
+                        => ERROR:
+                           do not understand query
+
+                For help, see:
+                    https://publish.obsidian.md/tasks/Queries/Combining+Filters
+
+                Problem statement:
+                    ( filename includesx {{query.file.filenameWithoutExtension}} ) =>
+                    ( filename includesx Example Path )
+                "
+            `);
+        });
     });
 
     describe('parsing placeholders', () => {
diff --git a/tests/Query/Statement.test.ts b/tests/Query/Statement.test.ts
index aa804c19..4f4947a0 100644
--- a/tests/Query/Statement.test.ts
+++ b/tests/Query/Statement.test.ts
@@ -6,6 +6,8 @@ describe('Statement', () => {
 world`;
         const final = 'hello world';
         const instruction = new Statement(raw, final);
+        expect(instruction.allLinesIdentical()).toEqual(false);
+
         expect(instruction.rawInstruction).toEqual(raw);
         expect(instruction.anyContinuationLinesRemoved).toEqual('hello world');
         expect(instruction.anyPlaceholdersExpanded).toEqual('hello world');
@@ -15,6 +17,8 @@ world`;
         it('should explain simplest statement', () => {
             const instruction = 'hello world';
             const statement = new Statement(instruction, instruction);
+            expect(statement.allLinesIdentical()).toEqual(true);
+
             expect(statement.explainStatement('')).toEqual(instruction);
             expect(statement.explainStatement('  ')).toEqual('  hello world');
         });
@@ -29,7 +33,11 @@ world`;
         it('should show unexpanded and expanded placeholders, if they differ', () => {
             const instruction = '{{fake placeholder}}';
             const statement = new Statement(instruction, instruction);
+            expect(statement.allLinesIdentical()).toEqual(true);
+
             statement.recordExpandedPlaceholders('expanded placeholder');
+            expect(statement.allLinesIdentical()).toEqual(false);
+
             expect(statement.explainStatement('  ')).toEqual(`  {{fake placeholder}} =>
   expanded placeholder`);
         });