@pshurygin/ngx-quicklink
Version:
Quicklink for Angular
359 lines (344 loc) • 15.1 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/router'), require('rxjs')) :
typeof define === 'function' && define.amd ? define('@pshurygin/ngx-quicklink', ['exports', '@angular/core', '@angular/router', 'rxjs'], factory) :
(global = global || self, factory((global.pshurygin = global.pshurygin || {}, global.pshurygin['ngx-quicklink'] = {}), global.ng.core, global.ng.router, global.rxjs));
}(this, function (exports, core, router, rxjs) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function __param(paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
}
function __metadata(metadataKey, metadataValue) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
}
// 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([
core.Injectable(),
__metadata("design:paramtypes", [router.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[router.PRIMARY_OUTLET])
return false;
return containsSegmentGroup(container.children[router.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 core.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([
core.Injectable(),
__metadata("design:paramtypes", [router.RouterPreloader, PrefetchRegistry, core.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([
core.Injectable(),
__metadata("design:paramtypes", [router.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([
core.Input(),
__metadata("design:type", Object)
], LinkDirective.prototype, "routerLink", void 0);
LinkDirective = __decorate([
core.Directive({
selector: '[routerLink]'
}),
__param(0, core.Inject(LinkHandler)),
__param(2, core.Optional()),
__param(3, core.Optional()),
__metadata("design:paramtypes", [Array, core.ElementRef,
router.RouterLink,
router.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 rxjs.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 rxjs.EMPTY;
}
// Prevent from preloading
if (route.data && route.data.preload === false) {
return rxjs.EMPTY;
}
var fullPath = findPath(this.router.config, route);
if (this.queue.shouldPrefetch(fullPath)) {
this.loading.add(route);
return load();
}
return rxjs.EMPTY;
};
QuicklinkStrategy = __decorate([
core.Injectable(),
__metadata("design:paramtypes", [PrefetchRegistry, router.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 === router.PRIMARY_OUTLET || !route.outlet;
}
var QuicklinkModule = /** @class */ (function () {
function QuicklinkModule() {
}
QuicklinkModule = __decorate([
core.NgModule({
declarations: [LinkDirective],
providers: [
{
provide: LinkHandler,
useClass: ObservableLinkHandler,
multi: true
},
{
provide: LinkHandler,
useClass: PreloadLinkHandler,
multi: true
},
PrefetchRegistry,
QuicklinkStrategy
],
exports: [LinkDirective]
})
], QuicklinkModule);
return QuicklinkModule;
}());
exports.QuicklinkModule = QuicklinkModule;
exports.QuicklinkStrategy = QuicklinkStrategy;
exports.ɵa = LinkHandler;
exports.ɵb = ObservableLinkHandler;
exports.ɵc = PreloadLinkHandler;
exports.ɵe = PrefetchRegistry;
exports.ɵɵLinkDirective = LinkDirective;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=pshurygin-ngx-quicklink.umd.js.map