diff --git a/src/core/frames/frame_controller.ts b/src/core/frames/frame_controller.ts index 85254930d..bcaaa57c1 100644 --- a/src/core/frames/frame_controller.ts +++ b/src/core/frames/frame_controller.ts @@ -23,7 +23,7 @@ import { ViewDelegate, ViewRenderOptions } from "../view" import { getAction, expandURL, urlsAreEqual, locationIsVisitable } from "../url" import { FormSubmitObserver, FormSubmitObserverDelegate } from "../../observers/form_submit_observer" import { FrameView } from "./frame_view" -import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor" +import { LinkClickObserver, LinkClickObserverDelegate } from "../../observers/link_click_observer" import { FormLinkClickObserver, FormLinkClickObserverDelegate } from "../../observers/form_link_click_observer" import { FrameRenderer } from "./frame_renderer" import { session } from "../index" @@ -39,14 +39,14 @@ export class FrameController FormSubmissionDelegate, FrameElementDelegate, FormLinkClickObserverDelegate, - LinkInterceptorDelegate, + LinkClickObserverDelegate, ViewDelegate> { readonly element: FrameElement readonly view: FrameView readonly appearanceObserver: AppearanceObserver readonly formLinkClickObserver: FormLinkClickObserver - readonly linkInterceptor: LinkInterceptor + readonly linkClickObserver: LinkClickObserver readonly formSubmitObserver: FormSubmitObserver formSubmission?: FormSubmission fetchResponseLoaded = (_fetchResponse: FetchResponse) => {} @@ -65,7 +65,7 @@ export class FrameController this.view = new FrameView(this, this.element) this.appearanceObserver = new AppearanceObserver(this, this.element) this.formLinkClickObserver = new FormLinkClickObserver(this, this.element) - this.linkInterceptor = new LinkInterceptor(this, this.element) + this.linkClickObserver = new LinkClickObserver(this, this.element) this.restorationIdentifier = uuid() this.formSubmitObserver = new FormSubmitObserver(this, this.element) } @@ -79,7 +79,7 @@ export class FrameController this.loadSourceURL() } this.formLinkClickObserver.start() - this.linkInterceptor.start() + this.linkClickObserver.start() this.formSubmitObserver.start() } } @@ -89,7 +89,7 @@ export class FrameController this.connected = false this.appearanceObserver.stop() this.formLinkClickObserver.stop() - this.linkInterceptor.stop() + this.linkClickObserver.stop() this.formSubmitObserver.stop() } } @@ -180,7 +180,7 @@ export class FrameController // Form link click observer delegate willSubmitFormLinkToLocation(link: Element): boolean { - return this.shouldInterceptNavigation(link) + return link.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(link) } submittedFormLinkToLocation(link: Element, _location: URL, form: HTMLFormElement): void { @@ -188,14 +188,14 @@ export class FrameController if (frame) form.setAttribute("data-turbo-frame", frame.id) } - // Link interceptor delegate + // Link click observer delegate - shouldInterceptLinkClick(element: Element, _url: string) { + willFollowLinkToLocation(element: Element) { return this.shouldInterceptNavigation(element) } - linkClickIntercepted(element: Element, url: string) { - this.navigateFrame(element, url) + followedLinkToLocation(element: Element, location: URL) { + this.navigateFrame(element, location.href) } // Form submit observer delegate diff --git a/src/core/frames/frame_redirector.ts b/src/core/frames/frame_redirector.ts index 93c9bbc5f..3a4dc141e 100644 --- a/src/core/frames/frame_redirector.ts +++ b/src/core/frames/frame_redirector.ts @@ -1,37 +1,38 @@ import { FormSubmitObserver, FormSubmitObserverDelegate } from "../../observers/form_submit_observer" import { FrameElement } from "../../elements/frame_element" -import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor" import { expandURL, getAction, locationIsVisitable } from "../url" +import { LinkClickObserver, LinkClickObserverDelegate } from "../../observers/link_click_observer" +import { session } from "../../index" -export class FrameRedirector implements LinkInterceptorDelegate, FormSubmitObserverDelegate { +export class FrameRedirector implements LinkClickObserverDelegate, FormSubmitObserverDelegate { readonly element: Element - readonly linkInterceptor: LinkInterceptor + readonly linkClickObserver: LinkClickObserver readonly formSubmitObserver: FormSubmitObserver constructor(element: Element) { this.element = element - this.linkInterceptor = new LinkInterceptor(this, element) + this.linkClickObserver = new LinkClickObserver(this, element) this.formSubmitObserver = new FormSubmitObserver(this, element) } start() { - this.linkInterceptor.start() + this.linkClickObserver.start() this.formSubmitObserver.start() } stop() { - this.linkInterceptor.stop() + this.linkClickObserver.stop() this.formSubmitObserver.stop() } - shouldInterceptLinkClick(element: Element, _url: string) { + willFollowLinkToLocation(element: Element) { return this.shouldRedirect(element) } - linkClickIntercepted(element: Element, url: string) { + followedLinkToLocation(element: Element, url: URL) { const frame = this.findFrameElement(element) if (frame) { - frame.delegate.linkClickIntercepted(element, url) + frame.delegate.followedLinkToLocation(element, url) } } @@ -59,8 +60,12 @@ export class FrameRedirector implements LinkInterceptorDelegate, FormSubmitObser } private shouldRedirect(element: Element, submitter?: HTMLElement) { - const frame = this.findFrameElement(element, submitter) - return frame ? frame != element.closest("turbo-frame") : false + if (session.elementIsNavigatable(element) && (!submitter || session.submitterIsNavigatable(submitter))) { + const frame = this.findFrameElement(element, submitter) + return frame ? frame != element.closest("turbo-frame") : false + } else { + return false + } } private findFrameElement(element: Element, submitter?: HTMLElement) { diff --git a/src/core/frames/link_interceptor.ts b/src/core/frames/link_interceptor.ts deleted file mode 100644 index 65ff1066f..000000000 --- a/src/core/frames/link_interceptor.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { TurboClickEvent, TurboBeforeVisitEvent } from "../session" - -export interface LinkInterceptorDelegate { - shouldInterceptLinkClick(element: Element, url: string): boolean - linkClickIntercepted(element: Element, url: string): void -} - -export class LinkInterceptor { - readonly delegate: LinkInterceptorDelegate - readonly element: Element - private clickEvent?: Event - - constructor(delegate: LinkInterceptorDelegate, element: Element) { - this.delegate = delegate - this.element = element - } - - start() { - this.element.addEventListener("click", this.clickBubbled) - document.addEventListener("turbo:click", this.linkClicked) - document.addEventListener("turbo:before-visit", this.willVisit) - } - - stop() { - this.element.removeEventListener("click", this.clickBubbled) - document.removeEventListener("turbo:click", this.linkClicked) - document.removeEventListener("turbo:before-visit", this.willVisit) - } - - clickBubbled = (event: Event) => { - if (this.respondsToEventTarget(event.target)) { - this.clickEvent = event - } else { - delete this.clickEvent - } - } - - linkClicked = ((event: TurboClickEvent) => { - if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) { - if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url)) { - this.clickEvent.preventDefault() - event.preventDefault() - this.delegate.linkClickIntercepted(event.target, event.detail.url) - } - } - delete this.clickEvent - }) - - willVisit = ((_event: TurboBeforeVisitEvent) => { - delete this.clickEvent - }) - - respondsToEventTarget(target: EventTarget | null) { - const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null - return element && element.closest("turbo-frame, html") == this.element - } -} diff --git a/src/elements/frame_element.ts b/src/elements/frame_element.ts index 3e7676cad..ca9218c9b 100644 --- a/src/elements/frame_element.ts +++ b/src/elements/frame_element.ts @@ -1,6 +1,6 @@ import { FetchResponse } from "../http/fetch_response" import { Snapshot } from "../core/snapshot" -import { LinkInterceptorDelegate } from "../core/frames/link_interceptor" +import { LinkClickObserverDelegate } from "../observers/link_click_observer" import { FormSubmitObserverDelegate } from "../observers/form_submit_observer" export enum FrameLoadingStyle { @@ -10,7 +10,7 @@ export enum FrameLoadingStyle { export type FrameElementObservedAttribute = keyof FrameElement & ("disabled" | "complete" | "loading" | "src") -export interface FrameElementDelegate extends LinkInterceptorDelegate, FormSubmitObserverDelegate { +export interface FrameElementDelegate extends LinkClickObserverDelegate, FormSubmitObserverDelegate { connect(): void disconnect(): void completeChanged(): void diff --git a/src/observers/form_link_click_observer.ts b/src/observers/form_link_click_observer.ts index 6ff364de8..2c24c1468 100644 --- a/src/observers/form_link_click_observer.ts +++ b/src/observers/form_link_click_observer.ts @@ -6,20 +6,20 @@ export type FormLinkClickObserverDelegate = { } export class FormLinkClickObserver implements LinkClickObserverDelegate { - readonly linkInterceptor: LinkClickObserver + readonly linkClickObserver: LinkClickObserver readonly delegate: FormLinkClickObserverDelegate constructor(delegate: FormLinkClickObserverDelegate, element: HTMLElement) { this.delegate = delegate - this.linkInterceptor = new LinkClickObserver(this, element) + this.linkClickObserver = new LinkClickObserver(this, element) } start() { - this.linkInterceptor.start() + this.linkClickObserver.start() } stop() { - this.linkInterceptor.stop() + this.linkClickObserver.stop() } willFollowLinkToLocation(link: Element, location: URL, originalEvent: MouseEvent): boolean {