import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Directive, Input, HostListener, ContentChildren, forwardRef, Inject, HostBinding, NgModule } from '@angular/core';
import * as i2 from '@angular/router';
import { NavigationEnd, RouterModule } from '@angular/router';
import { ReplaySubject, Subject, Subscription } from 'rxjs';
import { filter, debounceTime } from 'rxjs/operators';
import { scrollAnimate } from 'ng-devui/utils';

class AnchorDirective {
    constructor(el) {
        this.el = el;
        this.anchorActive = 'active';
        this.activeChangeSubject = new ReplaySubject(1);
        this.REACH_TOP_VISION_OFFSET = 50;
        this.THROTTLE_DELAY = 100;
        this.THROTTLE_TRIGGER = 600;
        this.throttle = () => {
            const fn = this.checkActiveStatus;
            const time = Date.now();
            if (this.scrollTimer) {
                clearTimeout(this.scrollTimer);
            }
            if (!this.scrollPreStart) {
                this.scrollPreStart = time;
            }
            if (time - this.scrollPreStart > this.THROTTLE_TRIGGER) {
                fn();
                this.scrollPreStart = null;
                this.scrollTimer = null;
            }
            else {
                this.scrollTimer = setTimeout(() => {
                    fn();
                    this.scrollPreStart = null;
                    this.scrollTimer = null;
                }, this.THROTTLE_DELAY);
            }
        };
        this.checkActiveStatus = (activeChangeBy) => {
            if (this.boxElement.isScrollingToTarget) {
                return;
            }
            const top = this.element.getBoundingClientRect().top - ((this.boxElement.view && this.boxElement.view.top) || 0);
            const bottom = this.element.getBoundingClientRect().bottom - ((this.boxElement.view && this.boxElement.view.top) || 0);
            // 首个个特殊处理
            if (this.anchor === this.boxElement.defaultAnchor) {
                this.activeChangeBy = activeChangeBy || 'scroll';
                this.isActive = bottom > this.REACH_TOP_VISION_OFFSET;
                return;
            }
            // 默认处理
            this.activeChangeBy = activeChangeBy || 'scroll';
            this.isActive = bottom > this.REACH_TOP_VISION_OFFSET && top < this.REACH_TOP_VISION_OFFSET;
        };
        this.element = this.el.nativeElement;
    }
    set isActive(active) {
        this._isActive = active;
        this.activeChangeSubject.next(active);
    }
    get isActive() {
        return this._isActive;
    }
    set boxElement(box) {
        this._boxElement = box;
        this.updateScrollListenTarget();
    }
    get boxElement() {
        return this._boxElement;
    }
    ngAfterViewInit() {
        this.activeChangeSubscription = this.activeChangeSubject.asObservable().subscribe((active) => {
            if (this.lastActiveBy) {
                this.element.classList.remove(this.lastActiveBy);
            }
            if (active) {
                this.element.classList.add(this.anchorActive);
                this.lastActiveBy = 'anchor-active-by-' + this.activeChangeBy;
                // setTimeout是为了this.lastActiveBy每次都能被再次触发
                setTimeout(() => {
                    this.element.classList.add(this.lastActiveBy);
                }, 0);
            }
            else {
                this.element.classList.remove(this.anchorActive);
            }
        });
        setTimeout(() => {
            this.checkActiveStatus('initial');
        });
    }
    ngOnDestroy() {
        this.scrollListenTarget.removeEventListener('scroll', this.throttle);
        if (this.activeChangeSubscription) {
            this.activeChangeSubscription.unsubscribe();
        }
    }
    beFocused() {
        this.boxElement.forceActiveAnchor(this.anchor, 'click-inside');
        this.boxElement.isScrollingToTarget = false;
    }
    updateScrollListenTarget() {
        if (this.scrollListenTarget) {
            return;
        }
        if (this.boxElement && typeof window !== 'undefined') {
            this.scrollListenTarget = this.boxElement.scrollTarget || window; // window有scroll事件，document.documentElement没有scroll事件
        }
        this.scrollListenTarget.addEventListener('scroll', this.throttle, { passive: true });
    }
}
AnchorDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
AnchorDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.0", type: AnchorDirective, selector: "[dAnchor]", inputs: { anchor: ["dAnchor", "anchor"], anchorActive: "anchorActive" }, host: { listeners: { "click": "beFocused()" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[dAnchor]',
                }]
        }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { anchor: [{
                type: Input,
                args: ['dAnchor']
            }], anchorActive: [{
                type: Input
            }], beFocused: [{
                type: HostListener,
                args: ['click']
            }] } });

class AnchorBoxDirective {
    constructor() {
        this.isScrollingToTarget = false;
        this.activeChangeSubject = new Subject();
        this.activeChange = this.activeChangeSubject.asObservable();
        this.refreshAnchorMap = new Subject();
    }
    set anchorList(list) {
        if (this.sub) {
            this.sub.unsubscribe();
        }
        this.sub = new Subscription();
        this.anchorMap = {};
        this._anchorList = list;
        this._anchorList.toArray().forEach(targetAnchor => {
            this.anchorMap[targetAnchor.anchor] = targetAnchor;
            targetAnchor.boxElement = this;
            this.sub.add(targetAnchor.activeChangeSubject.pipe(filter(bool => !!bool)).subscribe(() => {
                this.activeChangeSubject.next(targetAnchor);
            }));
        });
        this.refreshAnchorMap.next();
    }
    get anchorList() {
        return this._anchorList;
    }
    forceActiveAnchor(anchorName, forceActiveSource = 'scroll', deactivateOtherAnchor = true) {
        this.anchorMap[anchorName].activeChangeBy = forceActiveSource;
        this.anchorMap[anchorName].isActive = true;
        if (deactivateOtherAnchor) {
            Object.keys(this.anchorMap)
                .filter(name => name !== anchorName)
                .map(name => this.anchorMap[name])
                .forEach(anchor => {
                anchor.activeChangeBy = forceActiveSource;
                anchor.isActive = false;
            });
        }
    }
    ngOnDestroy() {
        this.activeChangeSubject.complete();
        if (this.sub) {
            this.sub.unsubscribe();
        }
    }
}
AnchorBoxDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorBoxDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
AnchorBoxDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.0", type: AnchorBoxDirective, selector: "[dAnchorBox]", inputs: { view: "view", defaultAnchor: "defaultAnchor", scrollTarget: "scrollTarget" }, queries: [{ propertyName: "anchorList", predicate: AnchorDirective, descendants: true }], ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorBoxDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[dAnchorBox]'
                }]
        }], propDecorators: { view: [{
                type: Input
            }], defaultAnchor: [{
                type: Input
            }], scrollTarget: [{
                type: Input
            }], anchorList: [{
                type: ContentChildren,
                args: [AnchorDirective, { descendants: true }]
            }] } });

class AnchorLinkDirective {
    constructor(box) {
        this.bindAnchorAfterBoxReady = () => {
            if (this.boxElement.anchorMap) {
                setTimeout(() => {
                    this.anchorBlock = this.boxElement.anchorMap[this.anchorName];
                }, 0);
            }
            else {
                this.bindingAnchorTimer = setTimeout(this.bindAnchorAfterBoxReady, 500);
            }
        };
        this.boxElement = box;
    }
    get anchorActiveClass() {
        return this.anchorBlock && this.anchorBlock.isActive ? this.anchorActive || '' : '';
    }
    set anchorName(anchor) {
        this._anchorName = anchor;
        this.bindAnchorAfterBoxReady();
    }
    get anchorName() {
        return this._anchorName;
    }
    ngOnInit() {
        this.subscribeAnchorMapChange();
    }
    ngOnDestroy() {
        if (this.subscription) {
            this.subscription.unsubscribe();
        }
    }
    subscribeAnchorMapChange() {
        if (this.boxElement) {
            this.subscription = this.boxElement.refreshAnchorMap.subscribe(() => {
                if (this.bindingAnchorTimer) {
                    clearTimeout(this.bindingAnchorTimer);
                    this.bindingAnchorTimer = undefined;
                }
                this.bindAnchorAfterBoxReady();
            });
        }
    }
    scrollToAnchor(activeChangeBy) {
        if (typeof document === 'undefined') {
            return;
        }
        if (!this.anchorBlock) {
            return;
        }
        const callback = () => {
            setTimeout(() => {
                this.boxElement.forceActiveAnchor(this.anchorName, activeChangeBy || 'anchor-link');
                this.boxElement.isScrollingToTarget = false;
            }, 120);
        };
        ((container, anchor) => {
            let containerScrollTop = container.scrollTop;
            let containerOffsetTop = container.getBoundingClientRect().top;
            if (container === document.documentElement) {
                containerScrollTop += document.body.scrollTop; // scrollTop兼容性问题
                containerOffsetTop = 0; // offsettop抵消
            }
            scrollAnimate(container, containerScrollTop, containerScrollTop +
                anchor.getBoundingClientRect().top -
                containerOffsetTop -
                ((this.boxElement.view && this.boxElement.view.top) || 0), undefined, undefined, callback);
        })(this.boxElement.scrollTarget || document.documentElement, this.anchorBlock.element);
        this.boxElement.isScrollingToTarget = true;
    }
}
AnchorLinkDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorLinkDirective, deps: [{ token: forwardRef(() => AnchorBoxDirective) }], target: i0.ɵɵFactoryTarget.Directive });
AnchorLinkDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.0", type: AnchorLinkDirective, selector: "[dAnchorLink]", inputs: { anchorName: ["dAnchorLink", "anchorName"], anchorActive: "anchorActive" }, host: { listeners: { "click": "scrollToAnchor()" }, properties: { "class": "this.anchorActiveClass" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorLinkDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[dAnchorLink]',
                }]
        }], ctorParameters: function () {
        return [{ type: AnchorBoxDirective, decorators: [{
                        type: Inject,
                        args: [forwardRef(() => AnchorBoxDirective)]
                    }] }];
    }, propDecorators: { anchorActiveClass: [{
                type: HostBinding,
                args: ['class']
            }], anchorName: [{
                type: Input,
                args: ['dAnchorLink']
            }], anchorActive: [{
                type: Input
            }], scrollToAnchor: [{
                type: HostListener,
                args: ['click']
            }] } });

class AnchorBoxHashSupportDirective {
    constructor(box, router, route) {
        this.box = box;
        this.router = router;
        this.route = route;
        this.updateUrlWhenAnchorActive = true;
        this.scrollToAnchorByHashOnlyInit = false;
        this.sub = new Subscription();
        this.manual = false;
        this.navigateToHash = (targetAnchor) => {
            if (targetAnchor.activeChangeBy === 'initial') {
                return;
            }
            this.router.navigate([], { fragment: targetAnchor.anchor, replaceUrl: true });
            this.manual = true;
        };
        this.navigateToAnchor = (event) => {
            if (this.manual) {
                this.manual = false;
                return;
            }
            if (this.scrollToAnchorByHashOnlyInit) {
                return;
            }
            const frag = this.router.parseUrl(event.url).fragment;
            this.scrollToFragment(frag);
        };
        this.scrollToFragment = (frag) => {
            if (!frag) {
                return;
            }
            if (this.box.anchorMap[frag]) {
                const tempAnchor = new AnchorLinkDirective(this.box);
                tempAnchor.anchorName = frag;
                tempAnchor.anchorBlock = this.box.anchorMap[frag];
                tempAnchor.scrollToAnchor('fragment');
            }
        };
    }
    ngOnInit() {
        this.sub.add(this.box.activeChange.pipe(debounceTime(300), filter(anchor => this.updateUrlWhenAnchorActive)).subscribe(this.navigateToHash));
        this.sub.add(this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(this.navigateToAnchor));
    }
    ngAfterViewInit() {
        const frag = this.route.snapshot.fragment;
        setTimeout(() => {
            this.scrollToFragment(frag);
        }, 120);
    }
    ngOnDestroy() {
        if (this.sub) {
            this.sub.unsubscribe();
        }
    }
}
AnchorBoxHashSupportDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorBoxHashSupportDirective, deps: [{ token: AnchorBoxDirective }, { token: i2.Router }, { token: i2.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Directive });
AnchorBoxHashSupportDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.0", type: AnchorBoxHashSupportDirective, selector: "[dAnchorBox][dAnchorHashSupport]", inputs: { updateUrlWhenAnchorActive: "updateUrlWhenAnchorActive", scrollToAnchorByHashOnlyInit: "scrollToAnchorByHashOnlyInit" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorBoxHashSupportDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[dAnchorBox][dAnchorHashSupport]'
                }]
        }], ctorParameters: function () { return [{ type: AnchorBoxDirective }, { type: i2.Router }, { type: i2.ActivatedRoute }]; }, propDecorators: { updateUrlWhenAnchorActive: [{
                type: Input
            }], scrollToAnchorByHashOnlyInit: [{
                type: Input
            }] } });

class AnchorModule {
}
AnchorModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
AnchorModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.0", ngImport: i0, type: AnchorModule, declarations: [AnchorLinkDirective,
        AnchorBoxDirective,
        AnchorDirective,
        AnchorBoxHashSupportDirective], imports: [CommonModule,
        RouterModule], exports: [AnchorLinkDirective,
        AnchorBoxDirective,
        AnchorDirective,
        AnchorBoxHashSupportDirective] });
AnchorModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorModule, imports: [CommonModule,
        RouterModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: AnchorModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        CommonModule,
                        RouterModule
                    ],
                    declarations: [
                        AnchorLinkDirective,
                        AnchorBoxDirective,
                        AnchorDirective,
                        AnchorBoxHashSupportDirective
                    ],
                    exports: [
                        AnchorLinkDirective,
                        AnchorBoxDirective,
                        AnchorDirective,
                        AnchorBoxHashSupportDirective
                    ],
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { AnchorBoxDirective, AnchorBoxHashSupportDirective, AnchorDirective, AnchorLinkDirective, AnchorModule };
//# sourceMappingURL=ng-devui-anchor.mjs.map
