toolshed-8.x-1.x-dev/js/widgets/Autocomplete.js

js/widgets/Autocomplete.js
"use strict";

(({
  t,
  behaviors,
  debounce,
  Toolshed: ts
}) => {
  /**
   * Live region which is used to announce status of widget changes.
   */
  class LiveRegion extends ts.Element {
    /**
     * Initialize a new LiveRegion element.
     *
     * @param {Object} options
     *   Options to apply to the Live Regions.
     * @param {HTMLElement|ToolshedElement} attachTo
     *   The element to append this live region to.
     */
    constructor(options = {}, attachTo) {
      if (ts.isString(options.class)) {
        options.class = [options.class];
      }
      options.class = (options.class || []).concat(['visually-hidden', 'sr-only']);

      // Create a wrapper element for the two live update areas.
      super('div', options, attachTo || document.body);

      // Setup regions for swapping between, for live updating.
      const regionOpts = {
        role: 'status',
        'aria-live': 'polite',
        'aria-atomic': 'true'
      };
      this.active = 1;
      this.msg = '';
      this.regions = [new ts.Element('div', regionOpts, this), new ts.Element('div', regionOpts, this)];

      // Only announce the changes after a short delay to prevent the announcer
      // from talking over itself.
      this.updateMsg = debounce(() => {
        this.regions[this.active].textContent = '';
        this.active ^= 1; // eslint-disable-line no-bitwise
        this.regions[this.active].textContent = this.msg;
      }, 500);
    }

    /**
     * The current text message to announce in the live region.
     *
     * @return {string}
     *   Return the current message to be announced.
     */
    get message() {
      return this.msg;
    }

    /**
     * Set a new message for the live region. Note that there is a small delay
     * before the message is announced to avoid collisions of messages which
     * are too close together.
     *
     * @param {string} message
     *   Message to set for the live region.
     */
    set message(message) {
      this.msg = message;
      this.updateMsg();
    }
  }

  /**
   * Object which represents a single autocomplete select option. This object
   * manages its own resources and the display text as well as the underlying
   * value to pass back to the original input element.
   */
  class SuggestItem extends ts.Element {
    /**
     * Create a new SuggestItem representing values and a selectable element.
     *
     * @param {SuggestList} list
     *   The suggestion list that this item is a member of.
     * @param {string} id
     *   The option identifier to use for the element ID.
     * @param {Toolshed.Autocomplete} ac
     *   The callback when the item is clicked on.
     */
    constructor(list, id, ac) {
      super('a', {
        tabindex: 0,
        href: '#'
      });
      this.wrap = new ts.Element('li', {
        id,
        role: 'option',
        class: 'autocomplete__opt'
      });
      this.wrap.appendChild(this);
      this.parent = list;
      this.pos = -1;
      this.uri = null;
      this.on('click', e => {
        if (!this.uri) {
          e.preventDefault();
          e.stopPropagation();
          ac.selectItem(this);
        }
      }, true);
    }

    /**
     * The getter for the SuggestItem element ID.
     */
    get id() {
      return this.wrap.id;
    }

    /**
     * The setter for SuggestItem element ID.
     *
     * @param {string} value
     *   The ID to used for the SuggestItem HTMLElement and descendant ID.
     */
    set id(value) {
      this.wrap.id = value;
    }

    /**
     * Get the stored URI value of this suggest item.
     */
    get url() {
      return this.uri;
    }

    /**
     * Set the URL of the suggest item, if the item should work like a link,
     * rather than use the autocomplete value.
     *
     * @param {string} value
     *   A URL the Suggest item should send select when the item is clicked.
     */
    set url(value) {
      if (value && value !== '#') {
        this.uri = value;
        this.el.href = value;
      } else {
        this.uri = null;
        this.el.href = '#';
      }
    }

    /**
     * Update this item to show it has focus.
     */
    focus() {
      this.wrap.addClass('autocomplete__opt--active');
    }

    /**
     * Update this item to show that it doesn't have focus.
     */
    blur() {
      this.wrap.removeClass('autocomplete__opt--active');
    }

    /**
     * Clean up listeners and other allocated resources.
     *
     * @param {bool} detach
     *   Should the suggest item be removed from the DOM?
     */
    destroy(detach) {
      this.parent = null;
      super.destroy(detach);
      this.wrap.destroy(detach);
    }
  }

  /**
   * A list of SuggestItems, and manage the interactions with the autocomplete
   * suggestions traversal, building and clearing.
   */
  class SuggestList extends ts.Element {
    /**
     * Create a new list of SuggestItem objects.
     *
     * @param {SuggestList|null} list
     *   An object that contains a list of SuggestItem instances.
     * @param {string} wrapTag
     *   The HTMLElement tag to use to wrap this SuggestList.
     * @param {ToolshedAutocomplete} ac
     *   The autocomplete instance that owns this SuggestList.
     */
    constructor(list, wrapTag, ac) {
      super('ul', {
        class: 'autocomplete__options'
      });
      this.items = [];
      this.parent = list;
      this.ac = ac;
      this.pos = 0;
      this.wrap = new ts.Element(wrapTag);
      this.wrap.appendChild(this);
    }

    /**
     * Get the current number of suggests in the list.
     */
    get length() {
      let ct = 0;
      for (let i = 0; i < this.items.length; ++i) {
        ct += this.items[i] instanceof SuggestList ? this.items[i].length : 1;
      }
      return ct;
    }

    /**
     * Is this SuggestList empty of selectable suggestion items?
     *
     * @return {Boolean}
     *   Returns true if there are no selectable items currently available
     *   as suggestions for the autocomplete query.
     */
    isEmpty() {
      return !this.items.length;
    }

    /**
     * Set the label to display over a set items, if there is a caption.
     *
     * @param {string} text
     *   The label text to use as a caption over the SuggestList items.
     * @param {string} itemId
     *   The ID attribute for the wrapper element.
     */
    setLabel(text, itemId) {
      if (!this.label) {
        this.label = new ts.Element('span', {
          class: 'autocomplete__group-label'
        });
        this.wrap.prependChild(this.label);
      }
      if (itemId) {
        this.label.setAttrs({
          id: `${itemId}-label`
        });
        this.setAttrs({
          ariaLabelledby: `${itemId}-label`
        });
      }
      this.label.textContent = text && text.length ? text : '';
    }

    /**
     * Add a new SuggestItem to the SuggestList.
     *
     * @param {SuggestItem} item
     *   Add a SuggestItem to the end of the SuggestList.
     */
    addItem(item) {
      this.items.push(item);
      this.appendChild(item.wrap);
    }

    /**
     * Get the first item in the list.
     *
     * @return {SuggestItem|null}
     *   The first selectable SuggestItem or NULL if not items.
     */
    getFirst() {
      if (this.items.length) {
        return this.items[0] instanceof SuggestList ? this.items[0].getFirst() : this.items[0];
      }
      return null;
    }

    /**
     * Get the list item in the list.
     *
     * @return {SuggestItem|null}
     *   The last selectable SuggestItem in the list or NULL. This includes
     *   the last item in the nested set of items.
     */
    getLast() {
      if (this.items.length) {
        const idx = this.items.length - 1;
        return this.items[idx] instanceof SuggestList ? this.items[idx].getLast() : this.items[idx];
      }
      return null;
    }

    /**
     * Construct the list of selectable SuggestItems from the passed in data.
     *
     * @param {array|object} data
     *   The data representing the autocomplete suggestions / values available.
     * @param {string} baseId
     *   A pool of available SuggestItem to reuse.
     */
    buildItems(data, baseId) {
      let pos = 0;
      data.forEach(row => {
        let item;
        const itemId = `${baseId}-${pos}`;
        if (Array.isArray(row.list)) {
          if (!row.list.length) return;
          item = new SuggestList(this, 'li', this.ac);
          item.setAttrs({
            role: 'group'
          });
          item.setLabel(row.text, itemId);
          item.buildItems(row.list, itemId);
        } else if (row.value) {
          item = new SuggestItem(this, itemId, this.ac);
          item.text = row.text || row.value;
          item.value = row.value;
          if (row.url) item.url = row.url;
          this.ac.itemDisplay(item, row);
        }
        this.addItem(item);
        item.pos = pos++;
      });
    }

    /**
     * Remove suggestion items from the suggestioned items display.
     */
    clear() {
      this.items.forEach(item => item.destroy(true));
      this.items = [];
    }

    /**
     * Cleans up this autocomplete suggestions list, and frees resources and
     * event listeners.
     */
    destroy() {
      if (this.label) {
        this.label.destroy(true);
      }
      this.clear();
      super.destroy(true);
      this.wrap.destroy(true);
    }
  }

  /**
   * Class which encapsulates the behaviors of and autocomplete widget of a
   * textfield. It allows subclasses to override their fetchSuggestions() and
   * init() methods to allow for different methods of loading autocomplete
   * items to display.
   */
  ts.Autocomplete = class ToolshedAutocomplete {
    constructor(input, config = {}) {
      let ac;
      this.input = input;
      this.pending = false;
      this.config = {
        delay: 375,
        minLength: 3,
        requireSelect: true,
        ...config
      };

      // Reassociate a label element to point to the new autocomplete input.
      const inputLabel = this.input.id ? document.querySelector(`label[for='${this.input.id}']`) : null;

      // Create the main suggestion list wrapper.
      const list = new SuggestList(null, 'div', this);
      list.setAttrs({
        id: `${this.input.id}-listbox`,
        role: 'listbox'
      });
      list.on('blur', this.onBlur);
      this.list = list;
      if (inputLabel) {
        if (!inputLabel.id) inputLabel.id = `${this.input.id}-label`;
        list.setAttr('aria-labelledby', inputLabel.id);
      }

      // Create auto-suggestion pane.
      const wrapper = new ts.Element('div', {
        class: 'autocomplete__options-pane',
        style: {
          display: 'none'
        }
      });
      wrapper.on('mousedown', this.onMouseDown.bind(this));
      wrapper.appendChild(list.wrap);
      wrapper.attachTo(this.input, 'after');
      this.suggestWrap = wrapper;

      // Create a container for displaying empty results message.
      this.emptyMsg = new ts.Element('div', {
        class: 'autocomplete__empty-msg',
        style: {
          display: 'none'
        }
      }, wrapper);

      // Create a live region for announcing result updates.
      this.liveRegion = new LiveRegion();

      // Determine if the autocomplete value is separate from the displayed
      // text. When this value is split, we have a cloned AC textfield.
      if (this.config.separateValue) {
        ac = new ts.FormElement(this.input.cloneNode(), {
          placeholder: this.input.placeholder,
          value: this.input.dataset.text || this.formatDisplayValue(this.input.value) || '',
          'aria-autocomplete': 'list'
        });
        ac.removeClass('toolshed-autocomplete');
        ac.removeAttrs(['name', 'data-autocomplete']);
        this.input.style.display = 'none';
        ac.attachTo(this.input, 'after');

        // Remove these Drupal properties as this is just a stand-in object
        // and we don't want to submit or to catch any Drupal behaviors.
        delete ac.dataset.drupalSelector;
        delete ac.dataset.autocompletePath;
        delete ac.dataset.text;

        // Reassociate a label element to point to the new autocomplete input.
        if (inputLabel) {
          ac.id = `${this.input.id}-autocomplete`;
          inputLabel.htmlFor = ac.id;
        }
      } else {
        ac = new ts.FormElement(this.input);
        ac.setAttr('aria-autocomplete', 'both');
      }
      this.ac = ac;
      ac.setAttrs({
        class: 'form-autocomplete',
        autocomplete: 'off',
        role: 'combobox',
        'aria-owns': list.id,
        'aria-haspopup': 'listbox',
        'aria-expanded': 'false'
      });

      // Bind key change events.
      ac.on('keydown', this.onTextKeydown.bind(this));
      ac.on('input', this.onTextChange.bind(this));
      ac.on('focus', this.onFocus.bind(this));
      ac.on('blur', this.onBlur.bind(this));
      if (this.config.delay > 0) {
        this.fetchSuggestions = debounce(this.fetchSuggestions, this.config.delay);
      }
      if (this.config.params && this.input.form) {
        const {
          form
        } = this.input;
        this.onParamChange = this.onParamChange.bind(this);
        Object.values(this.config.params).forEach(elemId => {
          const el = form.querySelector(`#${elemId}`);
          if (el) el.addEventListener('change', this.onParamChange);
        });
      }
    }

    /**
     * Refresh the autocomplete suggests to display.
     *
     * @param {Array|Object} [data={}]
     *   Values to use as the autocomplete suggestions. The property keys are
     *   the value, and the property values are the display autocomplete value.
     */
    createSuggestions(data = {}) {
      this.activeItem = null;
      if (!this.list.isEmpty()) {
        this.list.clear();
      }
      if (data.list.length) {
        if (this.emptyMsg.style.display !== 'none') {
          this.emptyMsg.style.display = 'none';
        }
        this.list.buildItems(data.list, `${this.input.id}-opt`);
      } else {
        this.emptyMsg.textContent = data.empty ? data.empty : 'No results';
        this.emptyMsg.style.display = '';
      }
    }

    /**
     * Format the input value to the display text. By default the text that appears before ":" is
     * hidden and considered an internal value switch. Subclasses of this autocomplete can
     * override this and define their own display text value formatting.
     */
    formatDisplayValue(value) {
      return value ? value.replace(/^[^:]*?\s*:\s*/, '') : '';
    }

    /**
     * Clear the current input.
     */
    clearInput() {
      this.ac.value = '';
      this.input.value = '';
      this.clearSuggestions();
    }

    /**
     * Remove existing autocomplete suggestions.
     */
    clearSuggestions() {
      this.ac.removeAttrs('aria-activedescendant');
      this.activeItem = null;
      this.list.clear();
    }

    /**
     * Is the suggestion window open?
     *
     * @return {Boolean}
     *   TRUE if the suggestions window is open, otherwise FALSE.
     */
    isSuggestionsVisible() {
      return this.suggestWrap.style.display !== 'none';
    }

    /**
     * Position and expose the suggestions window if it is not already open.
     */
    displaySuggestions() {
      if (!this.isSuggestionsVisible()) {
        this.suggestWrap.setStyles({
          display: '',
          width: `${this.ac.el.clientWidth}px`,
          top: `${this.ac.el.offsetTop + this.ac.el.offsetHeight}px`,
          left: `${this.ac.el.offsetLeft}px`
        });
        this.ac.setAttrs({
          'aria-expanded': 'true'
        });
      }
      const ct = this.list.length;
      this.liveRegion.message = ct > 0 ? t('@count results available, use up and down arrow keys to navigate.', {
        '@count': ct
      }) : t('No search results.');
    }

    /**
     * Hide the suggestions window if it is open and reset the active item.
     */
    hideSuggestions() {
      this.activeItem = null;
      this.suggestWrap.style.display = 'none';
      this.ac.setAttrs({
        'aria-expanded': 'false'
      });
    }

    /**
     * Create the content to display in the autocomplete.
     *
     * Handles either building a simple text display or building the HTML to
     * a complex display, with a text label.
     *
     * @param {SuggestItem} item
     *   The suggestion item to change the inner content of.
     * @param {Object} data
     *   The raw data from the autocomplete response.
     */
    itemDisplay(item, data) {
      // eslint-disable-line class-methods-use-this
      if (data.html) {
        item.innerHTML = data.html;
        item.setAttrs({
          'aria-label': data.text || data.value
        });
      } else {
        item.textContent = item.text || item.value;
      }
    }

    /**
     * Set a SuggestItem as currently active based on the index. This method
     * ensures that any currently active items are blurred (unfocused) and
     * if the requested index is out of range, to select no items.
     *
     * @param {SuggestItem} item
     *   The item to set as the current active item.
     */
    setActiveItem(item) {
      if (this.activeItem) this.activeItem.blur();
      if (item) {
        item.focus();
        this.activeItem = item;
        if (item.id) {
          this.ac.setAttrs({
            'aria-activedescendant': item.id
          });
        }
      } else {
        this.activeItem = null;
        this.ac.removeAttrs('aria-activedescendant');
      }
    }

    /**
     * Transfer the values from the passed in item to the form elements.
     *
     * @param {SuggestItem} item
     *   Item to apply to as the values of the autocomplete widget.
     */
    selectItem(item) {
      if (item.url) {
        window.location = item.url;
      } else {
        if (this.ac.el !== this.input) {
          this.ac.value = item.text || item.value;
        }
        this.input.value = item.value;
      }
      this.hideSuggestions();
    }

    /**
     * Make the necessary requests and calls to get available text values.
     *
     * @param {string} text
     *   The text to try to match using the autocomplete service to
     *   generate suggestions with.
     * @param {bool} display
     *   Should the suggestions list be displayed after fetching suggestions.
     */
    fetchSuggestions(text, display = true) {
      if (!this.requester) {
        this.requester = ts.createRequester(this.config.uri);
      }
      if (this.pending && !this.pending.promise.isResolved) {
        // Abort any previously pending requests, so this late response
        // won't overwrite our desired values if it returns out of order.
        this.pending.xhr.abort();
      }
      if (!text || text.length < this.config.minLength) {
        if (this.isSuggestionsVisible()) this.hideSuggestions();
        return;
      }

      // Turn on the loading spinner.
      this.ac.addClass('ui-autocomplete-loading');

      // Apply additional request parameters if there are linked inputs.
      const requestParams = {};
      if (this.config.params) {
        Object.entries(this.config.params).forEach(([key, elemId]) => {
          const input = document.getElementById(elemId);
          if (input && input.value) requestParams[key] = input.value;
        });
      }
      requestParams.q = text;
      this.pending = this.requester(requestParams);
      this.pending.promise.then(response => {
        this.clearLoading(this.ac);
        this.createSuggestions(response);

        // If requested to display suggestions after they load.
        if (display) this.displaySuggestions();
      }, reason => {
        // Only remove the autocomplete loading if the request was aborted.
        if (!(reason.message && reason.message === 'Cancelled')) {
          this.clearLoading(this.ac);
        }
      });
    }

    /**
     * Clear the autocomplete loading status and displays.
     *
     * @param {ToolshedElement} el
     *   Autocomplete element to remove loading status from.
     */
    clearLoading(el) {
      // Clear the loading spinner.
      el.removeClass(['ui-autocomplete-loading', 'is-autocompleting']);

      // Hide Claro or other theme autocomplete message elements.
      const msg = el.parentElement.querySelector(':scope > [data-drupal-selector="autocomplete-message"]');
      if (msg) {
        msg.classList.add('hidden');
      }
    }

    /**
     * When focus is on the autocomplete input, show suggestions if they exist.
     */
    onFocus() {
      if (!this.list.isEmpty()) {
        this.displaySuggestions();
      }
    }

    /**
     * When focus leaves the autocomplete input, hide suggestions.
     *
     * @param {BlurEvent} e
     *   The blur event information for when the autocomplete loses focus.
     */
    onBlur(e) {
      if (this.isSuggestionsVisible()) {
        if (!e.relatedTarget || !(e.relatedTarget === this.ac.el || e.relatedTarget.closest('[role=listbox]') === this.list.el)) {
          this.hideSuggestions();
        }
      }
    }

    /**
     * Create a mouse down event which avoids having the AC input lose focus
     * from clicking into the suggestions wrapper. This inconveniently prevents
     * the click event because it hides the suggestions before the click event
     * can occur.
     *
     * @param {Event} event
     *   The mouse down event object.
     */
    // eslint-disable-next-line class-methods-use-this
    onMouseDown(event) {
      event.preventDefault();
    }

    /**
     * Callback for updating the input value when the textfield is updated.
     *
     * @param {Event} event
     *   The input changed event object.
     */
    onTextChange(event) {
      if (this.ac.el !== this.input) {
        this.input.value = this.config.requireSelect ? '' : this.ac.el.value;
      }
      this.fetchSuggestions(event.target.value);
    }

    /**
     * Input value changed callback to refresh our input values when a
     * dependent parameter changes.
     */
    onParamChange() {
      this.clearInput();
    }

    /**
     * Respond to keyboard navigation, and move the active item.
     *
     * @param {KeydownEvent} e
     *   Respond to a key-up event for the autocomplete input textfield.
     */
    onTextKeydown(e) {
      if (!this.isSuggestionsVisible()) return;
      let inc;
      let method;
      switch (e.keyCode) {
        case 13:
          // Enter key
          if (this.activeItem) {
            e.preventDefault();
            this.selectItem(this.activeItem);
            return;
          }

        // eslint-ignore-line no-fallthrough
        case 9: // Tab key
        case 27:
          // Escape key
          this.hideSuggestions();
          return;
        case 40:
          // Up key
          method = SuggestList.prototype.getFirst;
          inc = 1;
          break;
        case 38:
          // Down key
          method = SuggestList.prototype.getLast;
          inc = -1;
          break;
        default:
          return;
      }
      e.preventDefault();
      let item = this.activeItem;
      if (item) {
        let p = item.parent;
        let i = item.pos + inc;
        while (p && (i < 0 || p.items.length === i)) {
          item = p;
          i = item.pos + inc;
          p = item.parent;
        }
        if (p) {
          item = p.items[i];
        }
      }
      item = item || this.list;
      if (item instanceof SuggestList) {
        item = method.call(item);
      }
      this.setActiveItem(item);
    }

    /**
     * Clean up DOM and event listeners initialized by this autocompete widget.
     */
    destroy() {
      // Abort any pending request for this widget.
      if (this.pending && !this.pending.promise.isResolved) {
        this.pending.xhr.abort();
      }
      delete this.pending;
      if (this.config.params) {
        Object.values(this.config.params).forEach(elemId => {
          const el = document.getElementById(elemId);
          if (el) el.removeEventListener('change', this.onParamChange);
        });
      }
      if (this.ac.el !== this.input) {
        if (this.ac.id && this.input.id) {
          // Reassociate a label element to point to the original input.
          const inputLabel = document.querySelector(`label[for='${this.ac.id}']`);
          if (inputLabel) {
            inputLabel.htmlFor = this.input.id;
          }
        }
        this.ac.destroy(true);
      }
      this.list.destroy(true);
      this.emptyMsg.destroy(true);
      this.suggestWrap.destroy(true);
      this.liveRegion.destroy(true);
      this.input.style.display = '';
    }
  };

  /**
   * Find autocomplete inputs and attach the autocomplete behaviors to it.
   */
  behaviors.toolshedAutocomplete = {
    // Track instances of Autocomplete objects so they can be detached.
    instances: new Map(),
    attach(context) {
      ts.walkByClass(context, 'toolshed-autocomplete', item => {
        const settings = item.dataset.autocomplete ? JSON.parse(item.dataset.autocomplete) : {};
        if (item.dataset.params) {
          settings.params = JSON.parse(item.dataset.params);
        }
        if (settings.uri) {
          const ac = new ts.Autocomplete(item, settings);
          this.instances.set(item.id || item, ac);
        }
      }, 'autocomplete--processed');
    },
    detach(context, settings, trigger) {
      if (trigger === 'unload') {
        ts.walkBySelector(context, '.toolshed-autocomplete.autocomplete--processed', item => {
          const ac = this.instances.get(item.id || item);
          if (ac) {
            item.classList.remove('autocomplete--processed');
            this.instances.delete(item);
            ac.destroy();
          }
        });
      }
    }
  };
})(Drupal);
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2lkZ2V0cy9BdXRvY29tcGxldGUuanMiLCJuYW1lcyI6WyJ0IiwiYmVoYXZpb3JzIiwiZGVib3VuY2UiLCJUb29sc2hlZCIsInRzIiwiTGl2ZVJlZ2lvbiIsIkVsZW1lbnQiLCJjb25zdHJ1Y3RvciIsIm9wdGlvbnMiLCJhdHRhY2hUbyIsImlzU3RyaW5nIiwiY2xhc3MiLCJjb25jYXQiLCJkb2N1bWVudCIsImJvZHkiLCJyZWdpb25PcHRzIiwicm9sZSIsImFjdGl2ZSIsIm1zZyIsInJlZ2lvbnMiLCJ1cGRhdGVNc2ciLCJ0ZXh0Q29udGVudCIsIm1lc3NhZ2UiLCJTdWdnZXN0SXRlbSIsImxpc3QiLCJpZCIsImFjIiwidGFiaW5kZXgiLCJocmVmIiwid3JhcCIsImFwcGVuZENoaWxkIiwicGFyZW50IiwicG9zIiwidXJpIiwib24iLCJlIiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iLCJzZWxlY3RJdGVtIiwidmFsdWUiLCJ1cmwiLCJlbCIsImZvY3VzIiwiYWRkQ2xhc3MiLCJibHVyIiwicmVtb3ZlQ2xhc3MiLCJkZXN0cm95IiwiZGV0YWNoIiwiU3VnZ2VzdExpc3QiLCJ3cmFwVGFnIiwiaXRlbXMiLCJsZW5ndGgiLCJjdCIsImkiLCJpc0VtcHR5Iiwic2V0TGFiZWwiLCJ0ZXh0IiwiaXRlbUlkIiwibGFiZWwiLCJwcmVwZW5kQ2hpbGQiLCJzZXRBdHRycyIsImFyaWFMYWJlbGxlZGJ5IiwiYWRkSXRlbSIsIml0ZW0iLCJwdXNoIiwiZ2V0Rmlyc3QiLCJnZXRMYXN0IiwiaWR4IiwiYnVpbGRJdGVtcyIsImRhdGEiLCJiYXNlSWQiLCJmb3JFYWNoIiwicm93IiwiQXJyYXkiLCJpc0FycmF5IiwiaXRlbURpc3BsYXkiLCJjbGVhciIsIkF1dG9jb21wbGV0ZSIsIlRvb2xzaGVkQXV0b2NvbXBsZXRlIiwiaW5wdXQiLCJjb25maWciLCJwZW5kaW5nIiwiZGVsYXkiLCJtaW5MZW5ndGgiLCJyZXF1aXJlU2VsZWN0IiwiaW5wdXRMYWJlbCIsInF1ZXJ5U2VsZWN0b3IiLCJvbkJsdXIiLCJzZXRBdHRyIiwid3JhcHBlciIsInN0eWxlIiwiZGlzcGxheSIsIm9uTW91c2VEb3duIiwiYmluZCIsInN1Z2dlc3RXcmFwIiwiZW1wdHlNc2ciLCJsaXZlUmVnaW9uIiwic2VwYXJhdGVWYWx1ZSIsIkZvcm1FbGVtZW50IiwiY2xvbmVOb2RlIiwicGxhY2Vob2xkZXIiLCJkYXRhc2V0IiwiZm9ybWF0RGlzcGxheVZhbHVlIiwicmVtb3ZlQXR0cnMiLCJkcnVwYWxTZWxlY3RvciIsImF1dG9jb21wbGV0ZVBhdGgiLCJodG1sRm9yIiwiYXV0b2NvbXBsZXRlIiwib25UZXh0S2V5ZG93biIsIm9uVGV4dENoYW5nZSIsIm9uRm9jdXMiLCJmZXRjaFN1Z2dlc3Rpb25zIiwicGFyYW1zIiwiZm9ybSIsIm9uUGFyYW1DaGFuZ2UiLCJPYmplY3QiLCJ2YWx1ZXMiLCJlbGVtSWQiLCJhZGRFdmVudExpc3RlbmVyIiwiY3JlYXRlU3VnZ2VzdGlvbnMiLCJhY3RpdmVJdGVtIiwiZW1wdHkiLCJyZXBsYWNlIiwiY2xlYXJJbnB1dCIsImNsZWFyU3VnZ2VzdGlvbnMiLCJpc1N1Z2dlc3Rpb25zVmlzaWJsZSIsImRpc3BsYXlTdWdnZXN0aW9ucyIsInNldFN0eWxlcyIsIndpZHRoIiwiY2xpZW50V2lkdGgiLCJ0b3AiLCJvZmZzZXRUb3AiLCJvZmZzZXRIZWlnaHQiLCJsZWZ0Iiwib2Zmc2V0TGVmdCIsImhpZGVTdWdnZXN0aW9ucyIsImh0bWwiLCJpbm5lckhUTUwiLCJzZXRBY3RpdmVJdGVtIiwid2luZG93IiwibG9jYXRpb24iLCJyZXF1ZXN0ZXIiLCJjcmVhdGVSZXF1ZXN0ZXIiLCJwcm9taXNlIiwiaXNSZXNvbHZlZCIsInhociIsImFib3J0IiwicmVxdWVzdFBhcmFtcyIsImVudHJpZXMiLCJrZXkiLCJnZXRFbGVtZW50QnlJZCIsInEiLCJ0aGVuIiwicmVzcG9uc2UiLCJjbGVhckxvYWRpbmciLCJyZWFzb24iLCJwYXJlbnRFbGVtZW50IiwiY2xhc3NMaXN0IiwiYWRkIiwicmVsYXRlZFRhcmdldCIsImNsb3Nlc3QiLCJldmVudCIsInRhcmdldCIsImluYyIsIm1ldGhvZCIsImtleUNvZGUiLCJwcm90b3R5cGUiLCJwIiwiY2FsbCIsInJlbW92ZUV2ZW50TGlzdGVuZXIiLCJ0b29sc2hlZEF1dG9jb21wbGV0ZSIsImluc3RhbmNlcyIsIk1hcCIsImF0dGFjaCIsImNvbnRleHQiLCJ3YWxrQnlDbGFzcyIsInNldHRpbmdzIiwiSlNPTiIsInBhcnNlIiwic2V0IiwidHJpZ2dlciIsIndhbGtCeVNlbGVjdG9yIiwiZ2V0IiwicmVtb3ZlIiwiZGVsZXRlIiwiRHJ1cGFsIl0sInNvdXJjZXMiOlsid2lkZ2V0cy9BdXRvY29tcGxldGUuZXM2LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIigoe1xuICB0LFxuICBiZWhhdmlvcnMsXG4gIGRlYm91bmNlLFxuICBUb29sc2hlZDogdHMsXG59KSA9PiB7XG4gIC8qKlxuICAgKiBMaXZlIHJlZ2lvbiB3aGljaCBpcyB1c2VkIHRvIGFubm91bmNlIHN0YXR1cyBvZiB3aWRnZXQgY2hhbmdlcy5cbiAgICovXG4gIGNsYXNzIExpdmVSZWdpb24gZXh0ZW5kcyB0cy5FbGVtZW50IHtcbiAgICAvKipcbiAgICAgKiBJbml0aWFsaXplIGEgbmV3IExpdmVSZWdpb24gZWxlbWVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zXG4gICAgICogICBPcHRpb25zIHRvIGFwcGx5IHRvIHRoZSBMaXZlIFJlZ2lvbnMuXG4gICAgICogQHBhcmFtIHtIVE1MRWxlbWVudHxUb29sc2hlZEVsZW1lbnR9IGF0dGFjaFRvXG4gICAgICogICBUaGUgZWxlbWVudCB0byBhcHBlbmQgdGhpcyBsaXZlIHJlZ2lvbiB0by5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihvcHRpb25zID0ge30sIGF0dGFjaFRvKSB7XG4gICAgICBpZiAodHMuaXNTdHJpbmcob3B0aW9ucy5jbGFzcykpIHtcbiAgICAgICAgb3B0aW9ucy5jbGFzcyA9IFtvcHRpb25zLmNsYXNzXTtcbiAgICAgIH1cblxuICAgICAgb3B0aW9ucy5jbGFzcyA9IChvcHRpb25zLmNsYXNzIHx8IFtdKS5jb25jYXQoWyd2aXN1YWxseS1oaWRkZW4nLCAnc3Itb25seSddKTtcblxuICAgICAgLy8gQ3JlYXRlIGEgd3JhcHBlciBlbGVtZW50IGZvciB0aGUgdHdvIGxpdmUgdXBkYXRlIGFyZWFzLlxuICAgICAgc3VwZXIoJ2RpdicsIG9wdGlvbnMsIGF0dGFjaFRvIHx8IGRvY3VtZW50LmJvZHkpO1xuXG4gICAgICAvLyBTZXR1cCByZWdpb25zIGZvciBzd2FwcGluZyBiZXR3ZWVuLCBmb3IgbGl2ZSB1cGRhdGluZy5cbiAgICAgIGNvbnN0IHJlZ2lvbk9wdHMgPSB7XG4gICAgICAgIHJvbGU6ICdzdGF0dXMnLFxuICAgICAgICAnYXJpYS1saXZlJzogJ3BvbGl0ZScsXG4gICAgICAgICdhcmlhLWF0b21pYyc6ICd0cnVlJyxcbiAgICAgIH07XG5cbiAgICAgIHRoaXMuYWN0aXZlID0gMTtcbiAgICAgIHRoaXMubXNnID0gJyc7XG4gICAgICB0aGlzLnJlZ2lvbnMgPSBbXG4gICAgICAgIG5ldyB0cy5FbGVtZW50KCdkaXYnLCByZWdpb25PcHRzLCB0aGlzKSxcbiAgICAgICAgbmV3IHRzLkVsZW1lbnQoJ2RpdicsIHJlZ2lvbk9wdHMsIHRoaXMpLFxuICAgICAgXTtcblxuICAgICAgLy8gT25seSBhbm5vdW5jZSB0aGUgY2hhbmdlcyBhZnRlciBhIHNob3J0IGRlbGF5IHRvIHByZXZlbnQgdGhlIGFubm91bmNlclxuICAgICAgLy8gZnJvbSB0YWxraW5nIG92ZXIgaXRzZWxmLlxuICAgICAgdGhpcy51cGRhdGVNc2cgPSBkZWJvdW5jZSgoKSA9PiB7XG4gICAgICAgIHRoaXMucmVnaW9uc1t0aGlzLmFjdGl2ZV0udGV4dENvbnRlbnQgPSAnJztcbiAgICAgICAgdGhpcy5hY3RpdmUgXj0gMTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1iaXR3aXNlXG4gICAgICAgIHRoaXMucmVnaW9uc1t0aGlzLmFjdGl2ZV0udGV4dENvbnRlbnQgPSB0aGlzLm1zZztcbiAgICAgIH0sIDUwMCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVGhlIGN1cnJlbnQgdGV4dCBtZXNzYWdlIHRvIGFubm91bmNlIGluIHRoZSBsaXZlIHJlZ2lvbi5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge3N0cmluZ31cbiAgICAgKiAgIFJldHVybiB0aGUgY3VycmVudCBtZXNzYWdlIHRvIGJlIGFubm91bmNlZC5cbiAgICAgKi9cbiAgICBnZXQgbWVzc2FnZSgpIHtcbiAgICAgIHJldHVybiB0aGlzLm1zZztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXQgYSBuZXcgbWVzc2FnZSBmb3IgdGhlIGxpdmUgcmVnaW9uLiBOb3RlIHRoYXQgdGhlcmUgaXMgYSBzbWFsbCBkZWxheVxuICAgICAqIGJlZm9yZSB0aGUgbWVzc2FnZSBpcyBhbm5vdW5jZWQgdG8gYXZvaWQgY29sbGlzaW9ucyBvZiBtZXNzYWdlcyB3aGljaFxuICAgICAqIGFyZSB0b28gY2xvc2UgdG9nZXRoZXIuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZVxuICAgICAqICAgTWVzc2FnZSB0byBzZXQgZm9yIHRoZSBsaXZlIHJlZ2lvbi5cbiAgICAgKi9cbiAgICBzZXQgbWVzc2FnZShtZXNzYWdlKSB7XG4gICAgICB0aGlzLm1zZyA9IG1lc3NhZ2U7XG4gICAgICB0aGlzLnVwZGF0ZU1zZygpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBPYmplY3Qgd2hpY2ggcmVwcmVzZW50cyBhIHNpbmdsZSBhdXRvY29tcGxldGUgc2VsZWN0IG9wdGlvbi4gVGhpcyBvYmplY3RcbiAgICogbWFuYWdlcyBpdHMgb3duIHJlc291cmNlcyBhbmQgdGhlIGRpc3BsYXkgdGV4dCBhcyB3ZWxsIGFzIHRoZSB1bmRlcmx5aW5nXG4gICAqIHZhbHVlIHRvIHBhc3MgYmFjayB0byB0aGUgb3JpZ2luYWwgaW5wdXQgZWxlbWVudC5cbiAgICovXG4gIGNsYXNzIFN1Z2dlc3RJdGVtIGV4dGVuZHMgdHMuRWxlbWVudCB7XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGEgbmV3IFN1Z2dlc3RJdGVtIHJlcHJlc2VudGluZyB2YWx1ZXMgYW5kIGEgc2VsZWN0YWJsZSBlbGVtZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIHtTdWdnZXN0TGlzdH0gbGlzdFxuICAgICAqICAgVGhlIHN1Z2dlc3Rpb24gbGlzdCB0aGF0IHRoaXMgaXRlbSBpcyBhIG1lbWJlciBvZi5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gaWRcbiAgICAgKiAgIFRoZSBvcHRpb24gaWRlbnRpZmllciB0byB1c2UgZm9yIHRoZSBlbGVtZW50IElELlxuICAgICAqIEBwYXJhbSB7VG9vbHNoZWQuQXV0b2NvbXBsZXRlfSBhY1xuICAgICAqICAgVGhlIGNhbGxiYWNrIHdoZW4gdGhlIGl0ZW0gaXMgY2xpY2tlZCBvbi5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihsaXN0LCBpZCwgYWMpIHtcbiAgICAgIHN1cGVyKCdhJywgeyB0YWJpbmRleDogMCwgaHJlZjogJyMnIH0pO1xuXG4gICAgICB0aGlzLndyYXAgPSBuZXcgdHMuRWxlbWVudCgnbGknLCB7IGlkLCByb2xlOiAnb3B0aW9uJywgY2xhc3M6ICdhdXRvY29tcGxldGVfX29wdCcgfSk7XG4gICAgICB0aGlzLndyYXAuYXBwZW5kQ2hpbGQodGhpcyk7XG5cbiAgICAgIHRoaXMucGFyZW50ID0gbGlzdDtcbiAgICAgIHRoaXMucG9zID0gLTE7XG4gICAgICB0aGlzLnVyaSA9IG51bGw7XG5cbiAgICAgIHRoaXMub24oJ2NsaWNrJywgKGUpID0+IHtcbiAgICAgICAgaWYgKCF0aGlzLnVyaSkge1xuICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICBlLnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgIGFjLnNlbGVjdEl0ZW0odGhpcyk7XG4gICAgICAgIH1cbiAgICAgIH0sIHRydWUpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRoZSBnZXR0ZXIgZm9yIHRoZSBTdWdnZXN0SXRlbSBlbGVtZW50IElELlxuICAgICAqL1xuICAgIGdldCBpZCgpIHtcbiAgICAgIHJldHVybiB0aGlzLndyYXAuaWQ7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVGhlIHNldHRlciBmb3IgU3VnZ2VzdEl0ZW0gZWxlbWVudCBJRC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZVxuICAgICAqICAgVGhlIElEIHRvIHVzZWQgZm9yIHRoZSBTdWdnZXN0SXRlbSBIVE1MRWxlbWVudCBhbmQgZGVzY2VuZGFudCBJRC5cbiAgICAgKi9cbiAgICBzZXQgaWQodmFsdWUpIHtcbiAgICAgIHRoaXMud3JhcC5pZCA9IHZhbHVlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgc3RvcmVkIFVSSSB2YWx1ZSBvZiB0aGlzIHN1Z2dlc3QgaXRlbS5cbiAgICAgKi9cbiAgICBnZXQgdXJsKCkge1xuICAgICAgcmV0dXJuIHRoaXMudXJpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldCB0aGUgVVJMIG9mIHRoZSBzdWdnZXN0IGl0ZW0sIGlmIHRoZSBpdGVtIHNob3VsZCB3b3JrIGxpa2UgYSBsaW5rLFxuICAgICAqIHJhdGhlciB0aGFuIHVzZSB0aGUgYXV0b2NvbXBsZXRlIHZhbHVlLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlXG4gICAgICogICBBIFVSTCB0aGUgU3VnZ2VzdCBpdGVtIHNob3VsZCBzZW5kIHNlbGVjdCB3aGVuIHRoZSBpdGVtIGlzIGNsaWNrZWQuXG4gICAgICovXG4gICAgc2V0IHVybCh2YWx1ZSkge1xuICAgICAgaWYgKHZhbHVlICYmIHZhbHVlICE9PSAnIycpIHtcbiAgICAgICAgdGhpcy51cmkgPSB2YWx1ZTtcbiAgICAgICAgdGhpcy5lbC5ocmVmID0gdmFsdWU7XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgdGhpcy51cmkgPSBudWxsO1xuICAgICAgICB0aGlzLmVsLmhyZWYgPSAnIyc7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVXBkYXRlIHRoaXMgaXRlbSB0byBzaG93IGl0IGhhcyBmb2N1cy5cbiAgICAgKi9cbiAgICBmb2N1cygpIHtcbiAgICAgIHRoaXMud3JhcC5hZGRDbGFzcygnYXV0b2NvbXBsZXRlX19vcHQtLWFjdGl2ZScpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZSB0aGlzIGl0ZW0gdG8gc2hvdyB0aGF0IGl0IGRvZXNuJ3QgaGF2ZSBmb2N1cy5cbiAgICAgKi9cbiAgICBibHVyKCkge1xuICAgICAgdGhpcy53cmFwLnJlbW92ZUNsYXNzKCdhdXRvY29tcGxldGVfX29wdC0tYWN0aXZlJyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2xlYW4gdXAgbGlzdGVuZXJzIGFuZCBvdGhlciBhbGxvY2F0ZWQgcmVzb3VyY2VzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtib29sfSBkZXRhY2hcbiAgICAgKiAgIFNob3VsZCB0aGUgc3VnZ2VzdCBpdGVtIGJlIHJlbW92ZWQgZnJvbSB0aGUgRE9NP1xuICAgICAqL1xuICAgIGRlc3Ryb3koZGV0YWNoKSB7XG4gICAgICB0aGlzLnBhcmVudCA9IG51bGw7XG4gICAgICBzdXBlci5kZXN0cm95KGRldGFjaCk7XG4gICAgICB0aGlzLndyYXAuZGVzdHJveShkZXRhY2gpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBIGxpc3Qgb2YgU3VnZ2VzdEl0ZW1zLCBhbmQgbWFuYWdlIHRoZSBpbnRlcmFjdGlvbnMgd2l0aCB0aGUgYXV0b2NvbXBsZXRlXG4gICAqIHN1Z2dlc3Rpb25zIHRyYXZlcnNhbCwgYnVpbGRpbmcgYW5kIGNsZWFyaW5nLlxuICAgKi9cbiAgY2xhc3MgU3VnZ2VzdExpc3QgZXh0ZW5kcyB0cy5FbGVtZW50IHtcbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBuZXcgbGlzdCBvZiBTdWdnZXN0SXRlbSBvYmplY3RzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtTdWdnZXN0TGlzdHxudWxsfSBsaXN0XG4gICAgICogICBBbiBvYmplY3QgdGhhdCBjb250YWlucyBhIGxpc3Qgb2YgU3VnZ2VzdEl0ZW0gaW5zdGFuY2VzLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSB3cmFwVGFnXG4gICAgICogICBUaGUgSFRNTEVsZW1lbnQgdGFnIHRvIHVzZSB0byB3cmFwIHRoaXMgU3VnZ2VzdExpc3QuXG4gICAgICogQHBhcmFtIHtUb29sc2hlZEF1dG9jb21wbGV0ZX0gYWNcbiAgICAgKiAgIFRoZSBhdXRvY29tcGxldGUgaW5zdGFuY2UgdGhhdCBvd25zIHRoaXMgU3VnZ2VzdExpc3QuXG4gICAgICovXG4gICAgY29uc3RydWN0b3IobGlzdCwgd3JhcFRhZywgYWMpIHtcbiAgICAgIHN1cGVyKCd1bCcsIHsgY2xhc3M6ICdhdXRvY29tcGxldGVfX29wdGlvbnMnIH0pO1xuXG4gICAgICB0aGlzLml0ZW1zID0gW107XG4gICAgICB0aGlzLnBhcmVudCA9IGxpc3Q7XG4gICAgICB0aGlzLmFjID0gYWM7XG4gICAgICB0aGlzLnBvcyA9IDA7XG5cbiAgICAgIHRoaXMud3JhcCA9IG5ldyB0cy5FbGVtZW50KHdyYXBUYWcpO1xuICAgICAgdGhpcy53cmFwLmFwcGVuZENoaWxkKHRoaXMpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgY3VycmVudCBudW1iZXIgb2Ygc3VnZ2VzdHMgaW4gdGhlIGxpc3QuXG4gICAgICovXG4gICAgZ2V0IGxlbmd0aCgpIHtcbiAgICAgIGxldCBjdCA9IDA7XG5cbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5pdGVtcy5sZW5ndGg7ICsraSkge1xuICAgICAgICBjdCArPSAodGhpcy5pdGVtc1tpXSBpbnN0YW5jZW9mIFN1Z2dlc3RMaXN0KSA/IHRoaXMuaXRlbXNbaV0ubGVuZ3RoIDogMTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGN0O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIElzIHRoaXMgU3VnZ2VzdExpc3QgZW1wdHkgb2Ygc2VsZWN0YWJsZSBzdWdnZXN0aW9uIGl0ZW1zP1xuICAgICAqXG4gICAgICogQHJldHVybiB7Qm9vbGVhbn1cbiAgICAgKiAgIFJldHVybnMgdHJ1ZSBpZiB0aGVyZSBhcmUgbm8gc2VsZWN0YWJsZSBpdGVtcyBjdXJyZW50bHkgYXZhaWxhYmxlXG4gICAgICogICBhcyBzdWdnZXN0aW9ucyBmb3IgdGhlIGF1dG9jb21wbGV0ZSBxdWVyeS5cbiAgICAgKi9cbiAgICBpc0VtcHR5KCkge1xuICAgICAgcmV0dXJuICF0aGlzLml0ZW1zLmxlbmd0aDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXQgdGhlIGxhYmVsIHRvIGRpc3BsYXkgb3ZlciBhIHNldCBpdGVtcywgaWYgdGhlcmUgaXMgYSBjYXB0aW9uLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IHRleHRcbiAgICAgKiAgIFRoZSBsYWJlbCB0ZXh0IHRvIHVzZSBhcyBhIGNhcHRpb24gb3ZlciB0aGUgU3VnZ2VzdExpc3QgaXRlbXMuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGl0ZW1JZFxuICAgICAqICAgVGhlIElEIGF0dHJpYnV0ZSBmb3IgdGhlIHdyYXBwZXIgZWxlbWVudC5cbiAgICAgKi9cbiAgICBzZXRMYWJlbCh0ZXh0LCBpdGVtSWQpIHtcbiAgICAgIGlmICghdGhpcy5sYWJlbCkge1xuICAgICAgICB0aGlzLmxhYmVsID0gbmV3IHRzLkVsZW1lbnQoJ3NwYW4nLCB7IGNsYXNzOiAnYXV0b2NvbXBsZXRlX19ncm91cC1sYWJlbCcgfSk7XG4gICAgICAgIHRoaXMud3JhcC5wcmVwZW5kQ2hpbGQodGhpcy5sYWJlbCk7XG4gICAgICB9XG5cbiAgICAgIGlmIChpdGVtSWQpIHtcbiAgICAgICAgdGhpcy5sYWJlbC5zZXRBdHRycyh7IGlkOiBgJHtpdGVtSWR9LWxhYmVsYCB9KTtcbiAgICAgICAgdGhpcy5zZXRBdHRycyh7IGFyaWFMYWJlbGxlZGJ5OiBgJHtpdGVtSWR9LWxhYmVsYCB9KTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5sYWJlbC50ZXh0Q29udGVudCA9ICh0ZXh0ICYmIHRleHQubGVuZ3RoKSA/IHRleHQgOiAnJztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGQgYSBuZXcgU3VnZ2VzdEl0ZW0gdG8gdGhlIFN1Z2dlc3RMaXN0LlxuICAgICAqXG4gICAgICogQHBhcmFtIHtTdWdnZXN0SXRlbX0gaXRlbVxuICAgICAqICAgQWRkIGEgU3VnZ2VzdEl0ZW0gdG8gdGhlIGVuZCBvZiB0aGUgU3VnZ2VzdExpc3QuXG4gICAgICovXG4gICAgYWRkSXRlbShpdGVtKSB7XG4gICAgICB0aGlzLml0ZW1zLnB1c2goaXRlbSk7XG4gICAgICB0aGlzLmFwcGVuZENoaWxkKGl0ZW0ud3JhcCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IHRoZSBmaXJzdCBpdGVtIGluIHRoZSBsaXN0LlxuICAgICAqXG4gICAgICogQHJldHVybiB7U3VnZ2VzdEl0ZW18bnVsbH1cbiAgICAgKiAgIFRoZSBmaXJzdCBzZWxlY3RhYmxlIFN1Z2dlc3RJdGVtIG9yIE5VTEwgaWYgbm90IGl0ZW1zLlxuICAgICAqL1xuICAgIGdldEZpcnN0KCkge1xuICAgICAgaWYgKHRoaXMuaXRlbXMubGVuZ3RoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLml0ZW1zWzBdIGluc3RhbmNlb2YgU3VnZ2VzdExpc3QgPyB0aGlzLml0ZW1zWzBdLmdldEZpcnN0KCkgOiB0aGlzLml0ZW1zWzBdO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBHZXQgdGhlIGxpc3QgaXRlbSBpbiB0aGUgbGlzdC5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge1N1Z2dlc3RJdGVtfG51bGx9XG4gICAgICogICBUaGUgbGFzdCBzZWxlY3RhYmxlIFN1Z2dlc3RJdGVtIGluIHRoZSBsaXN0IG9yIE5VTEwuIFRoaXMgaW5jbHVkZXNcbiAgICAgKiAgIHRoZSBsYXN0IGl0ZW0gaW4gdGhlIG5lc3RlZCBzZXQgb2YgaXRlbXMuXG4gICAgICovXG4gICAgZ2V0TGFzdCgpIHtcbiAgICAgIGlmICh0aGlzLml0ZW1zLmxlbmd0aCkge1xuICAgICAgICBjb25zdCBpZHggPSB0aGlzLml0ZW1zLmxlbmd0aCAtIDE7XG4gICAgICAgIHJldHVybiB0aGlzLml0ZW1zW2lkeF0gaW5zdGFuY2VvZiBTdWdnZXN0TGlzdCA/IHRoaXMuaXRlbXNbaWR4XS5nZXRMYXN0KCkgOiB0aGlzLml0ZW1zW2lkeF07XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENvbnN0cnVjdCB0aGUgbGlzdCBvZiBzZWxlY3RhYmxlIFN1Z2dlc3RJdGVtcyBmcm9tIHRoZSBwYXNzZWQgaW4gZGF0YS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7YXJyYXl8b2JqZWN0fSBkYXRhXG4gICAgICogICBUaGUgZGF0YSByZXByZXNlbnRpbmcgdGhlIGF1dG9jb21wbGV0ZSBzdWdnZXN0aW9ucyAvIHZhbHVlcyBhdmFpbGFibGUuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGJhc2VJZFxuICAgICAqICAgQSBwb29sIG9mIGF2YWlsYWJsZSBTdWdnZXN0SXRlbSB0byByZXVzZS5cbiAgICAgKi9cbiAgICBidWlsZEl0ZW1zKGRhdGEsIGJhc2VJZCkge1xuICAgICAgbGV0IHBvcyA9IDA7XG5cbiAgICAgIGRhdGEuZm9yRWFjaCgocm93KSA9PiB7XG4gICAgICAgIGxldCBpdGVtO1xuICAgICAgICBjb25zdCBpdGVtSWQgPSBgJHtiYXNlSWR9LSR7cG9zfWA7XG5cbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkocm93Lmxpc3QpKSB7XG4gICAgICAgICAgaWYgKCFyb3cubGlzdC5sZW5ndGgpIHJldHVybjtcblxuICAgICAgICAgIGl0ZW0gPSBuZXcgU3VnZ2VzdExpc3QodGhpcywgJ2xpJywgdGhpcy5hYyk7XG4gICAgICAgICAgaXRlbS5zZXRBdHRycyh7IHJvbGU6ICdncm91cCcgfSk7XG4gICAgICAgICAgaXRlbS5zZXRMYWJlbChyb3cudGV4dCwgaXRlbUlkKTtcbiAgICAgICAgICBpdGVtLmJ1aWxkSXRlbXMocm93Lmxpc3QsIGl0ZW1JZCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAocm93LnZhbHVlKSB7XG4gICAgICAgICAgaXRlbSA9IG5ldyBTdWdnZXN0SXRlbSh0aGlzLCBpdGVtSWQsIHRoaXMuYWMpO1xuICAgICAgICAgIGl0ZW0udGV4dCA9IChyb3cudGV4dCB8fCByb3cudmFsdWUpO1xuICAgICAgICAgIGl0ZW0udmFsdWUgPSByb3cudmFsdWU7XG5cbiAgICAgICAgICBpZiAocm93LnVybCkgaXRlbS51cmwgPSByb3cudXJsO1xuXG4gICAgICAgICAgdGhpcy5hYy5pdGVtRGlzcGxheShpdGVtLCByb3cpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5hZGRJdGVtKGl0ZW0pO1xuICAgICAgICBpdGVtLnBvcyA9IHBvcysrO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIHN1Z2dlc3Rpb24gaXRlbXMgZnJvbSB0aGUgc3VnZ2VzdGlvbmVkIGl0ZW1zIGRpc3BsYXkuXG4gICAgICovXG4gICAgY2xlYXIoKSB7XG4gICAgICB0aGlzLml0ZW1zLmZvckVhY2goKGl0ZW0pID0+IGl0ZW0uZGVzdHJveSh0cnVlKSk7XG4gICAgICB0aGlzLml0ZW1zID0gW107XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2xlYW5zIHVwIHRoaXMgYXV0b2NvbXBsZXRlIHN1Z2dlc3Rpb25zIGxpc3QsIGFuZCBmcmVlcyByZXNvdXJjZXMgYW5kXG4gICAgICogZXZlbnQgbGlzdGVuZXJzLlxuICAgICAqL1xuICAgIGRlc3Ryb3koKSB7XG4gICAgICBpZiAodGhpcy5sYWJlbCkge1xuICAgICAgICB0aGlzLmxhYmVsLmRlc3Ryb3kodHJ1ZSk7XG4gICAgICB9XG4gICAgICB0aGlzLmNsZWFyKCk7XG5cbiAgICAgIHN1cGVyLmRlc3Ryb3kodHJ1ZSk7XG4gICAgICB0aGlzLndyYXAuZGVzdHJveSh0cnVlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2xhc3Mgd2hpY2ggZW5jYXBzdWxhdGVzIHRoZSBiZWhhdmlvcnMgb2YgYW5kIGF1dG9jb21wbGV0ZSB3aWRnZXQgb2YgYVxuICAgKiB0ZXh0ZmllbGQuIEl0IGFsbG93cyBzdWJjbGFzc2VzIHRvIG92ZXJyaWRlIHRoZWlyIGZldGNoU3VnZ2VzdGlvbnMoKSBhbmRcbiAgICogaW5pdCgpIG1ldGhvZHMgdG8gYWxsb3cgZm9yIGRpZmZlcmVudCBtZXRob2RzIG9mIGxvYWRpbmcgYXV0b2NvbXBsZXRlXG4gICAqIGl0ZW1zIHRvIGRpc3BsYXkuXG4gICAqL1xuICB0cy5BdXRvY29tcGxldGUgPSBjbGFzcyBUb29sc2hlZEF1dG9jb21wbGV0ZSB7XG4gICAgY29uc3RydWN0b3IoaW5wdXQsIGNvbmZpZyA9IHt9KSB7XG4gICAgICBsZXQgYWM7XG5cbiAgICAgIHRoaXMuaW5wdXQgPSBpbnB1dDtcbiAgICAgIHRoaXMucGVuZGluZyA9IGZhbHNlO1xuICAgICAgdGhpcy5jb25maWcgPSB7XG4gICAgICAgIGRlbGF5OiAzNzUsXG4gICAgICAgIG1pbkxlbmd0aDogMyxcbiAgICAgICAgcmVxdWlyZVNlbGVjdDogdHJ1ZSxcbiAgICAgICAgLi4uY29uZmlnLFxuICAgICAgfTtcblxuICAgICAgLy8gUmVhc3NvY2lhdGUgYSBsYWJlbCBlbGVtZW50IHRvIHBvaW50IHRvIHRoZSBuZXcgYXV0b2NvbXBsZXRlIGlucHV0LlxuICAgICAgY29uc3QgaW5wdXRMYWJlbCA9ICh0aGlzLmlucHV0LmlkKVxuICAgICAgICA/IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYGxhYmVsW2Zvcj0nJHt0aGlzLmlucHV0LmlkfSddYCkgOiBudWxsO1xuXG4gICAgICAvLyBDcmVhdGUgdGhlIG1haW4gc3VnZ2VzdGlvbiBsaXN0IHdyYXBwZXIuXG4gICAgICBjb25zdCBsaXN0ID0gbmV3IFN1Z2dlc3RMaXN0KG51bGwsICdkaXYnLCB0aGlzKTtcbiAgICAgIGxpc3Quc2V0QXR0cnMoeyBpZDogYCR7dGhpcy5pbnB1dC5pZH0tbGlzdGJveGAsIHJvbGU6ICdsaXN0Ym94JyB9KTtcbiAgICAgIGxpc3Qub24oJ2JsdXInLCB0aGlzLm9uQmx1cik7XG4gICAgICB0aGlzLmxpc3QgPSBsaXN0O1xuXG4gICAgICBpZiAoaW5wdXRMYWJlbCkge1xuICAgICAgICBpZiAoIWlucHV0TGFiZWwuaWQpIGlucHV0TGFiZWwuaWQgPSBgJHt0aGlzLmlucHV0LmlkfS1sYWJlbGA7XG4gICAgICAgIGxpc3Quc2V0QXR0cignYXJpYS1sYWJlbGxlZGJ5JywgaW5wdXRMYWJlbC5pZCk7XG4gICAgICB9XG5cbiAgICAgIC8vIENyZWF0ZSBhdXRvLXN1Z2dlc3Rpb24gcGFuZS5cbiAgICAgIGNvbnN0IHdyYXBwZXIgPSBuZXcgdHMuRWxlbWVudCgnZGl2JywgeyBjbGFzczogJ2F1dG9jb21wbGV0ZV9fb3B0aW9ucy1wYW5lJywgc3R5bGU6IHsgZGlzcGxheTogJ25vbmUnIH0gfSk7XG4gICAgICB3cmFwcGVyLm9uKCdtb3VzZWRvd24nLCB0aGlzLm9uTW91c2VEb3duLmJpbmQodGhpcykpO1xuICAgICAgd3JhcHBlci5hcHBlbmRDaGlsZChsaXN0LndyYXApO1xuICAgICAgd3JhcHBlci5hdHRhY2hUbyh0aGlzLmlucHV0LCAnYWZ0ZXInKTtcbiAgICAgIHRoaXMuc3VnZ2VzdFdyYXAgPSB3cmFwcGVyO1xuXG4gICAgICAvLyBDcmVhdGUgYSBjb250YWluZXIgZm9yIGRpc3BsYXlpbmcgZW1wdHkgcmVzdWx0cyBtZXNzYWdlLlxuICAgICAgdGhpcy5lbXB0eU1zZyA9IG5ldyB0cy5FbGVtZW50KCdkaXYnLCB7XG4gICAgICAgIGNsYXNzOiAnYXV0b2NvbXBsZXRlX19lbXB0eS1tc2cnLFxuICAgICAgICBzdHlsZTogeyBkaXNwbGF5OiAnbm9uZScgfSxcbiAgICAgIH0sIHdyYXBwZXIpO1xuXG4gICAgICAvLyBDcmVhdGUgYSBsaXZlIHJlZ2lvbiBmb3IgYW5ub3VuY2luZyByZXN1bHQgdXBkYXRlcy5cbiAgICAgIHRoaXMubGl2ZVJlZ2lvbiA9IG5ldyBMaXZlUmVnaW9uKCk7XG5cbiAgICAgIC8vIERldGVybWluZSBpZiB0aGUgYXV0b2NvbXBsZXRlIHZhbHVlIGlzIHNlcGFyYXRlIGZyb20gdGhlIGRpc3BsYXllZFxuICAgICAgLy8gdGV4dC4gV2hlbiB0aGlzIHZhbHVlIGlzIHNwbGl0LCB3ZSBoYXZlIGEgY2xvbmVkIEFDIHRleHRmaWVsZC5cbiAgICAgIGlmICh0aGlzLmNvbmZpZy5zZXBhcmF0ZVZhbHVlKSB7XG4gICAgICAgIGFjID0gbmV3IHRzLkZvcm1FbGVtZW50KHRoaXMuaW5wdXQuY2xvbmVOb2RlKCksIHtcbiAgICAgICAgICBwbGFjZWhvbGRlcjogdGhpcy5pbnB1dC5wbGFjZWhvbGRlcixcbiAgICAgICAgICB2YWx1ZTogdGhpcy5pbnB1dC5kYXRhc2V0LnRleHQgfHwgdGhpcy5mb3JtYXREaXNwbGF5VmFsdWUodGhpcy5pbnB1dC52YWx1ZSkgfHwgJycsXG4gICAgICAgICAgJ2FyaWEtYXV0b2NvbXBsZXRlJzogJ2xpc3QnLFxuICAgICAgICB9KTtcbiAgICAgICAgYWMucmVtb3ZlQ2xhc3MoJ3Rvb2xzaGVkLWF1dG9jb21wbGV0ZScpO1xuICAgICAgICBhYy5yZW1vdmVBdHRycyhbJ25hbWUnLCAnZGF0YS1hdXRvY29tcGxldGUnXSk7XG5cbiAgICAgICAgdGhpcy5pbnB1dC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgICBhYy5hdHRhY2hUbyh0aGlzLmlucHV0LCAnYWZ0ZXInKTtcblxuICAgICAgICAvLyBSZW1vdmUgdGhlc2UgRHJ1cGFsIHByb3BlcnRpZXMgYXMgdGhpcyBpcyBqdXN0IGEgc3RhbmQtaW4gb2JqZWN0XG4gICAgICAgIC8vIGFuZCB3ZSBkb24ndCB3YW50IHRvIHN1Ym1pdCBvciB0byBjYXRjaCBhbnkgRHJ1cGFsIGJlaGF2aW9ycy5cbiAgICAgICAgZGVsZXRlIGFjLmRhdGFzZXQuZHJ1cGFsU2VsZWN0b3I7XG4gICAgICAgIGRlbGV0ZSBhYy5kYXRhc2V0LmF1dG9jb21wbGV0ZVBhdGg7XG4gICAgICAgIGRlbGV0ZSBhYy5kYXRhc2V0LnRleHQ7XG5cbiAgICAgICAgLy8gUmVhc3NvY2lhdGUgYSBsYWJlbCBlbGVtZW50IHRvIHBvaW50IHRvIHRoZSBuZXcgYXV0b2NvbXBsZXRlIGlucHV0LlxuICAgICAgICBpZiAoaW5wdXRMYWJlbCkge1xuICAgICAgICAgIGFjLmlkID0gYCR7dGhpcy5pbnB1dC5pZH0tYXV0b2NvbXBsZXRlYDtcbiAgICAgICAgICBpbnB1dExhYmVsLmh0bWxGb3IgPSBhYy5pZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZWxzZSB7XG4gICAgICAgIGFjID0gbmV3IHRzLkZvcm1FbGVtZW50KHRoaXMuaW5wdXQpO1xuICAgICAgICBhYy5zZXRBdHRyKCdhcmlhLWF1dG9jb21wbGV0ZScsICdib3RoJyk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuYWMgPSBhYztcbiAgICAgIGFjLnNldEF0dHJzKHtcbiAgICAgICAgY2xhc3M6ICdmb3JtLWF1dG9jb21wbGV0ZScsXG4gICAgICAgIGF1dG9jb21wbGV0ZTogJ29mZicsXG4gICAgICAgIHJvbGU6ICdjb21ib2JveCcsXG4gICAgICAgICdhcmlhLW93bnMnOiBsaXN0LmlkLFxuICAgICAgICAnYXJpYS1oYXNwb3B1cCc6ICdsaXN0Ym94JyxcbiAgICAgICAgJ2FyaWEtZXhwYW5kZWQnOiAnZmFsc2UnLFxuICAgICAgfSk7XG5cbiAgICAgIC8vIEJpbmQga2V5IGNoYW5nZSBldmVudHMuXG4gICAgICBhYy5vbigna2V5ZG93bicsIHRoaXMub25UZXh0S2V5ZG93bi5iaW5kKHRoaXMpKTtcbiAgICAgIGFjLm9uKCdpbnB1dCcsIHRoaXMub25UZXh0Q2hhbmdlLmJpbmQodGhpcykpO1xuICAgICAgYWMub24oJ2ZvY3VzJywgdGhpcy5vbkZvY3VzLmJpbmQodGhpcykpO1xuICAgICAgYWMub24oJ2JsdXInLCB0aGlzLm9uQmx1ci5iaW5kKHRoaXMpKTtcblxuICAgICAgaWYgKHRoaXMuY29uZmlnLmRlbGF5ID4gMCkge1xuICAgICAgICB0aGlzLmZldGNoU3VnZ2VzdGlvbnMgPSBkZWJvdW5jZSh0aGlzLmZldGNoU3VnZ2VzdGlvbnMsIHRoaXMuY29uZmlnLmRlbGF5KTtcbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuY29uZmlnLnBhcmFtcyAmJiB0aGlzLmlucHV0LmZvcm0pIHtcbiAgICAgICAgY29uc3QgeyBmb3JtIH0gPSB0aGlzLmlucHV0O1xuICAgICAgICB0aGlzLm9uUGFyYW1DaGFuZ2UgPSB0aGlzLm9uUGFyYW1DaGFuZ2UuYmluZCh0aGlzKTtcblxuICAgICAgICBPYmplY3QudmFsdWVzKHRoaXMuY29uZmlnLnBhcmFtcykuZm9yRWFjaCgoZWxlbUlkKSA9PiB7XG4gICAgICAgICAgY29uc3QgZWwgPSBmb3JtLnF1ZXJ5U2VsZWN0b3IoYCMke2VsZW1JZH1gKTtcblxuICAgICAgICAgIGlmIChlbCkgZWwuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgdGhpcy5vblBhcmFtQ2hhbmdlKTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVmcmVzaCB0aGUgYXV0b2NvbXBsZXRlIHN1Z2dlc3RzIHRvIGRpc3BsYXkuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0FycmF5fE9iamVjdH0gW2RhdGE9e31dXG4gICAgICogICBWYWx1ZXMgdG8gdXNlIGFzIHRoZSBhdXRvY29tcGxldGUgc3VnZ2VzdGlvbnMuIFRoZSBwcm9wZXJ0eSBrZXlzIGFyZVxuICAgICAqICAgdGhlIHZhbHVlLCBhbmQgdGhlIHByb3BlcnR5IHZhbHVlcyBhcmUgdGhlIGRpc3BsYXkgYXV0b2NvbXBsZXRlIHZhbHVlLlxuICAgICAqL1xuICAgIGNyZWF0ZVN1Z2dlc3Rpb25zKGRhdGEgPSB7fSkge1xuICAgICAgdGhpcy5hY3RpdmVJdGVtID0gbnVsbDtcblxuICAgICAgaWYgKCF0aGlzLmxpc3QuaXNFbXB0eSgpKSB7XG4gICAgICAgIHRoaXMubGlzdC5jbGVhcigpO1xuICAgICAgfVxuXG4gICAgICBpZiAoZGF0YS5saXN0Lmxlbmd0aCkge1xuICAgICAgICBpZiAodGhpcy5lbXB0eU1zZy5zdHlsZS5kaXNwbGF5ICE9PSAnbm9uZScpIHtcbiAgICAgICAgICB0aGlzLmVtcHR5TXNnLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmxpc3QuYnVpbGRJdGVtcyhkYXRhLmxpc3QsIGAke3RoaXMuaW5wdXQuaWR9LW9wdGApO1xuICAgICAgfVxuICAgICAgZWxzZSB7XG4gICAgICAgIHRoaXMuZW1wdHlNc2cudGV4dENvbnRlbnQgPSAoZGF0YS5lbXB0eSkgPyBkYXRhLmVtcHR5IDogJ05vIHJlc3VsdHMnO1xuICAgICAgICB0aGlzLmVtcHR5TXNnLnN0eWxlLmRpc3BsYXkgPSAnJztcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGb3JtYXQgdGhlIGlucHV0IHZhbHVlIHRvIHRoZSBkaXNwbGF5IHRleHQuIEJ5IGRlZmF1bHQgdGhlIHRleHQgdGhhdCBhcHBlYXJzIGJlZm9yZSBcIjpcIiBpc1xuICAgICAqIGhpZGRlbiBhbmQgY29uc2lkZXJlZCBhbiBpbnRlcm5hbCB2YWx1ZSBzd2l0Y2guIFN1YmNsYXNzZXMgb2YgdGhpcyBhdXRvY29tcGxldGUgY2FuXG4gICAgICogb3ZlcnJpZGUgdGhpcyBhbmQgZGVmaW5lIHRoZWlyIG93biBkaXNwbGF5IHRleHQgdmFsdWUgZm9ybWF0dGluZy5cbiAgICAgKi9cbiAgICBmb3JtYXREaXNwbGF5VmFsdWUodmFsdWUpIHtcbiAgICAgIHJldHVybiB2YWx1ZSA/IHZhbHVlLnJlcGxhY2UoL15bXjpdKj9cXHMqOlxccyovLCAnJykgOiAnJztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbGVhciB0aGUgY3VycmVudCBpbnB1dC5cbiAgICAgKi9cbiAgICBjbGVhcklucHV0KCkge1xuICAgICAgdGhpcy5hYy52YWx1ZSA9ICcnO1xuICAgICAgdGhpcy5pbnB1dC52YWx1ZSA9ICcnO1xuICAgICAgdGhpcy5jbGVhclN1Z2dlc3Rpb25zKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIGV4aXN0aW5nIGF1dG9jb21wbGV0ZSBzdWdnZXN0aW9ucy5cbiAgICAgKi9cbiAgICBjbGVhclN1Z2dlc3Rpb25zKCkge1xuICAgICAgdGhpcy5hYy5yZW1vdmVBdHRycygnYXJpYS1hY3RpdmVkZXNjZW5kYW50Jyk7XG4gICAgICB0aGlzLmFjdGl2ZUl0ZW0gPSBudWxsO1xuICAgICAgdGhpcy5saXN0LmNsZWFyKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSXMgdGhlIHN1Z2dlc3Rpb24gd2luZG93IG9wZW4/XG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtCb29sZWFufVxuICAgICAqICAgVFJVRSBpZiB0aGUgc3VnZ2VzdGlvbnMgd2luZG93IGlzIG9wZW4sIG90aGVyd2lzZSBGQUxTRS5cbiAgICAgKi9cbiAgICBpc1N1Z2dlc3Rpb25zVmlzaWJsZSgpIHtcbiAgICAgIHJldHVybiB0aGlzLnN1Z2dlc3RXcmFwLnN0eWxlLmRpc3BsYXkgIT09ICdub25lJztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBQb3NpdGlvbiBhbmQgZXhwb3NlIHRoZSBzdWdnZXN0aW9ucyB3aW5kb3cgaWYgaXQgaXMgbm90IGFscmVhZHkgb3Blbi5cbiAgICAgKi9cbiAgICBkaXNwbGF5U3VnZ2VzdGlvbnMoKSB7XG4gICAgICBpZiAoIXRoaXMuaXNTdWdnZXN0aW9uc1Zpc2libGUoKSkge1xuICAgICAgICB0aGlzLnN1Z2dlc3RXcmFwLnNldFN0eWxlcyh7XG4gICAgICAgICAgZGlzcGxheTogJycsXG4gICAgICAgICAgd2lkdGg6IGAke3RoaXMuYWMuZWwuY2xpZW50V2lkdGh9cHhgLFxuICAgICAgICAgIHRvcDogYCR7dGhpcy5hYy5lbC5vZmZzZXRUb3AgKyB0aGlzLmFjLmVsLm9mZnNldEhlaWdodH1weGAsXG4gICAgICAgICAgbGVmdDogYCR7dGhpcy5hYy5lbC5vZmZzZXRMZWZ0fXB4YCxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5hYy5zZXRBdHRycyh7ICdhcmlhLWV4cGFuZGVkJzogJ3RydWUnIH0pO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBjdCA9IHRoaXMubGlzdC5sZW5ndGg7XG4gICAgICB0aGlzLmxpdmVSZWdpb24ubWVzc2FnZSA9IGN0ID4gMFxuICAgICAgICA/IHQoJ0Bjb3VudCByZXN1bHRzIGF2YWlsYWJsZSwgdXNlIHVwIGFuZCBkb3duIGFycm93IGtleXMgdG8gbmF2aWdhdGUuJywgeyAnQGNvdW50JzogY3QgfSlcbiAgICAgICAgOiB0KCdObyBzZWFyY2ggcmVzdWx0cy4nKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBIaWRlIHRoZSBzdWdnZXN0aW9ucyB3aW5kb3cgaWYgaXQgaXMgb3BlbiBhbmQgcmVzZXQgdGhlIGFjdGl2ZSBpdGVtLlxuICAgICAqL1xuICAgIGhpZGVTdWdnZXN0aW9ucygpIHtcbiAgICAgIHRoaXMuYWN0aXZlSXRlbSA9IG51bGw7XG4gICAgICB0aGlzLnN1Z2dlc3RXcmFwLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgICB0aGlzLmFjLnNldEF0dHJzKHsgJ2FyaWEtZXhwYW5kZWQnOiAnZmFsc2UnIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENyZWF0ZSB0aGUgY29udGVudCB0byBkaXNwbGF5IGluIHRoZSBhdXRvY29tcGxldGUuXG4gICAgICpcbiAgICAgKiBIYW5kbGVzIGVpdGhlciBidWlsZGluZyBhIHNpbXBsZSB0ZXh0IGRpc3BsYXkgb3IgYnVpbGRpbmcgdGhlIEhUTUwgdG9cbiAgICAgKiBhIGNvbXBsZXggZGlzcGxheSwgd2l0aCBhIHRleHQgbGFiZWwuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge1N1Z2dlc3RJdGVtfSBpdGVtXG4gICAgICogICBUaGUgc3VnZ2VzdGlvbiBpdGVtIHRvIGNoYW5nZSB0aGUgaW5uZXIgY29udGVudCBvZi5cbiAgICAgKiBAcGFyYW0ge09iamVjdH0gZGF0YVxuICAgICAqICAgVGhlIHJhdyBkYXRhIGZyb20gdGhlIGF1dG9jb21wbGV0ZSByZXNwb25zZS5cbiAgICAgKi9cbiAgICBpdGVtRGlzcGxheShpdGVtLCBkYXRhKSB7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgY2xhc3MtbWV0aG9kcy11c2UtdGhpc1xuICAgICAgaWYgKGRhdGEuaHRtbCkge1xuICAgICAgICBpdGVtLmlubmVySFRNTCA9IGRhdGEuaHRtbDtcbiAgICAgICAgaXRlbS5zZXRBdHRycyh7ICdhcmlhLWxhYmVsJzogZGF0YS50ZXh0IHx8IGRhdGEudmFsdWUgfSk7XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgaXRlbS50ZXh0Q29udGVudCA9IGl0ZW0udGV4dCB8fCBpdGVtLnZhbHVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldCBhIFN1Z2dlc3RJdGVtIGFzIGN1cnJlbnRseSBhY3RpdmUgYmFzZWQgb24gdGhlIGluZGV4LiBUaGlzIG1ldGhvZFxuICAgICAqIGVuc3VyZXMgdGhhdCBhbnkgY3VycmVudGx5IGFjdGl2ZSBpdGVtcyBhcmUgYmx1cnJlZCAodW5mb2N1c2VkKSBhbmRcbiAgICAgKiBpZiB0aGUgcmVxdWVzdGVkIGluZGV4IGlzIG91dCBvZiByYW5nZSwgdG8gc2VsZWN0IG5vIGl0ZW1zLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtTdWdnZXN0SXRlbX0gaXRlbVxuICAgICAqICAgVGhlIGl0ZW0gdG8gc2V0IGFzIHRoZSBjdXJyZW50IGFjdGl2ZSBpdGVtLlxuICAgICAqL1xuICAgIHNldEFjdGl2ZUl0ZW0oaXRlbSkge1xuICAgICAgaWYgKHRoaXMuYWN0aXZlSXRlbSkgdGhpcy5hY3RpdmVJdGVtLmJsdXIoKTtcblxuICAgICAgaWYgKGl0ZW0pIHtcbiAgICAgICAgaXRlbS5mb2N1cygpO1xuICAgICAgICB0aGlzLmFjdGl2ZUl0ZW0gPSBpdGVtO1xuXG4gICAgICAgIGlmIChpdGVtLmlkKSB7XG4gICAgICAgICAgdGhpcy5hYy5zZXRBdHRycyh7ICdhcmlhLWFjdGl2ZWRlc2NlbmRhbnQnOiBpdGVtLmlkIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgdGhpcy5hY3RpdmVJdGVtID0gbnVsbDtcbiAgICAgICAgdGhpcy5hYy5yZW1vdmVBdHRycygnYXJpYS1hY3RpdmVkZXNjZW5kYW50Jyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVHJhbnNmZXIgdGhlIHZhbHVlcyBmcm9tIHRoZSBwYXNzZWQgaW4gaXRlbSB0byB0aGUgZm9ybSBlbGVtZW50cy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7U3VnZ2VzdEl0ZW19IGl0ZW1cbiAgICAgKiAgIEl0ZW0gdG8gYXBwbHkgdG8gYXMgdGhlIHZhbHVlcyBvZiB0aGUgYXV0b2NvbXBsZXRlIHdpZGdldC5cbiAgICAgKi9cbiAgICBzZWxlY3RJdGVtKGl0ZW0pIHtcbiAgICAgIGlmIChpdGVtLnVybCkge1xuICAgICAgICB3aW5kb3cubG9jYXRpb24gPSBpdGVtLnVybDtcbiAgICAgIH1cbiAgICAgIGVsc2Uge1xuICAgICAgICBpZiAodGhpcy5hYy5lbCAhPT0gdGhpcy5pbnB1dCkge1xuICAgICAgICAgIHRoaXMuYWMudmFsdWUgPSBpdGVtLnRleHQgfHwgaXRlbS52YWx1ZTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmlucHV0LnZhbHVlID0gaXRlbS52YWx1ZTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5oaWRlU3VnZ2VzdGlvbnMoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBNYWtlIHRoZSBuZWNlc3NhcnkgcmVxdWVzdHMgYW5kIGNhbGxzIHRvIGdldCBhdmFpbGFibGUgdGV4dCB2YWx1ZXMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdGV4dFxuICAgICAqICAgVGhlIHRleHQgdG8gdHJ5IHRvIG1hdGNoIHVzaW5nIHRoZSBhdXRvY29tcGxldGUgc2VydmljZSB0b1xuICAgICAqICAgZ2VuZXJhdGUgc3VnZ2VzdGlvbnMgd2l0aC5cbiAgICAgKiBAcGFyYW0ge2Jvb2x9IGRpc3BsYXlcbiAgICAgKiAgIFNob3VsZCB0aGUgc3VnZ2VzdGlvbnMgbGlzdCBiZSBkaXNwbGF5ZWQgYWZ0ZXIgZmV0Y2hpbmcgc3VnZ2VzdGlvbnMuXG4gICAgICovXG4gICAgZmV0Y2hTdWdnZXN0aW9ucyh0ZXh0LCBkaXNwbGF5ID0gdHJ1ZSkge1xuICAgICAgaWYgKCF0aGlzLnJlcXVlc3Rlcikge1xuICAgICAgICB0aGlzLnJlcXVlc3RlciA9IHRzLmNyZWF0ZVJlcXVlc3Rlcih0aGlzLmNvbmZpZy51cmkpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5wZW5kaW5nICYmICF0aGlzLnBlbmRpbmcucHJvbWlzZS5pc1Jlc29sdmVkKSB7XG4gICAgICAgIC8vIEFib3J0IGFueSBwcmV2aW91c2x5IHBlbmRpbmcgcmVxdWVzdHMsIHNvIHRoaXMgbGF0ZSByZXNwb25zZVxuICAgICAgICAvLyB3b24ndCBvdmVyd3JpdGUgb3VyIGRlc2lyZWQgdmFsdWVzIGlmIGl0IHJldHVybnMgb3V0IG9mIG9yZGVyLlxuICAgICAgICB0aGlzLnBlbmRpbmcueGhyLmFib3J0KCk7XG4gICAgICB9XG5cbiAgICAgIGlmICghdGV4dCB8fCB0ZXh0Lmxlbmd0aCA8IHRoaXMuY29uZmlnLm1pbkxlbmd0aCkge1xuICAgICAgICBpZiAodGhpcy5pc1N1Z2dlc3Rpb25zVmlzaWJsZSgpKSB0aGlzLmhpZGVTdWdnZXN0aW9ucygpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIC8vIFR1cm4gb24gdGhlIGxvYWRpbmcgc3Bpbm5lci5cbiAgICAgIHRoaXMuYWMuYWRkQ2xhc3MoJ3VpLWF1dG9jb21wbGV0ZS1sb2FkaW5nJyk7XG5cbiAgICAgIC8vIEFwcGx5IGFkZGl0aW9uYWwgcmVxdWVzdCBwYXJhbWV0ZXJzIGlmIHRoZXJlIGFyZSBsaW5rZWQgaW5wdXRzLlxuICAgICAgY29uc3QgcmVxdWVzdFBhcmFtcyA9IHt9O1xuICAgICAgaWYgKHRoaXMuY29uZmlnLnBhcmFtcykge1xuICAgICAgICBPYmplY3QuZW50cmllcyh0aGlzLmNvbmZpZy5wYXJhbXMpLmZvckVhY2goKFtrZXksIGVsZW1JZF0pID0+IHtcbiAgICAgICAgICBjb25zdCBpbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGVsZW1JZCk7XG5cbiAgICAgICAgICBpZiAoaW5wdXQgJiYgaW5wdXQudmFsdWUpIHJlcXVlc3RQYXJhbXNba2V5XSA9IGlucHV0LnZhbHVlO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIHJlcXVlc3RQYXJhbXMucSA9IHRleHQ7XG5cbiAgICAgIHRoaXMucGVuZGluZyA9IHRoaXMucmVxdWVzdGVyKHJlcXVlc3RQYXJhbXMpO1xuICAgICAgdGhpcy5wZW5kaW5nLnByb21pc2UudGhlbihcbiAgICAgICAgKHJlc3BvbnNlKSA9PiB7XG4gICAgICAgICAgdGhpcy5jbGVhckxvYWRpbmcodGhpcy5hYyk7XG4gICAgICAgICAgdGhpcy5jcmVhdGVTdWdnZXN0aW9ucyhyZXNwb25zZSk7XG5cbiAgICAgICAgICAvLyBJZiByZXF1ZXN0ZWQgdG8gZGlzcGxheSBzdWdnZXN0aW9ucyBhZnRlciB0aGV5IGxvYWQuXG4gICAgICAgICAgaWYgKGRpc3BsYXkpIHRoaXMuZGlzcGxheVN1Z2dlc3Rpb25zKCk7XG4gICAgICAgIH0sXG4gICAgICAgIChyZWFzb24pID0+IHtcbiAgICAgICAgICAvLyBPbmx5IHJlbW92ZSB0aGUgYXV0b2NvbXBsZXRlIGxvYWRpbmcgaWYgdGhlIHJlcXVlc3Qgd2FzIGFib3J0ZWQuXG4gICAgICAgICAgaWYgKCEocmVhc29uLm1lc3NhZ2UgJiYgcmVhc29uLm1lc3NhZ2UgPT09ICdDYW5jZWxsZWQnKSkge1xuICAgICAgICAgICAgdGhpcy5jbGVhckxvYWRpbmcodGhpcy5hYyk7XG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbGVhciB0aGUgYXV0b2NvbXBsZXRlIGxvYWRpbmcgc3RhdHVzIGFuZCBkaXNwbGF5cy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7VG9vbHNoZWRFbGVtZW50fSBlbFxuICAgICAqICAgQXV0b2NvbXBsZXRlIGVsZW1lbnQgdG8gcmVtb3ZlIGxvYWRpbmcgc3RhdHVzIGZyb20uXG4gICAgICovXG4gICAgY2xlYXJMb2FkaW5nKGVsKSB7XG4gICAgICAvLyBDbGVhciB0aGUgbG9hZGluZyBzcGlubmVyLlxuICAgICAgZWwucmVtb3ZlQ2xhc3MoWyd1aS1hdXRvY29tcGxldGUtbG9hZGluZycsICdpcy1hdXRvY29tcGxldGluZyddKTtcblxuICAgICAgLy8gSGlkZSBDbGFybyBvciBvdGhlciB0aGVtZSBhdXRvY29tcGxldGUgbWVzc2FnZSBlbGVtZW50cy5cbiAgICAgIGNvbnN0IG1zZyA9IGVsLnBhcmVudEVsZW1lbnQucXVlcnlTZWxlY3RvcignOnNjb3BlID4gW2RhdGEtZHJ1cGFsLXNlbGVjdG9yPVwiYXV0b2NvbXBsZXRlLW1lc3NhZ2VcIl0nKTtcbiAgICAgIGlmIChtc2cpIHtcbiAgICAgICAgbXNnLmNsYXNzTGlzdC5hZGQoJ2hpZGRlbicpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFdoZW4gZm9jdXMgaXMgb24gdGhlIGF1dG9jb21wbGV0ZSBpbnB1dCwgc2hvdyBzdWdnZXN0aW9ucyBpZiB0aGV5IGV4aXN0LlxuICAgICAqL1xuICAgIG9uRm9jdXMoKSB7XG4gICAgICBpZiAoIXRoaXMubGlzdC5pc0VtcHR5KCkpIHtcbiAgICAgICAgdGhpcy5kaXNwbGF5U3VnZ2VzdGlvbnMoKTtcbiAgICAgIH1cbiAgICB9XG5cblxuICAgIC8qKlxuICAgICAqIFdoZW4gZm9jdXMgbGVhdmVzIHRoZSBhdXRvY29tcGxldGUgaW5wdXQsIGhpZGUgc3VnZ2VzdGlvbnMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0JsdXJFdmVudH0gZVxuICAgICAqICAgVGhlIGJsdXIgZXZlbnQgaW5mb3JtYXRpb24gZm9yIHdoZW4gdGhlIGF1dG9jb21wbGV0ZSBsb3NlcyBmb2N1cy5cbiAgICAgKi9cbiAgICBvbkJsdXIoZSkge1xuICAgICAgaWYgKHRoaXMuaXNTdWdnZXN0aW9uc1Zpc2libGUoKSkge1xuICAgICAgICBpZiAoIWUucmVsYXRlZFRhcmdldCB8fCAhKGUucmVsYXRlZFRhcmdldCA9PT0gdGhpcy5hYy5lbCB8fCBlLnJlbGF0ZWRUYXJnZXQuY2xvc2VzdCgnW3JvbGU9bGlzdGJveF0nKSA9PT0gdGhpcy5saXN0LmVsKSkge1xuICAgICAgICAgIHRoaXMuaGlkZVN1Z2dlc3Rpb25zKCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBtb3VzZSBkb3duIGV2ZW50IHdoaWNoIGF2b2lkcyBoYXZpbmcgdGhlIEFDIGlucHV0IGxvc2UgZm9jdXNcbiAgICAgKiBmcm9tIGNsaWNraW5nIGludG8gdGhlIHN1Z2dlc3Rpb25zIHdyYXBwZXIuIFRoaXMgaW5jb252ZW5pZW50bHkgcHJldmVudHNcbiAgICAgKiB0aGUgY2xpY2sgZXZlbnQgYmVjYXVzZSBpdCBoaWRlcyB0aGUgc3VnZ2VzdGlvbnMgYmVmb3JlIHRoZSBjbGljayBldmVudFxuICAgICAqIGNhbiBvY2N1ci5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50XG4gICAgICogICBUaGUgbW91c2UgZG93biBldmVudCBvYmplY3QuXG4gICAgICovXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNsYXNzLW1ldGhvZHMtdXNlLXRoaXNcbiAgICBvbk1vdXNlRG93bihldmVudCkge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYWxsYmFjayBmb3IgdXBkYXRpbmcgdGhlIGlucHV0IHZhbHVlIHdoZW4gdGhlIHRleHRmaWVsZCBpcyB1cGRhdGVkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtFdmVudH0gZXZlbnRcbiAgICAgKiAgIFRoZSBpbnB1dCBjaGFuZ2VkIGV2ZW50IG9iamVjdC5cbiAgICAgKi9cbiAgICBvblRleHRDaGFuZ2UoZXZlbnQpIHtcbiAgICAgIGlmICh0aGlzLmFjLmVsICE9PSB0aGlzLmlucHV0KSB7XG4gICAgICAgIHRoaXMuaW5wdXQudmFsdWUgPSB0aGlzLmNvbmZpZy5yZXF1aXJlU2VsZWN0ID8gJycgOiB0aGlzLmFjLmVsLnZhbHVlO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmZldGNoU3VnZ2VzdGlvbnMoZXZlbnQudGFyZ2V0LnZhbHVlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBJbnB1dCB2YWx1ZSBjaGFuZ2VkIGNhbGxiYWNrIHRvIHJlZnJlc2ggb3VyIGlucHV0IHZhbHVlcyB3aGVuIGFcbiAgICAgKiBkZXBlbmRlbnQgcGFyYW1ldGVyIGNoYW5nZXMuXG4gICAgICovXG4gICAgb25QYXJhbUNoYW5nZSgpIHtcbiAgICAgIHRoaXMuY2xlYXJJbnB1dCgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlc3BvbmQgdG8ga2V5Ym9hcmQgbmF2aWdhdGlvbiwgYW5kIG1vdmUgdGhlIGFjdGl2ZSBpdGVtLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtLZXlkb3duRXZlbnR9IGVcbiAgICAgKiAgIFJlc3BvbmQgdG8gYSBrZXktdXAgZXZlbnQgZm9yIHRoZSBhdXRvY29tcGxldGUgaW5wdXQgdGV4dGZpZWxkLlxuICAgICAqL1xuICAgIG9uVGV4dEtleWRvd24oZSkge1xuICAgICAgaWYgKCF0aGlzLmlzU3VnZ2VzdGlvbnNWaXNpYmxlKCkpIHJldHVybjtcblxuICAgICAgbGV0IGluYztcbiAgICAgIGxldCBtZXRob2Q7XG5cbiAgICAgIHN3aXRjaCAoZS5rZXlDb2RlKSB7XG4gICAgICAgIGNhc2UgMTM6IC8vIEVudGVyIGtleVxuICAgICAgICAgIGlmICh0aGlzLmFjdGl2ZUl0ZW0pIHtcbiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIHRoaXMuc2VsZWN0SXRlbSh0aGlzLmFjdGl2ZUl0ZW0pO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAvLyBlc2xpbnQtaWdub3JlLWxpbmUgbm8tZmFsbHRocm91Z2hcbiAgICAgICAgY2FzZSA5OiAvLyBUYWIga2V5XG4gICAgICAgIGNhc2UgMjc6IC8vIEVzY2FwZSBrZXlcbiAgICAgICAgICB0aGlzLmhpZGVTdWdnZXN0aW9ucygpO1xuICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBjYXNlIDQwOiAvLyBVcCBrZXlcbiAgICAgICAgICBtZXRob2QgPSBTdWdnZXN0TGlzdC5wcm90b3R5cGUuZ2V0Rmlyc3Q7XG4gICAgICAgICAgaW5jID0gMTtcbiAgICAgICAgICBicmVhaztcblxuICAgICAgICBjYXNlIDM4OiAvLyBEb3duIGtleVxuICAgICAgICAgIG1ldGhvZCA9IFN1Z2dlc3RMaXN0LnByb3RvdHlwZS5nZXRMYXN0O1xuICAgICAgICAgIGluYyA9IC0xO1xuICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG5cbiAgICAgIGxldCBpdGVtID0gdGhpcy5hY3RpdmVJdGVtO1xuICAgICAgaWYgKGl0ZW0pIHtcbiAgICAgICAgbGV0IHAgPSBpdGVtLnBhcmVudDtcbiAgICAgICAgbGV0IGkgPSBpdGVtLnBvcyArIGluYztcblxuICAgICAgICB3aGlsZSAocCAmJiAoaSA8IDAgfHwgcC5pdGVtcy5sZW5ndGggPT09IGkpKSB7XG4gICAgICAgICAgaXRlbSA9IHA7XG4gICAgICAgICAgaSA9IGl0ZW0ucG9zICsgaW5jO1xuICAgICAgICAgIHAgPSBpdGVtLnBhcmVudDtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChwKSB7XG4gICAgICAgICAgaXRlbSA9IHAuaXRlbXNbaV07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaXRlbSA9IGl0ZW0gfHwgdGhpcy5saXN0O1xuICAgICAgaWYgKGl0ZW0gaW5zdGFuY2VvZiBTdWdnZXN0TGlzdCkge1xuICAgICAgICBpdGVtID0gbWV0aG9kLmNhbGwoaXRlbSk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuc2V0QWN0aXZlSXRlbShpdGVtKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbGVhbiB1cCBET00gYW5kIGV2ZW50IGxpc3RlbmVycyBpbml0aWFsaXplZCBieSB0aGlzIGF1dG9jb21wZXRlIHdpZGdldC5cbiAgICAgKi9cbiAgICBkZXN0cm95KCkge1xuICAgICAgLy8gQWJvcnQgYW55IHBlbmRpbmcgcmVxdWVzdCBmb3IgdGhpcyB3aWRnZXQuXG4gICAgICBpZiAodGhpcy5wZW5kaW5nICYmICF0aGlzLnBlbmRpbmcucHJvbWlzZS5pc1Jlc29sdmVkKSB7XG4gICAgICAgIHRoaXMucGVuZGluZy54aHIuYWJvcnQoKTtcbiAgICAgIH1cbiAgICAgIGRlbGV0ZSB0aGlzLnBlbmRpbmc7XG5cbiAgICAgIGlmICh0aGlzLmNvbmZpZy5wYXJhbXMpIHtcbiAgICAgICAgT2JqZWN0LnZhbHVlcyh0aGlzLmNvbmZpZy5wYXJhbXMpLmZvckVhY2goKGVsZW1JZCkgPT4ge1xuICAgICAgICAgIGNvbnN0IGVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoZWxlbUlkKTtcblxuICAgICAgICAgIGlmIChlbCkgZWwucmVtb3ZlRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgdGhpcy5vblBhcmFtQ2hhbmdlKTtcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLmFjLmVsICE9PSB0aGlzLmlucHV0KSB7XG4gICAgICAgIGlmICh0aGlzLmFjLmlkICYmIHRoaXMuaW5wdXQuaWQpIHtcbiAgICAgICAgICAvLyBSZWFzc29jaWF0ZSBhIGxhYmVsIGVsZW1lbnQgdG8gcG9pbnQgdG8gdGhlIG9yaWdpbmFsIGlucHV0LlxuICAgICAgICAgIGNvbnN0IGlucHV0TGFiZWwgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBsYWJlbFtmb3I9JyR7dGhpcy5hYy5pZH0nXWApO1xuICAgICAgICAgIGlmIChpbnB1dExhYmVsKSB7XG4gICAgICAgICAgICBpbnB1dExhYmVsLmh0bWxGb3IgPSB0aGlzLmlucHV0LmlkO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuYWMuZGVzdHJveSh0cnVlKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5saXN0LmRlc3Ryb3kodHJ1ZSk7XG4gICAgICB0aGlzLmVtcHR5TXNnLmRlc3Ryb3kodHJ1ZSk7XG4gICAgICB0aGlzLnN1Z2dlc3RXcmFwLmRlc3Ryb3kodHJ1ZSk7XG4gICAgICB0aGlzLmxpdmVSZWdpb24uZGVzdHJveSh0cnVlKTtcbiAgICAgIHRoaXMuaW5wdXQuc3R5bGUuZGlzcGxheSA9ICcnO1xuICAgIH1cbiAgfTtcblxuICAvKipcbiAgICogRmluZCBhdXRvY29tcGxldGUgaW5wdXRzIGFuZCBhdHRhY2ggdGhlIGF1dG9jb21wbGV0ZSBiZWhhdmlvcnMgdG8gaXQuXG4gICAqL1xuICBiZWhhdmlvcnMudG9vbHNoZWRBdXRvY29tcGxldGUgPSB7XG4gICAgLy8gVHJhY2sgaW5zdGFuY2VzIG9mIEF1dG9jb21wbGV0ZSBvYmplY3RzIHNvIHRoZXkgY2FuIGJlIGRldGFjaGVkLlxuICAgIGluc3RhbmNlczogbmV3IE1hcCgpLFxuXG4gICAgYXR0YWNoKGNvbnRleHQpIHtcbiAgICAgIHRzLndhbGtCeUNsYXNzKGNvbnRleHQsICd0b29sc2hlZC1hdXRvY29tcGxldGUnLCAoaXRlbSkgPT4ge1xuICAgICAgICBjb25zdCBzZXR0aW5ncyA9IGl0ZW0uZGF0YXNldC5hdXRvY29tcGxldGUgPyBKU09OLnBhcnNlKGl0ZW0uZGF0YXNldC5hdXRvY29tcGxldGUpIDoge307XG5cbiAgICAgICAgaWYgKGl0ZW0uZGF0YXNldC5wYXJhbXMpIHtcbiAgICAgICAgICBzZXR0aW5ncy5wYXJhbXMgPSBKU09OLnBhcnNlKGl0ZW0uZGF0YXNldC5wYXJhbXMpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHNldHRpbmdzLnVyaSkge1xuICAgICAgICAgIGNvbnN0IGFjID0gbmV3IHRzLkF1dG9jb21wbGV0ZShpdGVtLCBzZXR0aW5ncyk7XG4gICAgICAgICAgdGhpcy5pbnN0YW5jZXMuc2V0KGl0ZW0uaWQgfHwgaXRlbSwgYWMpO1xuICAgICAgICB9XG4gICAgICB9LCAnYXV0b2NvbXBsZXRlLS1wcm9jZXNzZWQnKTtcbiAgICB9LFxuXG4gICAgZGV0YWNoKGNvbnRleHQsIHNldHRpbmdzLCB0cmlnZ2VyKSB7XG4gICAgICBpZiAodHJpZ2dlciA9PT0gJ3VubG9hZCcpIHtcbiAgICAgICAgdHMud2Fsa0J5U2VsZWN0b3IoY29udGV4dCwgJy50b29sc2hlZC1hdXRvY29tcGxldGUuYXV0b2NvbXBsZXRlLS1wcm9jZXNzZWQnLCAoaXRlbSkgPT4ge1xuICAgICAgICAgIGNvbnN0IGFjID0gdGhpcy5pbnN0YW5jZXMuZ2V0KGl0ZW0uaWQgfHwgaXRlbSk7XG5cbiAgICAgICAgICBpZiAoYWMpIHtcbiAgICAgICAgICAgIGl0ZW0uY2xhc3NMaXN0LnJlbW92ZSgnYXV0b2NvbXBsZXRlLS1wcm9jZXNzZWQnKTtcbiAgICAgICAgICAgIHRoaXMuaW5zdGFuY2VzLmRlbGV0ZShpdGVtKTtcbiAgICAgICAgICAgIGFjLmRlc3Ryb3koKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0sXG4gIH07XG59KShEcnVwYWwpO1xuIl0sIm1hcHBpbmdzIjoiOztBQUFBLENBQUMsQ0FBQztFQUNBQSxDQUFDO0VBQ0RDLFNBQVM7RUFDVEMsUUFBUTtFQUNSQyxRQUFRLEVBQUVDO0FBQ1osQ0FBQyxLQUFLO0VBQ0o7QUFDRjtB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

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc