|
import { BROWSER, DEV } from 'esm-env'; |
|
import { writable } from 'svelte/store'; |
|
import { assets } from '__sveltekit/paths'; |
|
import { version } from '__sveltekit/environment'; |
|
import { PRELOAD_PRIORITIES } from './constants.js'; |
|
|
|
|
|
|
|
export const origin = BROWSER ? location.origin : ''; |
|
|
|
|
|
export function resolve_url(url) { |
|
if (url instanceof URL) return url; |
|
|
|
let baseURI = document.baseURI; |
|
|
|
if (!baseURI) { |
|
const baseTags = document.getElementsByTagName('base'); |
|
baseURI = baseTags.length ? baseTags[0].href : document.URL; |
|
} |
|
|
|
return new URL(url, baseURI); |
|
} |
|
|
|
export function scroll_state() { |
|
return { |
|
x: pageXOffset, |
|
y: pageYOffset |
|
}; |
|
} |
|
|
|
const warned = new WeakSet(); |
|
|
|
|
|
|
|
const valid_link_options = ({ |
|
'preload-code': ['', 'off', 'false', 'tap', 'hover', 'viewport', 'eager'], |
|
'preload-data': ['', 'off', 'false', 'tap', 'hover'], |
|
keepfocus: ['', 'true', 'off', 'false'], |
|
noscroll: ['', 'true', 'off', 'false'], |
|
reload: ['', 'true', 'off', 'false'], |
|
replacestate: ['', 'true', 'off', 'false'] |
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function link_option(element, name) { |
|
const value = ( |
|
element.getAttribute(`data-sveltekit-${name}`) |
|
); |
|
|
|
if (DEV) { |
|
validate_link_option(element, name, value); |
|
} |
|
|
|
return value; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function validate_link_option(element, name, value) { |
|
if (value === null) return; |
|
|
|
|
|
if (!warned.has(element) && !valid_link_options[name].includes(value)) { |
|
console.error( |
|
`Unexpected value for ${name} — should be one of ${valid_link_options[name] |
|
.map((option) => JSON.stringify(option)) |
|
.join(', ')}`, |
|
element |
|
); |
|
|
|
warned.add(element); |
|
} |
|
} |
|
|
|
const levels = { |
|
...PRELOAD_PRIORITIES, |
|
'': PRELOAD_PRIORITIES.hover |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
function parent_element(element) { |
|
let parent = element.assignedSlot ?? element.parentNode; |
|
|
|
|
|
if (parent?.nodeType === 11) parent = parent.host; |
|
|
|
return (parent); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export function find_anchor(element, target) { |
|
while (element && element !== target) { |
|
if (element.nodeName.toUpperCase() === 'A' && element.hasAttribute('href')) { |
|
return (element); |
|
} |
|
|
|
element = (parent_element(element)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export function get_link_info(a, base) { |
|
|
|
let url; |
|
|
|
try { |
|
url = new URL(a instanceof SVGAElement ? a.href.baseVal : a.href, document.baseURI); |
|
} catch {} |
|
|
|
const target = a instanceof SVGAElement ? a.target.baseVal : a.target; |
|
|
|
const external = |
|
!url || |
|
!!target || |
|
is_external_url(url, base) || |
|
(a.getAttribute('rel') || '').split(/\s+/).includes('external'); |
|
|
|
const download = url?.origin === origin && a.hasAttribute('download'); |
|
|
|
return { url, external, target, download }; |
|
} |
|
|
|
|
|
|
|
|
|
export function get_router_options(element) { |
|
|
|
let keepfocus = null; |
|
|
|
|
|
let noscroll = null; |
|
|
|
|
|
let preload_code = null; |
|
|
|
|
|
let preload_data = null; |
|
|
|
|
|
let reload = null; |
|
|
|
|
|
let replace_state = null; |
|
|
|
|
|
let el = element; |
|
|
|
while (el && el !== document.documentElement) { |
|
if (preload_code === null) preload_code = link_option(el, 'preload-code'); |
|
if (preload_data === null) preload_data = link_option(el, 'preload-data'); |
|
if (keepfocus === null) keepfocus = link_option(el, 'keepfocus'); |
|
if (noscroll === null) noscroll = link_option(el, 'noscroll'); |
|
if (reload === null) reload = link_option(el, 'reload'); |
|
if (replace_state === null) replace_state = link_option(el, 'replacestate'); |
|
|
|
el = (parent_element(el)); |
|
} |
|
|
|
|
|
function get_option_state(value) { |
|
switch (value) { |
|
case '': |
|
case 'true': |
|
return true; |
|
case 'off': |
|
case 'false': |
|
return false; |
|
default: |
|
return undefined; |
|
} |
|
} |
|
|
|
return { |
|
preload_code: levels[preload_code ?? 'off'], |
|
preload_data: levels[preload_data ?? 'off'], |
|
keepfocus: get_option_state(keepfocus), |
|
noscroll: get_option_state(noscroll), |
|
reload: get_option_state(reload), |
|
replace_state: get_option_state(replace_state) |
|
}; |
|
} |
|
|
|
|
|
export function notifiable_store(value) { |
|
const store = writable(value); |
|
let ready = true; |
|
|
|
function notify() { |
|
ready = true; |
|
store.update((val) => val); |
|
} |
|
|
|
|
|
function set(new_value) { |
|
ready = false; |
|
store.set(new_value); |
|
} |
|
|
|
|
|
function subscribe(run) { |
|
|
|
let old_value; |
|
return store.subscribe((new_value) => { |
|
if (old_value === undefined || (ready && new_value !== old_value)) { |
|
run((old_value = new_value)); |
|
} |
|
}); |
|
} |
|
|
|
return { notify, set, subscribe }; |
|
} |
|
|
|
export function create_updated_store() { |
|
const { set, subscribe } = writable(false); |
|
|
|
if (DEV || !BROWSER) { |
|
return { |
|
subscribe, |
|
|
|
check: async () => false |
|
}; |
|
} |
|
|
|
const interval = __SVELTEKIT_APP_VERSION_POLL_INTERVAL__; |
|
|
|
|
|
let timeout; |
|
|
|
|
|
async function check() { |
|
clearTimeout(timeout); |
|
|
|
if (interval) timeout = setTimeout(check, interval); |
|
|
|
try { |
|
const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, { |
|
headers: { |
|
pragma: 'no-cache', |
|
'cache-control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!res.ok) { |
|
return false; |
|
} |
|
|
|
const data = await res.json(); |
|
const updated = data.version !== version; |
|
|
|
if (updated) { |
|
set(true); |
|
clearTimeout(timeout); |
|
} |
|
|
|
return updated; |
|
} catch { |
|
return false; |
|
} |
|
} |
|
|
|
if (interval) timeout = setTimeout(check, interval); |
|
|
|
return { |
|
subscribe, |
|
check |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export function is_external_url(url, base) { |
|
return url.origin !== origin || !url.pathname.startsWith(base); |
|
} |
|
|