toolshed-8.x-1.x-dev/js/EventListener.js

js/EventListener.js
"use strict";

(({
  Toolshed: ts,
  debounce
}) => {
  /**
   * Creates a queue of callable functions that can get managed and triggered
   * together. The main purpose of this for queuing event listeners or
   * registering a series of callbacks.
   *
   * CallList are orderable, and can be used to insert listeners around a
   * specified reference point (before or after another callable).
   */
  class CallList {
    /**
     * Ensure that a listener is a valid handler for the event used by this
     * EventListener. This test is for checking the listener before adding it
     * to the list of active listeners for this event.
     *
     * @param {Array|function} callable
     *   The object to test if it is valid for handling this event.
     *
     * @return {bool}
     *   Boolean to indicate if this listener is valid for handling this event.
     *   _true_ IFF this listener can be added and used with this event object.
     */
    static isCallable(callable) {
      let obj = null;
      let func = callable;
      if (callable instanceof Array) {
        [obj, func] = callable;
      }
      return typeof func === 'function' && (!obj || obj instanceof Object);
    }

    /**
     * Execute a callable item, with the parameters passed to the underlying
     * function or method. A callable is either a function or an array
     * (containing an object to use as the "this" and a method to execute).
     *
     * @param {function|Array} callable
     *   A callable is either a function that can be called directly or is an
     *   array that contains an object (used for "this") and a method to call.
     * @param {*[]} ...args
     *   Additional arguments passed with the called item.
     *
     * @return {*|null}
     *   The result of the callabable
     */
    static callItem(callable, ...args) {
      if (callable instanceof Array) {
        const [obj, func] = callable;
        return func.apply(obj, args);
      }
      if (typeof callable === 'function') {
        return callable(...args);
      }
      throw new Error('Unable to execute callable method.');
    }

    /**
     * Create a new instance of a callable list of items.
     */
    constructor() {
      this.list = [];
    }

    /**
     * Get the current size of the callable list.
     *
     * @return {int}
     *   The current size of the callables list array.
     */
    size() {
      return this.list.length;
    }

    /**
     * Make a call to a list of callables.
     *
     * @param {Event} param
     *   argument object to pass to each of the callables as they get
     *   called respectively.
     */
    call(param, ...args) {
      this.list.forEach(cb => CallList.callItem(cb, param, ...args));
    }

    /**
     * If there is a valid atPos, place the callable at this position,
     * otherwise, just add it to the end of the list. This allows some
     * flexibility to place callabbles at the start of the list, or
     * before other callables.
     *
     * @param {Array|function} callable
     *   A callable object to add to the list.
     * @param {int} atPos
     *   Index to add the callable at. This allows callables to be run in
     *   a different order than they maybe registered in.
     */
    add(callable, atPos) {
      if (!CallList.isCallable(callable)) {
        throw new Error('Trying to add new callback, but it is not a valid callable.');
      }

      // Ensure that all existing references to this event are removed.
      // Prevents the event from being called more than once unintentionally.
      this.remove(callable);
      if (atPos !== null && atPos >= 0) this.list.splice(atPos - 1, 0, callable);else this.list.push(callable);
    }

    /**
     * Remove the specified callable from the list of callables.
     *
     * @param {Array|function} callable
     *  A listener object that requests to get removed.
     */
    remove(callable) {
      let pos = this.indexOf(callable);
      while (pos >= 0) {
        this.list.splice(pos, 1);
        pos = this.indexOf(callable, pos);
      }
    }

    /**
     * Look for an a matching listener in the objects listener registry.
     * Listeners can be stored as either function references, or arrays. If
     * an array, the first item in the array is the object calling context,
     * and the second parameter is function to call.
     *
     * Matches can be found by function, array (object and function) or just
     * by matching the object contexts.
     *
     * @param {Array|Object|function} needle
     *   The matching listener to locate in the listeners array. If the param
     *   is an array, look for the matching object and function. If an object
     *   is passed in, find the first occurance of the object as the object
     *   context in the arrays. If a function, just search for the function.
     * @param {int} start
     *   The starting position in the listener array to search for the callback.
     *
     * @return {int}
     *   The index where the matching listener was found. If a matching listener
     *   was not found, return -1 to indicate no match is available.
     */
    indexOf(needle, start = 0) {
      if (typeof needle === 'function') {
        // For functions, matching is direct and straightforward.
        return this.list.indexOf(needle, start);
      }
      const [obj, func] = needle instanceof Array ? needle : [null, needle];
      for (; start < this.list.length; ++start) {
        const item = this.list[start];
        const [context, callback] = item instanceof Array ? item : [null, item];
        if (obj === context && (!func || func === callback)) {
          return start;
        }
      }
      return -1;
    }
  }

  /**
   * Attach DOM and element events, which allow global registration and utilize
   * a CallList for event calling and management. These event listeners
   * have options for handling debounce, callables (@see CallList) and
   * auto registration (only adding the event listener to the DOM when listener
   * is added).
   *
   * Toolshed.EventListener.{eventName} namespace. Some of the events in that
   * namespace may have customized event callback. An example of this is defined
   * in ./screen-events.es6.js which are used by the Toolshed.Dock.
   */
  ts.EventListener = class {
    /**
     * Constructor for creating of event listeners.
     *
     * @param {DOMElement} elem
     *   DOM element that will be the target of the event.
     * @param {string} eventName
     *   The event.
     * @param {null|object} options
     *   method:
     *     Name of the method to call on all listeners (special cases). Will call
     *     the default "on[this.eventName]" method if left blank.
     *   useCapture:
     *     Use capture instead of bubbling for event propagation.
     *   passive:
     *     Event handlers will not call preventDefault() which can enable browser
     *     optimatization that no longer need to wait for all handlers to complete
     *     before triggering other events like scrolling.
     *   debounce:
     *     Determine if the event only triggers using debounce handling. This means
     *     that events will only fire off after a short delay.
     *
     *     If null or FALSE, no debounce will be used, and the event registered
     *     fires off as soon as the event is raised.
     *
     *     If TRUE then use the default debounce delay. If an integer, than use the
     *     value as the delay in milliseconds.
     */
    constructor(elem, eventName, options) {
      options = options || {}; // options can be left blank.

      this.elem = elem;
      this.event = eventName;
      this.autoListen = options.autoListen || false;
      this.listeners = new CallList();

      // Check and properly organize the event options to be used later.
      if (options.debounce) {
        this.debounce = typeof options.debounce === 'boolean' ? 100 : options.debounce;
      }

      // Allow for addEventListener options as described here
      // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
      // I am also employing the https://github.com/WICG/EventListenerOptions
      // as a polyfill, but support will not be available for IE8 and earlier.
      this.eventOpts = {
        capture: options.capture || false,
        passive: options.passive || false
      };
    }

    /**
     * Trigger the event for all the registered listeners. Custom
     * EventListeners are most likely to override this function in order
     * to create implement special functionality, triggered by events.
     *
     * @param {Object} event
     *   The event object that was generated and passed to the event handler.
     */
    _run(event, ...args) {
      this.listners.call(event, ...args);
    }

    /**
     * Trigger the event manaully.
     *
     * @param {Event|null} event
     *   Event data to use with this event.
     *
     * @return {Drupal.Toolshed.EventListener}
     *   Return this instance of this EventListener for the purpose of chaining.
     */
    trigger(event, ...args) {
      this._run(event || new Event(this.event), args);
      return this;
    }

    /**
     * Register the event, and keep track of the callback so it can be removed
     * later if we need to disable / remove the listener at a later time.
     *
     * @return {Drupal.Toolshed.EventListener}
     *   Return this instance of this EventListener for the purpose of chaining.
     */
    listen() {
      if (!this.callback && (!this.autoListen || this.listeners.size())) {
        this.callback = this.debounce && this.debounce > 0 && debounce ? debounce(this._run.bind(this), this.debounce) : this._run.bind(this);
        this.elem.addEventListener(this.event, this.callback, this.eventOpts);
      }
      return this;
    }

    /**
     * Stop listening for this event, and unregister from any event listeners.
     *
     * @return {Drupal.Toolshed.EventListener}
     *   Return this instance of this EventListener for the purpose of chaining.
     */
    ignore() {
      if (this.callback) {
        this.elem.removeEventListener(this.event, this.callback);
        delete this.callback;
      }
      return this;
    }

    /**
     * If there is a valid atPos, place the listener at this position,
     * otherwise, just add it to the end of the list. This allows some
     * flexibility to place listeners at the start of the list, or
     * before other listeners.
     *
     * @param {Object} listener
     *   A listener object that contains the a method 'on' + [this.eventName].
     * @param {int} atPos
     *   Index to add the listener at. This allows listeners to be run in
     *   a different order than they maybe registered in.
     *
     * @return {Drupal.Toolshed.EventListener}
     *   Return this instance of this EventListener for the purpose of chaining.
     */
    add(listener, atPos) {
      this.listeners.add(listener, atPos);
      if (this.autoListen) this.listen();
      return this;
    }

    /**
     * Add a new listener before an existing listener already in the list.
     * If [before] is null, then insert at the start of the list.
     *
     * @param {Array|function} listener
     *   A listener object that contains the a method 'on' + [this.eventName].
     * @param {Array|function} before
     *   Listener object that is used to position the new listener.
     *
     * @return {Drupal.Toolshed.EventListener}
     *   Return this instance of this EventListener for the purpose of chaining.
     */
    addBefore(listener, before) {
      const pos = before ? this.listeners.indexOf(before) : 0;
      return this.add(listener, pos < 0 ? 0 : pos);
    }

    /**
     * Add a new listener after an existing listener already in the list.
     * If [after] is null, then insert at the end of the list.
     *
     * @param {Array|function} listener
     *  A listener object that represents a callable.
     * @param {Array|function} after
     *  Listener object that is used to position the new listener.
     *
     * @return {Drupal.Toolshed.EventListener}
     *   Return this instance of this EventListener for the purpose of chaining.
     */
    addAfter(listener, after) {
      let pos = null;
      if (after) {
        pos = this.listeners.indexOf(after);
        pos = pos >= 0 ? pos + 1 : -1;
      }
      return this.add(listener, pos);
    }

    /**
     * Remove the specified listener from the list of event listeners.
     * This assume there should only be one entry pert callback.
     *
     * @param {Array|function} listener
     *  A listener object that requests to get removed.
     *
     * @return {Drupal.Toolshed.EventListener}
     *   Return this instance of this EventListener for the purpose of chaining.
     */
    remove(listener) {
      this.listeners.remove(listener);

      // If there are no listeners and the autoListen option is on, turn off
      // listening. This prevents the event from being called for no reason.
      if (this.autoListen && !this.listeners.size()) this.ignore();
      return this;
    }

    /**
     * Clean-up events and data.
     */
    destroy() {
      this.ignore();
    }
  };

  /**
   * Event listener for media query listeners.
   */
  ts.MediaQueryListener = class {
    /**
     * Constructs a new Media Query listener instance.
     *
     * @param {Object[]} breakpoints
     *   An array of breakpoints in the order they should be checked. Each
     *   breakpoint object is expected to have an `mq`, `inverted` and `event`
     *   property which help determine what event to call when a Media Query
     *   listener triggers.
     */
    constructor(breakpoints) {
      this.mode = null;
      this.curBp = null;
      this.bps = new Map();
      this.aliases = new Map();
      breakpoints.forEach(bp => {
        const mql = window.matchMedia(bp.mediaQuery);
        if (bp.event && !this.aliases.has(bp.event)) {
          this.aliases.set(bp.event, {
            on: new CallList(),
            off: new CallList()
          });
        }
        this.bps.set(bp.id, {
          query: mql,
          mode: bp.event || null,
          inverted: bp.inverted || false
        });
      });
    }

    /**
     * Alter the current breakpoint, and trigger the related events.
     *
     * @param {string} newBp
     *   The ID of the breakpoint to trigger.
     */
    _changeMode(newBp) {
      let newMode = null;

      // If the mode changed, trigger the appropriate action.
      if (newBp === this.curBp) return;
      if (this.curBp) {
        const offList = this.bps.get(this.curBp);
        if (offList && offList.off) offList.off.call(this.curBp, 'off');
      }
      this.curBp = newBp;
      if (newBp) {
        const onList = this.bps.get(newBp);
        if (onList) {
          newMode = onList.mode;
          if (onList.on) onList.on.call(this.curBp, 'on');
        }
      }
      if (newMode !== this.mode) {
        if (this.mode) {
          const offMode = this.aliases.get(this.mode);
          if (offMode) offMode.off.call(this.mode, 'off');
        }
        this.mode = newMode;
        if (newMode) {
          const onMode = this.aliases.get(newMode);
          if (onMode) onMode.on.call(this.mode, 'on');
        }
      }
    }

    /**
     * Check the registered breakpoints in order to see which one is active.
     *
     * @return {string|null}
     *   The query mapped event if a matching breakpoint is found, otherwise
     *   return null to mean no event.
     */
    checkBreakpoints() {
      const bps = Array.from(this.bps.entries());
      for (let i = 0; i < bps.length; ++i) {
        const [id, bp] = bps[i];
        if (!bp.query.matches !== !bp.inverted) {
          return id;
        }
      }
      return null;
    }

    /**
     * Add a new listener to a breakpoint or mode.
     *
     * @param {string} bpId
     *   The ID of the breakpoint to watch for, or the media query event to
     *   attach the listener callable to.
     * @param {[type]} listener
     *   The callable callback to get called when the breakpoint matches the
     *   action specified (either on or off).
     * @param {string} action
     *   Either 'on' or 'off' to indicated of the callback should be called
     *   when the media query is active or deactivated respectively.
     *
     * @return {Object}
     *   The current instance of the MediaQueryListener, and the "this" object.
     */
    add(bpId, listener, action = 'on') {
      if (action === 'on' || action === 'off') {
        let target;
        let curState;
        if (this.aliases.has(bpId)) {
          target = this.aliases.get(bpId);
          curState = this.mode;
        } else if (this.bps.has(bpId)) {
          target = this.bps.get(bpId);
          curState = this.curBp;
          if (!target[action]) target[action] = new CallList();
        } else {
          throw new Error(`Error adding ${bpId} with action ${action}`);
        }
        target[action].add(listener);
        // Trigger 'on' event when listener is added if current state matches
        // screen sizes targeted by added listener.
        if (curState === bpId && action === 'on') {
          CallList.callItem(listener, bpId, 'on');
        }
        // Trigger 'off' event when listener is added if current state does not
        // match screen sizes targeted by added listener.
        else if (curState !== bpId && action === 'off') {
          CallList.callItem(listener, bpId, 'off');
        }
      }
      return this;
    }

    /**
     * Listen for changes of the registered breakpoints.
     */
    listen() {
      let current;
      this.bps.forEach((bp, id) => {
        if (!bp.callback) {
          bp.callback = mql => {
            const mode = !mql.matches !== !bp.inverted ? id : this.checkBreakpoints();
            this._changeMode(mode);
          };
          bp.query.addListener(bp.callback);
        }

        // Stop updating this after the first breakpoint to trigger.
        if (!current && !bp.query.matches !== !bp.inverted) {
          current = id;
        }
      });
      this._changeMode(current);
    }

    /**
     * Unregister all media query listeners, and ignore all breakpoint events.
     */
    ignore() {
      this.bps.forEach((id, bp) => {
        if (bp.callback) {
          bp.query.removeListener(bp.callback);
          delete bp.callback;
        }
      });
    }
  };
})(Drupal);
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZlbnRMaXN0ZW5lci5qcyIsIm5hbWVzIjpbIlRvb2xzaGVkIiwidHMiLCJkZWJvdW5jZSIsIkNhbGxMaXN0IiwiaXNDYWxsYWJsZSIsImNhbGxhYmxlIiwib2JqIiwiZnVuYyIsIkFycmF5IiwiT2JqZWN0IiwiY2FsbEl0ZW0iLCJhcmdzIiwiYXBwbHkiLCJFcnJvciIsImNvbnN0cnVjdG9yIiwibGlzdCIsInNpemUiLCJsZW5ndGgiLCJjYWxsIiwicGFyYW0iLCJmb3JFYWNoIiwiY2IiLCJhZGQiLCJhdFBvcyIsInJlbW92ZSIsInNwbGljZSIsInB1c2giLCJwb3MiLCJpbmRleE9mIiwibmVlZGxlIiwic3RhcnQiLCJpdGVtIiwiY29udGV4dCIsImNhbGxiYWNrIiwiRXZlbnRMaXN0ZW5lciIsImVsZW0iLCJldmVudE5hbWUiLCJvcHRpb25zIiwiZXZlbnQiLCJhdXRvTGlzdGVuIiwibGlzdGVuZXJzIiwiZXZlbnRPcHRzIiwiY2FwdHVyZSIsInBhc3NpdmUiLCJfcnVuIiwibGlzdG5lcnMiLCJ0cmlnZ2VyIiwiRXZlbnQiLCJsaXN0ZW4iLCJiaW5kIiwiYWRkRXZlbnRMaXN0ZW5lciIsImlnbm9yZSIsInJlbW92ZUV2ZW50TGlzdGVuZXIiLCJsaXN0ZW5lciIsImFkZEJlZm9yZSIsImJlZm9yZSIsImFkZEFmdGVyIiwiYWZ0ZXIiLCJkZXN0cm95IiwiTWVkaWFRdWVyeUxpc3RlbmVyIiwiYnJlYWtwb2ludHMiLCJtb2RlIiwiY3VyQnAiLCJicHMiLCJNYXAiLCJhbGlhc2VzIiwiYnAiLCJtcWwiLCJ3aW5kb3ciLCJtYXRjaE1lZGlhIiwibWVkaWFRdWVyeSIsImhhcyIsInNldCIsIm9uIiwib2ZmIiwiaWQiLCJxdWVyeSIsImludmVydGVkIiwiX2NoYW5nZU1vZGUiLCJuZXdCcCIsIm5ld01vZGUiLCJvZmZMaXN0IiwiZ2V0Iiwib25MaXN0Iiwib2ZmTW9kZSIsIm9uTW9kZSIsImNoZWNrQnJlYWtwb2ludHMiLCJmcm9tIiwiZW50cmllcyIsImkiLCJtYXRjaGVzIiwiYnBJZCIsImFjdGlvbiIsInRhcmdldCIsImN1clN0YXRlIiwiY3VycmVudCIsImFkZExpc3RlbmVyIiwicmVtb3ZlTGlzdGVuZXIiLCJEcnVwYWwiXSwic291cmNlcyI6WyJFdmVudExpc3RlbmVyLmVzNi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIoKHsgVG9vbHNoZWQ6IHRzLCBkZWJvdW5jZSB9KSA9PiB7XG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgcXVldWUgb2YgY2FsbGFibGUgZnVuY3Rpb25zIHRoYXQgY2FuIGdldCBtYW5hZ2VkIGFuZCB0cmlnZ2VyZWRcbiAgICogdG9nZXRoZXIuIFRoZSBtYWluIHB1cnBvc2Ugb2YgdGhpcyBmb3IgcXVldWluZyBldmVudCBsaXN0ZW5lcnMgb3JcbiAgICogcmVnaXN0ZXJpbmcgYSBzZXJpZXMgb2YgY2FsbGJhY2tzLlxuICAgKlxuICAgKiBDYWxsTGlzdCBhcmUgb3JkZXJhYmxlLCBhbmQgY2FuIGJlIHVzZWQgdG8gaW5zZXJ0IGxpc3RlbmVycyBhcm91bmQgYVxuICAgKiBzcGVjaWZpZWQgcmVmZXJlbmNlIHBvaW50IChiZWZvcmUgb3IgYWZ0ZXIgYW5vdGhlciBjYWxsYWJsZSkuXG4gICAqL1xuICBjbGFzcyBDYWxsTGlzdCB7XG4gICAgLyoqXG4gICAgICogRW5zdXJlIHRoYXQgYSBsaXN0ZW5lciBpcyBhIHZhbGlkIGhhbmRsZXIgZm9yIHRoZSBldmVudCB1c2VkIGJ5IHRoaXNcbiAgICAgKiBFdmVudExpc3RlbmVyLiBUaGlzIHRlc3QgaXMgZm9yIGNoZWNraW5nIHRoZSBsaXN0ZW5lciBiZWZvcmUgYWRkaW5nIGl0XG4gICAgICogdG8gdGhlIGxpc3Qgb2YgYWN0aXZlIGxpc3RlbmVycyBmb3IgdGhpcyBldmVudC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7QXJyYXl8ZnVuY3Rpb259IGNhbGxhYmxlXG4gICAgICogICBUaGUgb2JqZWN0IHRvIHRlc3QgaWYgaXQgaXMgdmFsaWQgZm9yIGhhbmRsaW5nIHRoaXMgZXZlbnQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtib29sfVxuICAgICAqICAgQm9vbGVhbiB0byBpbmRpY2F0ZSBpZiB0aGlzIGxpc3RlbmVyIGlzIHZhbGlkIGZvciBoYW5kbGluZyB0aGlzIGV2ZW50LlxuICAgICAqICAgX3RydWVfIElGRiB0aGlzIGxpc3RlbmVyIGNhbiBiZSBhZGRlZCBhbmQgdXNlZCB3aXRoIHRoaXMgZXZlbnQgb2JqZWN0LlxuICAgICAqL1xuICAgIHN0YXRpYyBpc0NhbGxhYmxlKGNhbGxhYmxlKSB7XG4gICAgICBsZXQgb2JqID0gbnVsbDtcbiAgICAgIGxldCBmdW5jID0gY2FsbGFibGU7XG5cbiAgICAgIGlmIChjYWxsYWJsZSBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICAgIFtvYmosIGZ1bmNdID0gY2FsbGFibGU7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiAodHlwZW9mIGZ1bmMgPT09ICdmdW5jdGlvbicpICYmICghb2JqIHx8IG9iaiBpbnN0YW5jZW9mIE9iamVjdCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRXhlY3V0ZSBhIGNhbGxhYmxlIGl0ZW0sIHdpdGggdGhlIHBhcmFtZXRlcnMgcGFzc2VkIHRvIHRoZSB1bmRlcmx5aW5nXG4gICAgICogZnVuY3Rpb24gb3IgbWV0aG9kLiBBIGNhbGxhYmxlIGlzIGVpdGhlciBhIGZ1bmN0aW9uIG9yIGFuIGFycmF5XG4gICAgICogKGNvbnRhaW5pbmcgYW4gb2JqZWN0IHRvIHVzZSBhcyB0aGUgXCJ0aGlzXCIgYW5kIGEgbWV0aG9kIHRvIGV4ZWN1dGUpLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbnxBcnJheX0gY2FsbGFibGVcbiAgICAgKiAgIEEgY2FsbGFibGUgaXMgZWl0aGVyIGEgZnVuY3Rpb24gdGhhdCBjYW4gYmUgY2FsbGVkIGRpcmVjdGx5IG9yIGlzIGFuXG4gICAgICogICBhcnJheSB0aGF0IGNvbnRhaW5zIGFuIG9iamVjdCAodXNlZCBmb3IgXCJ0aGlzXCIpIGFuZCBhIG1ldGhvZCB0byBjYWxsLlxuICAgICAqIEBwYXJhbSB7KltdfSAuLi5hcmdzXG4gICAgICogICBBZGRpdGlvbmFsIGFyZ3VtZW50cyBwYXNzZWQgd2l0aCB0aGUgY2FsbGVkIGl0ZW0uXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHsqfG51bGx9XG4gICAgICogICBUaGUgcmVzdWx0IG9mIHRoZSBjYWxsYWJhYmxlXG4gICAgICovXG4gICAgc3RhdGljIGNhbGxJdGVtKGNhbGxhYmxlLCAuLi5hcmdzKSB7XG4gICAgICBpZiAoY2FsbGFibGUgaW5zdGFuY2VvZiBBcnJheSkge1xuICAgICAgICBjb25zdCBbb2JqLCBmdW5jXSA9IGNhbGxhYmxlO1xuICAgICAgICByZXR1cm4gZnVuYy5hcHBseShvYmosIGFyZ3MpO1xuICAgICAgfVxuXG4gICAgICBpZiAodHlwZW9mIGNhbGxhYmxlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHJldHVybiBjYWxsYWJsZSguLi5hcmdzKTtcbiAgICAgIH1cblxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbmFibGUgdG8gZXhlY3V0ZSBjYWxsYWJsZSBtZXRob2QuJyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGEgbmV3IGluc3RhbmNlIG9mIGEgY2FsbGFibGUgbGlzdCBvZiBpdGVtcy5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgIHRoaXMubGlzdCA9IFtdO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgY3VycmVudCBzaXplIG9mIHRoZSBjYWxsYWJsZSBsaXN0LlxuICAgICAqXG4gICAgICogQHJldHVybiB7aW50fVxuICAgICAqICAgVGhlIGN1cnJlbnQgc2l6ZSBvZiB0aGUgY2FsbGFibGVzIGxpc3QgYXJyYXkuXG4gICAgICovXG4gICAgc2l6ZSgpIHtcbiAgICAgIHJldHVybiB0aGlzLmxpc3QubGVuZ3RoO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE1ha2UgYSBjYWxsIHRvIGEgbGlzdCBvZiBjYWxsYWJsZXMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0V2ZW50fSBwYXJhbVxuICAgICAqICAgYXJndW1lbnQgb2JqZWN0IHRvIHBhc3MgdG8gZWFjaCBvZiB0aGUgY2FsbGFibGVzIGFzIHRoZXkgZ2V0XG4gICAgICogICBjYWxsZWQgcmVzcGVjdGl2ZWx5LlxuICAgICAqL1xuICAgIGNhbGwocGFyYW0sIC4uLmFyZ3MpIHtcbiAgICAgIHRoaXMubGlzdC5mb3JFYWNoKChjYikgPT4gQ2FsbExpc3QuY2FsbEl0ZW0oY2IsIHBhcmFtLCAuLi5hcmdzKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSWYgdGhlcmUgaXMgYSB2YWxpZCBhdFBvcywgcGxhY2UgdGhlIGNhbGxhYmxlIGF0IHRoaXMgcG9zaXRpb24sXG4gICAgICogb3RoZXJ3aXNlLCBqdXN0IGFkZCBpdCB0byB0aGUgZW5kIG9mIHRoZSBsaXN0LiBUaGlzIGFsbG93cyBzb21lXG4gICAgICogZmxleGliaWxpdHkgdG8gcGxhY2UgY2FsbGFiYmxlcyBhdCB0aGUgc3RhcnQgb2YgdGhlIGxpc3QsIG9yXG4gICAgICogYmVmb3JlIG90aGVyIGNhbGxhYmxlcy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7QXJyYXl8ZnVuY3Rpb259IGNhbGxhYmxlXG4gICAgICogICBBIGNhbGxhYmxlIG9iamVjdCB0byBhZGQgdG8gdGhlIGxpc3QuXG4gICAgICogQHBhcmFtIHtpbnR9IGF0UG9zXG4gICAgICogICBJbmRleCB0byBhZGQgdGhlIGNhbGxhYmxlIGF0LiBUaGlzIGFsbG93cyBjYWxsYWJsZXMgdG8gYmUgcnVuIGluXG4gICAgICogICBhIGRpZmZlcmVudCBvcmRlciB0aGFuIHRoZXkgbWF5YmUgcmVnaXN0ZXJlZCBpbi5cbiAgICAgKi9cbiAgICBhZGQoY2FsbGFibGUsIGF0UG9zKSB7XG4gICAgICBpZiAoIUNhbGxMaXN0LmlzQ2FsbGFibGUoY2FsbGFibGUpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignVHJ5aW5nIHRvIGFkZCBuZXcgY2FsbGJhY2ssIGJ1dCBpdCBpcyBub3QgYSB2YWxpZCBjYWxsYWJsZS4nKTtcbiAgICAgIH1cblxuICAgICAgLy8gRW5zdXJlIHRoYXQgYWxsIGV4aXN0aW5nIHJlZmVyZW5jZXMgdG8gdGhpcyBldmVudCBhcmUgcmVtb3ZlZC5cbiAgICAgIC8vIFByZXZlbnRzIHRoZSBldmVudCBmcm9tIGJlaW5nIGNhbGxlZCBtb3JlIHRoYW4gb25jZSB1bmludGVudGlvbmFsbHkuXG4gICAgICB0aGlzLnJlbW92ZShjYWxsYWJsZSk7XG5cbiAgICAgIGlmIChhdFBvcyAhPT0gbnVsbCAmJiBhdFBvcyA+PSAwKSB0aGlzLmxpc3Quc3BsaWNlKGF0UG9zIC0gMSwgMCwgY2FsbGFibGUpO1xuICAgICAgZWxzZSB0aGlzLmxpc3QucHVzaChjYWxsYWJsZSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIHRoZSBzcGVjaWZpZWQgY2FsbGFibGUgZnJvbSB0aGUgbGlzdCBvZiBjYWxsYWJsZXMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0FycmF5fGZ1bmN0aW9ufSBjYWxsYWJsZVxuICAgICAqICBBIGxpc3RlbmVyIG9iamVjdCB0aGF0IHJlcXVlc3RzIHRvIGdldCByZW1vdmVkLlxuICAgICAqL1xuICAgIHJlbW92ZShjYWxsYWJsZSkge1xuICAgICAgbGV0IHBvcyA9IHRoaXMuaW5kZXhPZihjYWxsYWJsZSk7XG5cbiAgICAgIHdoaWxlIChwb3MgPj0gMCkge1xuICAgICAgICB0aGlzLmxpc3Quc3BsaWNlKHBvcywgMSk7XG4gICAgICAgIHBvcyA9IHRoaXMuaW5kZXhPZihjYWxsYWJsZSwgcG9zKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBMb29rIGZvciBhbiBhIG1hdGNoaW5nIGxpc3RlbmVyIGluIHRoZSBvYmplY3RzIGxpc3RlbmVyIHJlZ2lzdHJ5LlxuICAgICAqIExpc3RlbmVycyBjYW4gYmUgc3RvcmVkIGFzIGVpdGhlciBmdW5jdGlvbiByZWZlcmVuY2VzLCBvciBhcnJheXMuIElmXG4gICAgICogYW4gYXJyYXksIHRoZSBmaXJzdCBpdGVtIGluIHRoZSBhcnJheSBpcyB0aGUgb2JqZWN0IGNhbGxpbmcgY29udGV4dCxcbiAgICAgKiBhbmQgdGhlIHNlY29uZCBwYXJhbWV0ZXIgaXMgZnVuY3Rpb24gdG8gY2FsbC5cbiAgICAgKlxuICAgICAqIE1hdGNoZXMgY2FuIGJlIGZvdW5kIGJ5IGZ1bmN0aW9uLCBhcnJheSAob2JqZWN0IGFuZCBmdW5jdGlvbikgb3IganVzdFxuICAgICAqIGJ5IG1hdGNoaW5nIHRoZSBvYmplY3QgY29udGV4dHMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0FycmF5fE9iamVjdHxmdW5jdGlvbn0gbmVlZGxlXG4gICAgICogICBUaGUgbWF0Y2hpbmcgbGlzdGVuZXIgdG8gbG9jYXRlIGluIHRoZSBsaXN0ZW5lcnMgYXJyYXkuIElmIHRoZSBwYXJhbVxuICAgICAqICAgaXMgYW4gYXJyYXksIGxvb2sgZm9yIHRoZSBtYXRjaGluZyBvYmplY3QgYW5kIGZ1bmN0aW9uLiBJZiBhbiBvYmplY3RcbiAgICAgKiAgIGlzIHBhc3NlZCBpbiwgZmluZCB0aGUgZmlyc3Qgb2NjdXJhbmNlIG9mIHRoZSBvYmplY3QgYXMgdGhlIG9iamVjdFxuICAgICAqICAgY29udGV4dCBpbiB0aGUgYXJyYXlzLiBJZiBhIGZ1bmN0aW9uLCBqdXN0IHNlYXJjaCBmb3IgdGhlIGZ1bmN0aW9uLlxuICAgICAqIEBwYXJhbSB7aW50fSBzdGFydFxuICAgICAqICAgVGhlIHN0YXJ0aW5nIHBvc2l0aW9uIGluIHRoZSBsaXN0ZW5lciBhcnJheSB0byBzZWFyY2ggZm9yIHRoZSBjYWxsYmFjay5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge2ludH1cbiAgICAgKiAgIFRoZSBpbmRleCB3aGVyZSB0aGUgbWF0Y2hpbmcgbGlzdGVuZXIgd2FzIGZvdW5kLiBJZiBhIG1hdGNoaW5nIGxpc3RlbmVyXG4gICAgICogICB3YXMgbm90IGZvdW5kLCByZXR1cm4gLTEgdG8gaW5kaWNhdGUgbm8gbWF0Y2ggaXMgYXZhaWxhYmxlLlxuICAgICAqL1xuICAgIGluZGV4T2YobmVlZGxlLCBzdGFydCA9IDApIHtcbiAgICAgIGlmICh0eXBlb2YgbmVlZGxlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIC8vIEZvciBmdW5jdGlvbnMsIG1hdGNoaW5nIGlzIGRpcmVjdCBhbmQgc3RyYWlnaHRmb3J3YXJkLlxuICAgICAgICByZXR1cm4gdGhpcy5saXN0LmluZGV4T2YobmVlZGxlLCBzdGFydCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IFtvYmosIGZ1bmNdID0gKG5lZWRsZSBpbnN0YW5jZW9mIEFycmF5KSA/IG5lZWRsZSA6IFtudWxsLCBuZWVkbGVdO1xuICAgICAgZm9yICg7IHN0YXJ0IDwgdGhpcy5saXN0Lmxlbmd0aDsgKytzdGFydCkge1xuICAgICAgICBjb25zdCBpdGVtID0gdGhpcy5saXN0W3N0YXJ0XTtcbiAgICAgICAgY29uc3QgW2NvbnRleHQsIGNhbGxiYWNrXSA9IChpdGVtIGluc3RhbmNlb2YgQXJyYXkpID8gaXRlbSA6IFtudWxsLCBpdGVtXTtcblxuICAgICAgICBpZiAob2JqID09PSBjb250ZXh0ICYmICghZnVuYyB8fCBmdW5jID09PSBjYWxsYmFjaykpIHtcbiAgICAgICAgICByZXR1cm4gc3RhcnQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiAtMTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQXR0YWNoIERPTSBhbmQgZWxlbWVudCBldmVudHMsIHdoaWNoIGFsbG93IGdsb2JhbCByZWdpc3RyYXRpb24gYW5kIHV0aWxpemVcbiAgICogYSBDYWxsTGlzdCBmb3IgZXZlbnQgY2FsbGluZyBhbmQgbWFuYWdlbWVudC4gVGhlc2UgZXZlbnQgbGlzdGVuZXJzXG4gICAqIGhhdmUgb3B0aW9ucyBmb3IgaGFuZGxpbmcgZGVib3VuY2UsIGNhbGxhYmxlcyAoQHNlZSBDYWxsTGlzdCkgYW5kXG4gICAqIGF1dG8gcmVnaXN0cmF0aW9uIChvbmx5IGFkZGluZyB0aGUgZXZlbnQgbGlzdGVuZXIgdG8gdGhlIERPTSB3aGVuIGxpc3RlbmVyXG4gICAqIGlzIGFkZGVkKS5cbiAgICpcbiAgICogVG9vbHNoZWQuRXZlbnRMaXN0ZW5lci57ZXZlbnROYW1lfSBuYW1lc3BhY2UuIFNvbWUgb2YgdGhlIGV2ZW50cyBpbiB0aGF0XG4gICAqIG5hbWVzcGFjZSBtYXkgaGF2ZSBjdXN0b21pemVkIGV2ZW50IGNhbGxiYWNrLiBBbiBleGFtcGxlIG9mIHRoaXMgaXMgZGVmaW5lZFxuICAgKiBpbiAuL3NjcmVlbi1ldmVudHMuZXM2LmpzIHdoaWNoIGFyZSB1c2VkIGJ5IHRoZSBUb29sc2hlZC5Eb2NrLlxuICAgKi9cbiAgdHMuRXZlbnRMaXN0ZW5lciA9IGNsYXNzIHtcbiAgICAvKipcbiAgICAgKiBDb25zdHJ1Y3RvciBmb3IgY3JlYXRpbmcgb2YgZXZlbnQgbGlzdGVuZXJzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtET01FbGVtZW50fSBlbGVtXG4gICAgICogICBET00gZWxlbWVudCB0aGF0IHdpbGwgYmUgdGhlIHRhcmdldCBvZiB0aGUgZXZlbnQuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZVxuICAgICAqICAgVGhlIGV2ZW50LlxuICAgICAqIEBwYXJhbSB7bnVsbHxvYmplY3R9IG9wdGlvbnNcbiAgICAgKiAgIG1ldGhvZDpcbiAgICAgKiAgICAgTmFtZSBvZiB0aGUgbWV0aG9kIHRvIGNhbGwgb24gYWxsIGxpc3RlbmVycyAoc3BlY2lhbCBjYXNlcykuIFdpbGwgY2FsbFxuICAgICAqICAgICB0aGUgZGVmYXVsdCBcIm9uW3RoaXMuZXZlbnROYW1lXVwiIG1ldGhvZCBpZiBsZWZ0IGJsYW5rLlxuICAgICAqICAgdXNlQ2FwdHVyZTpcbiAgICAgKiAgICAgVXNlIGNhcHR1cmUgaW5zdGVhZCBvZiBidWJibGluZyBmb3IgZXZlbnQgcHJvcGFnYXRpb24uXG4gICAgICogICBwYXNzaXZlOlxuICAgICAqICAgICBFdmVudCBoYW5kbGVycyB3aWxsIG5vdCBjYWxsIHByZXZlbnREZWZhdWx0KCkgd2hpY2ggY2FuIGVuYWJsZSBicm93c2VyXG4gICAgICogICAgIG9wdGltYXRpemF0aW9uIHRoYXQgbm8gbG9uZ2VyIG5lZWQgdG8gd2FpdCBmb3IgYWxsIGhhbmRsZXJzIHRvIGNvbXBsZXRlXG4gICAgICogICAgIGJlZm9yZSB0cmlnZ2VyaW5nIG90aGVyIGV2ZW50cyBsaWtlIHNjcm9sbGluZy5cbiAgICAgKiAgIGRlYm91bmNlOlxuICAgICAqICAgICBEZXRlcm1pbmUgaWYgdGhlIGV2ZW50IG9ubHkgdHJpZ2dlcnMgdXNpbmcgZGVib3VuY2UgaGFuZGxpbmcuIFRoaXMgbWVhbnNcbiAgICAgKiAgICAgdGhhdCBldmVudHMgd2lsbCBvbmx5IGZpcmUgb2ZmIGFmdGVyIGEgc2hvcnQgZGVsYXkuXG4gICAgICpcbiAgICAgKiAgICAgSWYgbnVsbCBvciBGQUxTRSwgbm8gZGVib3VuY2Ugd2lsbCBiZSB1c2VkLCBhbmQgdGhlIGV2ZW50IHJlZ2lzdGVyZWRcbiAgICAgKiAgICAgZmlyZXMgb2ZmIGFzIHNvb24gYXMgdGhlIGV2ZW50IGlzIHJhaXNlZC5cbiAgICAgKlxuICAgICAqICAgICBJZiBUUlVFIHRoZW4gdXNlIHRoZSBkZWZhdWx0IGRlYm91bmNlIGRlbGF5LiBJZiBhbiBpbnRlZ2VyLCB0aGFuIHVzZSB0aGVcbiAgICAgKiAgICAgdmFsdWUgYXMgdGhlIGRlbGF5IGluIG1pbGxpc2Vjb25kcy5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihlbGVtLCBldmVudE5hbWUsIG9wdGlvbnMpIHtcbiAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9OyAvLyBvcHRpb25zIGNhbiBiZSBsZWZ0IGJsYW5rLlxuXG4gICAgICB0aGlzLmVsZW0gPSBlbGVtO1xuICAgICAgdGhpcy5ldmVudCA9IGV2ZW50TmFtZTtcbiAgICAgIHRoaXMuYXV0b0xpc3RlbiA9IG9wdGlvbnMuYXV0b0xpc3RlbiB8fCBmYWxzZTtcbiAgICAgIHRoaXMubGlzdGVuZXJzID0gbmV3IENhbGxMaXN0KCk7XG5cbiAgICAgIC8vIENoZWNrIGFuZCBwcm9wZXJseSBvcmdhbml6ZSB0aGUgZXZlbnQgb3B0aW9ucyB0byBiZSB1c2VkIGxhdGVyLlxuICAgICAgaWYgKG9wdGlvbnMuZGVib3VuY2UpIHtcbiAgICAgICAgdGhpcy5kZWJvdW5jZSA9ICh0eXBlb2Ygb3B0aW9ucy5kZWJvdW5jZSA9PT0gJ2Jvb2xlYW4nKSA/IDEwMCA6IG9wdGlvbnMuZGVib3VuY2U7XG4gICAgICB9XG5cbiAgICAgIC8vIEFsbG93IGZvciBhZGRFdmVudExpc3RlbmVyIG9wdGlvbnMgYXMgZGVzY3JpYmVkIGhlcmVcbiAgICAgIC8vIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9FdmVudFRhcmdldC9hZGRFdmVudExpc3RlbmVyXG4gICAgICAvLyBJIGFtIGFsc28gZW1wbG95aW5nIHRoZSBodHRwczovL2dpdGh1Yi5jb20vV0lDRy9FdmVudExpc3RlbmVyT3B0aW9uc1xuICAgICAgLy8gYXMgYSBwb2x5ZmlsbCwgYnV0IHN1cHBvcnQgd2lsbCBub3QgYmUgYXZhaWxhYmxlIGZvciBJRTggYW5kIGVhcmxpZXIuXG4gICAgICB0aGlzLmV2ZW50T3B0cyA9IHtcbiAgICAgICAgY2FwdHVyZTogb3B0aW9ucy5jYXB0dXJlIHx8IGZhbHNlLFxuICAgICAgICBwYXNzaXZlOiBvcHRpb25zLnBhc3NpdmUgfHwgZmFsc2UsXG4gICAgICB9O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRyaWdnZXIgdGhlIGV2ZW50IGZvciBhbGwgdGhlIHJlZ2lzdGVyZWQgbGlzdGVuZXJzLiBDdXN0b21cbiAgICAgKiBFdmVudExpc3RlbmVycyBhcmUgbW9zdCBsaWtlbHkgdG8gb3ZlcnJpZGUgdGhpcyBmdW5jdGlvbiBpbiBvcmRlclxuICAgICAqIHRvIGNyZWF0ZSBpbXBsZW1lbnQgc3BlY2lhbCBmdW5jdGlvbmFsaXR5LCB0cmlnZ2VyZWQgYnkgZXZlbnRzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGV2ZW50XG4gICAgICogICBUaGUgZXZlbnQgb2JqZWN0IHRoYXQgd2FzIGdlbmVyYXRlZCBhbmQgcGFzc2VkIHRvIHRoZSBldmVudCBoYW5kbGVyLlxuICAgICAqL1xuICAgIF9ydW4oZXZlbnQsIC4uLmFyZ3MpIHtcbiAgICAgIHRoaXMubGlzdG5lcnMuY2FsbChldmVudCwgLi4uYXJncyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVHJpZ2dlciB0aGUgZXZlbnQgbWFuYXVsbHkuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0V2ZW50fG51bGx9IGV2ZW50XG4gICAgICogICBFdmVudCBkYXRhIHRvIHVzZSB3aXRoIHRoaXMgZXZlbnQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtEcnVwYWwuVG9vbHNoZWQuRXZlbnRMaXN0ZW5lcn1cbiAgICAgKiAgIFJldHVybiB0aGlzIGluc3RhbmNlIG9mIHRoaXMgRXZlbnRMaXN0ZW5lciBmb3IgdGhlIHB1cnBvc2Ugb2YgY2hhaW5pbmcuXG4gICAgICovXG4gICAgdHJpZ2dlcihldmVudCwgLi4uYXJncykge1xuICAgICAgdGhpcy5fcnVuKGV2ZW50IHx8IG5ldyBFdmVudCh0aGlzLmV2ZW50KSwgYXJncyk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciB0aGUgZXZlbnQsIGFuZCBrZWVwIHRyYWNrIG9mIHRoZSBjYWxsYmFjayBzbyBpdCBjYW4gYmUgcmVtb3ZlZFxuICAgICAqIGxhdGVyIGlmIHdlIG5lZWQgdG8gZGlzYWJsZSAvIHJlbW92ZSB0aGUgbGlzdGVuZXIgYXQgYSBsYXRlciB0aW1lLlxuICAgICAqXG4gICAgICogQHJldHVybiB7RHJ1cGFsLlRvb2xzaGVkLkV2ZW50TGlzdGVuZXJ9XG4gICAgICogICBSZXR1cm4gdGhpcyBpbnN0YW5jZSBvZiB0aGlzIEV2ZW50TGlzdGVuZXIgZm9yIHRoZSBwdXJwb3NlIG9mIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGxpc3RlbigpIHtcbiAgICAgIGlmICghdGhpcy5jYWxsYmFjayAmJiAoIXRoaXMuYXV0b0xpc3RlbiB8fCB0aGlzLmxpc3RlbmVycy5zaXplKCkpKSB7XG4gICAgICAgIHRoaXMuY2FsbGJhY2sgPSAodGhpcy5kZWJvdW5jZSAmJiB0aGlzLmRlYm91bmNlID4gMCAmJiBkZWJvdW5jZSlcbiAgICAgICAgICA/IGRlYm91bmNlKHRoaXMuX3J1bi5iaW5kKHRoaXMpLCB0aGlzLmRlYm91bmNlKSA6IHRoaXMuX3J1bi5iaW5kKHRoaXMpO1xuXG4gICAgICAgIHRoaXMuZWxlbS5hZGRFdmVudExpc3RlbmVyKHRoaXMuZXZlbnQsIHRoaXMuY2FsbGJhY2ssIHRoaXMuZXZlbnRPcHRzKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN0b3AgbGlzdGVuaW5nIGZvciB0aGlzIGV2ZW50LCBhbmQgdW5yZWdpc3RlciBmcm9tIGFueSBldmVudCBsaXN0ZW5lcnMuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtEcnVwYWwuVG9vbHNoZWQuRXZlbnRMaXN0ZW5lcn1cbiAgICAgKiAgIFJldHVybiB0aGlzIGluc3RhbmNlIG9mIHRoaXMgRXZlbnRMaXN0ZW5lciBmb3IgdGhlIHB1cnBvc2Ugb2YgY2hhaW5pbmcuXG4gICAgICovXG4gICAgaWdub3JlKCkge1xuICAgICAgaWYgKHRoaXMuY2FsbGJhY2spIHtcbiAgICAgICAgdGhpcy5lbGVtLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5ldmVudCwgdGhpcy5jYWxsYmFjayk7XG4gICAgICAgIGRlbGV0ZSB0aGlzLmNhbGxiYWNrO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSWYgdGhlcmUgaXMgYSB2YWxpZCBhdFBvcywgcGxhY2UgdGhlIGxpc3RlbmVyIGF0IHRoaXMgcG9zaXRpb24sXG4gICAgICogb3RoZXJ3aXNlLCBqdXN0IGFkZCBpdCB0byB0aGUgZW5kIG9mIHRoZSBsaXN0LiBUaGlzIGFsbG93cyBzb21lXG4gICAgICogZmxleGliaWxpdHkgdG8gcGxhY2UgbGlzdGVuZXJzIGF0IHRoZSBzdGFydCBvZiB0aGUgbGlzdCwgb3JcbiAgICAgKiBiZWZvcmUgb3RoZXIgbGlzdGVuZXJzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGxpc3RlbmVyXG4gICAgICogICBBIGxpc3RlbmVyIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSBhIG1ldGhvZCAnb24nICsgW3RoaXMuZXZlbnROYW1lXS5cbiAgICAgKiBAcGFyYW0ge2ludH0gYXRQb3NcbiAgICAgKiAgIEluZGV4IHRvIGFkZCB0aGUgbGlzdGVuZXIgYXQuIFRoaXMgYWxsb3dzIGxpc3RlbmVycyB0byBiZSBydW4gaW5cbiAgICAgKiAgIGEgZGlmZmVyZW50IG9yZGVyIHRoYW4gdGhleSBtYXliZSByZWdpc3RlcmVkIGluLlxuICAgICAqXG4gICAgICogQHJldHVybiB7RHJ1cGFsLlRvb2xzaGVkLkV2ZW50TGlzdGVuZXJ9XG4gICAgICogICBSZXR1cm4gdGhpcyBpbnN0YW5jZSBvZiB0aGlzIEV2ZW50TGlzdGVuZXIgZm9yIHRoZSBwdXJwb3NlIG9mIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGFkZChsaXN0ZW5lciwgYXRQb3MpIHtcbiAgICAgIHRoaXMubGlzdGVuZXJzLmFkZChsaXN0ZW5lciwgYXRQb3MpO1xuXG4gICAgICBpZiAodGhpcy5hdXRvTGlzdGVuKSB0aGlzLmxpc3RlbigpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkIGEgbmV3IGxpc3RlbmVyIGJlZm9yZSBhbiBleGlzdGluZyBsaXN0ZW5lciBhbHJlYWR5IGluIHRoZSBsaXN0LlxuICAgICAqIElmIFtiZWZvcmVdIGlzIG51bGwsIHRoZW4gaW5zZXJ0IGF0IHRoZSBzdGFydCBvZiB0aGUgbGlzdC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7QXJyYXl8ZnVuY3Rpb259IGxpc3RlbmVyXG4gICAgICogICBBIGxpc3RlbmVyIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSBhIG1ldGhvZCAnb24nICsgW3RoaXMuZXZlbnROYW1lXS5cbiAgICAgKiBAcGFyYW0ge0FycmF5fGZ1bmN0aW9ufSBiZWZvcmVcbiAgICAgKiAgIExpc3RlbmVyIG9iamVjdCB0aGF0IGlzIHVzZWQgdG8gcG9zaXRpb24gdGhlIG5ldyBsaXN0ZW5lci5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge0RydXBhbC5Ub29sc2hlZC5FdmVudExpc3RlbmVyfVxuICAgICAqICAgUmV0dXJuIHRoaXMgaW5zdGFuY2Ugb2YgdGhpcyBFdmVudExpc3RlbmVyIGZvciB0aGUgcHVycG9zZSBvZiBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBhZGRCZWZvcmUobGlzdGVuZXIsIGJlZm9yZSkge1xuICAgICAgY29uc3QgcG9zID0gKGJlZm9yZSkgPyB0aGlzLmxpc3RlbmVycy5pbmRleE9mKGJlZm9yZSkgOiAwO1xuICAgICAgcmV0dXJuIHRoaXMuYWRkKGxpc3RlbmVyLCBwb3MgPCAwID8gMCA6IHBvcyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkIGEgbmV3IGxpc3RlbmVyIGFmdGVyIGFuIGV4aXN0aW5nIGxpc3RlbmVyIGFscmVhZHkgaW4gdGhlIGxpc3QuXG4gICAgICogSWYgW2FmdGVyXSBpcyBudWxsLCB0aGVuIGluc2VydCBhdCB0aGUgZW5kIG9mIHRoZSBsaXN0LlxuICAgICAqXG4gICAgICogQHBhcmFtIHtBcnJheXxmdW5jdGlvbn0gbGlzdGVuZXJcbiAgICAgKiAgQSBsaXN0ZW5lciBvYmplY3QgdGhhdCByZXByZXNlbnRzIGEgY2FsbGFibGUuXG4gICAgICogQHBhcmFtIHtBcnJheXxmdW5jdGlvbn0gYWZ0ZXJcbiAgICAgKiAgTGlzdGVuZXIgb2JqZWN0IHRoYXQgaXMgdXNlZCB0byBwb3NpdGlvbiB0aGUgbmV3IGxpc3RlbmVyLlxuICAgICAqXG4gICAgICogQHJldHVybiB7RHJ1cGFsLlRvb2xzaGVkLkV2ZW50TGlzdGVuZXJ9XG4gICAgICogICBSZXR1cm4gdGhpcyBpbnN0YW5jZSBvZiB0aGlzIEV2ZW50TGlzdGVuZXIgZm9yIHRoZSBwdXJwb3NlIG9mIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGFkZEFmdGVyKGxpc3RlbmVyLCBhZnRlcikge1xuICAgICAgbGV0IHBvcyA9IG51bGw7XG5cbiAgICAgIGlmIChhZnRlcikge1xuICAgICAgICBwb3MgPSB0aGlzLmxpc3RlbmVycy5pbmRleE9mKGFmdGVyKTtcbiAgICAgICAgcG9zID0gcG9zID49IDAgPyBwb3MgKyAxIDogLTE7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5hZGQobGlzdGVuZXIsIHBvcyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIHRoZSBzcGVjaWZpZWQgbGlzdGVuZXIgZnJvbSB0aGUgbGlzdCBvZiBldmVudCBsaXN0ZW5lcnMuXG4gICAgICogVGhpcyBhc3N1bWUgdGhlcmUgc2hvdWxkIG9ubHkgYmUgb25lIGVudHJ5IHBlcnQgY2FsbGJhY2suXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0FycmF5fGZ1bmN0aW9ufSBsaXN0ZW5lclxuICAgICAqICBBIGxpc3RlbmVyIG9iamVjdCB0aGF0IHJlcXVlc3RzIHRvIGdldCByZW1vdmVkLlxuICAgICAqXG4gICAgICogQHJldHVybiB7RHJ1cGFsLlRvb2xzaGVkLkV2ZW50TGlzdGVuZXJ9XG4gICAgICogICBSZXR1cm4gdGhpcyBpbnN0YW5jZSBvZiB0aGlzIEV2ZW50TGlzdGVuZXIgZm9yIHRoZSBwdXJwb3NlIG9mIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHJlbW92ZShsaXN0ZW5lcikge1xuICAgICAgdGhpcy5saXN0ZW5lcnMucmVtb3ZlKGxpc3RlbmVyKTtcblxuICAgICAgLy8gSWYgdGhlcmUgYXJlIG5vIGxpc3RlbmVycyBhbmQgdGhlIGF1dG9MaXN0ZW4gb3B0aW9uIGlzIG9uLCB0dXJuIG9mZlxuICAgICAgLy8gbGlzdGVuaW5nLiBUaGlzIHByZXZlbnRzIHRoZSBldmVudCBmcm9tIGJlaW5nIGNhbGxlZCBmb3Igbm8gcmVhc29uLlxuICAgICAgaWYgKHRoaXMuYXV0b0xpc3RlbiAmJiAhdGhpcy5saXN0ZW5lcnMuc2l6ZSgpKSB0aGlzLmlnbm9yZSgpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2xlYW4tdXAgZXZlbnRzIGFuZCBkYXRhLlxuICAgICAqL1xuICAgIGRlc3Ryb3koKSB7XG4gICAgICB0aGlzLmlnbm9yZSgpO1xuICAgIH1cbiAgfTtcblxuICAvKipcbiAgICogRXZlbnQgbGlzdGVuZXIgZm9yIG1lZGlhIHF1ZXJ5IGxpc3RlbmVycy5cbiAgICovXG4gIHRzLk1lZGlhUXVlcnlMaXN0ZW5lciA9IGNsYXNzIHtcbiAgICAvKipcbiAgICAgKiBDb25zdHJ1Y3RzIGEgbmV3IE1lZGlhIFF1ZXJ5IGxpc3RlbmVyIGluc3RhbmNlLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtPYmplY3RbXX0gYnJlYWtwb2ludHNcbiAgICAgKiAgIEFuIGFycmF5IG9mIGJyZWFrcG9pbnRzIGluIHRoZSBvcmRlciB0aGV5IHNob3VsZCBiZSBjaGVja2VkLiBFYWNoXG4gICAgICogICBicmVha3BvaW50IG9iamVjdCBpcyBleHBlY3RlZCB0byBoYXZlIGFuIGBtcWAsIGBpbnZlcnRlZGAgYW5kIGBldmVudGBcbiAgICAgKiAgIHByb3BlcnR5IHdoaWNoIGhlbHAgZGV0ZXJtaW5lIHdoYXQgZXZlbnQgdG8gY2FsbCB3aGVuIGEgTWVkaWEgUXVlcnlcbiAgICAgKiAgIGxpc3RlbmVyIHRyaWdnZXJzLlxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKGJyZWFrcG9pbnRzKSB7XG4gICAgICB0aGlzLm1vZGUgPSBudWxsO1xuICAgICAgdGhpcy5jdXJCcCA9IG51bGw7XG4gICAgICB0aGlzLmJwcyA9IG5ldyBNYXAoKTtcbiAgICAgIHRoaXMuYWxpYXNlcyA9IG5ldyBNYXAoKTtcblxuICAgICAgYnJlYWtwb2ludHMuZm9yRWFjaCgoYnApID0+IHtcbiAgICAgICAgY29uc3QgbXFsID0gd2luZG93Lm1hdGNoTWVkaWEoYnAubWVkaWFRdWVyeSk7XG5cbiAgICAgICAgaWYgKGJwLmV2ZW50ICYmICF0aGlzLmFsaWFzZXMuaGFzKGJwLmV2ZW50KSkge1xuICAgICAgICAgIHRoaXMuYWxpYXNlcy5zZXQoYnAuZXZlbnQsIHtcbiAgICAgICAgICAgIG9uOiBuZXcgQ2FsbExpc3QoKSxcbiAgICAgICAgICAgIG9mZjogbmV3IENhbGxMaXN0KCksXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmJwcy5zZXQoYnAuaWQsIHtcbiAgICAgICAgICBxdWVyeTogbXFsLFxuICAgICAgICAgIG1vZGU6IGJwLmV2ZW50IHx8IG51bGwsXG4gICAgICAgICAgaW52ZXJ0ZWQ6IGJwLmludmVydGVkIHx8IGZhbHNlLFxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFsdGVyIHRoZSBjdXJyZW50IGJyZWFrcG9pbnQsIGFuZCB0cmlnZ2VyIHRoZSByZWxhdGVkIGV2ZW50cy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBuZXdCcFxuICAgICAqICAgVGhlIElEIG9mIHRoZSBicmVha3BvaW50IHRvIHRyaWdnZXIuXG4gICAgICovXG4gICAgX2NoYW5nZU1vZGUobmV3QnApIHtcbiAgICAgIGxldCBuZXdNb2RlID0gbnVsbDtcblxuICAgICAgLy8gSWYgdGhlIG1vZGUgY2hhbmdlZCwgdHJpZ2dlciB0aGUgYXBwcm9wcmlhdGUgYWN0aW9uLlxuICAgICAgaWYgKG5ld0JwID09PSB0aGlzLmN1ckJwKSByZXR1cm47XG5cbiAgICAgIGlmICh0aGlzLmN1ckJwKSB7XG4gICAgICAgIGNvbnN0IG9mZkxpc3QgPSB0aGlzLmJwcy5nZXQodGhpcy5jdXJCcCk7XG4gICAgICAgIGlmIChvZmZMaXN0ICYmIG9mZkxpc3Qub2ZmKSBvZmZMaXN0Lm9mZi5jYWxsKHRoaXMuY3VyQnAsICdvZmYnKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5jdXJCcCA9IG5ld0JwO1xuICAgICAgaWYgKG5ld0JwKSB7XG4gICAgICAgIGNvbnN0IG9uTGlzdCA9IHRoaXMuYnBzLmdldChuZXdCcCk7XG5cbiAgICAgICAgaWYgKG9uTGlzdCkge1xuICAgICAgICAgIG5ld01vZGUgPSBvbkxpc3QubW9kZTtcbiAgICAgICAgICBpZiAob25MaXN0Lm9uKSBvbkxpc3Qub24uY2FsbCh0aGlzLmN1ckJwLCAnb24nKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAobmV3TW9kZSAhPT0gdGhpcy5tb2RlKSB7XG4gICAgICAgIGlmICh0aGlzLm1vZGUpIHtcbiAgICAgICAgICBjb25zdCBvZmZNb2RlID0gdGhpcy5hbGlhc2VzLmdldCh0aGlzLm1vZGUpO1xuICAgICAgICAgIGlmIChvZmZNb2RlKSBvZmZNb2RlLm9mZi5jYWxsKHRoaXMubW9kZSwgJ29mZicpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5tb2RlID0gbmV3TW9kZTtcbiAgICAgICAgaWYgKG5ld01vZGUpIHtcbiAgICAgICAgICBjb25zdCBvbk1vZGUgPSB0aGlzLmFsaWFzZXMuZ2V0KG5ld01vZGUpO1xuICAgICAgICAgIGlmIChvbk1vZGUpIG9uTW9kZS5vbi5jYWxsKHRoaXMubW9kZSwgJ29uJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVjayB0aGUgcmVnaXN0ZXJlZCBicmVha3BvaW50cyBpbiBvcmRlciB0byBzZWUgd2hpY2ggb25lIGlzIGFjdGl2ZS5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge3N0cmluZ3xudWxsfVxuICAgICAqICAgVGhlIHF1ZXJ5IG1hcHBlZCBldmVudCBpZiBhIG1hdGNoaW5nIGJyZWFrcG9pbnQgaXMgZm91bmQsIG90aGVyd2lzZVxuICAgICAqICAgcmV0dXJuIG51bGwgdG8gbWVhbiBubyBldmVudC5cbiAgICAgKi9cbiAgICBjaGVja0JyZWFrcG9pbnRzKCkge1xuICAgICAgY29uc3QgYnBzID0gQXJyYXkuZnJvbSh0aGlzLmJwcy5lbnRyaWVzKCkpO1xuXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJwcy5sZW5ndGg7ICsraSkge1xuICAgICAgICBjb25zdCBbaWQsIGJwXSA9IGJwc1tpXTtcblxuICAgICAgICBpZiAoIWJwLnF1ZXJ5Lm1hdGNoZXMgIT09ICFicC5pbnZlcnRlZCkge1xuICAgICAgICAgIHJldHVybiBpZDtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGQgYSBuZXcgbGlzdGVuZXIgdG8gYSBicmVha3BvaW50IG9yIG1vZGUuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gYnBJZFxuICAgICAqICAgVGhlIElEIG9mIHRoZSBicmVha3BvaW50IHRvIHdhdGNoIGZvciwgb3IgdGhlIG1lZGlhIHF1ZXJ5IGV2ZW50IHRvXG4gICAgICogICBhdHRhY2ggdGhlIGxpc3RlbmVyIGNhbGxhYmxlIHRvLlxuICAgICAqIEBwYXJhbSB7W3R5cGVdfSBsaXN0ZW5lclxuICAgICAqICAgVGhlIGNhbGxhYmxlIGNhbGxiYWNrIHRvIGdldCBjYWxsZWQgd2hlbiB0aGUgYnJlYWtwb2ludCBtYXRjaGVzIHRoZVxuICAgICAqICAgYWN0aW9uIHNwZWNpZmllZCAoZWl0aGVyIG9uIG9yIG9mZikuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGFjdGlvblxuICAgICAqICAgRWl0aGVyICdvbicgb3IgJ29mZicgdG8gaW5kaWNhdGVkIG9mIHRoZSBjYWxsYmFjayBzaG91bGQgYmUgY2FsbGVkXG4gICAgICogICB3aGVuIHRoZSBtZWRpYSBxdWVyeSBpcyBhY3RpdmUgb3IgZGVhY3RpdmF0ZWQgcmVzcGVjdGl2ZWx5LlxuICAgICAqXG4gICAgICogQHJldHVybiB7T2JqZWN0fVxuICAgICAqICAgVGhlIGN1cnJlbnQgaW5zdGFuY2Ugb2YgdGhlIE1lZGlhUXVlcnlMaXN0ZW5lciwgYW5kIHRoZSBcInRoaXNcIiBvYmplY3QuXG4gICAgICovXG4gICAgYWRkKGJwSWQsIGxpc3RlbmVyLCBhY3Rpb24gPSAnb24nKSB7XG4gICAgICBpZiAoYWN0aW9uID09PSAnb24nIHx8IGFjdGlvbiA9PT0gJ29mZicpIHtcbiAgICAgICAgbGV0IHRhcmdldDtcbiAgICAgICAgbGV0IGN1clN0YXRlO1xuXG4gICAgICAgIGlmICh0aGlzLmFsaWFzZXMuaGFzKGJwSWQpKSB7XG4gICAgICAgICAgdGFyZ2V0ID0gdGhpcy5hbGlhc2VzLmdldChicElkKTtcbiAgICAgICAgICBjdXJTdGF0ZSA9IHRoaXMubW9kZTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmICh0aGlzLmJwcy5oYXMoYnBJZCkpIHtcbiAgICAgICAgICB0YXJnZXQgPSB0aGlzLmJwcy5nZXQoYnBJZCk7XG4gICAgICAgICAgY3VyU3RhdGUgPSB0aGlzLmN1ckJwO1xuXG4gICAgICAgICAgaWYgKCF0YXJnZXRbYWN0aW9uXSkgdGFyZ2V0W2FjdGlvbl0gPSBuZXcgQ2FsbExpc3QoKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIGFkZGluZyAke2JwSWR9IHdpdGggYWN0aW9uICR7YWN0aW9ufWApO1xuICAgICAgICB9XG5cbiAgICAgICAgdGFyZ2V0W2FjdGlvbl0uYWRkKGxpc3RlbmVyKTtcbiAgICAgICAgLy8gVHJpZ2dlciAnb24nIGV2ZW50IHdoZW4gbGlzdGVuZXIgaXMgYWRkZWQgaWYgY3VycmVudCBzdGF0ZSBtYXRjaGVzXG4gICAgICAgIC8vIHNjcmVlbiBzaXplcyB0YXJnZXRlZCBieSBhZGRlZCBsaXN0ZW5lci5cbiAgICAgICAgaWYgKGN1clN0YXRlID09PSBicElkICYmIGFjdGlvbiA9PT0gJ29uJykge1xuICAgICAgICAgIENhbGxMaXN0LmNhbGxJdGVtKGxpc3RlbmVyLCBicElkLCAnb24nKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBUcmlnZ2VyICdvZmYnIGV2ZW50IHdoZW4gbGlzdGVuZXIgaXMgYWRkZWQgaWYgY3VycmVudCBzdGF0ZSBkb2VzIG5vdFxuICAgICAgICAvLyBtYXRjaCBzY3JlZW4gc2l6ZXMgdGFyZ2V0ZWQgYnkgYWRkZWQgbGlzdGVuZXIuXG4gICAgICAgIGVsc2UgaWYgKGN1clN0YXRlICE9PSBicElkICYmIGFjdGlvbiA9PT0gJ29mZicpIHtcbiAgICAgICAgICBDYWxsTGlzdC5jYWxsSXRlbShsaXN0ZW5lciwgYnBJZCwgJ29mZicpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIExpc3RlbiBmb3IgY2hhbmdlcyBvZiB0aGUgcmVnaXN0ZXJlZCBicmVha3BvaW50cy5cbiAgICAgKi9cbiAgICBsaXN0ZW4oKSB7XG4gICAgICBsZXQgY3VycmVudDtcblxuICAgICAgdGhpcy5icHMuZm9yRWFjaCgoYnAsIGlkKSA9PiB7XG4gICAgICAgIGlmICghYnAuY2FsbGJhY2spIHtcbiAgICAgICAgICBicC5jYWxsYmFjayA9ICgobXFsKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBtb2RlID0gKCFtcWwubWF0Y2hlcyAhPT0gIWJwLmludmVydGVkKSA/IGlkIDogdGhpcy5jaGVja0JyZWFrcG9pbnRzKCk7XG4gICAgICAgICAgICB0aGlzLl9jaGFuZ2VNb2RlKG1vZGUpO1xuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgYnAucXVlcnkuYWRkTGlzdGVuZXIoYnAuY2FsbGJhY2spO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gU3RvcCB1cGRhdGluZyB0aGlzIGFmdGVyIHRoZSBmaXJzdCBicmVha3BvaW50IHRvIHRyaWdnZXIuXG4gICAgICAgIGlmICghY3VycmVudCAmJiAoIWJwLnF1ZXJ5Lm1hdGNoZXMgIT09ICFicC5pbnZlcnRlZCkpIHtcbiAgICAgICAgICBjdXJyZW50ID0gaWQ7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICB0aGlzLl9jaGFuZ2VNb2RlKGN1cnJlbnQpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFVucmVnaXN0ZXIgYWxsIG1lZGlhIHF1ZXJ5IGxpc3RlbmVycywgYW5kIGlnbm9yZSBhbGwgYnJlYWtwb2ludCBldmVudHMuXG4gICAgICovXG4gICAgaWdub3JlKCkge1xuICAgICAgdGhpcy5icHMuZm9yRWFjaCgoaWQsIGJwKSA9PiB7XG4gICAgICAgIGlmIChicC5jYWxsYmFjaykge1xuICAgICAgICAgIGJwLnF1ZXJ5LnJlbW92ZUxpc3RlbmVyKGJwLmNhbGxiYWNrKTtcbiAgICAgICAgICBkZWxldGUgYnAuY2FsbGJhY2s7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgfTtcbn0pKERydXBhbCk7XG4iXSwibWFwcGluZ3MiOiI7O0FBQUEsQ0FBQyxDQUFDO0VBQUVBLFFBQVEsRUFBRUMsRUFBRTtFQUFFQztBQUFTLENBQUMsS0FBSztFQUMvQjtBQUNGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0VBQ0UsTUFBTUMsUUFBUSxDQUFDO0lBQ2I7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0lBQ0ksT0FBT0MsVUFBVSxDQUFDQyxRQUFRLEVBQUU7TUFDMUIsSUFBSUMsR0FBRyxHQUFHLElBQUk7TUFDZCxJQUFJQyxJQUFJLEdBQUdGLFFBQVE7TUFFbkIsSUFBSUEsUUFBUSxZQUFZRyxLQUFLLEVBQUU7UUFDN0IsQ0FBQ0YsR0FBRyxFQUFFQyxJQUFJLENBQUMsR0FBR0YsUUFBUTtNQUN4QjtNQUVBLE9BQVEsT0FBT0UsSUFBSSxLQUFLLFVBQVUsS0FBTSxDQUFDRCxHQUFHLElBQUlBLEdBQUcsWUFBWUcsTUFBTSxDQUFDO0lBQ3hFOztJQUVBO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7SUFD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

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

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