From f2c85be1f46f0bb4f8d64b3aa53ea4c4ef2ead00 Mon Sep 17 00:00:00 2001 From: Ash Searle Date: Sun, 22 Oct 2017 21:50:21 +0100 Subject: [PATCH 1/3] Fix #129: complete relative links solution --- src/index.js | 70 +++++++++++++++++++++++++++++++--------------------- test/dom.js | 16 ++++++++++++ 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/index.js b/src/index.js index 4ad59f99..d0805a2c 100644 --- a/src/index.js +++ b/src/index.js @@ -23,21 +23,41 @@ function setUrl(url, type='push') { } +function getCurrentLocation() { + return (customHistory && customHistory.location) || + (customHistory && customHistory.getCurrentLocation && customHistory.getCurrentLocation()) || + (typeof location!=='undefined' ? location : EMPTY); +} + + function getCurrentUrl() { - let url; - if (customHistory && customHistory.location) { - url = customHistory.location; - } - else if (customHistory && customHistory.getCurrentLocation) { - url = customHistory.getCurrentLocation(); - } - else { - url = typeof location!=='undefined' ? location : EMPTY; - } + let url = getCurrentLocation(); return `${url.pathname || ''}${url.search || ''}`; } +const a = typeof document!=='undefined' && document.createElement('a'); + +// Based on https://tools.ietf.org/html/rfc3986#appendix-B +const uriRegex = new RegExp('^([^:/?#]+:)?(?://([^/?#]*))?([^?#]*)((?:\\?[^#]*)?)((?:#.*)?)'); + +/* Resolve URL relative to current location */ +function resolve(url) { + let current = getCurrentLocation(); + if (a) { + a.setAttribute('href', url); + url = a.href; + } + let [,protocol,host,pathname,search] = uriRegex.exec(url); + if ( + (current.protocol && protocol !== current.protocol) || + (current.host && host !== current.host) + ) { + return; + } + return `${pathname}${search}`; +} + function route(url, replace=false) { if (typeof url!=='string' && url.url) { @@ -45,6 +65,9 @@ function route(url, replace=false) { url = url.url; } + url = resolve(url); + if (!url) return; + // only push URL into history if we can handle it if (canRoute(url)) { setUrl(url, replace ? 'replace' : 'push'); @@ -79,17 +102,11 @@ function routeTo(url) { function routeFromLink(node) { - // only valid elements - if (!node || !node.getAttribute) return; - - let href = node.getAttribute('href'), - target = node.getAttribute('target'); - - // ignore links with targets and non-path URLs - if (!href || !href.match(/^\//g) || (target && !target.match(/^_?self$/i))) return; + // ignore invalid & external links: + if (!node || (node.target && !node.target.match(/^_?self$/i))) return; // attempt to route, if no match simply cede control to browser - return route(href); + return route(node.pathname + node.search + node.hash); } @@ -117,12 +134,9 @@ function delegateLinkHandler(e) { let t = e.target; do { - if (String(t.nodeName).toUpperCase()==='A' && t.getAttribute('href') && isPreactElement(t)) { - if (t.hasAttribute('native')) return; + if (String(t.nodeName).toUpperCase()==='A' && t.pathname && isPreactElement(t) && !t.hasAttribute('native') && routeFromLink(t)) { // if link is handled by the router, prevent browser defaults - if (routeFromLink(t)) { - return prevent(e); - } + return prevent(e); } } while ((t=t.parentNode)); } @@ -131,13 +145,13 @@ function delegateLinkHandler(e) { let eventListenersInitialized = false; function initEventListeners() { - if (eventListenersInitialized){ - return; - } + if (eventListenersInitialized) return; if (typeof addEventListener==='function') { if (!customHistory) { - addEventListener('popstate', () => routeTo(getCurrentUrl())); + addEventListener('popstate', () => { + routeTo(getCurrentUrl()); + }); } addEventListener('click', delegateLinkHandler); } diff --git a/test/dom.js b/test/dom.js index 5687a832..758479b7 100644 --- a/test/dom.js +++ b/test/dom.js @@ -170,6 +170,22 @@ describe('dom', () => { route('/foo'); expect(routerRef.base.outerHTML).to.eql('

bar is

'); }); + + it('should support relative links', () => { + class A { + render() { + return go; + } + } + history.replaceState(null, null, '/foo/one'); + mount( + + + + ); + scratch.querySelector('a').click(); + expect(location.pathname).to.equal('/foo/two'); + }); }); describe('preact-router/match', () => { From 0381a367ac13c053d98e5d4f25e2a3abbf7a1fe1 Mon Sep 17 00:00:00 2001 From: Ash Searle Date: Sun, 22 Oct 2017 21:57:07 +0100 Subject: [PATCH 2/3] Fix #129: include hash for consistency --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index d0805a2c..d7441a6a 100644 --- a/src/index.js +++ b/src/index.js @@ -48,14 +48,14 @@ function resolve(url) { a.setAttribute('href', url); url = a.href; } - let [,protocol,host,pathname,search] = uriRegex.exec(url); + let [,protocol,host,pathname,search,hash] = uriRegex.exec(url); if ( (current.protocol && protocol !== current.protocol) || (current.host && host !== current.host) ) { return; } - return `${pathname}${search}`; + return `${pathname}${search}${hash}`; } From c763a39509f5b7b1a0c6f249f6d6554a286854e8 Mon Sep 17 00:00:00 2001 From: Ash Searle Date: Fri, 10 Nov 2017 18:57:59 +0000 Subject: [PATCH 3/3] Skip URL parse --- src/index.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/index.js b/src/index.js index d7441a6a..4c488720 100644 --- a/src/index.js +++ b/src/index.js @@ -38,24 +38,20 @@ function getCurrentUrl() { const a = typeof document!=='undefined' && document.createElement('a'); -// Based on https://tools.ietf.org/html/rfc3986#appendix-B -const uriRegex = new RegExp('^([^:/?#]+:)?(?://([^/?#]*))?([^?#]*)((?:\\?[^#]*)?)((?:#.*)?)'); - /* Resolve URL relative to current location */ function resolve(url) { let current = getCurrentLocation(); if (a) { a.setAttribute('href', url); - url = a.href; - } - let [,protocol,host,pathname,search,hash] = uriRegex.exec(url); - if ( - (current.protocol && protocol !== current.protocol) || - (current.host && host !== current.host) - ) { - return; + if ( + (current.protocol && a.protocol !== current.protocol) || + (current.host && a.host !== current.host) + ) { + return; + } + return a.pathname + a.search + a.hash; } - return `${pathname}${search}${hash}`; + return url; }