import React from "react"; import { parse } from "url"; import PropTypes from "prop-types"; import Router from "next/router"; export default class LegacyPageWrapper extends React.Component { static propTypes = { backendHost: PropTypes.string, pageContents: PropTypes.string, allowFollowingLinkToLocation: PropTypes.func, loadLegacyModules: PropTypes.func, onNavigate: PropTypes.func }; static defaultProps = { backendHost: "https://api.nomadhealth.com", allowFollowingLinkToLocation: () => true, loadLegacyModules: () => {}, onNavigate: location => { Router.push(String(location)); } }; static async getInitialProps({ asPath }) { const response = await fetch(this.props.backendHost + asPath); const pageContents = await response.text(); return { pageContents }; } pageRef = React.createRef(); shouldComponentUpdate() { return false; } componentDidMount() { if (this.pageRef.current && typeof window.addEventListener === "function") { addEventListener("click", this.clickCaptured, true); this.props.loadLegacyModules(this.pageRef.current); } } componentWillUnmount() { if (typeof window.removeEventListener === "function") { removeEventListener("click", this.clickCaptured, true); removeEventListener("click", this.clickBubbled, false); } } clickCaptured = () => { removeEventListener("click", this.clickBubbled, false); addEventListener("click", this.clickBubbled, false); }; clickBubbled = event => { if (this.clickEventIsSignificant(event)) { const link = this.getVisitableLinkForTarget(event.target); if (link) { const location = this.getVisitableLocationForLink(link); if ( location && this.props.allowFollowingLinkToLocation(link, location) ) { event.preventDefault(); this.props.onNavigate(location); } } } }; clickEventIsSignificant(event) { return !( (event.target && event.target.isContentEditable) || event.defaultPrevented || event.which > 1 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey ); } getVisitableLinkForTarget(target) { if (target instanceof Element) { return closest(target, "a[href]:not([target]):not([download])"); } } getVisitableLocationForLink(link) { const location = new Location(link.getAttribute("href") || ""); if (location.isLocal()) { return location; } } render() { return (
); } } export class Location { constructor(href) { this.href = href; this.url = parse(href, false, true); } static getOrigin() { const { protocol, hostname, port } = window.location; return `${protocol}//${hostname}${port ? ":" + port : ""}`; } isLocal() { const origin = parse(Location.getOrigin(), false, true); return ( !this.url.host || (this.url.protocol === origin.protocol && this.url.host === origin.host) ); } } export const closest = (() => { const html = document.documentElement; const match = html.matches || html.webkitMatchesSelector || html.msMatchesSelector || html.mozMatchesSelector; const closest = html.closest || function(selector) { let element = this; while (element) { if (match.call(element, selector)) { return element; } else { element = element.parentElement; } } }; return function(element, selector) { return closest.call(element, selector); }; })();