import { BaseController } from "../utilities/base_controller" ; import { useEventListener } from "../mixins/use_event_listener" ; import { warn } from "../utilities/logging" ; export class PrefetchController extends BaseController { static values = { mode : String } ; declare readonly modeValue : "intersect" | "mouseover" ; declare readonly hasModeValue : boolean ; get _mode ( ) : string { return this . hasModeValue ? this . modeValue : "mouseover" ; } get _supportsPrefetch ( ) : boolean { const link = document . createElement ( 'link' ) ; return link . relList ?. supports && link . relList ?. supports ( 'prefetch' ) ; } get _href ( ) : string { return ( this . el as HTMLAnchorElement ) . href ; } get _existingPrefetch ( ) : boolean { return ( document . head . querySelectorAll ( `link[rel="prefetch"][href="${ this . _href }"]` ) || [ ] ) . length > 0 ; } get _connectionSuitable ( ) : boolean { const connection = navigator . connection ; if ( ! connection ) { return true ; } if ( connection ) { if ( connection . saveData ) { warn ( this , 'Data Saving is enabled' ) ; return false ; } if ( /2g/ . test ( connection . effectiveType ) ) { warn ( this , "Network is too slow" ) ; return false ; } } return true ; } connect ( ) { if ( ! this . _supportsPrefetch ) { return ; } switch ( this . _mode ) { case "intersect" : this . _setupObserver ( ) ; break ; case "mouseover" : useEventListener ( this , this . el , "mouseover" , this . prefetch , { once : true } ) ; break ; default : throw new Error ( `'${ this . _mode }' is not a supported prefetch mode` ) ; } } _setupObserver ( ) { const observer = new IntersectionObserver ( ( [ entry ] , observer ) => { if ( entry . isIntersecting ) { this . prefetch ( ) ; observer . unobserve ( entry . target ) ; } } ) ; observer . observe ( this . element ) ; } prefetch ( ) { if ( this . _existingPrefetch || ! this . _connectionSuitable ) { return ; } const link = document . createElement ( 'link' ) ; Object . assign ( link , { rel : 'prefetch' , href : this . _href , as : 'document' } ) ; document . head . appendChild ( link ) ; } }