UNPKG

@pshurygin/ngx-quicklink

Version:
318 lines (311 loc) 11.5 kB
import { __decorate, __metadata, __param } from 'tslib'; import { Injectable, InjectionToken, NgZone, Input, Directive, Inject, Optional, ElementRef, NgModule } from '@angular/core'; import { Router, PRIMARY_OUTLET, RouterPreloader, RouterLink, RouterLinkWithHref } from '@angular/router'; import { EMPTY } from 'rxjs'; // Using a global registry so we can keep it populated across lazy-loaded // modules with different parent injectors which create instance of the registry. var globalRegistry = []; var PrefetchRegistry = /** @class */ (function () { function PrefetchRegistry(router) { this.router = router; this.trees = globalRegistry; } PrefetchRegistry.prototype.add = function (tree) { this.trees.push(tree); }; PrefetchRegistry.prototype.remove = function (tree) { this.trees.splice(this.trees.indexOf(tree), 1); }; PrefetchRegistry.prototype.shouldPrefetch = function (url) { var tree = this.router.parseUrl(url); return this.trees.some(containsTree.bind(null, tree)); }; PrefetchRegistry = __decorate([ Injectable(), __metadata("design:paramtypes", [Router]) ], PrefetchRegistry); return PrefetchRegistry; }()); function containsQueryParams(container, containee) { // TODO: This does not handle array params correctly. return (Object.keys(containee).length <= Object.keys(container).length && Object.keys(containee).every(function (key) { return containee[key] === container[key]; })); } function containsTree(containee, container) { return (containsQueryParams(container.queryParams, containee.queryParams) && containsSegmentGroup(container.root, containee.root, containee.root.segments)); } function containsSegmentGroup(container, containee, containeePaths) { if (container.segments.length > containeePaths.length) { var current = container.segments.slice(0, containeePaths.length); if (!equalPath(current, containeePaths)) return false; if (containee.hasChildren()) return false; return true; } else if (container.segments.length === containeePaths.length) { if (!equalPath(container.segments, containeePaths)) return false; if (!containee.hasChildren()) return true; for (var c in containee.children) { if (!container.children[c]) break; if (containsSegmentGroup(container.children[c], containee.children[c], containee.children[c].segments)) return true; } return false; } else { var current = containeePaths.slice(0, container.segments.length); var next = containeePaths.slice(container.segments.length); if (!equalPath(container.segments, current)) return false; if (!container.children[PRIMARY_OUTLET]) return false; return containsSegmentGroup(container.children[PRIMARY_OUTLET], containee, next); } } function equalPath(as, bs) { if (as.length !== bs.length) return false; return as.every(function (a, i) { return a.path === bs[i].path || a.path.startsWith(':') || bs[i].path.startsWith(':'); }); } var ɵ0 = function (cb) { var start = Date.now(); return setTimeout(function () { cb({ didTimeout: false, timeRemaining: function () { return Math.max(0, 50 - (Date.now() - start)); } }); }, 1); }, ɵ1 = function () { }; var requestIdleCallback = typeof window !== 'undefined' ? window.requestIdleCallback || ɵ0 : ɵ1; var observerSupported = function () { return typeof window !== 'undefined' ? !!window.IntersectionObserver : false; }; var LinkHandler = new InjectionToken('LinkHandler'); var ObservableLinkHandler = /** @class */ (function () { function ObservableLinkHandler(loader, queue, ngZone) { var _this = this; this.loader = loader; this.queue = queue; this.ngZone = ngZone; this.elementLink = new Map(); this.observer = observerSupported() ? new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { var link = entry.target; var routerLink_1 = _this.elementLink.get(link); if (!routerLink_1 || !routerLink_1.urlTree) return; _this.queue.add(routerLink_1.urlTree); _this.observer.unobserve(link); requestIdleCallback(function () { _this.loader.preload().subscribe(function () { return void 0; }); _this.queue.remove(routerLink_1.urlTree); }); } }); }) : null; } ObservableLinkHandler.prototype.register = function (el) { var _this = this; this.elementLink.set(el.element, el); this.ngZone.runOutsideAngular(function () { _this.observer.observe(el.element); }); }; // First call to unregister will not hit this. ObservableLinkHandler.prototype.unregister = function (el) { if (this.elementLink.has(el.element)) { this.observer.unobserve(el.element); this.elementLink.delete(el.element); } }; ObservableLinkHandler.prototype.supported = function () { return observerSupported(); }; ObservableLinkHandler = __decorate([ Injectable(), __metadata("design:paramtypes", [RouterPreloader, PrefetchRegistry, NgZone]) ], ObservableLinkHandler); return ObservableLinkHandler; }()); var PreloadLinkHandler = /** @class */ (function () { function PreloadLinkHandler(loader, queue) { this.loader = loader; this.queue = queue; } PreloadLinkHandler.prototype.register = function (el) { var _this = this; this.queue.add(el.urlTree); requestIdleCallback(function () { return _this.loader.preload().subscribe(function () { return void 0; }); }); }; PreloadLinkHandler.prototype.unregister = function (_) { }; PreloadLinkHandler.prototype.supported = function () { return true; }; PreloadLinkHandler = __decorate([ Injectable(), __metadata("design:paramtypes", [RouterPreloader, PrefetchRegistry]) ], PreloadLinkHandler); return PreloadLinkHandler; }()); var LinkDirective = /** @class */ (function () { function LinkDirective(linkHandlers, el, link, linkWithHref) { this.linkHandlers = linkHandlers; this.el = el; this.linkHandler = this.linkHandlers.filter(function (h) { return h.supported(); }).shift(); this.rl = link || linkWithHref; } LinkDirective.prototype.ngOnChanges = function (c) { if (c.routerLink) { this.linkHandler.unregister(this); this.linkHandler.register(this); } }; LinkDirective.prototype.ngOnDestroy = function () { this.linkHandler.unregister(this); }; Object.defineProperty(LinkDirective.prototype, "element", { get: function () { return this.el.nativeElement; }, enumerable: true, configurable: true }); Object.defineProperty(LinkDirective.prototype, "urlTree", { get: function () { return this.rl.urlTree; }, enumerable: true, configurable: true }); __decorate([ Input(), __metadata("design:type", Object) ], LinkDirective.prototype, "routerLink", void 0); LinkDirective = __decorate([ Directive({ selector: '[routerLink]' }), __param(0, Inject(LinkHandler)), __param(2, Optional()), __param(3, Optional()), __metadata("design:paramtypes", [Array, ElementRef, RouterLink, RouterLinkWithHref]) ], LinkDirective); return LinkDirective; }()); var QuicklinkStrategy = /** @class */ (function () { function QuicklinkStrategy(queue, router) { this.queue = queue; this.router = router; this.loading = new Set(); } QuicklinkStrategy.prototype.preload = function (route, load) { if (this.loading.has(route)) { // Don't preload the same route twice return EMPTY; } var conn = typeof window !== 'undefined' ? navigator.connection : undefined; if (conn) { // Don't preload if the user is on 2G. or if Save-Data is enabled.. if ((conn.effectiveType || '').includes('2g') || conn.saveData) return EMPTY; } // Prevent from preloading if (route.data && route.data.preload === false) { return EMPTY; } var fullPath = findPath(this.router.config, route); if (this.queue.shouldPrefetch(fullPath)) { this.loading.add(route); return load(); } return EMPTY; }; QuicklinkStrategy = __decorate([ Injectable(), __metadata("design:paramtypes", [PrefetchRegistry, Router]) ], QuicklinkStrategy); return QuicklinkStrategy; }()); var findPath = function (config, route) { config = config.slice(); var parent = new Map(); var visited = new Set(); var _loop_1 = function () { var el = config.shift(); visited.add(el); if (el === route) return "break"; var children = el.children || []; var current_1 = el._loadedConfig; if (current_1 && current_1.routes) { children = children.concat(current_1.routes); } children.forEach(function (r) { if (visited.has(r)) return; parent.set(r, el); config.push(r); }); }; while (config.length) { var state_1 = _loop_1(); if (state_1 === "break") break; } var path = ''; var current = route; while (current) { if (isPrimaryRoute(current)) { path = "/" + current.path + path; } else { path = "/(" + current.outlet + ":" + current.path + path + ")"; } current = parent.get(current); } return path; }; function isPrimaryRoute(route) { return route.outlet === PRIMARY_OUTLET || !route.outlet; } var QuicklinkModule = /** @class */ (function () { function QuicklinkModule() { } QuicklinkModule = __decorate([ NgModule({ declarations: [LinkDirective], providers: [ { provide: LinkHandler, useClass: ObservableLinkHandler, multi: true }, { provide: LinkHandler, useClass: PreloadLinkHandler, multi: true }, PrefetchRegistry, QuicklinkStrategy ], exports: [LinkDirective] }) ], QuicklinkModule); return QuicklinkModule; }()); export { QuicklinkModule, QuicklinkStrategy, LinkHandler as ɵa, ObservableLinkHandler as ɵb, PreloadLinkHandler as ɵc, PrefetchRegistry as ɵe, LinkDirective as ɵɵLinkDirective }; //# sourceMappingURL=pshurygin-ngx-quicklink.js.map