1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var tslib_1 = require("tslib");
|
4 | var react_1 = tslib_1.__importDefault(require("react"));
|
5 | var prefetch_1 = require("./context/prefetch");
|
6 | var EventListener_1 = require("./EventListener");
|
7 | exports.INTENTION_DELAY_MS = 150;
|
8 | var ConnectedPrefetcher = (function (_super) {
|
9 | tslib_1.__extends(ConnectedPrefetcher, _super);
|
10 | function ConnectedPrefetcher() {
|
11 | var _this = _super !== null && _super.apply(this, arguments) || this;
|
12 | _this.state = {};
|
13 | _this.prefetchAgressively = shouldPrefetchAggressively();
|
14 | _this.handlePressStart = function (_a) {
|
15 | var target = _a.target;
|
16 | _this.clearTimeout();
|
17 | if (target == null) {
|
18 | return;
|
19 | }
|
20 | var url = closestUrlFromNode(target);
|
21 | if (url != null) {
|
22 | _this.setState({ url: url });
|
23 | }
|
24 | };
|
25 | _this.handlePointerLeave = function (_a) {
|
26 | var target = _a.target, relatedTarget = _a.relatedTarget;
|
27 | var url = _this.state.url;
|
28 | var _b = _this, timeout = _b.timeout, timeoutUrl = _b.timeoutUrl;
|
29 | if (target == null) {
|
30 | if (timeout) {
|
31 | _this.clearTimeout();
|
32 | }
|
33 | return;
|
34 | }
|
35 | if (url == null && timeout == null) {
|
36 | return;
|
37 | }
|
38 | var closestUrl = closestUrlFromNode(target);
|
39 | var relatedUrl = relatedTarget && closestUrlFromNode(relatedTarget);
|
40 | if (timeout != null &&
|
41 | urlsEqual(closestUrl, timeoutUrl) &&
|
42 | !urlsEqual(relatedUrl, timeoutUrl)) {
|
43 | _this.clearTimeout();
|
44 | }
|
45 | if (urlsEqual(closestUrl, url) && !urlsEqual(relatedUrl, url)) {
|
46 | _this.setState({ url: undefined });
|
47 | }
|
48 | };
|
49 | _this.handlePointerEnter = function (_a) {
|
50 | var target = _a.target;
|
51 | if (target == null) {
|
52 | return;
|
53 | }
|
54 | var _b = _this, timeoutUrl = _b.timeoutUrl, timeout = _b.timeout;
|
55 | var url = closestUrlFromNode(target);
|
56 | if (url == null) {
|
57 | return;
|
58 | }
|
59 | if (timeout) {
|
60 | if (urlsEqual(url, timeoutUrl)) {
|
61 | return;
|
62 | }
|
63 | else {
|
64 | _this.clearTimeout();
|
65 | }
|
66 | }
|
67 | _this.timeoutUrl = url;
|
68 | _this.timeout = setTimeout(function () {
|
69 | _this.clearTimeout();
|
70 | _this.setState({ url: url });
|
71 | }, exports.INTENTION_DELAY_MS);
|
72 | };
|
73 | return _this;
|
74 | }
|
75 | ConnectedPrefetcher.prototype.render = function () {
|
76 | var url = this.state.url;
|
77 | var manager = this.props.manager;
|
78 | var preloadMarkup = url ? (react_1.default.createElement("div", { style: { visibility: 'hidden' } }, findMatches(manager.registered, url).map(function (_a, index) {
|
79 | var render = _a.render, path = _a.path;
|
80 |
|
81 | return react_1.default.createElement("div", { key: "" + path + index }, render(url));
|
82 | }))) : null;
|
83 | var expensiveListeners = this.prefetchAgressively ? (react_1.default.createElement(react_1.default.Fragment, null,
|
84 | react_1.default.createElement(EventListener_1.EventListener, { passive: true, event: "mouseover", handler: this.handlePointerEnter }),
|
85 | react_1.default.createElement(EventListener_1.EventListener, { passive: true, event: "focusin", handler: this.handlePointerEnter }),
|
86 | react_1.default.createElement(EventListener_1.EventListener, { passive: true, event: "mouseout", handler: this.handlePointerLeave }),
|
87 | react_1.default.createElement(EventListener_1.EventListener, { passive: true, event: "focusout", handler: this.handlePointerLeave }))) : null;
|
88 | return (react_1.default.createElement(react_1.default.Fragment, null,
|
89 | react_1.default.createElement(EventListener_1.EventListener, { passive: true, event: "mousedown", handler: this.handlePressStart }),
|
90 | react_1.default.createElement(EventListener_1.EventListener, { passive: true, event: "touchstart", handler: this.handlePressStart }),
|
91 | expensiveListeners,
|
92 | preloadMarkup));
|
93 | };
|
94 | ConnectedPrefetcher.prototype.clearTimeout = function () {
|
95 | if (this.timeout != null) {
|
96 | clearTimeout(this.timeout);
|
97 | this.timeout = undefined;
|
98 | this.timeoutUrl = undefined;
|
99 | }
|
100 | };
|
101 | return ConnectedPrefetcher;
|
102 | }(react_1.default.PureComponent));
|
103 | function Prefetcher(props) {
|
104 | return (react_1.default.createElement(prefetch_1.PrefetchContext.Consumer, null, function (manager) { return react_1.default.createElement(ConnectedPrefetcher, tslib_1.__assign({}, props, { manager: manager })); }));
|
105 | }
|
106 | exports.Prefetcher = Prefetcher;
|
107 | function shouldPrefetchAggressively() {
|
108 | return (typeof navigator === 'undefined' ||
|
109 | !('connection' in navigator) ||
|
110 | !navigator.connection.saveData);
|
111 | }
|
112 | function urlsEqual(first, second) {
|
113 | return ((first == null && first === second) ||
|
114 | (first != null && second != null && first.href === second.href));
|
115 | }
|
116 | function findMatches(records, url) {
|
117 | return tslib_1.__spread(records).filter(function (_a) {
|
118 | var match = _a.path;
|
119 | return matches(url, match);
|
120 | });
|
121 | }
|
122 | function matches(url, matcher) {
|
123 | return typeof matcher === 'string'
|
124 | ? matcher === url.pathname
|
125 | : matcher.test(url.pathname);
|
126 | }
|
127 | function closestUrlFromNode(element) {
|
128 | if (!(element instanceof HTMLElement)) {
|
129 | return undefined;
|
130 | }
|
131 |
|
132 | var closestUrl = element.closest('[href], [data-href]');
|
133 | if (closestUrl == null || !(closestUrl instanceof HTMLElement)) {
|
134 | return undefined;
|
135 | }
|
136 | var url = closestUrl.getAttribute('href') || closestUrl.getAttribute('data-href');
|
137 | try {
|
138 | return url ? new URL(url, window.location.href) : undefined;
|
139 | }
|
140 | catch (error) {
|
141 | return undefined;
|
142 | }
|
143 | }
|