UNPKG

com.phloxui

Version:

PhloxUI Ng2+ Framework

604 lines (603 loc) 56.4 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ import * as tslib_1 from "tslib"; import { AbstractNavItem } from './AbstractNavItem'; import { AbstractI18NApplicable } from '../share/AbstractI18NApplicable'; var /** @type {?} */ TOPIC_NAME_PREFIX = 'nav-menu.'; var /** @type {?} */ NAVIGATE_SUFFIX = 'navigate'; var /** @type {?} */ NAVIGATE_CMD_SUFFIX = 'cmd.navigate'; var /** @type {?} */ NAVIGATE_TOPIC_NAME = TOPIC_NAME_PREFIX + NAVIGATE_SUFFIX; var /** @type {?} */ NAVIGATE_CMD_TOPIC_NAME = TOPIC_NAME_PREFIX + NAVIGATE_CMD_SUFFIX; // unsupported: template constraints. /** * <p style="text-indent: 2em;"> * An abstract base class for <code>nav menu</code>. A <code>nav menu</code> may consist of one or more <code>nav item</code>s * ([[AbstractNavItem]]). The only difference between stadard <code>menu</code> ([[AbstractMenu]]) and <code>nav menu</code> is that * the <code>nav menu</code> has a main purpose on <code>navigation</code>. It also automatically publishes an event to the * [[ObservableManager]] on the topic "<code>nav-menu.[sub-topic-name].navigate</code>" when a navigation is performed. Furthermore, * to remotely control the navigation on this <code>nav menu</code> instance at runtime without having direct object reference, * you can use [[ObservableManager]]'s topic "<code>nav-menu.[sub-topic-name].cmd.navigate</code>" to publish a navigation * <code>command</code> to this <code>nav menu</code>. * </p> * * @author shiorin, tee4cute * @see [[AbstractNavItem]] * @see [[INavigator]] * @see [[ObservableManager]] * @abstract * @template T */ var AbstractNavMenu = /** @class */ (function (_super) { tslib_1.__extends(AbstractNavMenu, _super); function AbstractNavMenu(subTopicName, obsvMgr) { var _this = _super.call(this) || this; _this.items = []; _this.subTopicName = subTopicName; _this.navigateSubjects = []; _this.observableMgr = obsvMgr; return _this; } /** * <p style="text-indent: 1em;"> * Get the navigation event topic name with the given <code><b>subTopicName</b></code>. The returning topic name will be prefixed by * [[NAVIGATE_TOPIC_NAME]]. * </p> * @param {?} subTopicName * @return {?} */ AbstractNavMenu.getNavigateTopicName = /** * <p style="text-indent: 1em;"> * Get the navigation event topic name with the given <code><b>subTopicName</b></code>. The returning topic name will be prefixed by * [[NAVIGATE_TOPIC_NAME]]. * </p> * @param {?} subTopicName * @return {?} */ function (subTopicName) { if (subTopicName === null || typeof subTopicName === 'undefined' || subTopicName === '') { return AbstractNavMenu.NAVIGATE_TOPIC_NAME; } if (subTopicName.charAt(0) === '.') { subTopicName = subTopicName.substring(1); } if (subTopicName === '') { return AbstractNavMenu.NAVIGATE_TOPIC_NAME; } if (subTopicName.charAt(subTopicName.length - 1) !== '.') { subTopicName += '.'; } return TOPIC_NAME_PREFIX + subTopicName + NAVIGATE_SUFFIX; }; /** * <p style="text-indent: 1em;"> * Get the navigation cmd topic name with the given <code><b>subTopicName</b></code>. The returning topic name will be prefixed by * [[NAVIGATE_CMD_TOPIC_NAME]]. * </p> * @param {?} subTopicName * @return {?} */ AbstractNavMenu.getNavigateCmdTopicName = /** * <p style="text-indent: 1em;"> * Get the navigation cmd topic name with the given <code><b>subTopicName</b></code>. The returning topic name will be prefixed by * [[NAVIGATE_CMD_TOPIC_NAME]]. * </p> * @param {?} subTopicName * @return {?} */ function (subTopicName) { if (subTopicName === null || typeof subTopicName === 'undefined' || subTopicName === '') { return AbstractNavMenu.NAVIGATE_CMD_TOPIC_NAME; } if (subTopicName.charAt(0) === '.') { subTopicName = subTopicName.substring(1); } if (subTopicName === '') { return AbstractNavMenu.NAVIGATE_CMD_TOPIC_NAME; } if (subTopicName.charAt(subTopicName.length - 1) !== '.') { subTopicName += '.'; } return TOPIC_NAME_PREFIX + subTopicName + NAVIGATE_CMD_SUFFIX; }; /** * @param {?} navigate * @param {?} navigateCmd * @return {?} */ AbstractNavMenu.prototype.initNavMenuEventTopics = /** * @param {?} navigate * @param {?} navigateCmd * @return {?} */ function (navigate, navigateCmd) { var _this = this; // Create change event observable topics if (navigate !== null && typeof navigate === 'string') { this.navigateSubjects.push(this.observableMgr.createSubject(navigate)); } // Subscribe on CMD topics if (navigateCmd !== null && typeof navigateCmd === 'string') { this.observableMgr.subscribe(navigateCmd, function (item) { if (item instanceof AbstractNavItem) { _this.navigateTo(/** @type {?} */ (item)); // open here } }); } }; /** * @return {?} */ AbstractNavMenu.prototype.ngOnInit = /** * @return {?} */ function () { // Init super class event topics this.initNavMenuEventTopics(AbstractNavMenu.NAVIGATE_TOPIC_NAME, AbstractNavMenu.NAVIGATE_CMD_TOPIC_NAME); if (this.subTopicName !== null && this.subTopicName !== '' || typeof this.subTopicName !== 'undefined') { // Init sub topic event topics this.initNavMenuEventTopics(AbstractNavMenu.getNavigateTopicName(this.subTopicName), AbstractNavMenu.getNavigateCmdTopicName(this.subTopicName)); } }; /** * <p style="text-indent: 1em;"> * Get <code>nav item</code>s ([[AbstractNavItem]]) of this <code>nav menu</code>. * </p> * @return {?} */ AbstractNavMenu.prototype.getItems = /** * <p style="text-indent: 1em;"> * Get <code>nav item</code>s ([[AbstractNavItem]]) of this <code>nav menu</code>. * </p> * @return {?} */ function () { return this.items; }; /** * <p style="text-indent: 1em;"> * Get [[ObservableManager]]'s sub topic name associated with this <code>nav menu</code>. * </p> * @return {?} */ AbstractNavMenu.prototype.getSubTopicName = /** * <p style="text-indent: 1em;"> * Get [[ObservableManager]]'s sub topic name associated with this <code>nav menu</code>. * </p> * @return {?} */ function () { return this.subTopicName; }; /** * <p style="text-indent: 1em;"> * Get <code>nav item</code>s count in this <code>nav menu</code>. * </p> * @return {?} */ AbstractNavMenu.prototype.getItemCount = /** * <p style="text-indent: 1em;"> * Get <code>nav item</code>s count in this <code>nav menu</code>. * </p> * @return {?} */ function () { return this.items.length; }; /** * <p style="text-indent: 1em;"> * Get an active <code>nav item</code> index of this <code>nav menu</code>. * </p> * * @return {?} Returns <code>-1</code> if there is no current active item. Otherwise, return the array index of current * active item. */ AbstractNavMenu.prototype.getActiveIndex = /** * <p style="text-indent: 1em;"> * Get an active <code>nav item</code> index of this <code>nav menu</code>. * </p> * * @return {?} Returns <code>-1</code> if there is no current active item. Otherwise, return the array index of current * active item. */ function () { for (var /** @type {?} */ i = 0; i < this.items.length; i++) { if (this.items[i].isActive()) { return i; } } return -1; }; /** * <p style="text-indent: 1em;"> * Get an active <code>nav item</code> of this <code>nav menu</code>. * </p> * * @return {?} Returns <code>null</code> if there is no current active item. */ AbstractNavMenu.prototype.getActiveItem = /** * <p style="text-indent: 1em;"> * Get an active <code>nav item</code> of this <code>nav menu</code>. * </p> * * @return {?} Returns <code>null</code> if there is no current active item. */ function () { var /** @type {?} */ idx = this.getActiveIndex(); if (idx < 0) { return null; } return this.items[idx]; }; /** * <p style="text-indent: 1em;"> * Set <code>nav item</code>s of this <code>nav menu</code>. * </p> * @param {?} items * @return {?} */ AbstractNavMenu.prototype.setItems = /** * <p style="text-indent: 1em;"> * Set <code>nav item</code>s of this <code>nav menu</code>. * </p> * @param {?} items * @return {?} */ function (items) { this.items = items; }; /** * <p style="text-indent: 1em;"> * Add a new <code>nav item</code> into this <code>nav menu</code>. If there already is the given <code><b>item</b></code> in this * <code>nav menu</code>, this method will do nothing and return <code>false</code>. * </p> * * @param {?} item A new <code>nav item</code> to be added into this <code>nav menu</code>. * * @return {?} Returns <code>true</code> if the given <code><b>item</b></code> is added into this <code>nav menu</code>. Otherwise, returns <code>false</code>. */ AbstractNavMenu.prototype.addItem = /** * <p style="text-indent: 1em;"> * Add a new <code>nav item</code> into this <code>nav menu</code>. If there already is the given <code><b>item</b></code> in this * <code>nav menu</code>, this method will do nothing and return <code>false</code>. * </p> * * @param {?} item A new <code>nav item</code> to be added into this <code>nav menu</code>. * * @return {?} Returns <code>true</code> if the given <code><b>item</b></code> is added into this <code>nav menu</code>. Otherwise, returns <code>false</code>. */ function (item) { if (this.containsItem(item)) { return false; } this.items.push(item); return true; }; /** * <p style="text-indent: 1em;"> * Remove the given <code><b>item</b></code> from this <code>nav menu</code>. If the given <code><b>item</b></code> is not in this <code>nav menu</code>, this method * will do nothing and return <code>false</code>. If the <code><b>item</b></code> being removed is a current active item, this <code>nav menu</code> will be navigated * to the first <code>nav item</code>. * </p> * * @param {?} item A <code>nav item</code> to be removed from this <code>nav menu</code>. * * @return {?} Returns <code>true</code> if the given <code><b>item</b></code> is found and removed from this <code>nav menu</code>. Otherwise, returns <code>false</code>. */ AbstractNavMenu.prototype.removeItem = /** * <p style="text-indent: 1em;"> * Remove the given <code><b>item</b></code> from this <code>nav menu</code>. If the given <code><b>item</b></code> is not in this <code>nav menu</code>, this method * will do nothing and return <code>false</code>. If the <code><b>item</b></code> being removed is a current active item, this <code>nav menu</code> will be navigated * to the first <code>nav item</code>. * </p> * * @param {?} item A <code>nav item</code> to be removed from this <code>nav menu</code>. * * @return {?} Returns <code>true</code> if the given <code><b>item</b></code> is found and removed from this <code>nav menu</code>. Otherwise, returns <code>false</code>. */ function (item) { var /** @type {?} */ idx = this.items.indexOf(item); if (idx < 0) { return false; } this.items.splice(idx, 1); if (item.active) { // Set page active to false. item.active = false; // If it is currently active page, we've to navigate to another one. this.navigateToIndex(0); } return true; }; /** * <p style="text-indent: 1em;"> * To check that the given <code><b>item</b></code> is in this <code>nav menu</code> or not. * </p> * * @param {?} item A <code>nav item</code> to check that it is in this <code>nav menu</code> or not. * * @return {?} Returns <code>true</code> if the given <code><b>item</b></code> is in this <code>nav menu</code>. */ AbstractNavMenu.prototype.containsItem = /** * <p style="text-indent: 1em;"> * To check that the given <code><b>item</b></code> is in this <code>nav menu</code> or not. * </p> * * @param {?} item A <code>nav item</code> to check that it is in this <code>nav menu</code> or not. * * @return {?} Returns <code>true</code> if the given <code><b>item</b></code> is in this <code>nav menu</code>. */ function (item) { return this.items.indexOf(item) >= 0; }; /** * <p style="text-indent: 1em;"> * Get a <code>nav item</code> ([[AbstractNavItem]]) from the given index (<code><b>idx</b></code>). * </p> * * @param {?} idx The index of <code>nav item</code> to get. * * @return {?} Returns an instance of <code>nav item</code> ([[AbstractNavItem]]) at the specified index (<code><b>idx</b></code>). * Returns <code>null</code> if the given index is out of range. */ AbstractNavMenu.prototype.getItemByIndex = /** * <p style="text-indent: 1em;"> * Get a <code>nav item</code> ([[AbstractNavItem]]) from the given index (<code><b>idx</b></code>). * </p> * * @param {?} idx The index of <code>nav item</code> to get. * * @return {?} Returns an instance of <code>nav item</code> ([[AbstractNavItem]]) at the specified index (<code><b>idx</b></code>). * Returns <code>null</code> if the given index is out of range. */ function (idx) { if (idx === null || typeof idx === 'undefined' || idx < 0 || idx >= this.items.length) { return null; } return this.items[idx]; }; /** * <p style="text-indent: 1em;"> * Get a <code>nav item</code> ([[AbstractNavItem]]) by the given <code><b>name</b></code>. * </p> * * @param {?} name The name of a <code>nav item</code> to get. * * @return {?} Returns an instance of <code>nav item</code> ([[AbstractNavItem]]) with the given <code><b>name</b></code>. * Returns <code>null</code> if not found. */ AbstractNavMenu.prototype.getItemByName = /** * <p style="text-indent: 1em;"> * Get a <code>nav item</code> ([[AbstractNavItem]]) by the given <code><b>name</b></code>. * </p> * * @param {?} name The name of a <code>nav item</code> to get. * * @return {?} Returns an instance of <code>nav item</code> ([[AbstractNavItem]]) with the given <code><b>name</b></code>. * Returns <code>null</code> if not found. */ function (name) { var /** @type {?} */ idx = this.getItemIndexByName(name); if (idx < 0) { return null; } return this.items[idx]; }; /** * <p style="text-indent: 1em;"> * Get the index of the given <code>nav item</code> (<code><b>item</b></code>). * </p> * * @param {?} item The <code>nav item</code> to find index. * * @return {?} Returns an index of the specified <code><b>item</b></code>. Returns <code>-1</code> if not found. */ AbstractNavMenu.prototype.getItemIndex = /** * <p style="text-indent: 1em;"> * Get the index of the given <code>nav item</code> (<code><b>item</b></code>). * </p> * * @param {?} item The <code>nav item</code> to find index. * * @return {?} Returns an index of the specified <code><b>item</b></code>. Returns <code>-1</code> if not found. */ function (item) { return this.items.indexOf(item); }; /** * <p style="text-indent: 1em;"> * Get the index of the given item <code><b>name</b></code>. * </p> * * @param {?} name The item name to find index. * * @return {?} Returns an index of <code>nav item</code> having the specified <code><b>name</b></code>. Returns <code>-1</code> if not found. */ AbstractNavMenu.prototype.getItemIndexByName = /** * <p style="text-indent: 1em;"> * Get the index of the given item <code><b>name</b></code>. * </p> * * @param {?} name The item name to find index. * * @return {?} Returns an index of <code>nav item</code> having the specified <code><b>name</b></code>. Returns <code>-1</code> if not found. */ function (name) { if (name === null || typeof name === 'undefined') { return -1; } for (var /** @type {?} */ i = 0; i < this.items.length; i++) { if (this.items[i].getName() === name) { return i; } } return -1; }; /** * <p style="text-indent: 1em;"> * Navigate this <code>nav menu</code> to the specified <code>nav <b>item</b></code>. This method returns <code>Promise</code> to support * asynchronous execution. The <code>result</code> value of returned <code>Promise</code> is a boolean indicating the navigation result. * The result value must be <code>true</code> if the navigation is successful. Otherwise, for example, if the given <code><b>item</b></code> * does not exist, the result value will be <code>false</code>. * </p> * * @param {?} item The <code>nav item</code> to navigate to. * * @return {?} Returns a <code>Promise</code> which will be resolved when the navigation is done. The <code>Promise</code>'s result will be a * boolean value which its value will be <code>true</code> if the navigation is successful. */ AbstractNavMenu.prototype.navigateTo = /** * <p style="text-indent: 1em;"> * Navigate this <code>nav menu</code> to the specified <code>nav <b>item</b></code>. This method returns <code>Promise</code> to support * asynchronous execution. The <code>result</code> value of returned <code>Promise</code> is a boolean indicating the navigation result. * The result value must be <code>true</code> if the navigation is successful. Otherwise, for example, if the given <code><b>item</b></code> * does not exist, the result value will be <code>false</code>. * </p> * * @param {?} item The <code>nav item</code> to navigate to. * * @return {?} Returns a <code>Promise</code> which will be resolved when the navigation is done. The <code>Promise</code>'s result will be a * boolean value which its value will be <code>true</code> if the navigation is successful. */ function (item) { var _this = this; if (item === null || typeof item === 'undefined') { return Promise.resolve(false); } if (item.isActive()) { // The item is currently active. Do nothing ... return Promise.resolve(false); } return this.doNavigate(item).then(function (success) { if (success) { var /** @type {?} */ curActive = _this.getActiveItem(); if (curActive !== null) { // Set current page active to false curActive.active = false; } item.active = true; // Emit change event if (Array.isArray(_this.navigateSubjects)) { _this.navigateSubjects.forEach(function (obsv, idx) { obsv.next(item); }); } } else { console.debug('Not navigate to "' + item.getName() + '".'); } return Promise.resolve(success); }); }; /** * <p style="text-indent: 1em;"> * Navigate this <code>nav menu</code> to the specified index (<code><b>idx</b></code>). This method returns <code>Promise</code> to support * asynchronous execution. The <code>result</code> value of returned <code>Promise</code> is a boolean indicating the navigation result. * The result value must be <code>true</code> if the navigation is successful. Otherwise, the result value will be <code>false</code>. * </p> * * @param {?} idx The item index to navigate to. * * @return {?} Returns a <code>Promise</code> which will be resolved when the navigation is done. The <code>Promise</code>'s result will be a * boolean value which its value will be <code>true</code> if the navigation is successful. */ AbstractNavMenu.prototype.navigateToIndex = /** * <p style="text-indent: 1em;"> * Navigate this <code>nav menu</code> to the specified index (<code><b>idx</b></code>). This method returns <code>Promise</code> to support * asynchronous execution. The <code>result</code> value of returned <code>Promise</code> is a boolean indicating the navigation result. * The result value must be <code>true</code> if the navigation is successful. Otherwise, the result value will be <code>false</code>. * </p> * * @param {?} idx The item index to navigate to. * * @return {?} Returns a <code>Promise</code> which will be resolved when the navigation is done. The <code>Promise</code>'s result will be a * boolean value which its value will be <code>true</code> if the navigation is successful. */ function (idx) { var /** @type {?} */ page = this.getItemByIndex(idx); if (page === null) { return Promise.resolve(false); } return this.navigateTo(page); }; /** * <p style="text-indent: 1em;"> * Navigate this <code>nav menu</code> to a <code>nav item</code> having the specified <code><b>name</b></code>. This method returns * <code>Promise</code> to support asynchronous execution. The <code>result</code> value of returned <code>Promise</code> is a boolean indicating * the navigation result. The result value must be <code>true</code> if the navigation is successful. Otherwise, for example, if the given <code><b>item</b></code> * does not exist, the result value will be <code>false</code>. * </p> * * @param {?} name The name of a <code>nav item</code> to navigate to. * * @return {?} Returns a <code>Promise</code> which will be resolved when the navigation is done. The <code>Promise</code>'s result will be a * boolean value which its value will be <code>true</code> if the navigation is successful. */ AbstractNavMenu.prototype.navigateToName = /** * <p style="text-indent: 1em;"> * Navigate this <code>nav menu</code> to a <code>nav item</code> having the specified <code><b>name</b></code>. This method returns * <code>Promise</code> to support asynchronous execution. The <code>result</code> value of returned <code>Promise</code> is a boolean indicating * the navigation result. The result value must be <code>true</code> if the navigation is successful. Otherwise, for example, if the given <code><b>item</b></code> * does not exist, the result value will be <code>false</code>. * </p> * * @param {?} name The name of a <code>nav item</code> to navigate to. * * @return {?} Returns a <code>Promise</code> which will be resolved when the navigation is done. The <code>Promise</code>'s result will be a * boolean value which its value will be <code>true</code> if the navigation is successful. */ function (name) { var /** @type {?} */ page = this.getItemByName(name); if (page === null) { return Promise.resolve(false); } return this.navigateTo(page); }; /** * <p style="text-indent: 1em;"> * Reset this <code>nav menu</code>. This method simply navigates back to the item at index <code>0</code>. * </p> * @return {?} */ AbstractNavMenu.prototype.reset = /** * <p style="text-indent: 1em;"> * Reset this <code>nav menu</code>. This method simply navigates back to the item at index <code>0</code>. * </p> * @return {?} */ function () { this.navigateToIndex(0); }; AbstractNavMenu.NAVIGATE_TOPIC_NAME = NAVIGATE_TOPIC_NAME; AbstractNavMenu.NAVIGATE_CMD_TOPIC_NAME = NAVIGATE_CMD_TOPIC_NAME; return AbstractNavMenu; }(AbstractI18NApplicable)); export { AbstractNavMenu }; function AbstractNavMenu_tsickle_Closure_declarations() { /** @type {?} */ AbstractNavMenu.NAVIGATE_TOPIC_NAME; /** @type {?} */ AbstractNavMenu.NAVIGATE_CMD_TOPIC_NAME; /** @type {?} */ AbstractNavMenu.prototype.items; /** @type {?} */ AbstractNavMenu.prototype.subTopicName; /** @type {?} */ AbstractNavMenu.prototype.navigateSubjects; /** @type {?} */ AbstractNavMenu.prototype.observableMgr; /** * <p style="text-indent: 1em;"> * A method to perform the actual navigation, for example, changing a router's path, etc. * </p> * * @abstract * @param {?} item A <code>nav item</code> to navigate to. * * @return {?} Returns a <code>Promise</code> which will be resolved when the navigation is done. The <code>Promise</code>'s result will be a * boolean value which its value will be <code>true</code> if the navigation is successful. */ AbstractNavMenu.prototype.doNavigate = function (item) { }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWJzdHJhY3ROYXZNZW51LmpzIiwic291cmNlUm9vdCI6Im5nOi8vY29tLnBobG94dWkvIiwic291cmNlcyI6WyJsaWIvY29tcG9uZW50L0Fic3RyYWN0TmF2TWVudS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUlBLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUlwRCxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUV6RSxxQkFBTSxpQkFBaUIsR0FBVyxXQUFXLENBQUM7QUFFOUMscUJBQU0sZUFBZSxHQUFXLFVBQVUsQ0FBQztBQUMzQyxxQkFBTSxtQkFBbUIsR0FBVyxjQUFjLENBQUM7QUFFbkQscUJBQU0sbUJBQW1CLEdBQVcsaUJBQWlCLEdBQUcsZUFBZSxDQUFDO0FBQ3hFLHFCQUFNLHVCQUF1QixHQUFXLGlCQUFpQixHQUFHLG1CQUFtQixDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7SUFrQlAsMkNBQXNCO0lBOEQ3Rix5QkFBWSxZQUFvQixFQUFFLE9BQTBCO1FBQTVELFlBQ0UsaUJBQU8sU0FNUjtRQUpDLEtBQUksQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQ2hCLEtBQUksQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO1FBQ2pDLEtBQUksQ0FBQyxnQkFBZ0IsR0FBRyxFQUFFLENBQUM7UUFDM0IsS0FBSSxDQUFDLGFBQWEsR0FBRyxPQUFPLENBQUM7O0tBQzlCOzs7Ozs7Ozs7SUExRGEsb0NBQW9COzs7Ozs7OztjQUFDLFlBQW9CO1FBQ3JELEVBQUUsQ0FBQyxDQUFDLFlBQVksS0FBSyxJQUFJLElBQUksT0FBTyxZQUFZLEtBQUssV0FBVyxJQUFJLFlBQVksS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3hGLE1BQU0sQ0FBQyxlQUFlLENBQUMsbUJBQW1CLENBQUM7U0FDNUM7UUFFRCxFQUFFLENBQUMsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFL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QU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztTQUMvQjtRQUVELE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7SUFnQnhCLHdDQUFjOzs7Ozs7Ozs7Ozs7O2NBQUMsSUFBWTtRQUNoQyxxQkFBSSxJQUFJLEdBQU0sSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUV2QyxFQUFFLENBQUMsQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQztZQUNsQixNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztTQUMvQjtRQUVELE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDOzs7Ozs7OztJQVF4QiwrQkFBSzs7Ozs7OztRQUNWLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUM7OzBDQXhaMkIsbUJBQW1COzhDQUNmLHVCQUF1QjswQkFyQ2xGO0VBa0N5RSxzQkFBc0I7U0FBekUsZUFBZSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE9uSW5pdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gJ3J4anMvU3ViamVjdCc7XG5cbmltcG9ydCB7IElOYXZpZ2F0b3IgfSBmcm9tICcuL0lOYXZpZ2F0b3InO1xuaW1wb3J0IHsgQWJzdHJhY3ROYXZJdGVtIH0gZnJvbSAnLi9BYnN0cmFjdE5hdkl0ZW0nO1xuXG5pbXBvcnQgeyBPYnNlcnZhYmxlTWFuYWdlciB9IGZyb20gJy4uL3NlcnZpY2Uvc2VydmljZXMnO1xuXG5pbXBvcnQgeyBBYnN0cmFjdEkxOE5BcHBsaWNhYmxlIH0gZnJvbSAnLi4vc2hhcmUvQWJzdHJhY3RJMThOQXBwbGljYWJsZSc7XG5cbmNvbnN0IFRPUElDX05BTUVfUFJFRklYOiBzdHJpbmcgPSAnbmF2LW1lbnUuJztcblxuY29uc3QgTkFWSUdBVEVfU1VGRklYOiBzdHJpbmcgPSAnbmF2aWdhdGUnO1xuY29uc3QgTkFWSUdBVEVfQ01EX1NVRkZJWDogc3RyaW5nID0gJ2NtZC5uYXZpZ2F0ZSc7XG5cbmNvbnN0IE5BVklHQVRFX1RPUElDX05BTUU6IHN0cmluZyA9IFRPUElDX05BTUVfUFJFRklYICsgTkFWSUdBVEVfU1VGRklYO1xuY29uc3QgTkFWSUdBVEVfQ01EX1RPUElDX05BTUU6IHN0cmluZyA9IFRPUElDX05BTUVfUFJFRklYICsgTkFWSUdBVEVfQ01EX1NVRkZJWDtcblxuLyoqXG4gKiA8cCBzdHlsZT1cInRleHQtaW5kZW50OiAyZW07XCI+XG4gKiBBbiBhYnN0cmFjdCBiYXNlIGNsYXNzIGZvciA8Y29kZT5uYXYgbWVudTwvY29kZT4uIEEgPGNvZGU+bmF2IG1lbnU8L2NvZGU+IG1heSBjb25zaXN0IG9mIG9uZSBvciBtb3JlIDxjb2RlPm5hdiBpdGVtPC9jb2RlPnNcbiAqIChbW0Fic3RyYWN0TmF2SXRlbV1dKS4gVGhlIG9ubHkgZGlmZmVyZW5jZSBiZXR3ZWVuIHN0YWRhcmQgPGNvZGU+bWVudTwvY29kZT4gKFtbQWJzdHJhY3RNZW51XV0pIGFuZCA8Y29kZT5uYXYgbWVudTwvY29kZT4gaXMgdGhhdFxuICogdGhlIDxjb2RlPm5hdiBtZW51PC9jb2RlPiBoYXMgYSBtYWluIHB1cnBvc2Ugb24gPGNvZGU+bmF2aWdhdGlvbjwvY29kZT4uIEl0IGFsc28gYXV0b21hdGljYWxseSBwdWJsaXNoZXMgYW4gZXZlbnQgdG8gdGhlXG4gKiBbW09ic2VydmFibGVNYW5hZ2VyXV0gb24gdGhlIHRvcGljIFwiPGNvZGU+bmF2LW1lbnUuW3N1Yi10b3BpYy1uYW1lXS5uYXZpZ2F0ZTwvY29kZT5cIiB3aGVuIGEgbmF2aWdhdGlvbiBpcyBwZXJmb3JtZWQuIEZ1cnRoZXJtb3JlLFxuICogdG8gcmVtb3RlbHkgY29udHJvbCB0aGUgbmF2aWdhdGlvbiBvbiB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPiBpbnN0YW5jZSBhdCBydW50aW1lIHdpdGhvdXQgaGF2aW5nIGRpcmVjdCBvYmplY3QgcmVmZXJlbmNlLFxuICogeW91IGNhbiB1c2UgW1tPYnNlcnZhYmxlTWFuYWdlcl1dJ3MgdG9waWMgXCI8Y29kZT5uYXYtbWVudS5bc3ViLXRvcGljLW5hbWVdLmNtZC5uYXZpZ2F0ZTwvY29kZT5cIiB0byBwdWJsaXNoIGEgbmF2aWdhdGlvblxuICogPGNvZGU+Y29tbWFuZDwvY29kZT4gdG8gdGhpcyA8Y29kZT5uYXYgbWVudTwvY29kZT4uXG4gKiA8L3A+XG4gKlxuICogQGF1dGhvciBzaGlvcmluLCB0ZWU0Y3V0ZVxuICogQHNlZSBbW0Fic3RyYWN0TmF2SXRlbV1dXG4gKiBAc2VlIFtbSU5hdmlnYXRvcl1dXG4gKiBAc2VlIFtbT2JzZXJ2YWJsZU1hbmFnZXJdXVxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQWJzdHJhY3ROYXZNZW51PFQgZXh0ZW5kcyBBYnN0cmFjdE5hdkl0ZW0+IGV4dGVuZHMgQWJzdHJhY3RJMThOQXBwbGljYWJsZSBpbXBsZW1lbnRzIElOYXZpZ2F0b3I8VD4sIE9uSW5pdCB7XG5cbiAgcHVibGljIHN0YXRpYyByZWFkb25seSBOQVZJR0FURV9UT1BJQ19OQU1FOiBzdHJpbmcgPSBOQVZJR0FURV9UT1BJQ19OQU1FO1xuICBwdWJsaWMgc3RhdGljIHJlYWRvbmx5IE5BVklHQVRFX0NNRF9UT1BJQ19OQU1FOiBzdHJpbmcgPSBOQVZJR0FURV9DTURfVE9QSUNfTkFNRTtcblxuICAvKipcbiAgICogPHAgc3R5bGU9XCJ0ZXh0LWluZGVudDogMWVtO1wiPlxuICAgKiBHZXQgdGhlIG5hdmlnYXRpb24gZXZlbnQgdG9waWMgbmFtZSB3aXRoIHRoZSBnaXZlbiA8Y29kZT48Yj5zdWJUb3BpY05hbWU8L2I+PC9jb2RlPi4gVGhlIHJldHVybmluZyB0b3BpYyBuYW1lIHdpbGwgYmUgcHJlZml4ZWQgYnlcbiAgICogW1tOQVZJR0FURV9UT1BJQ19OQU1FXV0uXG4gICAqIDwvcD5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgZ2V0TmF2aWdhdGVUb3BpY05hbWUoc3ViVG9waWNOYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGlmIChzdWJUb3BpY05hbWUgPT09IG51bGwgfHwgdHlwZW9mIHN1YlRvcGljTmFtZSA9PT0gJ3VuZGVmaW5lZCcgfHwgc3ViVG9waWNOYW1lID09PSAnJykge1xuICAgICAgcmV0dXJuIEFic3RyYWN0TmF2TWVudS5OQVZJR0FURV9UT1BJQ19OQU1FO1xuICAgIH1cblxuICAgIGlmIChzdWJUb3BpY05hbWUuY2hhckF0KDApID09PSAnLicpIHtcbiAgICAgIHN1YlRvcGljTmFtZSA9IHN1YlRvcGljTmFtZS5zdWJzdHJpbmcoMSk7XG4gICAgfVxuXG4gICAgaWYgKHN1YlRvcGljTmFtZSA9PT0gJycpIHtcbiAgICAgIHJldHVybiBBYnN0cmFjdE5hdk1lbnUuTkFWSUdBVEVfVE9QSUNfTkFNRTtcbiAgICB9XG5cbiAgICBpZiAoc3ViVG9waWNOYW1lLmNoYXJBdChzdWJUb3BpY05hbWUubGVuZ3RoIC0gMSkgIT09ICcuJykge1xuICAgICAgc3ViVG9waWNOYW1lICs9ICcuJztcbiAgICB9XG5cbiAgICByZXR1cm4gVE9QSUNfTkFNRV9QUkVGSVggKyBzdWJUb3BpY05hbWUgKyBOQVZJR0FURV9TVUZGSVg7XG4gIH1cblxuICAvKipcbiAgICogPHAgc3R5bGU9XCJ0ZXh0LWluZGVudDogMWVtO1wiPlxuICAgKiBHZXQgdGhlIG5hdmlnYXRpb24gY21kIHRvcGljIG5hbWUgd2l0aCB0aGUgZ2l2ZW4gPGNvZGU+PGI+c3ViVG9waWNOYW1lPC9iPjwvY29kZT4uIFRoZSByZXR1cm5pbmcgdG9waWMgbmFtZSB3aWxsIGJlIHByZWZpeGVkIGJ5XG4gICAqIFtbTkFWSUdBVEVfQ01EX1RPUElDX05BTUVdXS5cbiAgICogPC9wPlxuICAgKi9cbiAgcHVibGljIHN0YXRpYyBnZXROYXZpZ2F0ZUNtZFRvcGljTmFtZShzdWJUb3BpY05hbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgaWYgKHN1YlRvcGljTmFtZSA9PT0gbnVsbCB8fCB0eXBlb2Ygc3ViVG9waWNOYW1lID09PSAndW5kZWZpbmVkJyB8fCBzdWJUb3BpY05hbWUgPT09ICcnKSB7XG4gICAgICByZXR1cm4gQWJzdHJhY3ROYXZNZW51Lk5BVklHQVRFX0NNRF9UT1BJQ19OQU1FO1xuICAgIH1cblxuICAgIGlmIChzdWJUb3BpY05hbWUuY2hhckF0KDApID09PSAnLicpIHtcbiAgICAgIHN1YlRvcGljTmFtZSA9IHN1YlRvcGljTmFtZS5zdWJzdHJpbmcoMSk7XG4gICAgfVxuXG4gICAgaWYgKHN1YlRvcGljTmFtZSA9PT0gJycpIHtcbiAgICAgIHJldHVybiBBYnN0cmFjdE5hdk1lbnUuTkFWSUdBVEVfQ01EX1RPUElDX05BTUU7XG4gICAgfVxuXG4gICAgaWYgKHN1YlRvcGljTmFtZS5jaGFyQXQoc3ViVG9waWNOYW1lLmxlbmd0aCAtIDEpICE9PSAnLicpIHtcbiAgICAgIHN1YlRvcGljTmFtZSArPSAnLic7XG4gICAgfVxuXG4gICAgcmV0dXJuIFRPUElDX05BTUVfUFJFRklYICsgc3ViVG9waWNOYW1lICsgTkFWSUdBVEVfQ01EX1NVRkZJWDtcbiAgfVxuXG4gIHByb3RlY3RlZCBpdGVtczogVFtdO1xuICBwcm90ZWN0ZWQgc3ViVG9waWNOYW1lOiBzdHJpbmc7XG4gIHByb3RlY3RlZCBuYXZpZ2F0ZVN1YmplY3RzOiBTdWJqZWN0PGFueT5bXTtcbiAgcHJvdGVjdGVkIG9ic2VydmFibGVNZ3I6IE9ic2VydmFibGVNYW5hZ2VyO1xuXG4gIGNvbnN0cnVjdG9yKHN1YlRvcGljTmFtZTogc3RyaW5nLCBvYnN2TWdyOiBPYnNlcnZhYmxlTWFuYWdlcikge1xuICAgIHN1cGVyKCk7XG5cbiAgICB0aGlzLml0ZW1zID0gW107XG4gICAgdGhpcy5zdWJUb3BpY05hbWUgPSBzdWJUb3BpY05hbWU7XG4gICAgdGhpcy5uYXZpZ2F0ZVN1YmplY3RzID0gW107XG4gICAgdGhpcy5vYnNlcnZhYmxlTWdyID0gb2Jzdk1ncjtcbiAgfVxuXG4gIHByaXZhdGUgaW5pdE5hdk1lbnVFdmVudFRvcGljcyhuYXZpZ2F0ZTogc3RyaW5nLCBuYXZpZ2F0ZUNtZDogc3RyaW5nKTogdm9pZCB7XG4gICAgLy8gQ3JlYXRlIGNoYW5nZSBldmVudCBvYnNlcnZhYmxlIHRvcGljc1xuICAgIGlmIChuYXZpZ2F0ZSAhPT0gbnVsbCAmJiB0eXBlb2YgbmF2aWdhdGUgPT09ICdzdHJpbmcnKSB7XG4gICAgICB0aGlzLm5hdmlnYXRlU3ViamVjdHMucHVzaCh0aGlzLm9ic2VydmFibGVNZ3IuY3JlYXRlU3ViamVjdChuYXZpZ2F0ZSkpO1xuICAgIH1cblxuICAgIC8vIFN1YnNjcmliZSBvbiBDTUQgdG9waWNzXG4gICAgaWYgKG5hdmlnYXRlQ21kICE9PSBudWxsICYmIHR5cGVvZiBuYXZpZ2F0ZUNtZCA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHRoaXMub2JzZXJ2YWJsZU1nci5zdWJzY3JpYmUobmF2aWdhdGVDbWQsIChpdGVtOiBhbnkpID0+IHtcbiAgICAgICAgaWYgKGl0ZW0gaW5zdGFuY2VvZiBBYnN0cmFjdE5hdkl0ZW0pIHtcbiAgICAgICAgICB0aGlzLm5hdmlnYXRlVG8oPFQ+aXRlbSk7IC8vIG9wZW4gaGVyZVxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgLy8gSW5pdCBzdXBlciBjbGFzcyBldmVudCB0b3BpY3NcbiAgICB0aGlzLmluaXROYXZNZW51RXZlbnRUb3BpY3MoXG4gICAgICBBYnN0cmFjdE5hdk1lbnUuTkFWSUdBVEVfVE9QSUNfTkFNRSxcbiAgICAgIEFic3RyYWN0TmF2TWVudS5OQVZJR0FURV9DTURfVE9QSUNfTkFNRVxuICAgICk7XG5cbiAgICBpZiAodGhpcy5zdWJUb3BpY05hbWUgIT09IG51bGwgJiYgdGhpcy5zdWJUb3BpY05hbWUgIT09ICcnIHx8IHR5cGVvZiB0aGlzLnN1YlRvcGljTmFtZSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIC8vIEluaXQgc3ViIHRvcGljIGV2ZW50IHRvcGljc1xuICAgICAgdGhpcy5pbml0TmF2TWVudUV2ZW50VG9waWNzKFxuICAgICAgICBBYnN0cmFjdE5hdk1lbnUuZ2V0TmF2aWdhdGVUb3BpY05hbWUodGhpcy5zdWJUb3BpY05hbWUpLFxuICAgICAgICBBYnN0cmFjdE5hdk1lbnUuZ2V0TmF2aWdhdGVDbWRUb3BpY05hbWUodGhpcy5zdWJUb3BpY05hbWUpXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiA8cCBzdHlsZT1cInRleHQtaW5kZW50OiAxZW07XCI+XG4gICAqIEdldCA8Y29kZT5uYXYgaXRlbTwvY29kZT5zIChbW0Fic3RyYWN0TmF2SXRlbV1dKSBvZiB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPi5cbiAgICogPC9wPlxuICAgKi9cbiAgcHVibGljIGdldEl0ZW1zKCk6IFRbXSB7XG4gICAgcmV0dXJuIHRoaXMuaXRlbXM7XG4gIH1cblxuICAvKipcbiAgICogPHAgc3R5bGU9XCJ0ZXh0LWluZGVudDogMWVtO1wiPlxuICAgKiBHZXQgW1tPYnNlcnZhYmxlTWFuYWdlcl1dJ3Mgc3ViIHRvcGljIG5hbWUgYXNzb2NpYXRlZCB3aXRoIHRoaXMgPGNvZGU+bmF2IG1lbnU8L2NvZGU+LlxuICAgKiA8L3A+XG4gICAqL1xuICBwdWJsaWMgZ2V0U3ViVG9waWNOYW1lKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuc3ViVG9waWNOYW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIDxwIHN0eWxlPVwidGV4dC1pbmRlbnQ6IDFlbTtcIj5cbiAgICogR2V0IDxjb2RlPm5hdiBpdGVtPC9jb2RlPnMgY291bnQgaW4gdGhpcyA8Y29kZT5uYXYgbWVudTwvY29kZT4uXG4gICAqIDwvcD5cbiAgICovXG4gIHB1YmxpYyBnZXRJdGVtQ291bnQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5pdGVtcy5sZW5ndGg7XG4gIH1cblxuICAvKipcbiAgICogPHAgc3R5bGU9XCJ0ZXh0LWluZGVudDogMWVtO1wiPlxuICAgKiBHZXQgYW4gYWN0aXZlIDxjb2RlPm5hdiBpdGVtPC9jb2RlPiBpbmRleCBvZiB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPi5cbiAgICogPC9wPlxuICAgKlxuICAgKiBAcmV0dXJuIFJldHVybnMgPGNvZGU+LTE8L2NvZGU+IGlmIHRoZXJlIGlzIG5vIGN1cnJlbnQgYWN0aXZlIGl0ZW0uIE90aGVyd2lzZSwgcmV0dXJuIHRoZSBhcnJheSBpbmRleCBvZiBjdXJyZW50XG4gICAqIGFjdGl2ZSBpdGVtLlxuICAgKi9cbiAgcHVibGljIGdldEFjdGl2ZUluZGV4KCk6IG51bWJlciB7XG4gICAgZm9yIChsZXQgaTogbnVtYmVyID0gMDsgaSA8IHRoaXMuaXRlbXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmICh0aGlzLml0ZW1zW2ldLmlzQWN0aXZlKCkpIHtcbiAgICAgICAgcmV0dXJuIGk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIC0xO1xuICB9XG5cbiAgLyoqXG4gICAqIDxwIHN0eWxlPVwidGV4dC1pbmRlbnQ6IDFlbTtcIj5cbiAgICogR2V0IGFuIGFjdGl2ZSA8Y29kZT5uYXYgaXRlbTwvY29kZT4gb2YgdGhpcyA8Y29kZT5uYXYgbWVudTwvY29kZT4uXG4gICAqIDwvcD5cbiAgICpcbiAgICogQHJldHVybiBSZXR1cm5zIDxjb2RlPm51bGw8L2NvZGU+IGlmIHRoZXJlIGlzIG5vIGN1cnJlbnQgYWN0aXZlIGl0ZW0uXG4gICAqL1xuICBwdWJsaWMgZ2V0QWN0aXZlSXRlbSgpOiBUIHtcbiAgICBsZXQgaWR4OiBudW1iZXIgPSB0aGlzLmdldEFjdGl2ZUluZGV4KCk7XG5cbiAgICBpZiAoaWR4IDwgMCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuaXRlbXNbaWR4XTtcbiAgfVxuXG4gIC8qKlxuICAgKiA8cCBzdHlsZT1cInRleHQtaW5kZW50OiAxZW07XCI+XG4gICAqIFNldCA8Y29kZT5uYXYgaXRlbTwvY29kZT5zIG9mIHRoaXMgPGNvZGU+bmF2IG1lbnU8L2NvZGU+LlxuICAgKiA8L3A+XG4gICAqL1xuICBwdWJsaWMgc2V0SXRlbXMoaXRlbXM6IFRbXSk6IHZvaWQge1xuICAgIHRoaXMuaXRlbXMgPSBpdGVtcztcbiAgfVxuXG4gIC8qKlxuICAgKiA8cCBzdHlsZT1cInRleHQtaW5kZW50OiAxZW07XCI+XG4gICAqIEFkZCBhIG5ldyA8Y29kZT5uYXYgaXRlbTwvY29kZT4gaW50byB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPi4gSWYgdGhlcmUgYWxyZWFkeSBpcyB0aGUgZ2l2ZW4gPGNvZGU+PGI+aXRlbTwvYj48L2NvZGU+IGluIHRoaXNcbiAgICogPGNvZGU+bmF2IG1lbnU8L2NvZGU+LCB0aGlzIG1ldGhvZCB3aWxsIGRvIG5vdGhpbmcgYW5kIHJldHVybiA8Y29kZT5mYWxzZTwvY29kZT4uXG4gICAqIDwvcD5cbiAgICpcbiAgICogQHBhcmFtIGl0ZW0gQSBuZXcgPGNvZGU+bmF2IGl0ZW08L2NvZGU+IHRvIGJlIGFkZGVkIGludG8gdGhpcyA8Y29kZT5uYXYgbWVudTwvY29kZT4uXG4gICAqXG4gICAqIEByZXR1cm4gUmV0dXJucyA8Y29kZT50cnVlPC9jb2RlPiBpZiB0aGUgZ2l2ZW4gPGNvZGU+PGI+aXRlbTwvYj48L2NvZGU+IGlzIGFkZGVkIGludG8gdGhpcyA8Y29kZT5uYXYgbWVudTwvY29kZT4uIE90aGVyd2lzZSwgcmV0dXJucyA8Y29kZT5mYWxzZTwvY29kZT4uXG4gICAqL1xuICBwdWJsaWMgYWRkSXRlbShpdGVtOiBUKTogYm9vbGVhbiB7XG4gICAgaWYgKHRoaXMuY29udGFpbnNJdGVtKGl0ZW0pKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgdGhpcy5pdGVtcy5wdXNoKGl0ZW0pO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogPHAgc3R5bGU9XCJ0ZXh0LWluZGVudDogMWVtO1wiPlxuICAgKiBSZW1vdmUgdGhlIGdpdmVuIDxjb2RlPjxiPml0ZW08L2I+PC9jb2RlPiBmcm9tIHRoaXMgPGNvZGU+bmF2IG1lbnU8L2NvZGU+LiBJZiB0aGUgZ2l2ZW4gPGNvZGU+PGI+aXRlbTwvYj48L2NvZGU+IGlzIG5vdCBpbiB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPiwgdGhpcyBtZXRob2RcbiAgICogd2lsbCBkbyBub3RoaW5nIGFuZCByZXR1cm4gPGNvZGU+ZmFsc2U8L2NvZGU+LiBJZiB0aGUgPGNvZGU+PGI+aXRlbTwvYj48L2NvZGU+IGJlaW5nIHJlbW92ZWQgaXMgYSBjdXJyZW50IGFjdGl2ZSBpdGVtLCB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPiB3aWxsIGJlIG5hdmlnYXRlZFxuICAgKiB0byB0aGUgZmlyc3QgPGNvZGU+bmF2IGl0ZW08L2NvZGU+LlxuICAgKiA8L3A+XG4gICAqXG4gICAqIEBwYXJhbSBpdGVtIEEgPGNvZGU+bmF2IGl0ZW08L2NvZGU+IHRvIGJlIHJlbW92ZWQgZnJvbSB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPi5cbiAgICpcbiAgICogQHJldHVybiBSZXR1cm5zIDxjb2RlPnRydWU8L2NvZGU+IGlmIHRoZSBnaXZlbiA8Y29kZT48Yj5pdGVtPC9iPjwvY29kZT4gaXMgZm91bmQgYW5kIHJlbW92ZWQgZnJvbSB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPi4gT3RoZXJ3aXNlLCByZXR1cm5zIDxjb2RlPmZhbHNlPC9jb2RlPi5cbiAgICovXG4gIHB1YmxpYyByZW1vdmVJdGVtKGl0ZW06IFQpOiBib29sZWFuIHtcbiAgICBsZXQgaWR4OiBudW1iZXIgPSB0aGlzLml0ZW1zLmluZGV4T2YoaXRlbSk7XG5cbiAgICBpZiAoaWR4IDwgMCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHRoaXMuaXRlbXMuc3BsaWNlKGlkeCwgMSk7XG5cbiAgICBpZiAoaXRlbS5hY3RpdmUpIHtcbiAgICAgIC8vIFNldCBwYWdlIGFjdGl2ZSB0byBmYWxzZS5cbiAgICAgIGl0ZW0uYWN0aXZlID0gZmFsc2U7XG5cbiAgICAgIC8vIElmIGl0IGlzIGN1cnJlbnRseSBhY3RpdmUgcGFnZSwgd2UndmUgdG8gbmF2aWdhdGUgdG8gYW5vdGhlciBvbmUuXG4gICAgICB0aGlzLm5hdmlnYXRlVG9JbmRleCgwKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiA8cCBzdHlsZT1cInRleHQtaW5kZW50OiAxZW07XCI+XG4gICAqIFRvIGNoZWNrIHRoYXQgdGhlIGdpdmVuIDxjb2RlPjxiPml0ZW08L2I+PC9jb2RlPiBpcyBpbiB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPiBvciBub3QuXG4gICAqIDwvcD5cbiAgICpcbiAgICogQHBhcmFtIGl0ZW0gQSA8Y29kZT5uYXYgaXRlbTwvY29kZT4gdG8gY2hlY2sgdGhhdCBpdCBpcyBpbiB0aGlzIDxjb2RlPm5hdiBtZW51PC9jb2RlPiBvciBub3QuXG4gICAqXG4gICAqIEByZXR1cm4gUmV0dXJucyA8Y29kZT50cnVlPC9jb2RlPiBpZiB0aGUgZ2l2ZW4gPGNvZGU+PGI+aXRlbTwvYj48L2NvZGU+IGlzIGluIHRoaXMgPGNvZGU+bmF2IG1lbnU8L2NvZGU+LlxuICAgKi9cbiAgcHVibGljIGNvbnRhaW5zSXRlbShpdGVtOiBUKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuaXRlbXMuaW5kZXhPZihpdGVtKSA+PSAwO1xuICB9XG5cbiAgLyoqXG4gICAqIDxwIHN0eWxlPVwidGV4dC1pbmRlbnQ6IDFlbTtcIj5cbiAgICogR2V0IGEgPGNvZGU+bmF2IGl0ZW08L2NvZGU+IChbW0Fic3RyYWN0TmF2SXRlbV1dKSBmcm9tIHRoZSBnaXZlbiBpbmRleCAoPGNvZGU+PGI+aWR4PC9iPjwvY29kZT4pLlxuICAgKiA8L3A+XG4gICAqXG4gICAqIEBwYXJhbSBpZHggVGhlIGluZGV4IG9mIDxjb2RlPm5hdiBpdGVtPC9jb2RlPiB0byBnZXQuXG4gICAqXG4gICAqIEByZXR1cm4gUmV0dXJucyBhbiBpbnN0YW5jZSBvZiA8Y29kZT5uYXYgaXRlbTwvY29kZT4gKFtbQWJzdHJhY3ROYXZJdGVtXV0pIGF0IHRoZSBzcGVjaWZpZWQgaW5kZXggKDxjb2RlPjxiPmlkeDwvYj48L2NvZGU+KS5cbiAgICogUmV0dXJucyA8Y29kZT5udWxsPC9jb2RlPiBpZiB0aGUgZ2l2ZW4gaW5kZXggaXMgb3V0IG9mIHJhbmdlLlxuICAgKi9cbiAgcHVibGljIGdldEl0ZW1CeUluZGV4KGlkeDogbnVtYmVyKTogVCB7XG4gICAgaWYgKGlkeCA9PT0gbnVsbCB8fCB0eXBlb2YgaWR4ID09PSAndW5kZWZpbmVkJyB8fCBpZHggPCAwIHx8IGlkeCA+PSB0aGlzLml0ZW1zLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuaXRlbXNbaWR4XTtcbiAgfVxuXG4gIC8qKlxuICAgKiA8cCBzdHlsZT1cInRleHQtaW5kZW50OiAxZW07XCI+XG4gICAqIEdldCBhIDxjb2RlPm5hdiBpdGVtPC9jb2RlPiAoW1tBYnN0cmFjdE5hdkl0ZW1dXSkgYnkgdGhlIGdpdmVuIDxjb2RlPjxiPm5hbWU8L2I+PC9jb2RlPi5cbiAgICogPC9wPlxuICAgKlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiBhIDxjb2RlPm5hdiBpdGVtPC9jb2RlPiB0byBnZXQuXG4gICAqXG4gICAqIEByZXR1cm4gUmV0dXJucyBhbiBpbnN0YW5jZSBvZiA8Y29kZT5uYXYgaXRlbTwvY29kZT4gKFtbQWJzdHJhY3ROYXZJdGVtXV0pIHdpdGggdGhlIGdpdmVuIDxjb2RlPjxiPm5hbWU8L2I+PC9jb2RlPi5cbiAgICogUmV0dXJucyA8Y29kZT5udWxsPC9jb2RlPiBpZiBub3QgZm91bmQuXG4gICAqL1xuICBwdWJsaWMgZ2V0SXRlbUJ5TmFtZShuYW1lOiBzdHJpbmcpOiBUIHtcbiAgICBsZXQgaWR4OiBudW1iZXIgPSB0aGlzLmdldEl0ZW1JbmRleEJ5TmFtZShuYW1lKTtcblxuICAgIGlmIChpZHggPCAwKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5pdGVtc1tpZHhdO1xuICB9XG5cbiAgLyoqXG4gICAqIDxwIHN0eWxlPVwidGV4dC1pbmRlbnQ6IDFlbTtcIj5cbiAgICogR2V0IHRoZSBpbmRleCBvZiB0aGUgZ2l2ZW4gPGNvZGU+bmF2IGl0ZW08L2NvZGU+ICg8Y29kZT48Yj5pdGVtPC9iPjwvY29kZT4pLlxuICAgKiA8L3A+XG4gICAqXG4gICAqIEBwYXJhbSBpdGVtIFRoZSA8Y29kZT5uYXYgaXRlbTwvY29kZT4gdG8gZmluZCBpbmRleC5cbiAgICpcbiAgICogQHJldHVybiBSZXR1cm5zIGFuIGluZGV4IG9mIHRoZSBzcGVjaWZpZWQgPGNvZGU+PGI+aXRlbTwvYj48L2NvZGU+LiBSZXR1cm5zIDxjb2RlPi0xPC9jb2RlPiBpZiBub3QgZm91bmQuXG4gICAqL1xuICBwdWJsaWMgZ2V0SXRlbUluZGV4KGl0ZW06IFQpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLml0ZW1zLmluZGV4T2YoaXRlbSk7XG4gIH1cblxuICAvKipcbiAgICogPHAgc3R5bGU9XCJ0ZXh0LWluZGVudDogMWVtO1wiPlxuICAgKiBHZXQgdGhlIGluZGV4IG9mIHRoZSBnaXZlbiBpdGVtIDxjb2RlPjxiPm5hbWU8L2I+PC9jb2RlPi5cbiAgICogPC9wPlxuICAgKlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgaXRlbSBuYW1lIHRvIGZpbmQgaW5kZXguXG4gICAqXG4gICAqIEByZXR1cm4gUmV0dXJucyBhbiBpbmRleCBvZiA8Y29kZT5uYXYgaXRlbTwvY29kZT4gaGF2aW5nIHRoZSBzcGVjaWZpZWQgPGNvZGU+PGI+bmFtZTwvYj48L2NvZGU+LiBSZXR1cm5zIDxjb2RlPi0xPC9jb2RlPiBpZiBub3QgZm91bmQuXG4gICAqL1xuICBwdWJsaWMgZ2V0SXRlbUluZGV4QnlOYW1lKG5hbWU6IHN0cmluZyk6IG51bWJlciB7XG4gICAgaWYgKG5hbWUgPT09IG51bGwgfHwgdHlwZW9mIG5hbWUgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuXG4gICAgZm9yIChsZXQga