UNPKG

@angular/material

Version:
335 lines 55.3 kB
import { normalizePassiveListenerOptions } from '@angular/cdk/platform'; import { isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader } from '@angular/cdk/a11y'; import { coerceElement } from '@angular/cdk/coercion'; import { RippleRef } from './ripple-ref'; import { RippleEventManager } from './ripple-event-manager'; /** * Default ripple animation configuration for ripples without an explicit * animation config specified. */ export const defaultRippleAnimationConfig = { enterDuration: 225, exitDuration: 150, }; /** * Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch * events to avoid synthetic mouse events. */ const ignoreMouseEventsTimeout = 800; /** Options used to bind a passive capturing event. */ const passiveCapturingEventOptions = normalizePassiveListenerOptions({ passive: true, capture: true, }); /** Events that signal that the pointer is down. */ const pointerDownEvents = ['mousedown', 'touchstart']; /** Events that signal that the pointer is up. */ const pointerUpEvents = ['mouseup', 'mouseleave', 'touchend', 'touchcancel']; /** * Helper service that performs DOM manipulations. Not intended to be used outside this module. * The constructor takes a reference to the ripple directive's host element and a map of DOM * event handlers to be installed on the element that triggers ripple animations. * This will eventually become a custom renderer once Angular support exists. * @docs-private */ export class RippleRenderer { static { this._eventManager = new RippleEventManager(); } constructor(_target, _ngZone, elementOrElementRef, _platform) { this._target = _target; this._ngZone = _ngZone; this._platform = _platform; /** Whether the pointer is currently down or not. */ this._isPointerDown = false; /** * Map of currently active ripple references. * The ripple reference is mapped to its element event listeners. * The reason why `| null` is used is that event listeners are added only * when the condition is truthy (see the `_startFadeOutTransition` method). */ this._activeRipples = new Map(); /** Whether pointer-up event listeners have been registered. */ this._pointerUpEventsRegistered = false; // Only do anything if we're on the browser. if (_platform.isBrowser) { this._containerElement = coerceElement(elementOrElementRef); } } /** * Fades in a ripple at the given coordinates. * @param x Coordinate within the element, along the X axis at which to start the ripple. * @param y Coordinate within the element, along the Y axis at which to start the ripple. * @param config Extra ripple options. */ fadeInRipple(x, y, config = {}) { const containerRect = (this._containerRect = this._containerRect || this._containerElement.getBoundingClientRect()); const animationConfig = { ...defaultRippleAnimationConfig, ...config.animation }; if (config.centered) { x = containerRect.left + containerRect.width / 2; y = containerRect.top + containerRect.height / 2; } const radius = config.radius || distanceToFurthestCorner(x, y, containerRect); const offsetX = x - containerRect.left; const offsetY = y - containerRect.top; const enterDuration = animationConfig.enterDuration; const ripple = document.createElement('div'); ripple.classList.add('mat-ripple-element'); ripple.style.left = `${offsetX - radius}px`; ripple.style.top = `${offsetY - radius}px`; ripple.style.height = `${radius * 2}px`; ripple.style.width = `${radius * 2}px`; // If a custom color has been specified, set it as inline style. If no color is // set, the default color will be applied through the ripple theme styles. if (config.color != null) { ripple.style.backgroundColor = config.color; } ripple.style.transitionDuration = `${enterDuration}ms`; this._containerElement.appendChild(ripple); // By default the browser does not recalculate the styles of dynamically created // ripple elements. This is critical to ensure that the `scale` animates properly. // We enforce a style recalculation by calling `getComputedStyle` and *accessing* a property. // See: https://gist.github.com/paulirish/5d52fb081b3570c81e3a const computedStyles = window.getComputedStyle(ripple); const userTransitionProperty = computedStyles.transitionProperty; const userTransitionDuration = computedStyles.transitionDuration; // Note: We detect whether animation is forcibly disabled through CSS (e.g. through // `transition: none` or `display: none`). This is technically unexpected since animations are // controlled through the animation config, but this exists for backwards compatibility. This // logic does not need to be super accurate since it covers some edge cases which can be easily // avoided by users. const animationForciblyDisabledThroughCss = userTransitionProperty === 'none' || // Note: The canonical unit for serialized CSS `<time>` properties is seconds. Additionally // some browsers expand the duration for every property (in our case `opacity` and `transform`). userTransitionDuration === '0s' || userTransitionDuration === '0s, 0s' || // If the container is 0x0, it's likely `display: none`. (containerRect.width === 0 && containerRect.height === 0); // Exposed reference to the ripple that will be returned. const rippleRef = new RippleRef(this, ripple, config, animationForciblyDisabledThroughCss); // Start the enter animation by setting the transform/scale to 100%. The animation will // execute as part of this statement because we forced a style recalculation before. // Note: We use a 3d transform here in order to avoid an issue in Safari where // the ripples aren't clipped when inside the shadow DOM (see #24028). ripple.style.transform = 'scale3d(1, 1, 1)'; rippleRef.state = 0 /* RippleState.FADING_IN */; if (!config.persistent) { this._mostRecentTransientRipple = rippleRef; } let eventListeners = null; // Do not register the `transition` event listener if fade-in and fade-out duration // are set to zero. The events won't fire anyway and we can save resources here. if (!animationForciblyDisabledThroughCss && (enterDuration || animationConfig.exitDuration)) { this._ngZone.runOutsideAngular(() => { const onTransitionEnd = () => this._finishRippleTransition(rippleRef); const onTransitionCancel = () => this._destroyRipple(rippleRef); ripple.addEventListener('transitionend', onTransitionEnd); // If the transition is cancelled (e.g. due to DOM removal), we destroy the ripple // directly as otherwise we would keep it part of the ripple container forever. // https://www.w3.org/TR/css-transitions-1/#:~:text=no%20longer%20in%20the%20document. ripple.addEventListener('transitioncancel', onTransitionCancel); eventListeners = { onTransitionEnd, onTransitionCancel }; }); } // Add the ripple reference to the list of all active ripples. this._activeRipples.set(rippleRef, eventListeners); // In case there is no fade-in transition duration, we need to manually call the transition // end listener because `transitionend` doesn't fire if there is no transition. if (animationForciblyDisabledThroughCss || !enterDuration) { this._finishRippleTransition(rippleRef); } return rippleRef; } /** Fades out a ripple reference. */ fadeOutRipple(rippleRef) { // For ripples already fading out or hidden, this should be a noop. if (rippleRef.state === 2 /* RippleState.FADING_OUT */ || rippleRef.state === 3 /* RippleState.HIDDEN */) { return; } const rippleEl = rippleRef.element; const animationConfig = { ...defaultRippleAnimationConfig, ...rippleRef.config.animation }; // This starts the fade-out transition and will fire the transition end listener that // removes the ripple element from the DOM. rippleEl.style.transitionDuration = `${animationConfig.exitDuration}ms`; rippleEl.style.opacity = '0'; rippleRef.state = 2 /* RippleState.FADING_OUT */; // In case there is no fade-out transition duration, we need to manually call the // transition end listener because `transitionend` doesn't fire if there is no transition. if (rippleRef._animationForciblyDisabledThroughCss || !animationConfig.exitDuration) { this._finishRippleTransition(rippleRef); } } /** Fades out all currently active ripples. */ fadeOutAll() { this._getActiveRipples().forEach(ripple => ripple.fadeOut()); } /** Fades out all currently active non-persistent ripples. */ fadeOutAllNonPersistent() { this._getActiveRipples().forEach(ripple => { if (!ripple.config.persistent) { ripple.fadeOut(); } }); } /** Sets up the trigger event listeners */ setupTriggerEvents(elementOrElementRef) { const element = coerceElement(elementOrElementRef); if (!this._platform.isBrowser || !element || element === this._triggerElement) { return; } // Remove all previously registered event listeners from the trigger element. this._removeTriggerEvents(); this._triggerElement = element; // Use event delegation for the trigger events since they're // set up during creation and are performance-sensitive. pointerDownEvents.forEach(type => { RippleRenderer._eventManager.addHandler(this._ngZone, type, element, this); }); } /** * Handles all registered events. * @docs-private */ handleEvent(event) { if (event.type === 'mousedown') { this._onMousedown(event); } else if (event.type === 'touchstart') { this._onTouchStart(event); } else { this._onPointerUp(); } // If pointer-up events haven't been registered yet, do so now. // We do this on-demand in order to reduce the total number of event listeners // registered by the ripples, which speeds up the rendering time for large UIs. if (!this._pointerUpEventsRegistered) { // The events for hiding the ripple are bound directly on the trigger, because: // 1. Some of them occur frequently (e.g. `mouseleave`) and any advantage we get from // delegation will be diminished by having to look through all the data structures often. // 2. They aren't as performance-sensitive, because they're bound only after the user // has interacted with an element. this._ngZone.runOutsideAngular(() => { pointerUpEvents.forEach(type => { this._triggerElement.addEventListener(type, this, passiveCapturingEventOptions); }); }); this._pointerUpEventsRegistered = true; } } /** Method that will be called if the fade-in or fade-in transition completed. */ _finishRippleTransition(rippleRef) { if (rippleRef.state === 0 /* RippleState.FADING_IN */) { this._startFadeOutTransition(rippleRef); } else if (rippleRef.state === 2 /* RippleState.FADING_OUT */) { this._destroyRipple(rippleRef); } } /** * Starts the fade-out transition of the given ripple if it's not persistent and the pointer * is not held down anymore. */ _startFadeOutTransition(rippleRef) { const isMostRecentTransientRipple = rippleRef === this._mostRecentTransientRipple; const { persistent } = rippleRef.config; rippleRef.state = 1 /* RippleState.VISIBLE */; // When the timer runs out while the user has kept their pointer down, we want to // keep only the persistent ripples and the latest transient ripple. We do this, // because we don't want stacked transient ripples to appear after their enter // animation has finished. if (!persistent && (!isMostRecentTransientRipple || !this._isPointerDown)) { rippleRef.fadeOut(); } } /** Destroys the given ripple by removing it from the DOM and updating its state. */ _destroyRipple(rippleRef) { const eventListeners = this._activeRipples.get(rippleRef) ?? null; this._activeRipples.delete(rippleRef); // Clear out the cached bounding rect if we have no more ripples. if (!this._activeRipples.size) { this._containerRect = null; } // If the current ref is the most recent transient ripple, unset it // avoid memory leaks. if (rippleRef === this._mostRecentTransientRipple) { this._mostRecentTransientRipple = null; } rippleRef.state = 3 /* RippleState.HIDDEN */; if (eventListeners !== null) { rippleRef.element.removeEventListener('transitionend', eventListeners.onTransitionEnd); rippleRef.element.removeEventListener('transitioncancel', eventListeners.onTransitionCancel); } rippleRef.element.remove(); } /** Function being called whenever the trigger is being pressed using mouse. */ _onMousedown(event) { // Screen readers will fire fake mouse events for space/enter. Skip launching a // ripple in this case for consistency with the non-screen-reader experience. const isFakeMousedown = isFakeMousedownFromScreenReader(event); const isSyntheticEvent = this._lastTouchStartEvent && Date.now() < this._lastTouchStartEvent + ignoreMouseEventsTimeout; if (!this._target.rippleDisabled && !isFakeMousedown && !isSyntheticEvent) { this._isPointerDown = true; this.fadeInRipple(event.clientX, event.clientY, this._target.rippleConfig); } } /** Function being called whenever the trigger is being pressed using touch. */ _onTouchStart(event) { if (!this._target.rippleDisabled && !isFakeTouchstartFromScreenReader(event)) { // Some browsers fire mouse events after a `touchstart` event. Those synthetic mouse // events will launch a second ripple if we don't ignore mouse events for a specific // time after a touchstart event. this._lastTouchStartEvent = Date.now(); this._isPointerDown = true; // Use `changedTouches` so we skip any touches where the user put // their finger down, but used another finger to tap the element again. const touches = event.changedTouches; // According to the typings the touches should always be defined, but in some cases // the browser appears to not assign them in tests which leads to flakes. if (touches) { for (let i = 0; i < touches.length; i++) { this.fadeInRipple(touches[i].clientX, touches[i].clientY, this._target.rippleConfig); } } } } /** Function being called whenever the trigger is being released. */ _onPointerUp() { if (!this._isPointerDown) { return; } this._isPointerDown = false; // Fade-out all ripples that are visible and not persistent. this._getActiveRipples().forEach(ripple => { // By default, only ripples that are completely visible will fade out on pointer release. // If the `terminateOnPointerUp` option is set, ripples that still fade in will also fade out. const isVisible = ripple.state === 1 /* RippleState.VISIBLE */ || (ripple.config.terminateOnPointerUp && ripple.state === 0 /* RippleState.FADING_IN */); if (!ripple.config.persistent && isVisible) { ripple.fadeOut(); } }); } _getActiveRipples() { return Array.from(this._activeRipples.keys()); } /** Removes previously registered event listeners from the trigger element. */ _removeTriggerEvents() { const trigger = this._triggerElement; if (trigger) { pointerDownEvents.forEach(type => RippleRenderer._eventManager.removeHandler(type, trigger, this)); if (this._pointerUpEventsRegistered) { pointerUpEvents.forEach(type => trigger.removeEventListener(type, this, passiveCapturingEventOptions)); } } } } /** * Returns the distance from the point (x, y) to the furthest corner of a rectangle. */ function distanceToFurthestCorner(x, y, rect) { const distX = Math.max(Math.abs(x - rect.left), Math.abs(x - rect.right)); const distY = Math.max(Math.abs(y - rect.top), Math.abs(y - rect.bottom)); return Math.sqrt(distX * distX + distY * distY); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmlwcGxlLXJlbmRlcmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21hdGVyaWFsL2NvcmUvcmlwcGxlL3JpcHBsZS1yZW5kZXJlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFRQSxPQUFPLEVBQVcsK0JBQStCLEVBQWtCLE1BQU0sdUJBQXVCLENBQUM7QUFDakcsT0FBTyxFQUFDLCtCQUErQixFQUFFLGdDQUFnQyxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDcEcsT0FBTyxFQUFDLGFBQWEsRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBQ3BELE9BQU8sRUFBQyxTQUFTLEVBQTRCLE1BQU0sY0FBYyxDQUFDO0FBQ2xFLE9BQU8sRUFBQyxrQkFBa0IsRUFBQyxNQUFNLHdCQUF3QixDQUFDO0FBb0IxRDs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSw0QkFBNEIsR0FBRztJQUMxQyxhQUFhLEVBQUUsR0FBRztJQUNsQixZQUFZLEVBQUUsR0FBRztDQUNsQixDQUFDO0FBRUY7OztHQUdHO0FBQ0gsTUFBTSx3QkFBd0IsR0FBRyxHQUFHLENBQUM7QUFFckMsc0RBQXNEO0FBQ3RELE1BQU0sNEJBQTRCLEdBQUcsK0JBQStCLENBQUM7SUFDbkUsT0FBTyxFQUFFLElBQUk7SUFDYixPQUFPLEVBQUUsSUFBSTtDQUNkLENBQUMsQ0FBQztBQUVILG1EQUFtRDtBQUNuRCxNQUFNLGlCQUFpQixHQUFHLENBQUMsV0FBVyxFQUFFLFlBQVksQ0FBQyxDQUFDO0FBRXRELGlEQUFpRDtBQUNqRCxNQUFNLGVBQWUsR0FBRyxDQUFDLFNBQVMsRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLGFBQWEsQ0FBQyxDQUFDO0FBRTdFOzs7Ozs7R0FNRztBQUNILE1BQU0sT0FBTyxjQUFjO2FBaUNWLGtCQUFhLEdBQUcsSUFBSSxrQkFBa0IsRUFBRSxBQUEzQixDQUE0QjtJQUV4RCxZQUNVLE9BQXFCLEVBQ3JCLE9BQWUsRUFD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Q0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FDaEUsQ0FBQztZQUVGLElBQUksSUFBSSxDQUFDLDBCQUEwQixFQUFFO2dCQUNuQyxlQUFlLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQzdCLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLDRCQUE0QixDQUFDLENBQ3RFLENBQUM7YUFDSDtTQUNGO0lBQ0gsQ0FBQzs7QUFHSDs7R0FFRztBQUNILFNBQVMsd0JBQXdCLENBQUMsQ0FBUyxFQUFFLENBQVMsRUFBRSxJQUFnQjtJQUN0RSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUMxRSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUMxRSxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssR0FBRyxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUM7QUFDbEQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuaW1wb3J0IHtFbGVtZW50UmVmLCBOZ1pvbmV9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtQbGF0Zm9ybSwgbm9ybWFsaXplUGFzc2l2ZUxpc3RlbmVyT3B0aW9ucywgX2dldEV2ZW50VGFyZ2V0fSBmcm9tICdAYW5ndWxhci9jZGsvcGxhdGZvcm0nO1xuaW1wb3J0IHtpc0Zha2VNb3VzZWRvd25Gcm9tU2NyZWVuUmVhZGVyLCBpc0Zha2VUb3VjaHN0YXJ0RnJvbVNjcmVlblJlYWRlcn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2ExMXknO1xuaW1wb3J0IHtjb2VyY2VFbGVtZW50fSBmcm9tICdAYW5ndWxhci9jZGsvY29lcmNpb24nO1xuaW1wb3J0IHtSaXBwbGVSZWYsIFJpcHBsZVN0YXRlLCBSaXBwbGVDb25maWd9IGZyb20gJy4vcmlwcGxlLXJlZic7XG5pbXBvcnQge1JpcHBsZUV2ZW50TWFuYWdlcn0gZnJvbSAnLi9yaXBwbGUtZXZlbnQtbWFuYWdlcic7XG5cbi8qKlxuICogSW50ZXJmYWNlIHRoYXQgZGVzY3JpYmVzIHRoZSB0YXJnZXQgZm9yIGxhdW5jaGluZyByaXBwbGVzLlxuICogSXQgZGVmaW5lcyB0aGUgcmlwcGxlIGNvbmZpZ3VyYXRpb24gYW5kIGRpc2FibGVkIHN0YXRlIGZvciBpbnRlcmFjdGlvbiByaXBwbGVzLlxuICogQGRvY3MtcHJpdmF0ZVxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJpcHBsZVRhcmdldCB7XG4gIC8qKiBDb25maWd1cmF0aW9uIGZvciByaXBwbGVzIHRoYXQgYXJlIGxhdW5jaGVkIG9uIHBvaW50ZXIgZG93bi4gKi9cbiAgcmlwcGxlQ29uZmlnOiBSaXBwbGVDb25maWc7XG4gIC8qKiBXaGV0aGVyIHJpcHBsZXMgb24gcG9pbnRlciBkb3duIHNob3VsZCBiZSBkaXNhYmxlZC4gKi9cbiAgcmlwcGxlRGlzYWJsZWQ6IGJvb2xlYW47XG59XG5cbi8qKiBJbnRlcmZhY2VzIHRoZSBkZWZpbmVzIHJpcHBsZSBlbGVtZW50IHRyYW5zaXRpb24gZXZlbnQgbGlzdGVuZXJzLiAqL1xuaW50ZXJmYWNlIFJpcHBsZUV2ZW50TGlzdGVuZXJzIHtcbiAgb25UcmFuc2l0aW9uRW5kOiBFdmVudExpc3RlbmVyO1xuICBvblRyYW5zaXRpb25DYW5jZWw6IEV2ZW50TGlzdGVuZXI7XG59XG5cbi8qKlxuICogRGVmYXVsdCByaXBwbGUgYW5pbWF0aW9uIGNvbmZpZ3VyYXRpb24gZm9yIHJpcHBsZXMgd2l0aG91dCBhbiBleHBsaWNpdFxuICogYW5pbWF0aW9uIGNvbmZpZyBzcGVjaWZpZWQuXG4gKi9cbmV4cG9ydCBjb25zdCBkZWZhdWx0UmlwcGxlQW5pbWF0aW9uQ29uZmlnID0ge1xuICBlbnRlckR1cmF0aW9uOiAyMjUsXG4gIGV4aXREdXJhdGlvbjogMTUwLFxufTtcblxuLyoqXG4gKiBUaW1lb3V0IGZvciBpZ25vcmluZyBtb3VzZSBldmVudHMuIE1vdXNlIGV2ZW50cyB3aWxsIGJlIHRlbXBvcmFyeSBpZ25vcmVkIGFmdGVyIHRvdWNoXG4gKiBldmVudHMgdG8gYXZvaWQgc3ludGhldGljIG1vdXNlIGV2ZW50cy5cbiAqL1xuY29uc3QgaWdub3JlTW91c2VFdmVudHNUaW1lb3V0ID0gODAwO1xuXG4vKiogT3B0aW9ucyB1c2VkIHRvIGJpbmQgYSBwYXNzaXZlIGNhcHR1cmluZyBldmVudC4gKi9cbmNvbnN0IHBhc3NpdmVDYXB0dXJpbmdFdmVudE9wdGlvbnMgPSBub3JtYWxpemVQYXNzaXZlTGlzdGVuZXJPcHRpb25zKHtcbiAgcGFzc2l2ZTogdHJ1ZSxcbiAgY2FwdHVyZTogdHJ1ZSxcbn0pO1xuXG4vKiogRXZlbnRzIHRoYXQgc2lnbmFsIHRoYXQgdGhlIHBvaW50ZXIgaXMgZG93bi4gKi9cbmNvbnN0IHBvaW50ZXJEb3duRXZlbnRzID0gWydtb3VzZWRvd24nLCAndG91Y2hzdGFydCddO1xuXG4vKiogRXZlbnRzIHRoYXQgc2lnbmFsIHRoYXQgdGhlIHBvaW50ZXIgaXMgdXAuICovXG5jb25zdCBwb2ludGVyVXBFdmVudHMgPSBbJ21vdXNldXAnLCAnbW91c2VsZWF2ZScsICd0b3VjaGVuZCcsICd0b3VjaGNhbmNlbCddO1xuXG4vKipcbiAqIEhlbHBlciBzZXJ2aWNlIHRoYXQgcGVyZm9ybXMgRE9NIG1hbmlwdWxhdGlvbnMuIE5vdCBpbnRlbmRlZCB0byBiZSB1c2VkIG91dHNpZGUgdGhpcyBtb2R1bGUuXG4gKiBUaGUgY29uc3RydWN0b3IgdGFrZXMgYSByZWZlcmVuY2UgdG8gdGhlIHJpcHBsZSBkaXJlY3RpdmUncyBob3N0IGVsZW1lbnQgYW5kIGEgbWFwIG9mIERPTVxuICogZXZlbnQgaGFuZGxlcnMgdG8gYmUgaW5zdGFsbGVkIG9uIHRoZSBlbGVtZW50IHRoYXQgdHJpZ2dlcnMgcmlwcGxlIGFuaW1hdGlvbnMuXG4gKiBUaGlzIHdpbGwgZXZlbnR1YWxseSBiZWNvbWUgYSBjdXN0b20gcmVuZGVyZXIgb25jZSBBbmd1bGFyIHN1cHBvcnQgZXhpc3RzLlxuICogQGRvY3MtcHJpdmF0ZVxuICovXG5leHBvcnQgY2xhc3MgUmlwcGxlUmVuZGVyZXIgaW1wbGVtZW50cyBFdmVudExpc3RlbmVyT2JqZWN0IHtcbiAgLyoqIEVsZW1lbnQgd2hlcmUgdGhlIHJpcHBsZXMgYXJlIGJlaW5nIGFkZGVkIHRvLiAqL1xuICBwcml2YXRlIF9jb250YWluZXJFbGVtZW50OiBIVE1MRWxlbWVudDtcblxuICAvKiogRWxlbWVudCB3aGljaCB0cmlnZ2VycyB0aGUgcmlwcGxlIGVsZW1lbnRzIG9uIG1vdXNlIGV2ZW50cy4gKi9cbiAgcHJpdmF0ZSBfdHJpZ2dlckVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgbnVsbDtcblxuICAvKiogV2hldGhlciB0aGUgcG9pbnRlciBpcyBjdXJyZW50bHkgZG93biBvciBub3QuICovXG4gIHByaXZhdGUgX2lzUG9pbnRlckRvd24gPSBmYWxzZTtcblxuICAvKipcbiAgICogTWFwIG9mIGN1cnJlbnRseSBhY3RpdmUgcmlwcGxlIHJlZmVyZW5jZXMuXG4gICAqIFRoZSByaXBwbGUgcmVmZXJlbmNlIGlzIG1hcHBlZCB0byBpdHMgZWxlbWVudCBldmVudCBsaXN0ZW5lcnMuXG4gICAqIFRoZSByZWFzb24gd2h5IGB8IG51bGxgIGlzIHVzZWQgaXMgdGhhdCBldmVudCBsaXN0ZW5lcnMgYXJlIGFkZGVkIG9ubHlcbiAgICogd2hlbiB0aGUgY29uZGl0aW9uIGlzIHRydXRoeSAoc2VlIHRoZSBgX3N0YXJ0RmFkZU91dFRyYW5zaXRpb25gIG1ldGhvZCkuXG4gICAqL1xuICBwcml2YXRlIF9hY3RpdmVSaXBwbGVzID0gbmV3IE1hcDxSaXBwbGVSZWYsIFJpcHBsZUV2ZW50TGlzdGVuZXJzIHwgbnVsbD4oKTtcblxuICAvKiogTGF0ZXN0IG5vbi1wZXJzaXN0ZW50IHJpcHBsZSB0aGF0IHdhcyB0cmlnZ2VyZWQuICovXG4gIHByaXZhdGUgX21vc3RSZWNlbnRUcmFuc2llbnRSaXBwbGU6IFJpcHBsZVJlZiB8IG51bGw7XG5cbiAgLyoqIFRpbWUgaW4gbWlsbGlzZWNvbmRzIHdoZW4gdGhlIGxhc3QgdG91Y2hzdGFydCBldmVudCBoYXBwZW5lZC4gKi9cbiAgcHJpdmF0ZSBfbGFzdFRvdWNoU3RhcnRFdmVudDogbnVtYmVyO1xuXG4gIC8qKiBXaGV0aGVyIHBvaW50ZXItdXAgZXZlbnQgbGlzdGVuZXJzIGhhdmUgYmVlbiByZWdpc3RlcmVkLiAqL1xuICBwcml2YXRlIF9wb2ludGVyVXBFdmVudHNSZWdpc3RlcmVkID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIENhY2hlZCBkaW1lbnNpb25zIG9mIHRoZSByaXBwbGUgY29udGFpbmVyLiBTZXQgd2hlbiB0aGUgZmlyc3RcbiAgICogcmlwcGxlIGlzIHNob3duIGFuZCBjbGVhcmVkIG9uY2Ugbm8gbW9yZSByaXBwbGVzIGFyZSB2aXNpYmxlLlxuICAgKi9cbiAgcHJpdmF0ZSBfY29udGFpbmVyUmVjdDogQ2xpZW50UmVjdCB8IG51bGw7XG5cbiAgcHJpdmF0ZSBzdGF0aWMgX2V2ZW50TWFuYWdlciA9IG5ldyBSaXBwbGVFdmVudE1hbmFnZXIoKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIF90YXJnZXQ6IFJpcHBsZVRhcmdldCxcbiAgICBwcml2YXRlIF9uZ1pvbmU6IE5nWm9uZSxcbiAgICBlbGVtZW50T3JFbGVtZW50UmVmOiBIVE1MRWxlbWVudCB8IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+LFxuICAgIHByaXZhdGUgX3BsYXRmb3JtOiBQbGF0Zm9ybSxcbiAgKSB7XG4gICAgLy8gT25seSBkbyBhbnl0aGluZyBpZiB3ZSdyZSBvbiB0aGUgYnJvd3Nlci5cbiAgICBpZiAoX3BsYXRmb3JtLmlzQnJvd3Nlcikge1xuICAgICAgdGhpcy5fY29udGFpbmVyRWxlbWVudCA9IGNvZXJjZUVsZW1lbnQoZWxlbWVudE9yRWxlbWVudFJlZik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZhZGVzIGluIGEgcmlwcGxlIGF0IHRoZSBnaXZlbiBjb29yZGluYXRlcy5cbiAgICogQHBhcmFtIHggQ29vcmRpbmF0ZSB3aXRoaW4gdGhlIGVsZW1lbnQsIGFsb25nIHRoZSBYIGF4aXMgYXQgd2hpY2ggdG8gc3RhcnQgdGhlIHJpcHBsZS5cbiAgICogQHBhcmFtIHkgQ29vcmRpbmF0ZSB3aXRoaW4gdGhlIGVsZW1lbnQsIGFsb25nIHRoZSBZIGF4aXMgYXQgd2hpY2ggdG8gc3RhcnQgdGhlIHJpcHBsZS5cbiAgICogQHBhcmFtIGNvbmZpZyBFeHRyYSByaXBwbGUgb3B0aW9ucy5cbiAgICovXG4gIGZhZGVJblJpcHBsZSh4OiBudW1iZXIsIHk6IG51bWJlciwgY29uZmlnOiBSaXBwbGVDb25maWcgPSB7fSk6IFJpcHBsZVJlZiB7XG4gICAgY29uc3QgY29udGFpbmVyUmVjdCA9ICh0aGlzLl9jb250YWluZXJSZWN0ID1cbiAgICAgIHRoaXMuX2NvbnRhaW5lclJlY3QgfHwgdGhpcy5fY29udGFpbmVyRWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSk7XG4gICAgY29uc3QgYW5pbWF0aW9uQ29uZmlnID0gey4uLmRlZmF1bHRSaXBwbGVBbmltYXRpb25Db25maWcsIC4uLmNvbmZpZy5hbmltYXRpb259O1xuXG4gICAgaWYgKGNvbmZpZy5jZW50ZXJlZCkge1xuICAgICAgeCA9IGNvbnRhaW5lclJlY3QubGVmdCArIGNvbnRhaW5lclJlY3Qud2lkdGggLyAyO1xuICAgICAgeSA9IGNvbnRhaW5lclJlY3QudG9wICsgY29udGFpbmVyUmVjdC5oZWlnaHQgLyAyO1xuICAgIH1cblxuICAgIGNvbnN0IHJhZGl1cyA9IGNvbmZpZy5yYWRpdXMgfHwgZGlzdGFuY2VUb0Z1cnRoZXN0Q29ybmVyKHgsIHksIGNvbnRhaW5lclJlY3QpO1xuICAgIGNvbnN0IG9mZnNldFggPSB4IC0gY29udGFpbmVyUmVjdC5sZWZ0O1xuICAgIGNvbnN0IG9mZnNldFkgPSB5IC0gY29udGFpbmVyUmVjdC50b3A7XG4gICAgY29uc3QgZW50ZXJEdXJhdGlvbiA9IGFuaW1hdGlvbkNvbmZpZy5lbnRlckR1cmF0aW9uO1xuXG4gICAgY29uc3QgcmlwcGxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgcmlwcGxlLmNsYXNzTGlzdC5hZGQoJ21hdC1yaXBwbGUtZWxlbWVudCcpO1xuXG4gICAgcmlwcGxlLnN0eWxlLmxlZnQgPSBgJHtvZmZzZXRYIC0gcmFkaXVzfXB4YDtcbiAgICByaXBwbGUuc3R5bGUudG9wID0gYCR7b2Zmc2V0WSAtIHJhZGl1c31weGA7XG4gICAgcmlwcGxlLnN0eWxlLmhlaWdodCA9IGAke3JhZGl1cyAqIDJ9cHhgO1xuICAgIHJpcHBsZS5zdHlsZS53aWR0aCA9IGAke3JhZGl1cyAqIDJ9cHhgO1xuXG4gICAgLy8gSWYgYSBjdXN0b20gY29sb3IgaGFzIGJlZW4gc3BlY2lmaWVkLCBzZXQgaXQgYXMgaW5saW5lIHN0eWxlLiBJZiBubyBjb2xvciBpc1xuICAgIC8vIHNldCwgdGhlIGRlZmF1bHQgY29sb3Igd2lsbCBiZSBhcHBsaWVkIHRocm91Z2ggdGhlIHJpcHBsZSB0aGVtZSBzdHlsZXMuXG4gICAgaWYgKGNvbmZpZy5jb2xvciAhPSBudWxsKSB7XG4gICAgICByaXBwbGUuc3R5bGUuYmFja2dyb3VuZENvbG9yID0gY29uZmlnLmNvbG9yO1xuICAgIH1cblxuICAgIHJpcHBsZS5zdHlsZS50cmFuc2l0aW9uRHVyYXRpb24gPSBgJHtlbnRlckR1cmF0aW9ufW1zYDtcblxuICAgIHRoaXMuX2NvbnRhaW5lckVsZW1lbnQuYXBwZW5kQ2hpbGQocmlwcGxlKTtcblxuICAgIC8vIEJ5IGRlZmF1bHQgdGhlIGJyb3dzZXIgZG9lcyBub3QgcmVjYWxjdWxhdGUgdGhlIHN0eWxlcyBvZiBkeW5hbWljYWxseSBjcmVhdGVkXG4gICAgLy8gcmlwcGxlIGVsZW1lbnRzLiBUaGlzIGlzIGNyaXRpY2FsIHRvIGVuc3VyZSB0aGF0IHRoZSBgc2NhbGVgIGFuaW1hdGVzIHByb3Blcmx5LlxuICAgIC8vIFdlIGVuZm9yY2UgYSBzdHlsZSByZWNhbGN1bGF0aW9uIGJ5IGNhbGxpbmcgYGdldENvbXB1dGVkU3R5bGVgIGFuZCAqYWNjZXNzaW5nKiBhIHByb3BlcnR5LlxuICAgIC8vIFNlZTogaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vcGF1bGlyaXNoLzVkNTJmYjA4MWIzNTcwYzgxZTNhXG4gICAgY29uc3QgY29tcHV0ZWRTdHlsZXMgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShyaXBwbGUpO1xuICAgIGNvbnN0IHVzZXJUcmFuc2l0aW9uUHJvcGVydHkgPSBjb21wdXRlZFN0eWxlcy50cmFuc2l0aW9uUHJvcGVydHk7XG4gICAgY29uc3QgdXNlclRyYW5zaXRpb25EdXJhdGlvbiA9IGNvbXB1dGVkU3R5bGVzLnRyYW5zaXRpb25EdXJhdGlvbjtcblxuICAgIC8vIE5vdGU6IFdlIGRldGVjdCB3aGV0aGVyIGFuaW1hdGlvbiBpcyBmb3JjaWJseSBkaXNhYmxlZCB0aHJvdWdoIENTUyAoZS5nLiB0aHJvdWdoXG4gICAgLy8gYHRyYW5zaXRpb246IG5vbmVgIG9yIGBkaXNwbGF5OiBub25lYCkuIFRoaXMgaXMgdGVjaG5pY2FsbHkgdW5leHBlY3RlZCBzaW5jZSBhbmltYXRpb25zIGFyZVxuICAgIC8vIGNvbnRyb2xsZWQgdGhyb3VnaCB0aGUgYW5pbWF0aW9uIGNvbmZpZywgYnV0IHRoaXMgZXhpc3RzIGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eS4gVGhpc1xuICAgIC8vIGxvZ2ljIGRvZXMgbm90IG5lZWQgdG8gYmUgc3VwZXIgYWNjdXJhdGUgc2luY2UgaXQgY292ZXJzIHNvbWUgZWRnZSBjYXNlcyB3aGljaCBjYW4gYmUgZWFzaWx5XG4gICAgLy8gYXZvaWRlZCBieSB1c2Vycy5cbiAgICBjb25zdCBhbmltYXRpb25Gb3JjaWJseURpc2FibGVkVGhyb3VnaENzcyA9XG4gICAgICB1c2VyVHJhbnNpdGlvblByb3BlcnR5ID09PSAnbm9uZScgfHxcbiAgICAgIC8vIE5vdGU6IFRoZSBjYW5vbmljYWwgdW5pdCBmb3Igc2VyaWFsaXplZCBDU1MgYDx0aW1lPmAgcHJvcGVydGllcyBpcyBzZWNvbmRzLiBBZGRpdGlvbmFsbHlcbiAgICAgIC8vIHNvbWUgYnJvd3NlcnMgZXhwYW5kIHRoZSBkdXJhdGlvbiBmb3IgZXZlcnkgcHJvcGVydHkgKGluIG91ciBjYXNlIGBvcGFjaXR5YCBhbmQgYHRyYW5zZm9ybWApLlxuICAgICAgdXNlclRyYW5zaXRpb25EdXJhdGlvbiA9PT0gJzBzJyB8fFxuICAgICAgdXNlclRyYW5zaXRpb25EdXJhdGlvbiA9PT0gJzBzLCAwcycgfHxcbiAgICAgIC8vIElmIHRoZSBjb250YWluZXIgaXMgMHgwLCBpdCdzIGxpa2VseSBgZGlzcGxheTogbm9uZWAuXG4gICAgICAoY29udGFpbmVyUmVjdC53aWR0aCA9PT0gMCAmJiBjb250YWluZXJSZWN0LmhlaWdodCA9PT0gMCk7XG5cbiAgICAvLyBFeHBvc2VkIHJlZmVyZW5jZSB0byB0aGUgcmlwcGxlIHRoYXQgd2lsbCBiZSByZXR1cm5lZC5cbiAgICBjb25zdCByaXBwbGVSZWYgPSBuZXcgUmlwcGxlUmVmKHRoaXMsIHJpcHBsZSwgY29uZmlnLCBhbmltYXRpb25Gb3JjaWJseURpc2FibGVkVGhyb3VnaENzcyk7XG5cbiAgICAvLyBTdGFydCB0aGUgZW50ZXIgYW5pbWF0aW9uIGJ5IHNldHRpbmcgdGhlIHRyYW5zZm9ybS9zY2FsZSB0byAxMDAlLiBUaGUgYW5pbWF0aW9uIHdpbGxcbiAgICAvLyBleGVjdXRlIGFzIHBhcnQgb2YgdGhpcyBzdGF0ZW1lbnQgYmVjYXVzZSB3ZSBmb3JjZWQgYSBzdHlsZSByZWNhbGN1bGF0aW9uIGJlZm9yZS5cbiAgICAvLyBOb3RlOiBXZSB1c2UgYSAzZCB0cmFuc2Zvcm0gaGVyZSBpbiBvcmRlciB0byBhdm9pZCBhbiBpc3N1ZSBpbiBTYWZhcmkgd2hlcmVcbiAgICAvLyB0aGUgcmlwcGxlcyBhcmVuJ3QgY2xpcHBlZCB3aGVuIGluc2lkZSB0aGUgc2hhZG93IERPTSAoc2VlICMyNDAyOCkuXG4gICAgcmlwcGxlLnN0eWxlLnRyYW5zZm9ybSA9ICdzY2FsZTNkKDEsIDEsIDEpJztcblxuICAgIHJpcHBsZVJlZi5zdGF0ZSA9IFJpcHBsZVN0YXRlLkZBRElOR19JTjtcblxuICAgIGlmICghY29uZmlnLnBlcnNpc3RlbnQpIHtcbiAgICAgIHRoaXMuX21vc3RSZWNlbnRUcmFuc2llbnRSaXBwbGUgPSByaXBwbGVSZWY7XG4gICAgfVxuXG4gICAgbGV0IGV2ZW50TGlzdGVuZXJzOiBSaXBwbGVFdmVudExpc3RlbmVycyB8IG51bGwgPSBudWxsO1xuXG4gICAgLy8gRG8gbm90IHJlZ2lzdGVyIHRoZSBgdHJhbnNpdGlvbmAgZXZlbnQgbGlzdGVuZXIgaWYgZmFkZS1pbiBhbmQgZmFkZS1vdXQgZHVyYXRpb25cbiAgICAvLyBhcmUgc2V0IHRvIHplcm8uIFRoZSBldmVudHMgd29uJ3QgZmlyZSBhbnl3YXkgYW5kIHdlIGNhbiBzYXZlIHJlc291cmNlcyBoZXJlLlxuICAgIGlmICghYW5pbWF0aW9uRm9yY2libHlEaXNhYmxlZFRocm91Z2hDc3MgJiYgKGVudGVyRHVyYXRpb24gfHwgYW5pbWF0aW9uQ29uZmlnLmV4aXREdXJhdGlvbikpIHtcbiAgICAgIHRoaXMuX25nWm9uZS5ydW5PdXRzaWRlQW5ndWxhcigoKSA9PiB7XG4gICAgICAgIGNvbnN0IG9uVHJhbnNpdGlvbkVuZCA9ICgpID0+IHRoaXMuX2ZpbmlzaFJpcHBsZVRyYW5zaXRpb24ocmlwcGxlUmVmKTtcbiAgICAgICAgY29uc3Qgb25UcmFuc2l0aW9uQ2FuY2VsID0gKCkgPT4gdGhpcy5fZGVzdHJveVJpcHBsZShyaXBwbGVSZWYpO1xuICAgICAgICByaXBwbGUuYWRkRXZlbnRMaXN0ZW5lcigndHJhbnNpdGlvbmVuZCcsIG9uVHJhbnNpdGlvbkVuZCk7XG4gICAgICAgIC8vIElmIHRoZSB0cmFuc2l0aW9uIGlzIGNhbmNlbGxlZCAoZS5nLiBkdWUgdG8gRE9NIHJlbW92YWwpLCB3ZSBkZXN0cm95IHRoZSByaXBwbGVcbiAgICAgICAgLy8gZGlyZWN0bHkgYXMgb3RoZXJ3aXNlIHdlIHdvdWxkIGtlZXAgaXQgcGFydCBvZiB0aGUgcmlwcGxlIGNvbnRhaW5lciBmb3JldmVyLlxuICAgICAgICAvLyBodHRwczovL3d3dy53My5vcmcvVFIvY3NzLXRyYW5zaXRpb25zLTEvIzp+OnRleHQ9bm8lMjBsb25nZXIlMjBpbiUyMHRoZSUyMGRvY3VtZW50LlxuICAgICAgICByaXBwbGUuYWRkRXZlbnRMaXN0ZW5lcigndHJhbnNpdGlvbmNhbmNlbCcsIG9uVHJhbnNpdGlvbkNhbmNlbCk7XG4gICAgICAgIGV2ZW50TGlzdGVuZXJzID0ge29uVHJhbnNpdGlvbkVuZCwgb25UcmFuc2l0aW9uQ2FuY2VsfTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEFkZCB0aGUgcmlwcGxlIHJlZmVyZW5jZSB0byB0aGUgbGlzdCBvZiBhbGwgYWN0aXZlIHJpcHBsZXMuXG4gICAgdGhpcy5fYWN0aXZlUmlwcGxlcy5zZXQocmlwcGxlUmVmLCBldmVudExpc3RlbmVycyk7XG5cbiAgICAvLyBJbiBjYXNlIHRoZXJlIGlzIG5vIGZhZGUtaW4gdHJhbnNpdGlvbiBkdXJhdGlvbiwgd2UgbmVlZCB0byBtYW51YWxseSBjYWxsIHRoZSB0cmFuc2l0aW9uXG4gICAgLy8gZW5kIGxpc3RlbmVyIGJlY2F1c2UgYHRyYW5zaXRpb25lbmRgIGRvZXNuJ3QgZmlyZSBpZiB0aGVyZSBpcyBubyB0cmFuc2l0aW9uLlxuICAgIGlmIChhbmltYXRpb25Gb3JjaWJseURpc2FibGVkVGhyb3VnaENzcyB8fCAhZW50ZXJEdXJhdGlvbikge1xuICAgICAgdGhpcy5fZmluaXNoUmlwcGxlVHJhbnNpdGlvbihyaXBwbGVSZWYpO1xuICAgIH1cblxuICAgIHJldHVybiByaXBwbGVSZWY7XG4gIH1cblxuICAvKiogRmFkZXMgb3V0IGEgcmlwcGxlIHJlZmVyZW5jZS4gKi9cbiAgZmFkZU91dFJpcHBsZShyaXBwbGVSZWY6IFJpcHBsZVJlZikge1xuICAgIC8vIEZvciByaXBwbGVzIGFscmVhZHkgZmFkaW5nIG91dCBvciBoaWRkZW4sIHRoaXMgc2hvdWxkIGJlIGEgbm9vcC5cbiAgICBpZiAocmlwcGxlUmVmLnN0YXRlID09PSBSaXBwbGVTdGF0ZS5GQURJTkdfT1VUIHx8IHJpcHBsZVJlZi5zdGF0ZSA9PT0gUmlwcGxlU3RhdGUuSElEREVOKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgcmlwcGxlRWwgPSByaXBwbGVSZWYuZWxlbWVudDtcbiAgICBjb25zdCBhbmltYXRpb25Db25maWcgPSB7Li4uZGVmYXVsdFJpcHBsZUFuaW1hdGlvbkNvbmZpZywgLi4ucmlwcGxlUmVmLmNvbmZpZy5hbmltYXRpb259O1xuXG4gICAgLy8gVGhpcyBzdGFydHMgdGhlIGZhZGUtb3V0IHRyYW5zaXRpb24gYW5kIHdpbGwgZmlyZSB0aGUgdHJhbnNpdGlvbiBlbmQgbGlzdGVuZXIgdGhhdFxuICAgIC8vIHJlbW92ZXMgdGhlIHJpcHBsZSBlbGVtZW50IGZyb20gdGhlIERPTS5cbiAgICByaXBwbGVFbC5zdHlsZS50cmFuc2l0aW9uRHVyYXRpb24gPSBgJHthbmltYXRpb25Db25maWcuZXhpdER1cmF0aW9ufW1zYDtcbiAgICByaXBwbGVFbC5zdHlsZS5vcGFjaXR5ID0gJzAnO1xuICAgIHJpcHBsZVJlZi5zdGF0ZSA9IFJpcHBsZVN0YXRlLkZBRElOR19PVVQ7XG5cbiAgICAvLyBJbiBjYXNlIHRoZXJlIGlzIG5vIGZhZGUtb3V0IHRyYW5zaXRpb24gZHVyYXRpb24sIHdlIG5lZWQgdG8gbWFudWFsbHkgY2FsbCB0aGVcbiAgICAvLyB0cmFuc2l0aW9uIGVuZCBsaXN0ZW5lciBiZWNhdXNlIGB0cmFuc2l0aW9uZW5kYCBkb2Vzbid0IGZpcmUgaWYgdGhlcmUgaXMgbm8gdHJhbnNpdGlvbi5cbiAgICBpZiAocmlwcGxlUmVmLl9hbmltYXRpb25Gb3JjaWJseURpc2FibGVkVGhyb3VnaENzcyB8fCAhYW5pbWF0aW9uQ29uZmlnLmV4aXREdXJhdGlvbikge1xuICAgICAgdGhpcy5fZmluaXNoUmlwcGxlVHJhbnNpdGlvbihyaXBwbGVSZWYpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBGYWRlcyBvdXQgYWxsIGN1cnJlbnRseSBhY3RpdmUgcmlwcGxlcy4gKi9cbiAgZmFkZU91dEFsbCgpIHtcbiAgICB0aGlzLl9nZXRBY3RpdmVSaXBwbGVzKCkuZm9yRWFjaChyaXBwbGUgPT4gcmlwcGxlLmZhZGVPdXQoKSk7XG4gIH1cblxuICAvKiogRmFkZXMgb3V0IGFsbCBjdXJyZW50bHkgYWN0aXZlIG5vbi1wZXJzaXN0ZW50IHJpcHBsZXMuICovXG4gIGZhZGVPdXRBbGxOb25QZXJzaXN0ZW50KCkge1xuICAgIHRoaXMuX2dldEFjdGl2ZVJpcHBsZXMoKS5mb3JFYWNoKHJpcHBsZSA9PiB7XG4gICAgICBpZiAoIXJpcHBsZS5jb25maWcucGVyc2lzdGVudCkge1xuICAgICAgICByaXBwbGUuZmFkZU91dCgpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqIFNldHMgdXAgdGhlIHRyaWdnZXIgZXZlbnQgbGlzdGVuZXJzICovXG4gIHNldHVwVHJpZ2dlckV2ZW50cyhlbGVtZW50T3JFbGVtZW50UmVmOiBIVE1MRWxlbWVudCB8IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGNvZXJjZUVsZW1lbnQoZWxlbWVudE9yRWxlbWVudFJlZik7XG5cbiAgICBpZiAoIXRoaXMuX3BsYXRmb3JtLmlzQnJvd3NlciB8fCAhZWxlbWVudCB8fCBlbGVtZW50ID09PSB0aGlzLl90cmlnZ2VyRWxlbWVudCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFJlbW92ZSBhbGwgcHJldmlvdXNseSByZWdpc3RlcmVkIGV2ZW50IGxpc3RlbmVycyBmcm9tIHRoZSB0cmlnZ2VyIGVsZW1lbnQuXG4gICAgdGhpcy5fcmVtb3ZlVHJpZ2dlckV2ZW50cygpO1xuICAgIHRoaXMuX3RyaWdnZXJFbGVtZW50ID0gZWxlbWVudDtcblxuICAgIC8vIFVzZSBldmVudCBkZWxlZ2F0aW9uIGZvciB0aGUgdHJpZ2dlciBldmVudHMgc2luY2UgdGhleSdyZVxuICAgIC8vIHNldCB1cCBkdXJpbmcgY3JlYXRpb24gYW5kIGFyZSBwZXJmb3JtYW5jZS1zZW5zaXRpdmUuXG4gICAgcG9pbnRlckRvd25FdmVudHMuZm9yRWFjaCh0eXBlID0+IHtcbiAgICAgIFJpcHBsZVJlbmRlcmVyLl9ldmVudE1hbmFnZXIuYWRkSGFuZGxlcih0aGlzLl9uZ1pvbmUsIHR5cGUsIGVsZW1lbnQsIHRoaXMpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEhhbmRsZXMgYWxsIHJlZ2lzdGVyZWQgZXZlbnRzLlxuICAgKiBAZG9jcy1wcml2YXRlXG4gICAqL1xuICBoYW5kbGVFdmVudChldmVudDogRXZlbnQpIHtcbiAgICBpZiAoZXZlbnQudHlwZSA9PT0gJ21vdXNlZG93bicpIHtcbiAgICAgIHRoaXMuX29uTW91c2Vkb3duKGV2ZW50IGFzIE1vdXNlRXZlbnQpO1xuICAgIH0gZWxzZSBpZiAoZXZlbnQudHlwZSA9PT0gJ3RvdWNoc3RhcnQnKSB7XG4gICAgICB0aGlzLl9vblRvdWNoU3RhcnQoZXZlbnQgYXMgVG91Y2hFdmVudCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuX29uUG9pbnRlclVwKCk7XG4gICAgfVxuXG4gICAgLy8gSWYgcG9pbnRlci11cCBldmVudHMgaGF2ZW4ndCBiZWVuIHJlZ2lzdGVyZWQgeWV0LCBkbyBzbyBub3cuXG4gICAgLy8gV2UgZG8gdGhpcyBvbi1kZW1hbmQgaW4gb3JkZXIgdG8gcmVkdWNlIHRoZSB0b3RhbCBudW1iZXIgb2YgZXZlbnQgbGlzdGVuZXJzXG4gICAgLy8gcmVnaXN0ZXJlZCBieSB0aGUgcmlwcGxlcywgd2hpY2ggc3BlZWRzIHVwIHRoZSByZW5kZXJpbmcgdGltZSBmb3IgbGFyZ2UgVUlzLlxuICAgIGlmICghdGhpcy5fcG9pbnRlclVwRXZlbnRzUmVnaXN0ZXJlZCkge1xuICAgICAgLy8gVGhlIGV2ZW50cyBmb3IgaGlkaW5nIHRoZSByaXBwbGUgYXJlIGJvdW5kIGRpcmVjdGx5IG9uIHRoZSB0cmlnZ2VyLCBiZWNhdXNlOlxuICAgICAgLy8gMS4gU29tZSBvZiB0aGVtIG9jY3VyIGZyZXF1ZW50bHkgKGUuZy4gYG1vdXNlbGVhdmVgKSBhbmQgYW55IGFkdmFudGFnZSB3ZSBnZXQgZnJvbVxuICAgICAgLy8gZGVsZWdhdGlvbiB3aWxsIGJlIGRpbWluaXNoZWQgYnkgaGF2aW5nIHRvIGxvb2sgdGhyb3VnaCBhbGwgdGhlIGRhdGEgc3RydWN0dXJlcyBvZnRlbi5cbiAgICAgIC8vIDIuIFRoZXkgYXJlbid0IGFzIHBlcmZvcm1hbmNlLXNlbnNpdGl2ZSwgYmVjYXVzZSB0aGV5J3JlIGJvdW5kIG9ubHkgYWZ0ZXIgdGhlIHVzZXJcbiAgICAgIC8vIGhhcyBpbnRlcmFjdGVkIHdpdGggYW4gZWxlbWVudC5cbiAgICAgIHRoaXMuX25nWm9uZS5ydW5PdXRzaWRlQW5ndWxhcigoKSA9PiB7XG4gICAgICAgIHBvaW50ZXJVcEV2ZW50cy5mb3JFYWNoKHR5cGUgPT4ge1xuICAgICAgICAgIHRoaXMuX3RyaWdnZXJFbGVtZW50IS5hZGRFdmVudExpc3RlbmVyKHR5cGUsIHRoaXMsIHBhc3NpdmVDYXB0dXJpbmdFdmVudE9wdGlvbnMpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuXG4gICAgICB0aGlzLl9wb2ludGVyVXBFdmVudHNSZWdpc3RlcmVkID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICAvKiogTWV0aG9kIHRoYXQgd2lsbCBiZSBjYWxsZWQgaWYgdGhlIGZhZGUtaW4gb3IgZmFkZS1pbiB0cmFuc2l0aW9uIGNvbXBsZXRlZC4gKi9cbiAgcHJpdmF0ZSBfZmluaXNoUmlwcGxlVHJhbnNpdGlvbihyaXBwbGVSZWY6IFJpcHBsZVJlZikge1xuICAgIGlmIChyaXBwbGVSZWYuc3RhdGUgPT09IFJpcHBsZVN0YXRlLkZBRElOR19JTikge1xuICAgICAgdGhpcy5fc3RhcnRGYWRlT3V0VHJhbnNpdGlvbihyaXBwbGVSZWYpO1xuICAgIH0gZWxzZSBpZiAocmlwcGxlUmVmLnN0YXRlID09PSBSaXBwbGVTdGF0ZS5GQURJTkdfT1VUKSB7XG4gICAgICB0aGlzLl9kZXN0cm95UmlwcGxlKHJpcHBsZVJlZik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0cyB0aGUgZmFkZS1vdXQgdHJhbnNpdGlvbiBvZiB0aGUgZ2l2ZW4gcmlwcGxlIGlmIGl0J3Mgbm90IHBlcnNpc3RlbnQgYW5kIHRoZSBwb2ludGVyXG4gICAqIGlzIG5vdCBoZWxkIGRvd24gYW55bW9yZS5cbiAgICovXG4gIHByaXZhdGUgX3N0YXJ0RmFkZU91dFRyYW5zaXRpb24ocmlwcGxlUmVmOiBSaXBwbGVSZWYpIHtcbiAgICBjb25zdCBpc01vc3RSZWNlbnRUcmFuc2llbnRSaXBwbGUgPSByaXBwbGVSZWYgPT09IHRoaXMuX21vc3RSZWNlbnRUcmFuc2llbnRSaXBwbGU7XG4gICAgY29uc3Qge3BlcnNpc3RlbnR9ID0gcmlwcGxlUmVmLmNvbmZpZztcblxuICAgIHJpcHBsZVJlZi5zdGF0ZSA9IFJpcHBsZVN0YXRlLlZJU0lCTEU7XG5cbiAgICAvLyBXaGVuIHRoZSB0aW1lciBydW5zIG91dCB3aGlsZSB0aGUgdXNlciBoYXMga2VwdCB0aGVpciBwb2ludGVyIGRvd24sIHdlIHdhbnQgdG9cbiAgICAvLyBrZWVwIG9ubHkgdGhlIHBlcnNpc3RlbnQgcmlwcGxlcyBhbmQgdGhlIGxhdGVzdCB0cmFuc2llbnQgcmlwcGxlLiBXZSBkbyB0aGlzLFxuICAgIC8vIGJlY2F1c2Ugd2UgZG9uJ3Qgd2FudCBzdGFja2VkIHRyYW5zaWVudCByaXBwbGVzIHRvIGFwcGVhciBhZnRlciB0aGVpciBlbnRlclxuICAgIC8vIGFuaW1hdGlvbiBoYXMgZmluaXNoZWQuXG4gICAgaWYgKCFwZXJzaXN0ZW50ICYmICghaXNNb3N0UmVjZW50VHJhbnNpZW50UmlwcGxlIHx8ICF0aGlzLl9pc1BvaW50ZXJEb3duKSkge1xuICAgICAgcmlwcGxlUmVmLmZhZGVPdXQoKTtcbiAgICB9XG4gIH1cblxuICAvKiogRGVzdHJveXMgdGhlIGdpdmVuIHJpcHBsZSBieSByZW1vdmluZyBpdCBmcm9tIHRoZSBET00gYW5kIHVwZGF0aW5nIGl0cyBzdGF0ZS4gKi9cbiAgcHJpdmF0ZSBfZGVzdHJveVJpcHBsZShyaXBwbGVSZWY6IFJpcHBsZVJlZikge1xuICAgIGNvbnN0IGV2ZW50TGlzdGVuZXJzID0gdGhpcy5fYWN0aXZlUmlwcGxlcy5nZXQocmlwcGxlUmVmKSA/PyBudWxsO1xuICAgIHRoaXMuX2FjdGl2ZVJpcHBsZXMuZGVsZXRlKHJpcHBsZVJlZik7XG5cbiAgICAvLyBDbGVhciBvdXQgdGhlIGNhY2hlZCBib3VuZGluZyByZWN0IGlmIHdlIGhhdmUgbm8gbW9yZSByaXBwbGVzLlxuICAgIGlmICghdGhpcy5fYWN0aXZlUmlwcGxlcy5zaXplKSB7XG4gICAgICB0aGlzLl9jb250YWluZXJSZWN0ID0gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBJZiB0aGUgY3VycmVudCByZWYgaXMgdGhlIG1vc3QgcmVjZW50IHRyYW5zaWVudCByaXBwbGUsIHVuc2V0IGl0XG4gICAgLy8gYXZvaWQgbWVtb3J5IGxlYWtzLlxuICAgIGlmIChyaXBwbGVSZWYgPT09IHRoaXMuX21vc3RSZWNlbnRUcmFuc2llbnRSaXBwbGUpIHtcbiAgICAgIHRo