1 |
|
2 |
|
3 |
|
4 | import { proxyCustomElement, HTMLElement, createEvent } from '@stencil/core/internal/client';
|
5 | import { c as componentOnReady, o as debounce } from './helpers.js';
|
6 |
|
7 | const ROUTER_INTENT_NONE = 'root';
|
8 | const ROUTER_INTENT_FORWARD = 'forward';
|
9 | const ROUTER_INTENT_BACK = 'back';
|
10 |
|
11 |
|
12 | const generatePath = (segments) => {
|
13 | const path = segments
|
14 | .filter(s => s.length > 0)
|
15 | .join('/');
|
16 | return '/' + path;
|
17 | };
|
18 | const generateUrl = (segments, useHash, queryString) => {
|
19 | let url = generatePath(segments);
|
20 | if (useHash) {
|
21 | url = '#' + url;
|
22 | }
|
23 | if (queryString !== undefined) {
|
24 | url += '?' + queryString;
|
25 | }
|
26 | return url;
|
27 | };
|
28 | const writeSegments = (history, root, useHash, segments, direction, state, queryString) => {
|
29 | const url = generateUrl([...parsePath(root).segments, ...segments], useHash, queryString);
|
30 | if (direction === ROUTER_INTENT_FORWARD) {
|
31 | history.pushState(state, '', url);
|
32 | }
|
33 | else {
|
34 | history.replaceState(state, '', url);
|
35 | }
|
36 | };
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | const chainToSegments = (chain) => {
|
45 | const segments = [];
|
46 | for (const route of chain) {
|
47 | for (const segment of route.segments) {
|
48 | if (segment[0] === ':') {
|
49 | const param = route.params && route.params[segment.slice(1)];
|
50 | if (!param) {
|
51 | return null;
|
52 | }
|
53 | segments.push(param);
|
54 | }
|
55 | else if (segment !== '') {
|
56 | segments.push(segment);
|
57 | }
|
58 | }
|
59 | }
|
60 | return segments;
|
61 | };
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | const removePrefix = (prefix, segments) => {
|
70 | if (prefix.length > segments.length) {
|
71 | return null;
|
72 | }
|
73 | if (prefix.length <= 1 && prefix[0] === '') {
|
74 | return segments;
|
75 | }
|
76 | for (let i = 0; i < prefix.length; i++) {
|
77 | if (prefix[i] !== segments[i]) {
|
78 | return null;
|
79 | }
|
80 | }
|
81 | if (segments.length === prefix.length) {
|
82 | return [''];
|
83 | }
|
84 | return segments.slice(prefix.length);
|
85 | };
|
86 | const readSegments = (loc, root, useHash) => {
|
87 | const prefix = parsePath(root).segments;
|
88 | const pathname = useHash ? loc.hash.slice(1) : loc.pathname;
|
89 | const segments = parsePath(pathname).segments;
|
90 | return removePrefix(prefix, segments);
|
91 | };
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 | const parsePath = (path) => {
|
98 | let segments = [''];
|
99 | let queryString;
|
100 | if (path != null) {
|
101 | const qsStart = path.indexOf('?');
|
102 | if (qsStart > -1) {
|
103 | queryString = path.substring(qsStart + 1);
|
104 | path = path.substring(0, qsStart);
|
105 | }
|
106 | segments = path.split('/')
|
107 | .map(s => s.trim())
|
108 | .filter(s => s.length > 0);
|
109 | if (segments.length === 0) {
|
110 | segments = [''];
|
111 | }
|
112 | }
|
113 | return { segments, queryString };
|
114 | };
|
115 |
|
116 | const printRoutes = (routes) => {
|
117 | console.group(`[ion-core] ROUTES[${routes.length}]`);
|
118 | for (const chain of routes) {
|
119 | const segments = [];
|
120 | chain.forEach(r => segments.push(...r.segments));
|
121 | const ids = chain.map(r => r.id);
|
122 | console.debug(`%c ${generatePath(segments)}`, 'font-weight: bold; padding-left: 20px', '=>\t', `(${ids.join(', ')})`);
|
123 | }
|
124 | console.groupEnd();
|
125 | };
|
126 | const printRedirects = (redirects) => {
|
127 | console.group(`[ion-core] REDIRECTS[${redirects.length}]`);
|
128 | for (const redirect of redirects) {
|
129 | if (redirect.to) {
|
130 | console.debug('FROM: ', `$c ${generatePath(redirect.from)}`, 'font-weight: bold', ' TO: ', `$c ${generatePath(redirect.to.segments)}`, 'font-weight: bold');
|
131 | }
|
132 | }
|
133 | console.groupEnd();
|
134 | };
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | const writeNavState = async (root, chain, direction, index, changed = false, animation) => {
|
145 | try {
|
146 |
|
147 | const outlet = searchNavNode(root);
|
148 |
|
149 | if (index >= chain.length || !outlet) {
|
150 | return changed;
|
151 | }
|
152 | await new Promise(resolve => componentOnReady(outlet, resolve));
|
153 | const route = chain[index];
|
154 | const result = await outlet.setRouteId(route.id, route.params, direction, animation);
|
155 |
|
156 |
|
157 | if (result.changed) {
|
158 | direction = ROUTER_INTENT_NONE;
|
159 | changed = true;
|
160 | }
|
161 |
|
162 | changed = await writeNavState(result.element, chain, direction, index + 1, changed, animation);
|
163 |
|
164 |
|
165 | if (result.markVisible) {
|
166 | await result.markVisible();
|
167 | }
|
168 | return changed;
|
169 | }
|
170 | catch (e) {
|
171 | console.error(e);
|
172 | return false;
|
173 | }
|
174 | };
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | const readNavState = async (root) => {
|
181 | const ids = [];
|
182 | let outlet;
|
183 | let node = root;
|
184 | while (outlet = searchNavNode(node)) {
|
185 | const id = await outlet.getRouteId();
|
186 | if (id) {
|
187 | node = id.element;
|
188 | id.element = undefined;
|
189 | ids.push(id);
|
190 | }
|
191 | else {
|
192 | break;
|
193 | }
|
194 | }
|
195 | return { ids, outlet };
|
196 | };
|
197 | const waitUntilNavNode = () => {
|
198 | if (searchNavNode(document.body)) {
|
199 | return Promise.resolve();
|
200 | }
|
201 | return new Promise(resolve => {
|
202 | window.addEventListener('ionNavWillLoad', () => resolve(), { once: true });
|
203 | });
|
204 | };
|
205 |
|
206 | const OUTLET_SELECTOR = ':not([no-router]) ion-nav, :not([no-router]) ion-tabs, :not([no-router]) ion-router-outlet';
|
207 | const searchNavNode = (root) => {
|
208 | if (!root) {
|
209 | return undefined;
|
210 | }
|
211 | if (root.matches(OUTLET_SELECTOR)) {
|
212 | return root;
|
213 | }
|
214 | const outlet = root.querySelector(OUTLET_SELECTOR);
|
215 | return outlet !== null && outlet !== void 0 ? outlet : undefined;
|
216 | };
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 | const matchesRedirect = (segments, redirect) => {
|
226 | const { from, to } = redirect;
|
227 | if (to === undefined) {
|
228 | return false;
|
229 | }
|
230 | if (from.length > segments.length) {
|
231 | return false;
|
232 | }
|
233 | for (let i = 0; i < from.length; i++) {
|
234 | const expected = from[i];
|
235 | if (expected === '*') {
|
236 | return true;
|
237 | }
|
238 | if (expected !== segments[i]) {
|
239 | return false;
|
240 | }
|
241 | }
|
242 | return from.length === segments.length;
|
243 | };
|
244 |
|
245 | const findRouteRedirect = (segments, redirects) => {
|
246 | return redirects.find(redirect => matchesRedirect(segments, redirect));
|
247 | };
|
248 | const matchesIDs = (ids, chain) => {
|
249 | const len = Math.min(ids.length, chain.length);
|
250 | let score = 0;
|
251 | for (let i = 0; i < len; i++) {
|
252 | const routeId = ids[i];
|
253 | const routeChain = chain[i];
|
254 |
|
255 | if (routeId.id.toLowerCase() !== routeChain.id) {
|
256 | break;
|
257 | }
|
258 | if (routeId.params) {
|
259 | const routeIdParams = Object.keys(routeId.params);
|
260 |
|
261 | if (routeIdParams.length === routeChain.segments.length) {
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 | const pathWithParams = routeIdParams.map(key => `:${key}`);
|
281 | for (let j = 0; j < pathWithParams.length; j++) {
|
282 |
|
283 | if (pathWithParams[j].toLowerCase() !== routeChain.segments[j]) {
|
284 | break;
|
285 | }
|
286 |
|
287 | score++;
|
288 | }
|
289 | }
|
290 | }
|
291 |
|
292 | score++;
|
293 | }
|
294 | return score;
|
295 | };
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 | const matchesSegments = (segments, chain) => {
|
304 | const inputSegments = new RouterSegments(segments);
|
305 | let matchesDefault = false;
|
306 | let allparams;
|
307 | for (let i = 0; i < chain.length; i++) {
|
308 | const chainSegments = chain[i].segments;
|
309 | if (chainSegments[0] === '') {
|
310 | matchesDefault = true;
|
311 | }
|
312 | else {
|
313 | for (const segment of chainSegments) {
|
314 | const data = inputSegments.next();
|
315 |
|
316 | if (segment[0] === ':') {
|
317 | if (data === '') {
|
318 | return null;
|
319 | }
|
320 | allparams = allparams || [];
|
321 | const params = allparams[i] || (allparams[i] = {});
|
322 | params[segment.slice(1)] = data;
|
323 | }
|
324 | else if (data !== segment) {
|
325 | return null;
|
326 | }
|
327 | }
|
328 | matchesDefault = false;
|
329 | }
|
330 | }
|
331 | const matches = (matchesDefault)
|
332 | ? matchesDefault === (inputSegments.next() === '')
|
333 | : true;
|
334 | if (!matches) {
|
335 | return null;
|
336 | }
|
337 | if (allparams) {
|
338 | return chain.map((route, i) => ({
|
339 | id: route.id,
|
340 | segments: route.segments,
|
341 | params: mergeParams(route.params, allparams[i]),
|
342 | beforeEnter: route.beforeEnter,
|
343 | beforeLeave: route.beforeLeave
|
344 | }));
|
345 | }
|
346 | return chain;
|
347 | };
|
348 |
|
349 |
|
350 |
|
351 |
|
352 | const mergeParams = (a, b) => {
|
353 | return a || b ? Object.assign(Object.assign({}, a), b) : undefined;
|
354 | };
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 | const findChainForIDs = (ids, chains) => {
|
363 | let match = null;
|
364 | let maxMatches = 0;
|
365 | for (const chain of chains) {
|
366 | const score = matchesIDs(ids, chain);
|
367 | if (score > maxMatches) {
|
368 | match = chain;
|
369 | maxMatches = score;
|
370 | }
|
371 | }
|
372 | if (match) {
|
373 | return match.map((route, i) => {
|
374 | var _a;
|
375 | return ({
|
376 | id: route.id,
|
377 | segments: route.segments,
|
378 | params: mergeParams(route.params, (_a = ids[i]) === null || _a === void 0 ? void 0 : _a.params)
|
379 | });
|
380 | });
|
381 | }
|
382 | return null;
|
383 | };
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 | const findChainForSegments = (segments, chains) => {
|
392 | let match = null;
|
393 | let bestScore = 0;
|
394 | for (const chain of chains) {
|
395 | const matchedChain = matchesSegments(segments, chain);
|
396 | if (matchedChain !== null) {
|
397 | const score = computePriority(matchedChain);
|
398 | if (score > bestScore) {
|
399 | bestScore = score;
|
400 | match = matchedChain;
|
401 | }
|
402 | }
|
403 | }
|
404 | return match;
|
405 | };
|
406 |
|
407 |
|
408 |
|
409 |
|
410 |
|
411 |
|
412 |
|
413 |
|
414 |
|
415 |
|
416 |
|
417 | const computePriority = (chain) => {
|
418 | let score = 1;
|
419 | let level = 1;
|
420 | for (const route of chain) {
|
421 | for (const segment of route.segments) {
|
422 | if (segment[0] === ':') {
|
423 | score += Math.pow(1, level);
|
424 | }
|
425 | else if (segment !== '') {
|
426 | score += Math.pow(2, level);
|
427 | }
|
428 | level++;
|
429 | }
|
430 | }
|
431 | return score;
|
432 | };
|
433 | class RouterSegments {
|
434 | constructor(segments) {
|
435 | this.segments = segments.slice();
|
436 | }
|
437 | next() {
|
438 | if (this.segments.length > 0) {
|
439 | return this.segments.shift();
|
440 | }
|
441 | return '';
|
442 | }
|
443 | }
|
444 |
|
445 | const readProp = (el, prop) => {
|
446 | if (prop in el) {
|
447 | return el[prop];
|
448 | }
|
449 | if (el.hasAttribute(prop)) {
|
450 | return el.getAttribute(prop);
|
451 | }
|
452 | return null;
|
453 | };
|
454 |
|
455 |
|
456 |
|
457 |
|
458 |
|
459 | const readRedirects = (root) => {
|
460 | return Array.from(root.children)
|
461 | .filter(el => el.tagName === 'ION-ROUTE-REDIRECT')
|
462 | .map(el => {
|
463 | const to = readProp(el, 'to');
|
464 | return {
|
465 | from: parsePath(readProp(el, 'from')).segments,
|
466 | to: to == null ? undefined : parsePath(to),
|
467 | };
|
468 | });
|
469 | };
|
470 |
|
471 |
|
472 |
|
473 |
|
474 |
|
475 | const readRoutes = (root) => {
|
476 | return flattenRouterTree(readRouteNodes(root));
|
477 | };
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 | const readRouteNodes = (node) => {
|
484 | return Array.from(node.children)
|
485 | .filter(el => el.tagName === 'ION-ROUTE' && el.component)
|
486 | .map(el => {
|
487 | const component = readProp(el, 'component');
|
488 | return {
|
489 | segments: parsePath(readProp(el, 'url')).segments,
|
490 | id: component.toLowerCase(),
|
491 | params: el.componentProps,
|
492 | beforeLeave: el.beforeLeave,
|
493 | beforeEnter: el.beforeEnter,
|
494 | children: readRouteNodes(el)
|
495 | };
|
496 | });
|
497 | };
|
498 |
|
499 |
|
500 |
|
501 |
|
502 |
|
503 | const flattenRouterTree = (nodes) => {
|
504 | const chains = [];
|
505 | for (const node of nodes) {
|
506 | flattenNode([], chains, node);
|
507 | }
|
508 | return chains;
|
509 | };
|
510 |
|
511 | const flattenNode = (chain, chains, node) => {
|
512 | chain = [...chain, {
|
513 | id: node.id,
|
514 | segments: node.segments,
|
515 | params: node.params,
|
516 | beforeLeave: node.beforeLeave,
|
517 | beforeEnter: node.beforeEnter
|
518 | }];
|
519 | if (node.children.length === 0) {
|
520 | chains.push(chain);
|
521 | return;
|
522 | }
|
523 | for (const child of node.children) {
|
524 | flattenNode(chain, chains, child);
|
525 | }
|
526 | };
|
527 |
|
528 | const Router = proxyCustomElement(class extends HTMLElement {
|
529 | constructor() {
|
530 | super();
|
531 | this.__registerHost();
|
532 | this.ionRouteWillChange = createEvent(this, "ionRouteWillChange", 7);
|
533 | this.ionRouteDidChange = createEvent(this, "ionRouteDidChange", 7);
|
534 | this.previousPath = null;
|
535 | this.busy = false;
|
536 | this.state = 0;
|
537 | this.lastState = 0;
|
538 | |
539 |
|
540 |
|
541 |
|
542 | this.root = '/';
|
543 | |
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 |
|
553 |
|
554 |
|
555 |
|
556 |
|
557 | this.useHash = true;
|
558 | }
|
559 | async componentWillLoad() {
|
560 | await waitUntilNavNode();
|
561 | const canProceed = await this.runGuards(this.getSegments());
|
562 | if (canProceed !== true) {
|
563 | if (typeof canProceed === 'object') {
|
564 | const { redirect } = canProceed;
|
565 | const path = parsePath(redirect);
|
566 | this.setSegments(path.segments, ROUTER_INTENT_NONE, path.queryString);
|
567 | await this.writeNavStateRoot(path.segments, ROUTER_INTENT_NONE);
|
568 | }
|
569 | }
|
570 | else {
|
571 | await this.onRoutesChanged();
|
572 | }
|
573 | }
|
574 | componentDidLoad() {
|
575 | window.addEventListener('ionRouteRedirectChanged', debounce(this.onRedirectChanged.bind(this), 10));
|
576 | window.addEventListener('ionRouteDataChanged', debounce(this.onRoutesChanged.bind(this), 100));
|
577 | }
|
578 | async onPopState() {
|
579 | const direction = this.historyDirection();
|
580 | let segments = this.getSegments();
|
581 | const canProceed = await this.runGuards(segments);
|
582 | if (canProceed !== true) {
|
583 | if (typeof canProceed === 'object') {
|
584 | segments = parsePath(canProceed.redirect).segments;
|
585 | }
|
586 | else {
|
587 | return false;
|
588 | }
|
589 | }
|
590 | return this.writeNavStateRoot(segments, direction);
|
591 | }
|
592 | onBackButton(ev) {
|
593 | ev.detail.register(0, processNextHandler => {
|
594 | this.back();
|
595 | processNextHandler();
|
596 | });
|
597 | }
|
598 |
|
599 | async canTransition() {
|
600 | const canProceed = await this.runGuards();
|
601 | if (canProceed !== true) {
|
602 | if (typeof canProceed === 'object') {
|
603 | return canProceed.redirect;
|
604 | }
|
605 | else {
|
606 | return false;
|
607 | }
|
608 | }
|
609 | return true;
|
610 | }
|
611 | |
612 |
|
613 |
|
614 |
|
615 |
|
616 |
|
617 | async push(path, direction = 'forward', animation) {
|
618 | var _a;
|
619 | if (path.startsWith('.')) {
|
620 | const currentPath = (_a = this.previousPath) !== null && _a !== void 0 ? _a : '/';
|
621 |
|
622 | const url = new URL(path, `https://host/${currentPath}`);
|
623 | path = url.pathname + url.search;
|
624 | }
|
625 | let parsedPath = parsePath(path);
|
626 | const canProceed = await this.runGuards(parsedPath.segments);
|
627 | if (canProceed !== true) {
|
628 | if (typeof canProceed === 'object') {
|
629 | parsedPath = parsePath(canProceed.redirect);
|
630 | }
|
631 | else {
|
632 | return false;
|
633 | }
|
634 | }
|
635 | this.setSegments(parsedPath.segments, direction, parsedPath.queryString);
|
636 | return this.writeNavStateRoot(parsedPath.segments, direction, animation);
|
637 | }
|
638 |
|
639 | back() {
|
640 | window.history.back();
|
641 | return Promise.resolve(this.waitPromise);
|
642 | }
|
643 |
|
644 | async printDebug() {
|
645 | printRoutes(readRoutes(this.el));
|
646 | printRedirects(readRedirects(this.el));
|
647 | }
|
648 |
|
649 | async navChanged(direction) {
|
650 | if (this.busy) {
|
651 | console.warn('[ion-router] router is busy, navChanged was cancelled');
|
652 | return false;
|
653 | }
|
654 | const { ids, outlet } = await readNavState(window.document.body);
|
655 | const routes = readRoutes(this.el);
|
656 | const chain = findChainForIDs(ids, routes);
|
657 | if (!chain) {
|
658 | console.warn('[ion-router] no matching URL for ', ids.map(i => i.id));
|
659 | return false;
|
660 | }
|
661 | const segments = chainToSegments(chain);
|
662 | if (!segments) {
|
663 | console.warn('[ion-router] router could not match path because some required param is missing');
|
664 | return false;
|
665 | }
|
666 | this.setSegments(segments, direction);
|
667 | await this.safeWriteNavState(outlet, chain, ROUTER_INTENT_NONE, segments, null, ids.length);
|
668 | return true;
|
669 | }
|
670 |
|
671 | onRedirectChanged() {
|
672 | const segments = this.getSegments();
|
673 | if (segments && findRouteRedirect(segments, readRedirects(this.el))) {
|
674 | this.writeNavStateRoot(segments, ROUTER_INTENT_NONE);
|
675 | }
|
676 | }
|
677 |
|
678 | onRoutesChanged() {
|
679 | return this.writeNavStateRoot(this.getSegments(), ROUTER_INTENT_NONE);
|
680 | }
|
681 | historyDirection() {
|
682 | var _a;
|
683 | const win = window;
|
684 | if (win.history.state === null) {
|
685 | this.state++;
|
686 | win.history.replaceState(this.state, win.document.title, (_a = win.document.location) === null || _a === void 0 ? void 0 : _a.href);
|
687 | }
|
688 | const state = win.history.state;
|
689 | const lastState = this.lastState;
|
690 | this.lastState = state;
|
691 | if (state > lastState || (state >= lastState && lastState > 0)) {
|
692 | return ROUTER_INTENT_FORWARD;
|
693 | }
|
694 | if (state < lastState) {
|
695 | return ROUTER_INTENT_BACK;
|
696 | }
|
697 | return ROUTER_INTENT_NONE;
|
698 | }
|
699 | async writeNavStateRoot(segments, direction, animation) {
|
700 | if (!segments) {
|
701 | console.error('[ion-router] URL is not part of the routing set');
|
702 | return false;
|
703 | }
|
704 |
|
705 | const redirects = readRedirects(this.el);
|
706 | const redirect = findRouteRedirect(segments, redirects);
|
707 | let redirectFrom = null;
|
708 | if (redirect) {
|
709 | const { segments: toSegments, queryString } = redirect.to;
|
710 | this.setSegments(toSegments, direction, queryString);
|
711 | redirectFrom = redirect.from;
|
712 | segments = toSegments;
|
713 | }
|
714 |
|
715 | const routes = readRoutes(this.el);
|
716 | const chain = findChainForSegments(segments, routes);
|
717 | if (!chain) {
|
718 | console.error('[ion-router] the path does not match any route');
|
719 | return false;
|
720 | }
|
721 |
|
722 | return this.safeWriteNavState(document.body, chain, direction, segments, redirectFrom, 0, animation);
|
723 | }
|
724 | async safeWriteNavState(node, chain, direction, segments, redirectFrom, index = 0, animation) {
|
725 | const unlock = await this.lock();
|
726 | let changed = false;
|
727 | try {
|
728 | changed = await this.writeNavState(node, chain, direction, segments, redirectFrom, index, animation);
|
729 | }
|
730 | catch (e) {
|
731 | console.error(e);
|
732 | }
|
733 | unlock();
|
734 | return changed;
|
735 | }
|
736 | async lock() {
|
737 | const p = this.waitPromise;
|
738 | let resolve;
|
739 | this.waitPromise = new Promise(r => resolve = r);
|
740 | if (p !== undefined) {
|
741 | await p;
|
742 | }
|
743 | return resolve;
|
744 | }
|
745 | |
746 |
|
747 |
|
748 |
|
749 |
|
750 |
|
751 | async runGuards(to = this.getSegments(), from) {
|
752 | if (from === undefined) {
|
753 | from = parsePath(this.previousPath).segments;
|
754 | }
|
755 | if (!to || !from) {
|
756 | return true;
|
757 | }
|
758 | const routes = readRoutes(this.el);
|
759 | const fromChain = findChainForSegments(from, routes);
|
760 | const beforeLeaveHook = fromChain && fromChain[fromChain.length - 1].beforeLeave;
|
761 | const canLeave = beforeLeaveHook ? await beforeLeaveHook() : true;
|
762 | if (canLeave === false || typeof canLeave === 'object') {
|
763 | return canLeave;
|
764 | }
|
765 | const toChain = findChainForSegments(to, routes);
|
766 | const beforeEnterHook = toChain && toChain[toChain.length - 1].beforeEnter;
|
767 | return beforeEnterHook ? beforeEnterHook() : true;
|
768 | }
|
769 | async writeNavState(node, chain, direction, segments, redirectFrom, index = 0, animation) {
|
770 | if (this.busy) {
|
771 | console.warn('[ion-router] router is busy, transition was cancelled');
|
772 | return false;
|
773 | }
|
774 | this.busy = true;
|
775 |
|
776 | const routeEvent = this.routeChangeEvent(segments, redirectFrom);
|
777 | if (routeEvent) {
|
778 | this.ionRouteWillChange.emit(routeEvent);
|
779 | }
|
780 | const changed = await writeNavState(node, chain, direction, index, false, animation);
|
781 | this.busy = false;
|
782 |
|
783 | if (routeEvent) {
|
784 | this.ionRouteDidChange.emit(routeEvent);
|
785 | }
|
786 | return changed;
|
787 | }
|
788 | setSegments(segments, direction, queryString) {
|
789 | this.state++;
|
790 | writeSegments(window.history, this.root, this.useHash, segments, direction, this.state, queryString);
|
791 | }
|
792 | getSegments() {
|
793 | return readSegments(window.location, this.root, this.useHash);
|
794 | }
|
795 | routeChangeEvent(toSegments, redirectFromSegments) {
|
796 | const from = this.previousPath;
|
797 | const to = generatePath(toSegments);
|
798 | this.previousPath = to;
|
799 | if (to === from) {
|
800 | return null;
|
801 | }
|
802 | const redirectedFrom = redirectFromSegments ? generatePath(redirectFromSegments) : null;
|
803 | return {
|
804 | from,
|
805 | redirectedFrom,
|
806 | to,
|
807 | };
|
808 | }
|
809 | get el() { return this; }
|
810 | }, [0, "ion-router", {
|
811 | "root": [1],
|
812 | "useHash": [4, "use-hash"],
|
813 | "canTransition": [64],
|
814 | "push": [64],
|
815 | "back": [64],
|
816 | "printDebug": [64],
|
817 | "navChanged": [64]
|
818 | }, [[8, "popstate", "onPopState"], [4, "ionBackButton", "onBackButton"]]]);
|
819 | function defineCustomElement$1() {
|
820 | if (typeof customElements === "undefined") {
|
821 | return;
|
822 | }
|
823 | const components = ["ion-router"];
|
824 | components.forEach(tagName => { switch (tagName) {
|
825 | case "ion-router":
|
826 | if (!customElements.get(tagName)) {
|
827 | customElements.define(tagName, Router);
|
828 | }
|
829 | break;
|
830 | } });
|
831 | }
|
832 |
|
833 | const IonRouter = Router;
|
834 | const defineCustomElement = defineCustomElement$1;
|
835 |
|
836 | export { IonRouter, defineCustomElement };
|