tc-ui-toolkit
Version:
React components used to develop tools for the desktop app translationCore
225 lines (170 loc) • 27.9 kB
JavaScript
"use strict";
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shouldRenderBreak = shouldRenderBreak;
exports.getPrecedingTextFromElementSiblings = exports.getPrecedingTextFromElement = exports.getPrecedingTextFromElementAndSiblings = exports.getPrecedingTextFromWindowSelection = exports.getSelectedTextFromWindowSelection = exports.getCurrentWindowSelection = exports.getSelectionFromCurrentWindowSelection = void 0;
var _stringPunctuationTokenizer = require("string-punctuation-tokenizer");
var stringHelpers = _interopRequireWildcard(require("./stringHelpers"));
/**
* @description - Gets the selection object from the currently selected text from the Web UI
* @param {String} entireText - the text that the selection should be in, ie verseText
* @param {Boolean} fallbackToPreviousWord - if true then we select previous word if selection is at end of word
* @return {Object} - the selection object to be used
* TODO: Find a way to test
*/
var getSelectionFromCurrentWindowSelection = function getSelectionFromCurrentWindowSelection(entireText) {
var fallbackToPreviousWord = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var selection; // response
var windowSelection = getCurrentWindowSelection();
var selectedText = getSelectedTextFromWindowSelection(windowSelection);
var precedingText = getPrecedingTextFromWindowSelection(windowSelection);
if (fallbackToPreviousWord && selectedText === ' ') {
// handle edge case of clicking near end of word
var words = (0, _stringPunctuationTokenizer.tokenize)({
text: precedingText
});
if (words && words.length) {
var lastWord = words[words.length - 1];
if (lastWord) {
var pos = precedingText.lastIndexOf(lastWord);
if (pos && pos + lastWord.length === precedingText.length) {
// if last word is at end of string
// use last word for selection
selectedText = lastWord;
precedingText = precedingText.substr(0, pos);
console.log("getSelectionFromCurrentWindowSelection() - switching to last word, selectedText ".concat(JSON.stringify(selectedText)));
}
}
}
} // Some edge cases leave a weird selection remaining, let's clean up.
selection = stringHelpers.generateSelection(selectedText, precedingText, entireText);
window.getSelection().empty();
return selection;
};
/**
* @description - Gets the window's Selection from the UI
* @return {Object} windowSelection - a windowSelection object from inside a compatible element
* TODO: Find a way to test
*/
exports.getSelectionFromCurrentWindowSelection = getSelectionFromCurrentWindowSelection;
var getCurrentWindowSelection = function getCurrentWindowSelection() {
return window.getSelection();
};
/**
* @description - Gets the window selected text from the windowSelection
* @param {Object} windowSelection - a windowSelection object from inside a compatible element
* @return {String} - selectedText
* TODO: Find a way to test
*/
exports.getCurrentWindowSelection = getCurrentWindowSelection;
var getSelectedTextFromWindowSelection = function getSelectedTextFromWindowSelection(windowSelection) {
return windowSelection.toString();
};
/**
* @description - Gets the precedingText from the windowSelection
* @param {Object} windowSelection - a windowSelection object from inside a compatible element
* @return {String} - the string of precedingText
* Implementation notes on why you can't just use the window.getSelection()
* getSelection is limited by same innerText node, and does not include span siblings
* indexOfTextSelection is broken by any other previous selection since it only knows its innerText node.
* TODO: Find a way to test
*/
exports.getSelectedTextFromWindowSelection = getSelectedTextFromWindowSelection;
var getPrecedingTextFromWindowSelection = function getPrecedingTextFromWindowSelection(windowSelection) {
var precedingText; // response
// concatenate spans etc... to get the precedingText from the windowSelection
if (windowSelection.rangeCount) {
// get the text after the preceding selection and current span selection is in.
var selectionRange = windowSelection.getRangeAt(0); // get the character index of what is selected in context of the span it is in.
var selectionRangeStart = selectionRange.startOffset; // get the container of the selection, this is a strange object, that logs as a string.
var textContainer = selectionRange.commonAncestorContainer; // get the parent span that contains the textContainer.
var element; // if the textContainer is #text, then use the parentElement - usually non-overlapping selection
if ('#text' === textContainer.nodeName) {
element = textContainer.parentElement;
} // if the textContainer is a span, then use it as the element
if ('SPAN' === textContainer.nodeName) {
element = textContainer;
} // if the textContainer is a div, its an overlapping selection, don't use commonAncestorContainer
if ('DIV' === textContainer.nodeName) {
element = selectionRange.startContainer.parentElement;
} // check for element, as textContainer can but rarely be something other than #text, span or div
if (element) {
precedingText = getPrecedingTextFromElementAndSiblings(element, selectionRangeStart, windowSelection);
}
}
return precedingText;
};
/**
* @description - gets the precedingText from the element ending at the selectionRangeStart
* @param {Element} element - the html element that has text and siblings with text
* @param {Int} selectionRangeStart - the character index of the start of the selection
* @return {String} - the string of precedingText
*/
exports.getPrecedingTextFromWindowSelection = getPrecedingTextFromWindowSelection;
var getPrecedingTextFromElementAndSiblings = function getPrecedingTextFromElementAndSiblings(element, selectionRangeStart, windowSelection) {
var precedingText; // response
var precedingTextFromElementSiblings = getPrecedingTextFromElementSiblings(element, windowSelection);
var precedingTextFromElement = getPrecedingTextFromElement(element, selectionRangeStart, windowSelection);
precedingText = precedingTextFromElementSiblings + precedingTextFromElement;
return precedingText;
};
/**
* @description - gets the precedingText from the element ending at the selectionRangeStart
* @param {Element} element - the html element that has text
* @param {Int} selectionRangeStart - the character index of the start of the selection
* @return {String} - the string of precedingText
*/
exports.getPrecedingTextFromElementAndSiblings = getPrecedingTextFromElementAndSiblings;
var getPrecedingTextFromElement = function getPrecedingTextFromElement(element, selectionRangeStart) {
var precedingText; // response
var text = element.textContent;
precedingText = text.slice(0, selectionRangeStart);
return precedingText;
};
/**
* @description - gets the precedingText from the element siblings
* @param {Element} element - the html element that has text and siblings with text
* @return {String} - the string of precedingText
*/
exports.getPrecedingTextFromElement = getPrecedingTextFromElement;
var getPrecedingTextFromElementSiblings = function getPrecedingTextFromElementSiblings(element, windowSelection) {
var precedingText = ''; // response
// get the previous sibling to start the loop
var previousSibling = element.previousElementSibling; // loop through previous spans to get their text
while (previousSibling) {
// just in case the previousSibling just happens to be a part of the selection
if (windowSelection && !windowSelection.containsNode(previousSibling)) {
// prepend the spans innerText to the precedingText
precedingText = previousSibling.textContent + precedingText;
} // move to the previous span, if none, it ends the loop
previousSibling = previousSibling.previousElementSibling;
}
return precedingText;
};
/**
* This is a helper method to determine if the selection needs a break char in
* between the selected words or not.
* @param {Array} selections - Array of word objects that the user selected
* @param {string} verseText - The entire verse string from the current check
* @returns {boolean} - Whether the View should display a break
*/
exports.getPrecedingTextFromElementSiblings = getPrecedingTextFromElementSiblings;
function shouldRenderBreak(selections, verseText) {
/** Need to get the words and occurrence of the selected edge words */
var endSelectedWord = selections[selections.length - 1].text.trim();
var endSelectedWordOccurrence = selections[selections.length - 1].occurrence;
var beginningSelectedWord = selections[0].text.trim();
var beginningSelectedWordOccurrence = selections[0].occurrence;
/** Using the occurrences to get the actual index of the word vs
* the first time it appears in verse text */
var indexOfBeginningSelection = verseText.split(beginningSelectedWord, beginningSelectedWordOccurrence).join(beginningSelectedWord).length;
var indexOfEndSelection = verseText.split(endSelectedWord, endSelectedWordOccurrence).join(endSelectedWord).length;
/** Checking the text in between selected words for a non-space character */
var textBetweenSelection = verseText.substring(indexOfBeginningSelection + beginningSelectedWord.length, indexOfEndSelection);
/** If the end index is the same as the beginning then it is the first word */
var showBreak = indexOfEndSelection !== indexOfBeginningSelection && textBetweenSelection.match(/\S/);
return showBreak;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9WZXJzZUNoZWNrL2hlbHBlcnMvd2luZG93U2VsZWN0aW9uSGVscGVycy5qcyJdLCJuYW1lcyI6WyJnZXRTZWxlY3Rpb25Gcm9tQ3VycmVudFdpbmRvd1NlbGVjdGlvbiIsImVudGlyZVRleHQiLCJmYWxsYmFja1RvUHJldmlvdXNXb3JkIiwic2VsZWN0aW9uIiwid2luZG93U2VsZWN0aW9uIiwiZ2V0Q3VycmVudFdpbmRvd1NlbGVjdGlvbiIsInNlbGVjdGVkVGV4dCIsImdldFNlbGVjdGVkVGV4dEZyb21XaW5kb3dTZWxlY3Rpb24iLCJwcmVjZWRpbmdUZXh0IiwiZ2V0UHJlY2VkaW5nVGV4dEZyb21XaW5kb3dTZWxlY3Rpb24iLCJ3b3JkcyIsInRleHQiLCJsZW5ndGgiLCJsYXN0V29yZCIsInBvcyIsImxhc3RJbmRleE9mIiwic3Vic3RyIiwiY29uc29sZSIsImxvZyIsIkpTT04iLCJzdHJpbmdpZnkiLCJzdHJpbmdIZWxwZXJzIiwiZ2VuZXJhdGVTZWxlY3Rpb24iLCJ3aW5kb3ciLCJnZXRTZWxlY3Rpb24iLCJlbXB0eSIsInRvU3RyaW5nIiwicmFuZ2VDb3VudCIsInNlbGVjdGlvblJhbmdlIiwiZ2V0UmFuZ2VBdCIsInNlbGVjdGlvblJhbmdlU3RhcnQiLCJzdGFydE9mZnNldCIsInRleHRDb250YWluZXIiLCJjb21tb25BbmNlc3RvckNvbnRhaW5lciIsImVsZW1lbnQiLCJub2RlTmFtZSIsInBhcmVudEVsZW1lbnQiLCJzdGFydENvbnRhaW5lciIsImdldFByZWNlZGluZ1RleHRGcm9tRWxlbWVudEFuZFNpYmxpbmdzIiwicHJlY2VkaW5nVGV4dEZyb21FbGVtZW50U2libGluZ3MiLCJnZXRQcmVjZWRpbmdUZXh0RnJvbUVsZW1lbnRTaWJsaW5ncyIsInByZWNlZGluZ1RleHRGcm9tRWxlbWVudCIsImdldFByZWNlZGluZ1RleHRGcm9tRWxlbWVudCIsInRleHRDb250ZW50Iiwic2xpY2UiLCJwcmV2aW91c1NpYmxpbmciLCJwcmV2aW91c0VsZW1lbnRTaWJsaW5nIiwiY29udGFpbnNOb2RlIiwic2hvdWxkUmVuZGVyQnJlYWsiLCJzZWxlY3Rpb25zIiwidmVyc2VUZXh0IiwiZW5kU2VsZWN0ZWRXb3JkIiwidHJpbSIsImVuZFNlbGVjdGVkV29yZE9jY3VycmVuY2UiLCJvY2N1cnJlbmNlIiwiYmVnaW5uaW5nU2VsZWN0ZWRXb3JkIiwiYmVnaW5uaW5nU2VsZWN0ZWRXb3JkT2NjdXJyZW5jZSIsImluZGV4T2ZCZWdpbm5pbmdTZWxlY3Rpb24iLCJzcGxpdCIsImpvaW4iLCJpbmRleE9mRW5kU2VsZWN0aW9uIiwidGV4dEJldHdlZW5TZWxlY3Rpb24iLCJzdWJzdHJpbmciLCJzaG93QnJlYWsiLCJtYXRjaCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUFBOztBQUNBOztBQUVBOzs7Ozs7O0FBT08sSUFBTUEsc0NBQXNDLEdBQUcsU0FBekNBLHNDQUF5QyxDQUFDQyxVQUFELEVBQWdEO0FBQUEsTUFBbkNDLHNCQUFtQyx1RUFBVixLQUFVO0FBQ3BHLE1BQUlDLFNBQUosQ0FEb0csQ0FDckY7O0FBQ2YsTUFBTUMsZUFBZSxHQUFHQyx5QkFBeUIsRUFBakQ7QUFDQSxNQUFJQyxZQUFZLEdBQUdDLGtDQUFrQyxDQUFDSCxlQUFELENBQXJEO0FBQ0EsTUFBSUksYUFBYSxHQUFHQyxtQ0FBbUMsQ0FBQ0wsZUFBRCxDQUF2RDs7QUFFQSxNQUFJRixzQkFBc0IsSUFBS0ksWUFBWSxLQUFLLEdBQWhELEVBQXNEO0FBQUU7QUFDdEQsUUFBTUksS0FBSyxHQUFHLDBDQUFTO0FBQUVDLE1BQUFBLElBQUksRUFBRUg7QUFBUixLQUFULENBQWQ7O0FBRUEsUUFBSUUsS0FBSyxJQUFJQSxLQUFLLENBQUNFLE1BQW5CLEVBQTJCO0FBQ3pCLFVBQU1DLFFBQVEsR0FBR0gsS0FBSyxDQUFDQSxLQUFLLENBQUNFLE1BQU4sR0FBZSxDQUFoQixDQUF0Qjs7QUFFQSxVQUFJQyxRQUFKLEVBQWM7QUFDWixZQUFNQyxHQUFHLEdBQUdOLGFBQWEsQ0FB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LFVBQVUsQ0FBQ3JDLE1BQVgsR0FBb0IsQ0FBckIsQ0FBVixDQUFrQzBDLFVBQXBFO0FBQ0EsTUFBTUMscUJBQXFCLEdBQUdOLFVBQVUsQ0FBQyxDQUFELENBQVYsQ0FBY3RDLElBQWQsQ0FBbUJ5QyxJQUFuQixFQUE5QjtBQUNBLE1BQU1JLCtCQUErQixHQUFHUCxVQUFVLENBQUMsQ0FBRCxDQUFWLENBQWNLLFVBQXREO0FBRUE7OztBQUVBLE1BQU1HLHlCQUF5QixHQUFHUCxTQUFTLENBQUNRLEtBQVYsQ0FBZ0JILHFCQUFoQixFQUF1Q0MsK0JBQXZDLEVBQXdFRyxJQUF4RSxDQUE2RUoscUJBQTdFLEVBQW9HM0MsTUFBdEk7QUFDQSxNQUFNZ0QsbUJBQW1CLEdBQUdWLFNBQVMsQ0FBQ1EsS0FBVixDQUFnQlAsZUFBaEIsRUFBaUNFLHlCQUFqQyxFQUE0RE0sSUFBNUQsQ0FBaUVSLGVBQWpFLEVBQWtGdkMsTUFBOUc7QUFFQTs7QUFDQSxNQUFNaUQsb0JBQW9CLEdBQUdYLFNBQVMsQ0FBQ1ksU0FBVixDQUFvQkwseUJBQXlCLEdBQUdGLHFCQUFxQixDQUFDM0MsTUFBdEUsRUFBOEVnRCxtQkFBOUUsQ0FBN0I7QUFDQTs7QUFDQSxNQUFNRyxTQUFTLEdBQUlILG1CQUFtQixLQUFLSCx5QkFBekIsSUFBdURJLG9CQUFvQixDQUFDRyxLQUFyQixDQUEyQixJQUEzQixDQUF6RTtBQUNBLFNBQU9ELFNBQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHRva2VuaXplIH0gZnJvbSAnc3RyaW5nLXB1bmN0dWF0aW9uLXRva2VuaXplcic7XG5pbXBvcnQgKiBhcyBzdHJpbmdIZWxwZXJzIGZyb20gJy4vc3RyaW5nSGVscGVycyc7XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIC0gR2V0cyB0aGUgc2VsZWN0aW9uIG9iamVjdCBmcm9tIHRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgdGV4dCBmcm9tIHRoZSBXZWIgVUlcbiAqIEBwYXJhbSB7U3RyaW5nfSBlbnRpcmVUZXh0IC0gdGhlIHRleHQgdGhhdCB0aGUgc2VsZWN0aW9uIHNob3VsZCBiZSBpbiwgaWUgdmVyc2VUZXh0XG4gKiBAcGFyYW0ge0Jvb2xlYW59IGZhbGxiYWNrVG9QcmV2aW91c1dvcmQgLSBpZiB0cnVlIHRoZW4gd2Ugc2VsZWN0IHByZXZpb3VzIHdvcmQgaWYgc2VsZWN0aW9uIGlzIGF0IGVuZCBvZiB3b3JkXG4gKiBAcmV0dXJuIHtPYmplY3R9IC0gdGhlIHNlbGVjdGlvbiBvYmplY3QgdG8gYmUgdXNlZFxuICogVE9ETzogRmluZCBhIHdheSB0byB0ZXN0XG4gKi9cbmV4cG9ydCBjb25zdCBnZXRTZWxlY3Rpb25Gcm9tQ3VycmVudFdpbmRvd1NlbGVjdGlvbiA9IChlbnRpcmVUZXh0LCBmYWxsYmFja1RvUHJldmlvdXNXb3JkID0gZmFsc2UpID0+IHtcbiAgbGV0IHNlbGVjdGlvbjsgLy8gcmVzcG9uc2VcbiAgY29uc3Qgd2luZG93U2VsZWN0aW9uID0gZ2V0Q3VycmVudFdpbmRvd1NlbGVjdGlvbigpO1xuICBsZXQgc2VsZWN0ZWRUZXh0ID0gZ2V0U2VsZWN0ZWRUZXh0RnJvbVdpbmRvd1NlbGVjdGlvbih3aW5kb3dTZWxlY3Rpb24pO1xuICBsZXQgcHJlY2VkaW5nVGV4dCA9IGdldFByZWNlZGluZ1RleHRGcm9tV2luZG93U2VsZWN0aW9uKHdpbmRvd1NlbGVjdGlvbik7XG5cbiAgaWYgKGZhbGxiYWNrVG9QcmV2aW91c1dvcmQgJiYgKHNlbGVjdGVkVGV4dCA9PT0gJyAnKSkgeyAvLyBoYW5kbGUgZWRnZSBjYXNlIG9mIGNsaWNraW5nIG5lYXIgZW5kIG9mIHdvcmRcbiAgICBjb25zdCB3b3JkcyA9IHRva2VuaXplKHsgdGV4dDogcHJlY2VkaW5nVGV4dCB9KTtcblxuICAgIGlmICh3b3JkcyAmJiB3b3Jkcy5sZW5ndGgpIHtcbiAgICAgIGNvbnN0IGxhc3RXb3JkID0gd29yZHNbd29yZHMubGVuZ3RoIC0gMV07XG5cbiAgICAgIGlmIChsYXN0V29yZCkge1xuICAgICAgICBjb25zdCBwb3MgPSBwcmVjZWRpbmdUZXh0Lmxhc3RJbmRleE9mKGxhc3RXb3JkKTtcblxuICAgICAgICBpZiAocG9zICYmIChwb3MgKyBsYXN0V29yZC5sZW5ndGggPT09IHByZWNlZGluZ1RleHQubGVuZ3RoKSkgeyAvLyBpZiBsYXN0IHdvcmQgaXMgYXQgZW5kIG9mIHN0cmluZ1xuICAgICAgICAgIC8vIHVzZSBsYXN0IHdvcmQgZm9yIHNlbGVjdGlvblxuICAgICAgICAgIHNlbGVjdGVkVGV4dCA9IGxhc3RXb3JkO1xuICAgICAgICAgIHByZWNlZGluZ1RleHQgPSBwcmVjZWRpbmdUZXh0LnN1YnN0cigwLCBwb3MpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBnZXRTZWxlY3Rpb25Gcm9tQ3VycmVudFdpbmRvd1NlbGVjdGlvbigpIC0gc3dpdGNoaW5nIHRvIGxhc3Qgd29yZCwgc2VsZWN0ZWRUZXh0ICR7SlNPTi5zdHJpbmdpZnkoc2VsZWN0ZWRUZXh0KX1gKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIFNvbWUgZWRnZSBjYXNlcyBsZWF2ZSBhIHdlaXJkIHNlbGVjdGlvbiByZW1haW5pbmcsIGxldCdzIGNsZWFuIHVwLlxuICBzZWxlY3Rpb24gPSBzdHJpbmdIZWxwZXJzLmdlbmVyYXRlU2VsZWN0aW9uKHNlbGVjdGVkVGV4dCwgcHJlY2VkaW5nVGV4dCwgZW50aXJlVGV4dCk7XG4gIHdpbmRvdy5nZXRTZWxlY3Rpb24oKS5lbXB0eSgpO1xuICByZXR1cm4gc2VsZWN0aW9uO1xufTtcbi8qKlxuKiBAZGVzY3JpcHRpb24gLSBHZXRzIHRoZSB3aW5kb3cncyBTZWxlY3Rpb24gZnJvbSB0aGUgVUlcbiogQHJldHVybiB7T2JqZWN0fSB3aW5kb3dTZWxlY3Rpb24gLSBhIHdpbmRvd1NlbGVjdGlvbiBvYmplY3QgZnJvbSBpbnNpZGUgYSBjb21wYXRpYmxlIGVsZW1lbnRcbiogVE9ETzogRmluZCBhIHdheSB0byB0ZXN0XG4qL1xuZXhwb3J0IGNvbnN0IGdldEN1cnJlbnRXaW5kb3dTZWxlY3Rpb24gPSAoKSA9PiB3aW5kb3cuZ2V0U2VsZWN0aW9uKCk7XG4vKipcbiogQGRlc2NyaXB0aW9uIC0gR2V0cyB0aGUgd2luZG93IHNlbGVjdGVkIHRleHQgZnJvbSB0aGUgd2luZG93U2VsZWN0aW9uXG4qIEBwYXJhbSB7T2JqZWN0fSB3aW5kb3dTZWxlY3Rpb24gLSBhIHdpbmRvd1NlbGVjdGlvbiBvYmplY3QgZnJvbSBpbnNpZGUgYSBjb21wYXRpYmxlIGVsZW1lbnRcbiogQHJldHVybiB7U3RyaW5nfSAtIHNlbGVjdGVkVGV4dFxuKiBUT0RPOiBGaW5kIGEgd2F5IHRvIHRlc3RcbiovXG5leHBvcnQgY29uc3QgZ2V0U2VsZWN0ZWRUZXh0RnJvbVdpbmRvd1NlbGVjdGlvbiA9ICh3aW5kb3dTZWxlY3Rpb24pID0+IHdpbmRvd1NlbGVjdGlvbi50b1N0cmluZygpO1xuLyoqXG4qIEBkZXNjcmlwdGlvbiAtIEdldHMgdGhlIHByZWNlZGluZ1RleHQgZnJvbSB0aGUgd2luZG93U2VsZWN0aW9uXG4qIEBwYXJhbSB7T2JqZWN0fSB3aW5kb3dTZWxlY3Rpb24gLSBhIHdpbmRvd1NlbGVjdGlvbiBvYmplY3QgZnJvbSBpbnNpZGUgYSBjb21wYXRpYmxlIGVsZW1lbnRcbiogQHJldHVybiB7U3RyaW5nfSAtIHRoZSBzdHJpbmcgb2YgcHJlY2VkaW5nVGV4dFxuKiBJbXBsZW1lbnRhdGlvbiBub3RlcyBvbiB3aHkgeW91IGNhbid0IGp1c3QgdXNlIHRoZSB3aW5kb3cuZ2V0U2VsZWN0aW9uKClcbiogZ2V0U2VsZWN0aW9uIGlzIGxpbWl0ZWQgYnkgc2FtZSBpbm5lclRleHQgbm9kZSwgYW5kIGRvZXMgbm90IGluY2x1ZGUgc3BhbiBzaWJsaW5nc1xuKiBpbmRleE9mVGV4dFNlbGVjdGlvbiBpcyBicm9rZW4gYnkgYW55IG90aGVyIHByZXZpb3VzIHNlbGVjdGlvbiBzaW5jZSBpdCBvbmx5IGtub3dzIGl0cyBpbm5lclRleHQgbm9kZS5cbiogVE9ETzogRmluZCBhIHdheSB0byB0ZXN0XG4qL1xuZXhwb3J0IGNvbnN0IGdldFByZWNlZGluZ1RleHRGcm9tV2luZG93U2VsZWN0aW9uID0gKHdpbmRvd1NlbGVjdGlvbikgPT4ge1xuICBsZXQgcHJlY2VkaW5nVGV4dDsgLy8gcmVzcG9uc2VcbiAgLy8gY29uY2F0ZW5hdGUgc3BhbnMgZXRjLi4uIHRvIGdldCB0aGUgcHJlY2VkaW5nVGV4dCBmcm9tIHRoZSB3aW5kb3dTZWxlY3Rpb25cblxuICBpZiAod2luZG93U2VsZWN0aW9uLnJhbmdlQ291bnQpIHtcbiAgICAvLyBnZXQgdGhlIHRleHQgYWZ0ZXIgdGhlIHByZWNlZGluZyBzZWxlY3Rpb24gYW5kIGN1cnJlbnQgc3BhbiBzZWxlY3Rpb24gaXMgaW4uXG4gICAgY29uc3Qgc2VsZWN0aW9uUmFuZ2UgPSB3aW5kb3dTZWxlY3Rpb24uZ2V0UmFuZ2VBdCgwKTtcbiAgICAvLyBnZXQgdGhlIGNoYXJhY3RlciBpbmRleCBvZiB3aGF0IGlzIHNlbGVjdGVkIGluIGNvbnRleHQgb2YgdGhlIHNwYW4gaXQgaXMgaW4uXG4gICAgY29uc3Qgc2VsZWN0aW9uUmFuZ2VTdGFydCA9IHNlbGVjdGlvblJhbmdlLnN0YXJ0T2Zmc2V0O1xuICAgIC8vIGdldCB0aGUgY29udGFpbmVyIG9mIHRoZSBzZWxlY3Rpb24sIHRoaXMgaXMgYSBzdHJhbmdlIG9iamVjdCwgdGhhdCBsb2dzIGFzIGEgc3RyaW5nLlxuICAgIGNvbnN0IHRleHRDb250YWluZXIgPSBzZWxlY3Rpb25SYW5nZS5jb21tb25BbmNlc3RvckNvbnRhaW5lcjtcbiAgICAvLyBnZXQgdGhlIHBhcmVudCBzcGFuIHRoYXQgY29udGFpbnMgdGhlIHRleHRDb250YWluZXIuXG5cbiAgICBsZXQgZWxlbWVudDtcblxuICAgIC8vIGlmIHRoZSB0ZXh0Q29udGFpbmVyIGlzICN0ZXh0LCB0aGVuIHVzZSB0aGUgcGFyZW50RWxlbWVudCAtIHVzdWFsbHkgbm9uLW92ZXJsYXBwaW5nIHNlbGVjdGlvblxuICAgIGlmICgnI3RleHQnID09PSB0ZXh0Q29udGFpbmVyLm5vZGVOYW1lKSB7XG4gICAgICBlbGVtZW50ID0gdGV4dENvbnRhaW5lci5wYXJlbnRFbGVtZW50O1xuICAgIH1cblxuICAgIC8vIGlmIHRoZSB0ZXh0Q29udGFpbmVyIGlzIGEgc3BhbiwgdGhlbiB1c2UgaXQgYXMgdGhlIGVsZW1lbnRcbiAgICBpZiAoJ1NQQU4nID09PSB0ZXh0Q29udGFpbmVyLm5vZGVOYW1lKSB7XG4gICAgICBlbGVtZW50ID0gdGV4dENvbnRhaW5lcjtcbiAgICB9XG5cbiAgICAvLyBpZiB0aGUgdGV4dENvbnRhaW5lciBpcyBhIGRpdiwgaXRzIGFuIG92ZXJsYXBwaW5nIHNlbGVjdGlvbiwgZG9uJ3QgdXNlIGNvbW1vbkFuY2VzdG9yQ29udGFpbmVyXG4gICAgaWYgKCdESVYnID09PSB0ZXh0Q29udGFpbmVyLm5vZGVOYW1lKSB7XG4gICAgICBlbGVtZW50ID0gc2VsZWN0aW9uUmFuZ2Uuc3RhcnRDb250YWluZXIucGFyZW50RWxlbWVudDtcbiAgICB9XG5cbiAgICAvLyBjaGVjayBmb3IgZWxlbWVudCwgYXMgdGV4dENvbnRhaW5lciBjYW4gYnV0IHJhcmVseSBiZSBzb21ldGhpbmcgb3RoZXIgdGhhbiAjdGV4dCwgc3BhbiBvciBkaXZcbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgcHJlY2VkaW5nVGV4dCA9IGdldFByZWNlZGluZ1RleHRGcm9tRWxlbWVudEFuZFNpYmxpbmdzKGVsZW1lbnQsIHNlbGVjdGlvblJhbmdlU3RhcnQsIHdpbmRvd1NlbGVjdGlvbik7XG4gICAgfVxuICB9XG4gIHJldHVybiBwcmVjZWRpbmdUZXh0O1xufTtcbi8qKlxuICogQGRlc2NyaXB0aW9uIC0gZ2V0cyB0aGUgcHJlY2VkaW5nVGV4dCBmcm9tIHRoZSBlbGVtZW50IGVuZGluZyBhdCB0aGUgc2VsZWN0aW9uUmFuZ2VTdGFydFxuICogQHBhcmFtIHtFbGVtZW50fSBlbGVtZW50IC0gdGhlIGh0bWwgZWxlbWVudCB0aGF0IGhhcyB0ZXh0IGFuZCBzaWJsaW5ncyB3aXRoIHRleHRcbiAqIEBwYXJhbSB7SW50fSBzZWxlY3Rpb25SYW5nZVN0YXJ0IC0gdGhlIGNoYXJhY3RlciBpbmRleCBvZiB0aGUgc3RhcnQgb2YgdGhlIHNlbGVjdGlvblxuICogQHJldHVybiB7U3RyaW5nfSAtIHRoZSBzdHJpbmcgb2YgcHJlY2VkaW5nVGV4dFxuICovXG5leHBvcnQgY29uc3QgZ2V0UHJlY2VkaW5nVGV4dEZyb21FbGVtZW50QW5kU2libGluZ3MgPSAoZWxlbWVudCwgc2VsZWN0aW9uUmFuZ2VTdGFydCwgd2luZG93U2VsZWN0aW9uKSA9PiB7XG4gIGxldCBwcmVjZWRpbmdUZXh0OyAvLyByZXNwb25zZVxuICBjb25zdCBwcmVjZWRpbmdUZXh0RnJvbUVsZW1lbnRTaWJsaW5ncyA9IGdldFByZWNlZGluZ1RleHRGcm9tRWxlbWVudFNpYmxpbmdzKGVsZW1lbnQsIHdpbmRvd1NlbGVjdGlvbik7XG4gIGNvbnN0IHByZWNlZGluZ1RleHRGcm9tRWxlbWVudCA9IGdldFByZWNlZGluZ1RleHRGcm9tRWxlbWVudChlbGVtZW50LCBzZWxlY3Rpb25SYW5nZVN0YXJ0LCB3aW5kb3dTZWxlY3Rpb24pO1xuICBwcmVjZWRpbmdUZXh0ID0gcHJlY2VkaW5nVGV4dEZyb21FbGVtZW50U2libGluZ3MgKyBwcmVjZWRpbmdUZXh0RnJvbUVsZW1lbnQ7XG4gIHJldHVybiBwcmVjZWRpbmdUZXh0O1xufTtcbi8qKlxuICogQGRlc2NyaXB0aW9uIC0gZ2V0cyB0aGUgcHJlY2VkaW5nVGV4dCBmcm9tIHRoZSBlbGVtZW50IGVuZGluZyBhdCB0aGUgc2VsZWN0aW9uUmFuZ2VTdGFydFxuICogQHBhcmFtIHtFbGVtZW50fSBlbGVtZW50IC0gdGhlIGh0bWwgZWxlbWVudCB0aGF0IGhhcyB0ZXh0XG4gKiBAcGFyYW0ge0ludH0gc2VsZWN0aW9uUmFuZ2VTdGFydCAtIHRoZSBjaGFyYWN0ZXIgaW5kZXggb2YgdGhlIHN0YXJ0IG9mIHRoZSBzZWxlY3Rpb25cbiAqIEByZXR1cm4ge1N0cmluZ30gLSB0aGUgc3RyaW5nIG9mIHByZWNlZGluZ1RleHRcbiAqL1xuZXhwb3J0IGNvbnN0IGdldFByZWNlZGluZ1RleHRGcm9tRWxlbWVudCA9IChlbGVtZW50LCBzZWxlY3Rpb25SYW5nZVN0YXJ0KSA9PiB7XG4gIGxldCBwcmVjZWRpbmdUZXh0OyAvLyByZXNwb25zZVxuICBjb25zdCB0ZXh0ID0gZWxlbWVudC50ZXh0Q29udGVudDtcbiAgcHJlY2VkaW5nVGV4dCA9IHRleHQuc2xpY2UoMCwgc2VsZWN0aW9uUmFuZ2VTdGFydCk7XG4gIHJldHVybiBwcmVjZWRpbmdUZXh0O1xufTtcbi8qKlxuICogQGRlc2NyaXB0aW9uIC0gZ2V0cyB0aGUgcHJlY2VkaW5nVGV4dCBmcm9tIHRoZSBlbGVtZW50IHNpYmxpbmdzXG4gKiBAcGFyYW0ge0VsZW1lbnR9IGVsZW1lbnQgLSB0aGUgaHRtbCBlbGVtZW50IHRoYXQgaGFzIHRleHQgYW5kIHNpYmxpbmdzIHdpdGggdGV4dFxuICogQHJldHVybiB7U3RyaW5nfSAtIHRoZSBzdHJpbmcgb2YgcHJlY2VkaW5nVGV4dFxuICovXG5leHBvcnQgY29uc3QgZ2V0UHJlY2VkaW5nVGV4dEZyb21FbGVtZW50U2libGluZ3MgPSAoZWxlbWVudCwgd2luZG93U2VsZWN0aW9uKSA9PiB7XG4gIGxldCBwcmVjZWRpbmdUZXh0ID0gJyc7IC8vIHJlc3BvbnNlXG4gIC8vIGdldCB0aGUgcHJldmlvdXMgc2libGluZyB0byBzdGFydCB0aGUgbG9vcFxuICBsZXQgcHJldmlvdXNTaWJsaW5nID0gZWxlbWVudC5wcmV2aW91c0VsZW1lbnRTaWJsaW5nO1xuXG4gIC8vIGxvb3AgdGhyb3VnaCBwcmV2aW91cyBzcGFucyB0byBnZXQgdGhlaXIgdGV4dFxuICB3aGlsZSAocHJldmlvdXNTaWJsaW5nKSB7XG4gICAgLy8ganVzdCBpbiBjYXNlIHRoZSBwcmV2aW91c1NpYmxpbmcganVzdCBoYXBwZW5zIHRvIGJlIGEgcGFydCBvZiB0aGUgc2VsZWN0aW9uXG4gICAgaWYgKHdpbmRvd1NlbGVjdGlvbiAmJiAhd2luZG93U2VsZWN0aW9uLmNvbnRhaW5zTm9kZShwcmV2aW91c1NpYmxpbmcpKSB7XG4gICAgICAvLyBwcmVwZW5kIHRoZSBzcGFucyBpbm5lclRleHQgdG8gdGhlIHByZWNlZGluZ1RleHRcbiAgICAgIHByZWNlZGluZ1RleHQgPSBwcmV2aW91c1NpYmxpbmcudGV4dENvbnRlbnQgKyBwcmVjZWRpbmdUZXh0O1xuICAgIH1cbiAgICAvLyBtb3ZlIHRvIHRoZSBwcmV2aW91cyBzcGFuLCBpZiBub25lLCBpdCBlbmRzIHRoZSBsb29wXG4gICAgcHJldmlvdXNTaWJsaW5nID0gcHJldmlvdXNTaWJsaW5nLnByZXZpb3VzRWxlbWVudFNpYmxpbmc7XG4gIH1cbiAgcmV0dXJuIHByZWNlZGluZ1RleHQ7XG59O1xuXG4vKipcbiAqIFRoaXMgaXMgYSBoZWxwZXIgbWV0aG9kIHRvIGRldGVybWluZSBpZiB0aGUgc2VsZWN0aW9uIG5lZWRzIGEgYnJlYWsgY2hhciBpblxuICogYmV0d2VlbiB0aGUgc2VsZWN0ZWQgd29yZHMgb3Igbm90LlxuICogQHBhcmFtIHtBcnJheX0gc2VsZWN0aW9ucyAtIEFycmF5IG9mIHdvcmQgb2JqZWN0cyB0aGF0IHRoZSB1c2VyIHNlbGVjdGVkXG4gKiBAcGFyYW0ge3N0cmluZ30gdmVyc2VUZXh0IC0gVGhlIGVudGlyZSB2ZXJzZSBzdHJpbmcgZnJvbSB0aGUgY3VycmVudCBjaGVja1xuICogQHJldHVybnMge2Jvb2xlYW59IC0gV2hldGhlciB0aGUgVmlldyBzaG91bGQgZGlzcGxheSBhIGJyZWFrXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzaG91bGRSZW5kZXJCcmVhayhzZWxlY3Rpb25zLCB2ZXJzZVRleHQpIHtcbiAgLyoqIE5lZWQgdG8gZ2V0IHRoZSB3b3JkcyBhbmQgb2NjdXJyZW5jZSBvZiB0aGUgc2VsZWN0ZWQgZWRnZSB3b3JkcyAqL1xuICBjb25zdCBlbmRTZWxlY3RlZFdvcmQgPSBzZWxlY3Rpb25zW3NlbGVjdGlvbnMubGVuZ3RoIC0gMV0udGV4dC50cmltKCk7XG4gIGNvbnN0IGVuZFNlbGVjdGVkV29yZE9jY3VycmVuY2UgPSBzZWxlY3Rpb25zW3NlbGVjdGlvbnMubGVuZ3RoIC0gMV0ub2NjdXJyZW5jZTtcbiAgY29uc3QgYmVnaW5uaW5nU2VsZWN0ZWRXb3JkID0gc2VsZWN0aW9uc1swXS50ZXh0LnRyaW0oKTtcbiAgY29uc3QgYmVnaW5uaW5nU2VsZWN0ZWRXb3JkT2NjdXJyZW5jZSA9IHNlbGVjdGlvbnNbMF0ub2NjdXJyZW5jZTtcblxuICAvKiogVXNpbmcgdGhlIG9jY3VycmVuY2VzIHRvIGdldCB0aGUgYWN0dWFsIGluZGV4IG9mIHRoZSB3b3JkIHZzXG4gICAqICB0aGUgZmlyc3QgdGltZSBpdCBhcHBlYXJzIGluIHZlcnNlIHRleHQgKi9cbiAgY29uc3QgaW5kZXhPZkJlZ2lubmluZ1NlbGVjdGlvbiA9IHZlcnNlVGV4dC5zcGxpdChiZWdpbm5pbmdTZWxlY3RlZFdvcmQsIGJlZ2lubmluZ1NlbGVjdGVkV29yZE9jY3VycmVuY2UpLmpvaW4oYmVnaW5uaW5nU2VsZWN0ZWRXb3JkKS5sZW5ndGg7XG4gIGNvbnN0IGluZGV4T2ZFbmRTZWxlY3Rpb24gPSB2ZXJzZVRleHQuc3BsaXQoZW5kU2VsZWN0ZWRXb3JkLCBlbmRTZWxlY3RlZFdvcmRPY2N1cnJlbmNlKS5qb2luKGVuZFNlbGVjdGVkV29yZCkubGVuZ3RoO1xuXG4gIC8qKiBDaGVja2luZyB0aGUgdGV4dCBpbiBiZXR3ZWVuIHNlbGVjdGVkIHdvcmRzIGZvciBhIG5vbi1zcGFjZSBjaGFyYWN0ZXIgKi9cbiAgY29uc3QgdGV4dEJldHdlZW5TZWxlY3Rpb24gPSB2ZXJzZVRleHQuc3Vic3RyaW5nKGluZGV4T2ZCZWdpbm5pbmdTZWxlY3Rpb24gKyBiZWdpbm5pbmdTZWxlY3RlZFdvcmQubGVuZ3RoLCBpbmRleE9mRW5kU2VsZWN0aW9uKTtcbiAgLyoqIElmIHRoZSBlbmQgaW5kZXggaXMgdGhlIHNhbWUgYXMgdGhlIGJlZ2lubmluZyB0aGVuIGl0IGlzIHRoZSBmaXJzdCB3b3JkICovXG4gIGNvbnN0IHNob3dCcmVhayA9IChpbmRleE9mRW5kU2VsZWN0aW9uICE9PSBpbmRleE9mQmVnaW5uaW5nU2VsZWN0aW9uKSAmJiB0ZXh0QmV0d2VlblNlbGVjdGlvbi5tYXRjaCgvXFxTLyk7XG4gIHJldHVybiBzaG93QnJlYWs7XG59XG4iXX0=