@danielmoncada/angular-datetime-picker
Version:
Angular Date Time Picker
208 lines • 26.6 kB
JavaScript
/**
* dialog-container.component
*/
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Inject, Optional, ViewChild } from '@angular/core';
import { animate, animateChild, keyframes, style, transition, trigger } from '@angular/animations';
import { DOCUMENT } from '@angular/common';
import { FocusTrapFactory } from '@angular/cdk/a11y';
import { BasePortalOutlet, CdkPortalOutlet } from '@angular/cdk/portal';
const zoomFadeIn = {
opacity: 0,
transform: 'translateX({{ x }}) translateY({{ y }}) scale({{scale}})'
};
const zoomFadeInFrom = {
opacity: 0,
transform: 'translateX({{ x }}) translateY({{ y }}) scale({{scale}})',
transformOrigin: '{{ ox }} {{ oy }}'
};
export class OwlDialogContainerComponent extends BasePortalOutlet {
constructor(changeDetector, elementRef, focusTrapFactory, document) {
super();
this.changeDetector = changeDetector;
this.elementRef = elementRef;
this.focusTrapFactory = focusTrapFactory;
this.document = document;
this.portalOutlet = null;
/** ID of the element that should be considered as the dialog's label. */
this.ariaLabelledBy = null;
/** Emits when an animation state changes. */
this.animationStateChanged = new EventEmitter();
this.isAnimating = false;
this.state = 'enter';
// for animation purpose
this.params = {
x: '0px',
y: '0px',
ox: '50%',
oy: '50%',
scale: 0
};
// A variable to hold the focused element before the dialog was open.
// This would help us to refocus back to element when the dialog was closed.
this.elementFocusedBeforeDialogWasOpened = null;
}
get config() {
return this._config;
}
get owlDialogContainerClass() {
return true;
}
get owlDialogContainerTabIndex() {
return -1;
}
get owlDialogContainerId() {
return this._config.id;
}
get owlDialogContainerRole() {
return this._config.role || null;
}
get owlDialogContainerAriaLabelledby() {
return this.ariaLabelledBy;
}
get owlDialogContainerAriaDescribedby() {
return this._config.ariaDescribedBy || null;
}
get owlDialogContainerAnimation() {
return { value: this.state, params: this.params };
}
ngOnInit() { }
/**
* Attach a ComponentPortal as content to this dialog container.
*/
attachComponentPortal(portal) {
if (this.portalOutlet.hasAttached()) {
throw Error('Attempting to attach dialog content after content is already attached');
}
this.savePreviouslyFocusedElement();
return this.portalOutlet.attachComponentPortal(portal);
}
attachTemplatePortal(portal) {
throw new Error('Method not implemented.');
}
setConfig(config) {
this._config = config;
if (config.event) {
this.calculateZoomOrigin(event);
}
}
onAnimationStart(event) {
this.isAnimating = true;
this.animationStateChanged.emit(event);
}
onAnimationDone(event) {
if (event.toState === 'enter') {
this.trapFocus();
}
else if (event.toState === 'exit') {
this.restoreFocus();
}
this.animationStateChanged.emit(event);
this.isAnimating = false;
}
startExitAnimation() {
this.state = 'exit';
this.changeDetector.markForCheck();
}
/**
* Calculate origin used in the `zoomFadeInFrom()`
* for animation purpose
*/
calculateZoomOrigin(event) {
if (!event) {
return;
}
const clientX = event.clientX;
const clientY = event.clientY;
const wh = window.innerWidth / 2;
const hh = window.innerHeight / 2;
const x = clientX - wh;
const y = clientY - hh;
const ox = clientX / window.innerWidth;
const oy = clientY / window.innerHeight;
this.params.x = `${x}px`;
this.params.y = `${y}px`;
this.params.ox = `${ox * 100}%`;
this.params.oy = `${oy * 100}%`;
this.params.scale = 0;
return;
}
/**
* Save the focused element before dialog was open
*/
savePreviouslyFocusedElement() {
if (this.document) {
this.elementFocusedBeforeDialogWasOpened = this.document
.activeElement;
Promise.resolve().then(() => this.elementRef.nativeElement.focus());
}
}
trapFocus() {
if (!this.focusTrap) {
this.focusTrap = this.focusTrapFactory.create(this.elementRef.nativeElement);
}
if (this._config.autoFocus) {
this.focusTrap.focusInitialElementWhenReady();
}
}
restoreFocus() {
const toFocus = this.elementFocusedBeforeDialogWasOpened;
// We need the extra check, because IE can set the `activeElement` to null in some cases.
if (toFocus && typeof toFocus.focus === 'function') {
toFocus.focus();
}
if (this.focusTrap) {
this.focusTrap.destroy();
}
}
}
OwlDialogContainerComponent.decorators = [
{ type: Component, args: [{
selector: 'owl-dialog-container',
template: "<ng-template [cdkPortalOutlet]></ng-template>\n",
animations: [
trigger('slideModal', [
transition('void => enter', [
style(zoomFadeInFrom),
animate('300ms cubic-bezier(0.35, 0, 0.25, 1)', style('*')),
animate('150ms', keyframes([
style({ transform: 'scale(1)', offset: 0 }),
style({ transform: 'scale(1.05)', offset: 0.3 }),
style({ transform: 'scale(.95)', offset: 0.8 }),
style({ transform: 'scale(1)', offset: 1.0 })
])),
animateChild()
], {
params: {
x: '0px',
y: '0px',
ox: '50%',
oy: '50%',
scale: 1
}
}),
transition('enter => exit', [animateChild(), animate(200, style(zoomFadeIn))], { params: { x: '0px', y: '0px', ox: '50%', oy: '50%' } })
])
],
host: {
'(@slideModal.start)': 'onAnimationStart($event)',
'(@slideModal.done)': 'onAnimationDone($event)',
'[class.owl-dialog-container]': 'owlDialogContainerClass',
'[attr.tabindex]': 'owlDialogContainerTabIndex',
'[attr.id]': 'owlDialogContainerId',
'[attr.role]': 'owlDialogContainerRole',
'[attr.aria-labelledby]': 'owlDialogContainerAriaLabelledby',
'[attr.aria-describedby]': 'owlDialogContainerAriaDescribedby',
'[@slideModal]': 'owlDialogContainerAnimation'
}
},] }
];
OwlDialogContainerComponent.ctorParameters = () => [
{ type: ChangeDetectorRef },
{ type: ElementRef },
{ type: FocusTrapFactory },
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] }
];
OwlDialogContainerComponent.propDecorators = {
portalOutlet: [{ type: ViewChild, args: [CdkPortalOutlet, { static: true },] }]
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlhbG9nLWNvbnRhaW5lci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9waWNrZXIvc3JjL2xpYi9kaWFsb2cvZGlhbG9nLWNvbnRhaW5lci5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEVBQ0gsaUJBQWlCLEVBQ2pCLFNBQVMsRUFFVCxVQUFVLEVBRVYsWUFBWSxFQUNaLE1BQU0sRUFFTixRQUFRLEVBQ1IsU0FBUyxFQUNaLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFDSCxPQUFPLEVBQ1AsWUFBWSxFQUVaLFNBQVMsRUFDVCxLQUFLLEVBQ0wsVUFBVSxFQUNWLE9BQU8sRUFDVixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMzQyxPQUFPLEVBQWEsZ0JBQWdCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNoRSxPQUFPLEVBQ0gsZ0JBQWdCLEVBQ2hCLGVBQWUsRUFHbEIsTUFBTSxxQkFBcUIsQ0FBQztBQUc3QixNQUFNLFVBQVUsR0FBRztJQUNmLE9BQU8sRUFBRSxDQUFDO0lBQ1YsU0FBUyxFQUFFLDBEQUEwRDtDQUN4RSxDQUFDO0FBQ0YsTUFBTSxjQUFjLEdBQUc7SUFDbkIsT0FBTyxFQUFFLENBQUM7SUFDVixTQUFTLEVBQUUsMERBQTBEO0lBQ3JFLGVBQWUsRUFBRSxtQkFBbUI7Q0FDdkMsQ0FBQztBQW9ERixNQUFNLE9BQU8sMkJBQTRCLFNBQVEsZ0JBQWdCO0lBZ0U3RCxZQUNZLGNBQWlDLEVBQ2pDLFVBQXNCLEVBQ3RCLGdCQUFrQyxFQUdsQyxRQUFhO1FBRXJCLEtBQUssRUFBRSxDQUFDO1FBUEEsbUJBQWMsR0FBZCxjQUFjLENBQW1CO1FBQ2pDLGVBQVUsR0FBVixVQUFVLENBQVk7UUFDdEIscUJBQWdCLEdBQWhCLGdCQUFnQixDQUFrQjtRQUdsQyxhQUFRLEdBQVIsUUFBUSxDQUFLO1FBbkV6QixpQkFBWSxHQUEyQixJQUFJLENBQUM7UUFLNUMseUVBQXlFO1FBQ2xFLG1CQUFjLEdBQWtCLElBQUksQ0FBQztRQUU1Qyw2Q0FBNkM7UUFDdEMsMEJBQXFCLEdBQUcsSUFBSSxZQUFZLEVBQWtCLENBQUM7UUFFM0QsZ0JBQVcsR0FBRyxLQUFLLENBQUM7UUFPbkIsVUFBSyxHQUE4QixPQUFPLENBQUM7UUFFbkQsd0JBQXdCO1FBQ2hCLFdBQU0sR0FBUTtZQUNsQixDQUFDLEVBQUUsS0FBSztZQUNSLENBQUMsRUFBRSxLQUFLO1lBQ1IsRUFBRSxFQUFFLEtBQUs7WUFDVCxFQUFFLEVBQUUsS0FBSztZQUNULEtBQUssRUFBRSxDQUFDO1NBQ1gsQ0FBQztRQUVGLHFFQUFxRTtRQUNyRSw0RUFBNEU7UUFDcEUsd0NBQW1DLEdBQXVCLElBQUksQ0FBQztJQXVDdkUsQ0FBQztJQXhERCxJQUFJLE1BQU07UUFDTixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDeEIsQ0FBQztJQWlCRCxJQUFJLHVCQUF1QjtRQUN2QixPQUFPLElBQUksQ0FBQztJQUNoQixDQUFDO0lBRUQsSUFBSSwwQkFBMEI7UUFDMUIsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUNkLENBQUM7SUFFRCxJQUFJLG9CQUFvQjtRQUNwQixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO0lBQzNCLENBQUM7SUFFRCxJQUFJLHNCQUFzQjtRQUN0QixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQztJQUNyQyxDQUFDO0lBRUQsSUFBSSxnQ0FBZ0M7UUFDaEMsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDO0lBQy9CLENBQUM7SUFFRCxJQUFJLGlDQUFpQztRQUNqQyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxJQUFJLElBQUksQ0FBQztJQUNoRCxDQUFDO0lBRUQsSUFBSSwyQkFBMkI7UUFDM0IsT0FBTyxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7SUFDdEQsQ0FBQztJQWFNLFFBQVEsS0FBSSxDQUFDO0lBRXBCOztPQUVHO0lBQ0kscUJBQXFCLENBQ3hCLE1BQTBCO1FBRTFCLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsRUFBRTtZQUNqQyxNQUFNLEtBQUssQ0FDUCx1RUFBdUUsQ0FDMUUsQ0FBQztTQUNMO1FBRUQsSUFBSSxDQUFDLDRCQUE0QixFQUFFLENBQUM7UUFDcEMsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDLHFCQUFxQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFFTSxvQkFBb0IsQ0FDdkIsTUFBeUI7UUFFekIsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0lBQy9DLENBQUM7SUFFTSxTQUFTLENBQUMsTUFBZ0M7UUFDN0MsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7UUFFdEIsSUFBSSxNQUFNLENBQUMsS0FBSyxFQUFFO1lBQ2QsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1NBQ25DO0lBQ0wsQ0FBQztJQUVNLGdCQUFnQixDQUFFLEtBQXFCO1FBQzFDLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUVNLGVBQWUsQ0FBRSxLQUFxQjtRQUN6QyxJQUFJLEtBQUssQ0FBQyxPQUFPLEtBQUssT0FBTyxFQUFFO1lBQzNCLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztTQUNwQjthQUFNLElBQUksS0FBSyxDQUFDLE9BQU8sS0FBSyxNQUFNLEVBQUU7WUFDakMsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1NBQ3ZCO1FBRUQsSUFBSSxDQUFDLHFCQUFxQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQztJQUM3QixDQUFDO0lBRU0sa0JBQWtCO1FBQ3JCLElBQUksQ0FBQyxLQUFLLEdBQUcsTUFBTSxDQUFDO1FBQ3BCLElBQUksQ0FBQyxjQUFjLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDdkMsQ0FBQztJQUVEOzs7T0FHRztJQUNLLG1CQUFtQixDQUFDLEtBQVU7UUFDbEMsSUFBSSxDQUFDLEtBQUssRUFBRTtZQUNSLE9BQU87U0FDVjtRQUVELE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUM7UUFDOUIsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQztRQUU5QixNQUFNLEVBQUUsR0FBRyxNQUFNLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQztRQUNqQyxNQUFNLEVBQUUsR0FBRyxNQUFNLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQztRQUNsQyxNQUFNLENBQUMsR0FBRyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sQ0FBQyxHQUFHLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDdkIsTUFBTSxFQUFFLEdBQUcsT0FBTyxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUM7UUFDdkMsTUFBTSxFQUFFLEdBQUcsT0FBTyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUM7UUFFeEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQztRQUN6QixJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxHQUFHLEdBQUcsRUFBRSxHQUFHLEdBQUcsR0FBRyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxHQUFHLEdBQUcsRUFBRSxHQUFHLEdBQUcsR0FBRyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQztRQUV0QixPQUFPO0lBQ1gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssNEJBQTRCO1FBQ2hDLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUNmLElBQUksQ0FBQyxtQ0FBbUMsR0FBRyxJQUFJLENBQUMsUUFBUTtpQkFDbkQsYUFBNEIsQ0FBQztZQUVsQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7U0FDdkU7SUFDTCxDQUFDO0lBRU8sU0FBUztRQUNiLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFO1lBQ2pCLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FDekMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQ2hDLENBQUM7U0FDTDtRQUVELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUU7WUFDeEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyw0QkFBNEIsRUFBRSxDQUFDO1NBQ2pEO0lBQ0wsQ0FBQztJQUVPLFlBQVk7UUFDaEIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLG1DQUFtQyxDQUFDO1FBRXpELHlGQUF5RjtRQUN6RixJQUFJLE9BQU8sSUFBSSxPQUFPLE9BQU8sQ0FBQyxLQUFLLEtBQUssVUFBVSxFQUFFO1lBQ2hELE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztTQUNuQjtRQUVELElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRTtZQUNoQixJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO1NBQzVCO0lBQ0wsQ0FBQzs7O1lBalBKLFNBQVMsU0FBQztnQkFDUCxRQUFRLEVBQUUsc0JBQXNCO2dCQUNoQywyREFBZ0Q7Z0JBQ2hELFVBQVUsRUFBRTtvQkFDUixPQUFPLENBQUMsWUFBWSxFQUFFO3dCQUNsQixVQUFVLENBQ04sZUFBZSxFQUNmOzRCQUNJLEtBQUssQ0FBQyxjQUFjLENBQUM7NEJBQ3JCLE9BQU8sQ0FBQyxzQ0FBc0MsRUFBRSxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7NEJBQzNELE9BQU8sQ0FDSCxPQUFPLEVBQ1AsU0FBUyxDQUFDO2dDQUNOLEtBQUssQ0FBQyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDO2dDQUMzQyxLQUFLLENBQUMsRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQztnQ0FDaEQsS0FBSyxDQUFDLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUM7Z0NBQy9DLEtBQUssQ0FBQyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDOzZCQUNoRCxDQUFDLENBQ0w7NEJBQ0QsWUFBWSxFQUFFO3lCQUNqQixFQUNEOzRCQUNJLE1BQU0sRUFBRTtnQ0FDSixDQUFDLEVBQUUsS0FBSztnQ0FDUixDQUFDLEVBQUUsS0FBSztnQ0FDUixFQUFFLEVBQUUsS0FBSztnQ0FDVCxFQUFFLEVBQUUsS0FBSztnQ0FDVCxLQUFLLEVBQUUsQ0FBQzs2QkFDWDt5QkFDSixDQUNKO3dCQUNELFVBQVUsQ0FDTixlQUFlLEVBQ2YsQ0FBQyxZQUFZLEVBQUUsRUFBRSxPQUFPLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLEVBQ2pELEVBQUUsTUFBTSxFQUFFLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQzNEO3FCQUNKLENBQUM7aUJBQ0w7Z0JBQ0QsSUFBSSxFQUFFO29CQUNGLHFCQUFxQixFQUFFLDBCQUEwQjtvQkFDakQsb0JBQW9CLEVBQUUseUJBQXlCO29CQUMvQyw4QkFBOEIsRUFBRSx5QkFBeUI7b0JBQ3pELGlCQUFpQixFQUFFLDRCQUE0QjtvQkFDL0MsV0FBVyxFQUFFLHNCQUFzQjtvQkFDbkMsYUFBYSxFQUFFLHdCQUF3QjtvQkFDdkMsd0JBQXdCLEVBQUUsa0NBQWtDO29CQUM1RCx5QkFBeUIsRUFBRSxtQ0FBbUM7b0JBQzlELGVBQWUsRUFBRSw2QkFBNkI7aUJBQ2pEO2FBQ0o7OztZQXpGRyxpQkFBaUI7WUFHakIsVUFBVTtZQWtCTSxnQkFBZ0I7NENBeUkzQixRQUFRLFlBQ1IsTUFBTSxTQUFDLFFBQVE7OzsyQkFuRW5CLFNBQVMsU0FBQyxlQUFlLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBkaWFsb2ctY29udGFpbmVyLmNvbXBvbmVudFxuICovXG5cbmltcG9ydCB7XG4gICAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgQ29tcG9uZW50LFxuICAgIENvbXBvbmVudFJlZixcbiAgICBFbGVtZW50UmVmLFxuICAgIEVtYmVkZGVkVmlld1JlZixcbiAgICBFdmVudEVtaXR0ZXIsXG4gICAgSW5qZWN0LFxuICAgIE9uSW5pdCxcbiAgICBPcHRpb25hbCxcbiAgICBWaWV3Q2hpbGRcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICAgIGFuaW1hdGUsXG4gICAgYW5pbWF0ZUNoaWxkLFxuICAgIEFuaW1hdGlvbkV2ZW50LFxuICAgIGtleWZyYW1lcyxcbiAgICBzdHlsZSxcbiAgICB0cmFuc2l0aW9uLFxuICAgIHRyaWdnZXJcbn0gZnJvbSAnQGFuZ3VsYXIvYW5pbWF0aW9ucyc7XG5pbXBvcnQgeyBET0NVTUVOVCB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBGb2N1c1RyYXAsIEZvY3VzVHJhcEZhY3RvcnkgfSBmcm9tICdAYW5ndWxhci9jZGsvYTExeSc7XG5pbXBvcnQge1xuICAgIEJhc2VQb3J0YWxPdXRsZXQsXG4gICAgQ2RrUG9ydGFsT3V0bGV0LFxuICAgIENvbXBvbmVudFBvcnRhbCxcbiAgICBUZW1wbGF0ZVBvcnRhbFxufSBmcm9tICdAYW5ndWxhci9jZGsvcG9ydGFsJztcbmltcG9ydCB7IE93bERpYWxvZ0NvbmZpZ0ludGVyZmFjZSB9IGZyb20gJy4vZGlhbG9nLWNvbmZpZy5jbGFzcyc7XG5cbmNvbnN0IHpvb21GYWRlSW4gPSB7XG4gICAgb3BhY2l0eTogMCxcbiAgICB0cmFuc2Zvcm06ICd0cmFuc2xhdGVYKHt7IHggfX0pIHRyYW5zbGF0ZVkoe3sgeSB9fSkgc2NhbGUoe3tzY2FsZX19KSdcbn07XG5jb25zdCB6b29tRmFkZUluRnJvbSA9IHtcbiAgICBvcGFjaXR5OiAwLFxuICAgIHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVgoe3sgeCB9fSkgdHJhbnNsYXRlWSh7eyB5IH19KSBzY2FsZSh7e3NjYWxlfX0pJyxcbiAgICB0cmFuc2Zvcm1PcmlnaW46ICd7eyBveCB9fSB7eyBveSB9fSdcbn07XG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAnb3dsLWRpYWxvZy1jb250YWluZXInLFxuICAgIHRlbXBsYXRlVXJsOiAnLi9kaWFsb2ctY29udGFpbmVyLmNvbXBvbmVudC5odG1sJyxcbiAgICBhbmltYXRpb25zOiBbXG4gICAgICAgIHRyaWdnZXIoJ3NsaWRlTW9kYWwnLCBbXG4gICAgICAgICAgICB0cmFuc2l0aW9uKFxuICAgICAgICAgICAgICAgICd2b2lkID0+IGVudGVyJyxcbiAgICAgICAgICAgICAgICBbXG4gICAgICAgICAgICAgICAgICAgIHN0eWxlKHpvb21GYWRlSW5Gcm9tKSxcbiAgICAgICAgICAgICAgICAgICAgYW5pbWF0ZSgnMzAwbXMgY3ViaWMtYmV6aWVyKDAuMzUsIDAsIDAuMjUsIDEpJywgc3R5bGUoJyonKSksXG4gICAgICAgICAgICAgICAgICAgIGFuaW1hdGUoXG4gICAgICAgICAgICAgICAgICAgICAgICAnMTUwbXMnLFxuICAgICAgICAgICAgICAgICAgICAgICAga2V5ZnJhbWVzKFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSh7IHRyYW5zZm9ybTogJ3NjYWxlKDEpJywgb2Zmc2V0OiAwIH0pLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlKHsgdHJhbnNmb3JtOiAnc2NhbGUoMS4wNSknLCBvZmZzZXQ6IDAuMyB9KSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSh7IHRyYW5zZm9ybTogJ3NjYWxlKC45NSknLCBvZmZzZXQ6IDAuOCB9KSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSh7IHRyYW5zZm9ybTogJ3NjYWxlKDEpJywgb2Zmc2V0OiAxLjAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIF0pXG4gICAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgICAgIGFuaW1hdGVDaGlsZCgpXG4gICAgICAgICAgICAgICAgXSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHBhcmFtczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgeDogJzBweCcsXG4gICAgICAgICAgICAgICAgICAgICAgICB5OiAnMHB4JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIG94OiAnNTAlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIG95OiAnNTAlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlOiAxXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApLFxuICAgICAgICAgICAgdHJhbnNpdGlvbihcbiAgICAgICAgICAgICAgICAnZW50ZXIgPT4gZXhpdCcsXG4gICAgICAgICAgICAgICAgW2FuaW1hdGVDaGlsZCgpLCBhbmltYXRlKDIwMCwgc3R5bGUoem9vbUZhZGVJbikpXSxcbiAgICAgICAgICAgICAgICB7IHBhcmFtczogeyB4OiAnMHB4JywgeTogJzBweCcsIG94OiAnNTAlJywgb3k6ICc1MCUnIH0gfVxuICAgICAgICAgICAgKVxuICAgICAgICBdKVxuICAgIF0sXG4gICAgaG9zdDoge1xuICAgICAgICAnKEBzbGlkZU1vZGFsLnN0YXJ0KSc6ICdvbkFuaW1hdGlvblN0YXJ0KCRldmVudCknLFxuICAgICAgICAnKEBzbGlkZU1vZGFsLmRvbmUpJzogJ29uQW5pbWF0aW9uRG9uZSgkZXZlbnQpJyxcbiAgICAgICAgJ1tjbGFzcy5vd2wtZGlhbG9nLWNvbnRhaW5lcl0nOiAnb3dsRGlhbG9nQ29udGFpbmVyQ2xhc3MnLFxuICAgICAgICAnW2F0dHIudGFiaW5kZXhdJzogJ293bERpYWxvZ0NvbnRhaW5lclRhYkluZGV4JyxcbiAgICAgICAgJ1thdHRyLmlkXSc6ICdvd2xEaWFsb2dDb250YWluZXJJZCcsXG4gICAgICAgICdbYXR0ci5yb2xlXSc6ICdvd2xEaWFsb2dDb250YWluZXJSb2xlJyxcbiAgICAgICAgJ1thdHRyLmFyaWEtbGFiZWxsZWRieV0nOiAnb3dsRGlhbG9nQ29udGFpbmVyQXJpYUxhYmVsbGVkYnknLFxuICAgICAgICAnW2F0dHIuYXJpYS1kZXNjcmliZWRieV0nOiAnb3dsRGlhbG9nQ29udGFpbmVyQXJpYURlc2NyaWJlZGJ5JyxcbiAgICAgICAgJ1tAc2xpZGVNb2RhbF0nOiAnb3dsRGlhbG9nQ29udGFpbmVyQW5pbWF0aW9uJ1xuICAgIH1cbn0pXG5leHBvcnQgY2xhc3MgT3dsRGlhbG9nQ29udGFpbmVyQ29tcG9uZW50IGV4dGVuZHMgQmFzZVBvcnRhbE91dGxldFxuICAgIGltcGxlbWVudHMgT25Jbml0IHtcbiAgICBAVmlld0NoaWxkKENka1BvcnRhbE91dGxldCwgeyBzdGF0aWM6IHRydWUgfSlcbiAgICBwb3J0YWxPdXRsZXQ6IENka1BvcnRhbE91dGxldCB8IG51bGwgPSBudWxsO1xuXG4gICAgLyoqIFRoZSBjbGFzcyB0aGF0IHRyYXBzIGFuZCBtYW5hZ2VzIGZvY3VzIHdpdGhpbiB0aGUgZGlhbG9nLiAqL1xuICAgIHByaXZhdGUgZm9jdXNUcmFwOiBGb2N1c1RyYXA7XG5cbiAgICAvKiogSUQgb2YgdGhlIGVsZW1lbnQgdGhhdCBzaG91bGQgYmUgY29uc2lkZXJlZCBhcyB0aGUgZGlhbG9nJ3MgbGFiZWwuICovXG4gICAgcHVibGljIGFyaWFMYWJlbGxlZEJ5OiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICAgIC8qKiBFbWl0cyB3aGVuIGFuIGFuaW1hdGlvbiBzdGF0ZSBjaGFuZ2VzLiAqL1xuICAgIHB1YmxpYyBhbmltYXRpb25TdGF0ZUNoYW5nZWQgPSBuZXcgRXZlbnRFbWl0dGVyPEFuaW1hdGlvbkV2ZW50PigpO1xuXG4gICAgcHVibGljIGlzQW5pbWF0aW5nID0gZmFsc2U7XG5cbiAgICBwcml2YXRlIF9jb25maWc6IE93bERpYWxvZ0NvbmZpZ0ludGVyZmFjZTtcbiAgICBnZXQgY29uZmlnKCk6IE93bERpYWxvZ0NvbmZpZ0ludGVyZmFjZSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jb25maWc7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzdGF0ZTogJ3ZvaWQnIHwgJ2VudGVyJyB8ICdleGl0JyA9ICdlbnRlcic7XG5cbiAgICAvLyBmb3IgYW5pbWF0aW9uIHB1cnBvc2VcbiAgICBwcml2YXRlIHBhcmFtczogYW55ID0ge1xuICAgICAgICB4OiAnMHB4JyxcbiAgICAgICAgeTogJzBweCcsXG4gICAgICAgIG94OiAnNTAlJyxcbiAgICAgICAgb3k6ICc1MCUnLFxuICAgICAgICBzY2FsZTogMFxuICAgIH07XG5cbiAgICAvLyBBIHZhcmlhYmxlIHRvIGhvbGQgdGhlIGZvY3VzZWQgZWxlbWVudCBiZWZvcmUgdGhlIGRpYWxvZyB3YXMgb3Blbi5cbiAgICAvLyBUaGlzIHdvdWxkIGhlbHAgdXMgdG8gcmVmb2N1cyBiYWNrIHRvIGVsZW1lbnQgd2hlbiB0aGUgZGlhbG9nIHdhcyBjbG9zZWQuXG4gICAgcHJpdmF0ZSBlbGVtZW50Rm9jdXNlZEJlZm9yZURpYWxvZ1dhc09wZW5lZDogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcblxuICAgIGdldCBvd2xEaWFsb2dDb250YWluZXJDbGFzcygpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgZ2V0IG93bERpYWxvZ0NvbnRhaW5lclRhYkluZGV4KCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiAtMTtcbiAgICB9XG5cbiAgICBnZXQgb3dsRGlhbG9nQ29udGFpbmVySWQoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NvbmZpZy5pZDtcbiAgICB9XG5cbiAgICBnZXQgb3dsRGlhbG9nQ29udGFpbmVyUm9sZSgpOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gdGhpcy5fY29uZmlnLnJvbGUgfHwgbnVsbDtcbiAgICB9XG5cbiAgICBnZXQgb3dsRGlhbG9nQ29udGFpbmVyQXJpYUxhYmVsbGVkYnkoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYXJpYUxhYmVsbGVkQnk7XG4gICAgfVxuXG4gICAgZ2V0IG93bERpYWxvZ0NvbnRhaW5lckFyaWFEZXNjcmliZWRieSgpOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gdGhpcy5fY29uZmlnLmFyaWFEZXNjcmliZWRCeSB8fCBudWxsO1xuICAgIH1cblxuICAgIGdldCBvd2xEaWFsb2dDb250YWluZXJBbmltYXRpb24oKTogYW55IHtcbiAgICAgICAgcmV0dXJuIHsgdmFsdWU6IHRoaXMuc3RhdGUsIHBhcmFtczogdGhpcy5wYXJhbXMgfTtcbiAgICB9XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSBjaGFuZ2VEZXRlY3RvcjogQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgICAgIHByaXZhdGUgZWxlbWVudFJlZjogRWxlbWVudFJlZixcbiAgICAgICAgcHJpdmF0ZSBmb2N1c1RyYXBGYWN0b3J5OiBGb2N1c1RyYXBGYWN0b3J5LFxuICAgICAgICBAT3B0aW9uYWwoKVxuICAgICAgICBASW5qZWN0KERPQ1VNRU5UKVxuICAgICAgICBwcml2YXRlIGRvY3VtZW50OiBhbnlcbiAgICApIHtcbiAgICAgICAgc3VwZXIoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdPbkluaXQoKSB7fVxuXG4gICAgLyoqXG4gICAgICogQXR0YWNoIGEgQ29tcG9uZW50UG9ydGFsIGFzIGNvbnRlbnQgdG8gdGhpcyBkaWFsb2cgY29udGFpbmVyLlxuICAgICAqL1xuICAgIHB1YmxpYyBhdHRhY2hDb21wb25lbnRQb3J0YWw8VD4oXG4gICAgICAgIHBvcnRhbDogQ29tcG9uZW50UG9ydGFsPFQ+XG4gICAgKTogQ29tcG9uZW50UmVmPFQ+IHtcbiAgICAgICAgaWYgKHRoaXMucG9ydGFsT3V0bGV0Lmhhc0F0dGFjaGVkKCkpIHtcbiAgICAgICAgICAgIHRocm93IEVycm9yKFxuICAgICAgICAgICAgICAgICdBdHRlbXB0aW5nIHRvIGF0dGFjaCBkaWFsb2cgY29udGVudCBhZnRlciBjb250ZW50IGlzIGFscmVhZHkgYXR0YWNoZWQnXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5zYXZlUHJldmlvdXNseUZvY3VzZWRFbGVtZW50KCk7XG4gICAgICAgIHJldHVybiB0aGlzLnBvcnRhbE91dGxldC5hdHRhY2hDb21wb25lbnRQb3J0YWwocG9ydGFsKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXR0YWNoVGVtcGxhdGVQb3J0YWw8Qz4oXG4gICAgICAgIHBvcnRhbDogVGVtcGxhdGVQb3J0YWw8Qz5cbiAgICApOiBFbWJlZGRlZFZpZXdSZWY8Qz4ge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01ldGhvZCBub3QgaW1wbGVtZW50ZWQuJyk7XG4gICAgfVxuXG4gICAgcHVibGljIHNldENvbmZpZyhjb25maWc6IE93bERpYWxvZ0NvbmZpZ0ludGVyZmFjZSk6IHZvaWQge1xuICAgICAgICB0aGlzLl9jb25maWcgPSBjb25maWc7XG5cbiAgICAgICAgaWYgKGNvbmZpZy5ldmVudCkge1xuICAgICAgICAgICAgdGhpcy5jYWxjdWxhdGVab29tT3JpZ2luKGV2ZW50KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBvbkFuaW1hdGlvblN0YXJ0KCBldmVudDogQW5pbWF0aW9uRXZlbnQgKTogdm9pZCB7XG4gICAgICAgIHRoaXMuaXNBbmltYXRpbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLmFuaW1hdGlvblN0YXRlQ2hhbmdlZC5lbWl0KGV2ZW50KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgb25BbmltYXRpb25Eb25lKCBldmVudDogQW5pbWF0aW9uRXZlbnQgKTogdm9pZCB7XG4gICAgICAgIGlmIChldmVudC50b1N0YXRlID09PSAnZW50ZXInKSB7XG4gICAgICAgICAgICB0aGlzLnRyYXBGb2N1cygpO1xuICAgICAgICB9IGVsc2UgaWYgKGV2ZW50LnRvU3RhdGUgPT09ICdleGl0Jykge1xuICAgICAgICAgICAgdGhpcy5yZXN0b3JlRm9jdXMoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuYW5pbWF0aW9uU3RhdGVDaGFuZ2VkLmVtaXQoZXZlbnQpO1xuICAgICAgICB0aGlzLmlzQW5pbWF0aW5nID0gZmFsc2U7XG4gICAgfVxuXG4gICAgcHVibGljIHN0YXJ0RXhpdEFuaW1hdGlvbigpIHtcbiAgICAgICAgdGhpcy5zdGF0ZSA9ICdleGl0JztcbiAgICAgICAgdGhpcy5jaGFuZ2VEZXRlY3Rvci5tYXJrRm9yQ2hlY2soKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGUgb3JpZ2luIHVzZWQgaW4gdGhlIGB6b29tRmFkZUluRnJvbSgpYFxuICAgICAqIGZvciBhbmltYXRpb24gcHVycG9zZVxuICAgICAqL1xuICAgIHByaXZhdGUgY2FsY3VsYXRlWm9vbU9yaWdpbihldmVudDogYW55KTogdm9pZCB7XG4gICAgICAgIGlmICghZXZlbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGNsaWVudFggPSBldmVudC5jbGllbnRYO1xuICAgICAgICBjb25zdCBjbGllbnRZID0gZXZlbnQuY2xpZW50WTtcblxuICAgICAgICBjb25zdCB3aCA9IHdpbmRvdy5pbm5lcldpZHRoIC8gMjtcbiAgICAgICAgY29uc3QgaGggPSB3aW5kb3cuaW5uZXJIZWlnaHQgLyAyO1xuICAgICAgICBjb25zdCB4ID0gY2xpZW50WCAtIHdoO1xuICAgICAgICBjb25zdCB5ID0gY2xpZW50WSAtIGhoO1xuICAgICAgICBjb25zdCBveCA9IGNsaWVudFggLyB3aW5kb3cuaW5uZXJXaWR0aDtcbiAgICAgICAgY29uc3Qgb3kgPSBjbGllbnRZIC8gd2luZG93LmlubmVySGVpZ2h0O1xuXG4gICAgICAgIHRoaXMucGFyYW1zLnggPSBgJHt4fXB4YDtcbiAgICAgICAgdGhpcy5wYXJhbXMueSA9IGAke3l9cHhgO1xuICAgICAgICB0aGlzLnBhcmFtcy5veCA9IGAke294ICogMTAwfSVgO1xuICAgICAgICB0aGlzLnBhcmFtcy5veSA9IGAke295ICogMTAwfSVgO1xuICAgICAgICB0aGlzLnBhcmFtcy5zY2FsZSA9IDA7XG5cbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNhdmUgdGhlIGZvY3VzZWQgZWxlbWVudCBiZWZvcmUgZGlhbG9nIHdhcyBvcGVuXG4gICAgICovXG4gICAgcHJpdmF0ZSBzYXZlUHJldmlvdXNseUZvY3VzZWRFbGVtZW50KCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5kb2N1bWVudCkge1xuICAgICAgICAgICAgdGhpcy5lbGVtZW50Rm9jdXNlZEJlZm9yZURpYWxvZ1dhc09wZW5lZCA9IHRoaXMuZG9jdW1lbnRcbiAgICAgICAgICAgICAgICAuYWN0aXZlRWxlbWVudCBhcyBIVE1MRWxlbWVudDtcblxuICAgICAgICAgICAgUHJvbWlzZS5yZXNvbHZlKCkudGhlbigoKSA9PiB0aGlzLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5mb2N1cygpKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgdHJhcEZvY3VzKCk6IHZvaWQge1xuICAgICAgICBpZiAoIXRoaXMuZm9jdXNUcmFwKSB7XG4gICAgICAgICAgICB0aGlzLmZvY3VzVHJhcCA9IHRoaXMuZm9jdXNUcmFwRmFjdG9yeS5jcmVhdGUoXG4gICAgICAgICAgICAgICAgdGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnRcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5fY29uZmlnLmF1dG9Gb2N1cykge1xuICAgICAgICAgICAgdGhpcy5mb2N1c1RyYXAuZm9jdXNJbml0aWFsRWxlbWVudFdoZW5SZWFkeSgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZXN0b3JlRm9jdXMoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IHRvRm9jdXMgPSB0aGlzLmVsZW1lbnRGb2N1c2VkQmVmb3JlRGlhbG9nV2FzT3BlbmVkO1xuXG4gICAgICAgIC8vIFdlIG5lZWQgdGhlIGV4dHJhIGNoZWNrLCBiZWNhdXNlIElFIGNhbiBzZXQgdGhlIGBhY3RpdmVFbGVtZW50YCB0byBudWxsIGluIHNvbWUgY2FzZXMuXG4gICAgICAgIGlmICh0b0ZvY3VzICYmIHR5cGVvZiB0b0ZvY3VzLmZvY3VzID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICB0b0ZvY3VzLmZvY3VzKCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5mb2N1c1RyYXApIHtcbiAgICAgICAgICAgIHRoaXMuZm9jdXNUcmFwLmRlc3Ryb3koKTtcbiAgICAgICAgfVxuICAgIH1cbn1cbiJdfQ==