11/*!
2- Turbo 8.0.13
2+ Turbo 8.0.17
33Copyright © 2025 37signals LLC
44 */
55( function ( prototype ) {
@@ -409,7 +409,11 @@ function doesNotTargetIFrame(name) {
409409}
410410
411411function findLinkFromClickTarget ( target ) {
412- return findClosestRecursively ( target , "a[href]:not([target^=_]):not([download])" ) ;
412+ const link = findClosestRecursively ( target , "a[href], a[xlink\\:href]" ) ;
413+ if ( ! link ) return null ;
414+ if ( link . hasAttribute ( "download" ) ) return null ;
415+ if ( link . hasAttribute ( "target" ) && link . target !== "_self" ) return null ;
416+ return link ;
413417}
414418
415419function getLocationForLink ( link ) {
@@ -488,8 +492,8 @@ function getExtension(url) {
488492}
489493
490494function isPrefixedBy ( baseURL , url ) {
491- const prefix = getPrefix ( url ) ;
492- return baseURL . href === expandURL ( prefix ) . href || baseURL . href . startsWith ( prefix ) ;
495+ const prefix = addTrailingSlash ( url . origin + url . pathname ) ;
496+ return addTrailingSlash ( baseURL . href ) === prefix || baseURL . href . startsWith ( prefix ) ;
493497}
494498
495499function locationIsVisitable ( location , rootLocation ) {
@@ -517,10 +521,6 @@ function getLastPathComponent(url) {
517521 return getPathComponents ( url ) . slice ( - 1 ) [ 0 ] ;
518522}
519523
520- function getPrefix ( url ) {
521- return addTrailingSlash ( url . origin + url . pathname ) ;
522- }
523-
524524function addTrailingSlash ( value ) {
525525 return value . endsWith ( "/" ) ? value : value + "/" ;
526526}
@@ -588,14 +588,12 @@ class LimitedSet extends Set {
588588
589589const recentRequests = new LimitedSet ( 20 ) ;
590590
591- const nativeFetch = window . fetch ;
592-
593591function fetchWithTurboHeaders ( url , options = { } ) {
594592 const modifiedHeaders = new Headers ( options . headers || { } ) ;
595593 const requestUID = uuid ( ) ;
596594 recentRequests . add ( requestUID ) ;
597595 modifiedHeaders . append ( "X-Turbo-Request-Id" , requestUID ) ;
598- return nativeFetch ( url , {
596+ return window . fetch ( url , {
599597 ...options ,
600598 headers : modifiedHeaders
601599 } ) ;
@@ -1243,8 +1241,8 @@ class View {
12431241 scrollToAnchor ( anchor ) {
12441242 const element = this . snapshot . getElementForAnchor ( anchor ) ;
12451243 if ( element ) {
1246- this . scrollToElement ( element ) ;
12471244 this . focusElement ( element ) ;
1245+ this . scrollToElement ( element ) ;
12481246 } else {
12491247 this . scrollToPosition ( {
12501248 x : 0 ,
@@ -2281,12 +2279,21 @@ function morphElements(currentElement, newElement, {callbacks: callbacks, ...opt
22812279 } ) ;
22822280}
22832281
2284- function morphChildren ( currentElement , newElement ) {
2282+ function morphChildren ( currentElement , newElement , options = { } ) {
22852283 morphElements ( currentElement , newElement . childNodes , {
2284+ ...options ,
22862285 morphStyle : "innerHTML"
22872286 } ) ;
22882287}
22892288
2289+ function shouldRefreshFrameWithMorphing ( currentFrame , newFrame ) {
2290+ return currentFrame instanceof FrameElement && newFrame instanceof Element && newFrame . nodeName === "TURBO-FRAME" && currentFrame . shouldReloadWithMorph && currentFrame . id === newFrame . id && ( ! newFrame . getAttribute ( "src" ) || urlsAreEqual ( currentFrame . src , newFrame . getAttribute ( "src" ) ) ) && ! currentFrame . closest ( "[data-turbo-permanent]" ) ;
2291+ }
2292+
2293+ function closestFrameReloadableWithMorphing ( node ) {
2294+ return node . parentElement . closest ( "turbo-frame[src][refresh=morph]" ) ;
2295+ }
2296+
22902297class DefaultIdiomorphCallbacks {
22912298 #beforeNodeMorphed;
22922299 constructor ( { beforeNodeMorphed : beforeNodeMorphed } = { } ) {
@@ -2344,7 +2351,17 @@ class MorphingFrameRenderer extends FrameRenderer {
23442351 newElement : newElement
23452352 }
23462353 } ) ;
2347- morphChildren ( currentElement , newElement ) ;
2354+ morphChildren ( currentElement , newElement , {
2355+ callbacks : {
2356+ beforeNodeMorphed : ( node , newNode ) => {
2357+ if ( shouldRefreshFrameWithMorphing ( node , newNode ) && closestFrameReloadableWithMorphing ( node ) === currentElement ) {
2358+ node . reload ( ) ;
2359+ return false ;
2360+ }
2361+ return true ;
2362+ }
2363+ }
2364+ } ) ;
23482365 }
23492366 async preservingPermanentElements ( callback ) {
23502367 return await callback ( ) ;
@@ -2596,7 +2613,8 @@ class PageSnapshot extends Snapshot {
25962613 return this . getSetting ( "visit-control" ) != "reload" ;
25972614 }
25982615 get prefersViewTransitions ( ) {
2599- return this . headSnapshot . getMetaValue ( "view-transition" ) === "same-origin" ;
2616+ const viewTransitionEnabled = this . getSetting ( "view-transition" ) === "true" || this . headSnapshot . getMetaValue ( "view-transition" ) === "same-origin" ;
2617+ return viewTransitionEnabled && ! window . matchMedia ( "(prefers-reduced-motion: reduce)" ) . matches ;
26002618 }
26012619 get shouldMorphPage ( ) {
26022620 return this . getSetting ( "refresh-method" ) === "morph" ;
@@ -3011,6 +3029,7 @@ class BrowserAdapter {
30113029 }
30123030 visitStarted ( visit ) {
30133031 this . location = visit . location ;
3032+ this . redirectedToLocation = null ;
30143033 visit . loadCachedSnapshot ( ) ;
30153034 visit . issueRequest ( ) ;
30163035 visit . goToSamePageAnchor ( ) ;
@@ -3025,6 +3044,9 @@ class BrowserAdapter {
30253044 }
30263045 visitRequestCompleted ( visit ) {
30273046 visit . loadResponse ( ) ;
3047+ if ( visit . response . redirected ) {
3048+ this . redirectedToLocation = visit . redirectedToLocation ;
3049+ }
30283050 }
30293051 visitRequestFailedWithStatusCode ( visit , statusCode ) {
30303052 switch ( statusCode ) {
@@ -3095,7 +3117,7 @@ class BrowserAdapter {
30953117 dispatch ( "turbo:reload" , {
30963118 detail : reason
30973119 } ) ;
3098- window . location . href = this . location ?. toString ( ) || window . location . href ;
3120+ window . location . href = ( this . redirectedToLocation || this . location ) ?. toString ( ) || window . location . href ;
30993121 }
31003122 get navigator ( ) {
31013123 return this . session . navigator ;
@@ -3338,6 +3360,7 @@ class LinkPrefetchObserver {
33383360 if ( this . delegate . canPrefetchRequestToLocation ( link , location ) ) {
33393361 this . #prefetchedLink = link ;
33403362 const fetchRequest = new FetchRequest ( this , FetchMethod . get , location , new URLSearchParams , target ) ;
3363+ fetchRequest . fetchOptions . priority = "low" ;
33413364 prefetchCache . setLater ( location . toString ( ) , fetchRequest , this . #cacheTtl) ;
33423365 }
33433366 }
@@ -3988,12 +4011,15 @@ class MorphingPageRenderer extends PageRenderer {
39884011 static renderElement ( currentElement , newElement ) {
39894012 morphElements ( currentElement , newElement , {
39904013 callbacks : {
3991- beforeNodeMorphed : element => ! canRefreshFrame ( element )
4014+ beforeNodeMorphed : ( node , newNode ) => {
4015+ if ( shouldRefreshFrameWithMorphing ( node , newNode ) && ! closestFrameReloadableWithMorphing ( node ) ) {
4016+ node . reload ( ) ;
4017+ return false ;
4018+ }
4019+ return true ;
4020+ }
39924021 }
39934022 } ) ;
3994- for ( const frame of currentElement . querySelectorAll ( "turbo-frame" ) ) {
3995- if ( canRefreshFrame ( frame ) ) frame . reload ( ) ;
3996- }
39974023 dispatch ( "turbo:morph" , {
39984024 detail : {
39994025 currentElement : currentElement ,
@@ -4012,10 +4038,6 @@ class MorphingPageRenderer extends PageRenderer {
40124038 }
40134039}
40144040
4015- function canRefreshFrame ( frame ) {
4016- return frame instanceof FrameElement && frame . src && frame . refresh === "morph" && ! frame . closest ( "[data-turbo-permanent]" ) ;
4017- }
4018-
40194041class SnapshotCache {
40204042 keys = [ ] ;
40214043 snapshots = { } ;
@@ -4616,6 +4638,14 @@ function setFormMode(mode) {
46164638 config . forms . mode = mode ;
46174639}
46184640
4641+ function morphBodyElements ( currentBody , newBody ) {
4642+ MorphingPageRenderer . renderElement ( currentBody , newBody ) ;
4643+ }
4644+
4645+ function morphTurboFrameElements ( currentFrame , newFrame ) {
4646+ MorphingFrameRenderer . renderElement ( currentFrame , newFrame ) ;
4647+ }
4648+
46194649var Turbo = Object . freeze ( {
46204650 __proto__ : null ,
46214651 navigator : navigator$1 ,
@@ -4635,7 +4665,11 @@ var Turbo = Object.freeze({
46354665 clearCache : clearCache ,
46364666 setProgressBarDelay : setProgressBarDelay ,
46374667 setConfirmMethod : setConfirmMethod ,
4638- setFormMode : setFormMode
4668+ setFormMode : setFormMode ,
4669+ morphBodyElements : morphBodyElements ,
4670+ morphTurboFrameElements : morphTurboFrameElements ,
4671+ morphChildren : morphChildren ,
4672+ morphElements : morphElements
46394673} ) ;
46404674
46414675class TurboFrameMissingError extends Error { }
@@ -5330,6 +5364,10 @@ var Turbo$1 = Object.freeze({
53305364 fetchEnctypeFromString : fetchEnctypeFromString ,
53315365 fetchMethodFromString : fetchMethodFromString ,
53325366 isSafe : isSafe ,
5367+ morphBodyElements : morphBodyElements ,
5368+ morphChildren : morphChildren ,
5369+ morphElements : morphElements ,
5370+ morphTurboFrameElements : morphTurboFrameElements ,
53335371 navigator : navigator$1 ,
53345372 registerAdapter : registerAdapter ,
53355373 renderStreamMessage : renderStreamMessage ,
0 commit comments