2022-03-21 17:43:24 +13:00
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
// to get started and then uncomment the line below.
// import "./user_socket.js"
// You can include dependencies in two ways.
//
// The simplest option is to put them in assets/vendor and
// import them using relative paths:
//
// import "../vendor/some-package.js"
//
// Alternatively, you can `npm install some-package --prefix assets` and import
// them using a path starting with the package name:
//
// import "some-package"
//
2022-09-08 03:53:36 +12:00
import scrollIntoView from "smooth-scroll-into-view-if-needed" ;
2022-03-21 17:43:24 +13:00
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
2022-09-08 03:53:36 +12:00
import "phoenix_html" ;
2022-03-21 17:43:24 +13:00
// Establish Phoenix Socket and LiveView configuration.
2022-09-08 03:53:36 +12:00
import { Socket } from "phoenix" ;
import { LiveSocket } from "phoenix_live_view" ;
import topbar from "../vendor/topbar" ;
2022-03-26 10:17:01 +13:00
2023-02-07 06:35:52 +13:00
function setCookie ( name , value ) {
2024-04-03 13:24:23 +13:00
document . cookie =
name + "=" + value + ";path=/;" + "expires=Fri, 31 Dec 9999 23:59:59 GMT;" ;
2023-02-07 06:35:52 +13:00
}
2022-09-06 05:47:12 +12:00
2023-02-07 06:35:52 +13:00
function getCookie ( name ) {
2024-04-03 13:24:23 +13:00
const cookie = document . cookie
. split ( "; " )
. find ( ( row ) => row . startsWith ( name + "=" ) ) ;
2023-09-27 19:26:16 +13:00
if ( cookie ) {
2024-04-03 13:24:23 +13:00
return cookie . split ( "=" ) [ 1 ] ;
2023-02-07 06:35:52 +13:00
}
}
function cookiesAreAllowed ( ) {
2024-04-03 13:24:23 +13:00
return getCookie ( "cookieconsent_status" ) === "allow" ;
2022-09-06 05:47:12 +12:00
}
2022-10-26 17:53:18 +13:00
function get _platform ( ) {
2023-09-27 19:26:16 +13:00
// 2022 way of detecting. Note : this userAgentData feature is available only in secure contexts (HTTPS)
2024-04-03 13:24:23 +13:00
if (
typeof navigator . userAgentData !== "undefined" &&
navigator . userAgentData != null
) {
2023-09-27 19:26:16 +13:00
return navigator . userAgentData . platform ;
}
// Deprecated but still works for most of the browser
2024-04-03 13:24:23 +13:00
if ( typeof navigator . platform !== "undefined" ) {
if (
typeof navigator . userAgent !== "undefined" &&
/android/ . test ( navigator . userAgent . toLowerCase ( ) )
) {
2023-09-27 19:26:16 +13:00
// android device's navigator.platform is often set as 'linux', so let's use userAgent for them
2024-04-03 13:24:23 +13:00
return "android" ;
2022-10-26 17:53:18 +13:00
}
2023-09-27 19:26:16 +13:00
return navigator . platform ;
}
2024-04-03 13:24:23 +13:00
return "unknown" ;
2022-10-26 17:53:18 +13:00
}
let platform = get _platform ( ) ;
let isOSX = /mac/ . test ( platform . toLowerCase ( ) ) ; // Mac desktop
2022-03-26 10:17:01 +13:00
const Hooks = { } ;
2023-10-03 16:35:59 +13:00
let selectedTheme ;
2022-09-08 03:53:36 +12:00
if ( cookiesAreAllowed ( ) ) {
2023-10-03 16:35:59 +13:00
selectedTheme = getCookie ( "theme" ) ;
2022-09-06 05:29:38 +12:00
}
2023-10-03 16:35:59 +13:00
selectedTheme = selectedTheme || "system" ;
2022-04-02 11:49:26 +13:00
2023-10-03 16:35:59 +13:00
setTheme ( selectedTheme ) ;
2022-04-02 11:49:26 +13:00
2022-09-08 03:53:36 +12:00
window
. matchMedia ( "(prefers-color-scheme: dark)" )
. addEventListener ( "change" , ( event ) => {
2023-10-03 16:35:59 +13:00
setTheme ( selectedTheme ) ;
2022-09-08 03:53:36 +12:00
} ) ;
2022-04-02 11:49:26 +13:00
2023-10-03 16:35:59 +13:00
function setTheme ( selectedTheme , doSetCookie = false ) {
let newTheme = selectedTheme ;
if ( selectedTheme == "system" ) {
if ( window . matchMedia ( "(prefers-color-scheme: dark)" ) . matches ) {
newTheme = "dark" ;
2022-04-02 11:49:26 +13:00
} else {
2023-10-03 16:35:59 +13:00
newTheme = "light" ;
2022-04-02 11:49:26 +13:00
}
}
2022-08-30 16:25:13 +12:00
2023-10-03 16:35:59 +13:00
document . documentElement . classList . add ( newTheme ) ;
2022-08-30 16:25:13 +12:00
2023-10-03 16:35:59 +13:00
if ( newTheme === "dark" ) {
2022-04-02 11:49:26 +13:00
document . documentElement . classList . remove ( "light" ) ;
} else {
document . documentElement . classList . remove ( "dark" ) ;
2022-09-08 03:53:36 +12:00
}
2022-04-02 11:49:26 +13:00
2023-10-03 16:35:59 +13:00
if ( doSetCookie && cookiesAreAllowed ( ) ) {
setCookie ( "theme" , selectedTheme ) ;
2022-09-06 05:29:38 +12:00
}
2022-04-02 11:49:26 +13:00
}
2022-03-26 10:17:01 +13:00
Hooks . ColorTheme = {
mounted ( ) {
2022-09-08 03:53:36 +12:00
this . handleEvent ( "set_theme" , ( payload ) => {
2023-10-03 16:35:59 +13:00
selectedTheme = payload . theme ;
2022-09-08 03:53:36 +12:00
setTheme ( payload . theme , true ) ;
} ) ;
} ,
} ;
2022-03-26 10:17:01 +13:00
2022-11-16 14:27:24 +13:00
let scrolled = false ;
Hooks . RightNav = {
mounted ( ) {
2024-04-03 13:24:23 +13:00
this . intersectionObserver = new IntersectionObserver (
( entries ) => this . onScrollChange ( entries ) ,
{ rootMargin : "-10% 0px -89% 0px" } ,
) ;
2022-11-16 14:27:24 +13:00
2024-04-03 13:24:23 +13:00
this . observeElements ( ) ;
window . addEventListener ( "hashchange" , ( event ) => {
this . handleHashChange ( ) ;
} ) ;
2022-11-16 14:27:24 +13:00
} ,
updated ( ) {
this . intersectionObserver . disconnect ( ) ;
this . observeElements ( ) ;
} ,
observeElements ( ) {
for ( el of document . querySelectorAll ( "#docs-window .nav-anchor" ) ) {
this . intersectionObserver . observe ( el ) ;
2022-04-01 09:59:53 +13:00
}
2022-11-16 14:27:24 +13:00
} ,
onScrollChange ( entries ) {
// Wait for scrolling from initial page load to complete
2024-04-03 13:24:23 +13:00
if ( ! scrolled ) {
return ;
}
2022-11-16 14:27:24 +13:00
for ( entry of entries ) {
if ( entry . isIntersecting ) {
this . setAriaCurrent ( entry . target . id ) ;
2022-04-01 09:59:53 +13:00
}
}
2022-11-16 14:27:24 +13:00
} ,
handleHashChange ( ) {
if ( window . location . hash ) {
2024-04-03 13:24:23 +13:00
this . setAriaCurrent ( window . location . hash . substring ( 1 ) ) ;
2022-04-01 09:59:53 +13:00
2022-11-16 14:27:24 +13:00
// Disable the insersection observer for 1s while the browser
// scrolls the selected element to the top.
scrolled = false ;
2024-04-03 13:24:23 +13:00
setTimeout ( ( ) => {
scrolled = true ;
} , 1000 ) ;
2022-11-16 14:27:24 +13:00
}
} ,
setAriaCurrent ( id ) {
const el = document . getElementById ( "right-nav-" + id ) ;
2022-09-08 03:53:36 +12:00
if ( el ) {
2024-04-03 13:24:23 +13:00
for ( elem of document . querySelectorAll ( "#right-nav a[aria-current]" ) ) {
elem . removeAttribute ( "aria-current" ) ;
2022-04-01 09:59:53 +13:00
}
2022-11-16 14:27:24 +13:00
el . setAttribute ( "aria-current" , "true" ) ;
2022-04-01 09:59:53 +13:00
}
2023-01-25 17:34:22 +13:00
} ,
} ;
2023-02-07 17:29:23 +13:00
Hooks . TextOverflow = {
2023-01-25 17:34:22 +13:00
mounted ( ) {
this . setTitlesForOverflowingLinks ( this . el ) ;
} ,
updated ( ) {
this . setTitlesForOverflowingLinks ( this . el ) ;
} ,
setTitlesForOverflowingLinks ( selector ) {
2023-02-07 17:29:23 +13:00
for ( elem of selector . querySelectorAll ( "a, span.text-ellipsis" ) ) {
2023-01-25 17:34:22 +13:00
if ( elem . offsetWidth < elem . scrollWidth ) {
elem . setAttribute ( "title" , elem . innerHTML . trim ( ) ) ;
}
}
} ,
} ;
2022-04-01 09:59:53 +13:00
2023-02-07 17:29:23 +13:00
Hooks . TableOfContentsTextOverflow = Hooks . TextOverflow ;
2022-09-08 03:53:36 +12:00
let csrfToken = document
. querySelector ( "meta[name='csrf-token']" )
. getAttribute ( "content" ) ;
2022-03-26 10:17:01 +13:00
let liveSocket = new LiveSocket ( "/live" , Socket , {
2022-11-16 14:27:24 +13:00
params : { _csrf _token : csrfToken , user _agent : window . navigator . userAgent } ,
2022-03-26 10:17:01 +13:00
hooks : Hooks ,
metadata : {
keydown : ( e ) => {
return {
key : e . key ,
2022-09-08 03:53:36 +12:00
metaKey : e . metaKey ,
} ;
} ,
} ,
2022-03-26 10:17:01 +13:00
} ) ;
2022-03-21 17:43:24 +13:00
2022-03-29 11:05:19 +13:00
// Show progress bar on live navigation and form submits. Only displays if still
// loading after 120 msec
2022-09-08 03:53:36 +12:00
topbar . config ( { barColors : { 0 : "#29d" } , shadowColor : "rgba(0, 0, 0, .3)" } ) ;
2022-03-29 11:05:19 +13:00
let topBarScheduled = undefined ;
2023-02-02 18:51:54 +13:00
window . addEventListener ( "phx:page-loading-start" , ( { detail } ) => {
2022-09-13 05:44:52 +12:00
scrolled = false ;
2022-10-26 17:28:25 +13:00
// close mobile sidebar on navigation
2024-04-03 13:24:23 +13:00
mobileSideBar = document . getElementById ( "mobile-sidebar-hide" ) ;
2022-11-16 14:27:24 +13:00
if ( mobileSideBar ) {
2024-04-03 13:24:23 +13:00
mobileSideBar . click ( ) ;
2022-10-26 17:28:25 +13:00
}
2023-02-06 15:30:29 +13:00
if ( ! topBarScheduled ) {
topBarScheduled = setTimeout ( ( ) => topbar . show ( ) , 120 ) ;
2022-09-08 03:53:36 +12:00
}
2022-03-29 11:05:19 +13:00
} ) ;
2022-09-13 05:44:52 +12:00
2022-11-16 14:27:24 +13:00
window . addEventListener ( "phx:page-loading-stop" , ( { detail } ) => {
2022-03-29 11:05:19 +13:00
clearTimeout ( topBarScheduled ) ;
2023-02-02 18:51:54 +13:00
topbar . hide ( ) ;
2022-03-29 11:05:19 +13:00
topBarScheduled = undefined ;
2022-11-16 14:27:24 +13:00
let scrollEl ;
if ( detail . kind === "initial" && window . location . hash ) {
scrollEl = document . getElementById ( window . location . hash . substring ( 1 ) ) ;
} else if ( detail . kind == "patch" && ! window . location . hash ) {
2024-04-03 13:24:23 +13:00
scrollEl =
document . querySelector ( "#docs-window .nav-anchor" ) ||
document . querySelector ( "#docs-window h1" ) ;
2022-11-16 14:27:24 +13:00
}
if ( scrollEl ) {
Hooks . RightNav . setAriaCurrent ( scrollEl . id ) ;
// Not using scroll polyfill here - doesn't respect scroll-padding-top CSS
2024-04-03 13:24:23 +13:00
scrollEl . scrollIntoView ( { block : "start" } ) ;
setTimeout ( ( ) => {
scrolled = true ;
} , 1000 ) ;
2022-11-16 14:27:24 +13:00
} else {
scrolled = true ;
2022-09-13 05:44:52 +12:00
}
2022-03-29 11:05:19 +13:00
} ) ;
2022-09-08 03:53:36 +12:00
window . addEventListener ( "js:focus" , ( e ) => e . target . focus ( ) ) ;
2022-03-21 17:43:24 +13:00
2022-03-26 16:16:20 +13:00
window . addEventListener ( "phx:js:scroll-to" , ( e ) => {
const target = document . getElementById ( e . detail . id ) ;
2022-11-16 14:27:24 +13:00
const boundary = target . closest ( ".scroll-parent" ) ;
2022-09-08 03:53:36 +12:00
scrollIntoView ( target , {
behavior : "smooth" ,
2022-09-13 05:44:52 +12:00
block : "start" ,
2022-09-08 03:53:36 +12:00
boundary : boundary ,
2022-03-28 10:26:35 +13:00
} ) ;
2022-03-26 16:16:20 +13:00
} ) ;
2022-03-30 18:07:17 +13:00
window . addEventListener ( "keydown" , ( event ) => {
2022-09-08 03:53:36 +12:00
if ( ( event . metaKey || event . ctrlKey ) && event . key === "k" ) {
document . getElementById ( "search-button" ) . click ( ) ;
2022-04-02 08:11:17 +13:00
event . preventDefault ( ) ;
2022-03-30 18:07:17 +13:00
}
2022-09-08 03:53:36 +12:00
} ) ;
2022-03-30 18:07:17 +13:00
window . addEventListener ( "keydown" , ( event ) => {
2022-09-08 03:53:36 +12:00
if ( event . key === "Escape" ) {
2024-04-03 13:24:23 +13:00
const closeSearchVersions = document . getElementById (
"close-search-versions" ,
) ;
2022-11-16 14:27:24 +13:00
if ( closeSearchVersions && closeSearchVersions . offsetParent !== null ) {
2024-04-03 13:24:23 +13:00
closeSearchVersions . click ( ) ;
2022-09-28 16:18:05 +13:00
} else {
document . getElementById ( "close-search" ) . click ( ) ;
}
2022-04-02 08:11:17 +13:00
event . preventDefault ( ) ;
2022-03-30 18:07:17 +13:00
}
2022-09-08 03:53:36 +12:00
} ) ;
2022-11-26 20:53:55 +13:00
window . addEventListener ( "phx:click-on-item" , ( event ) => {
document . getElementById ( event . detail . id ) . click ( ) ;
document . getElementById ( "close-search" ) . click ( ) ;
event . preventDefault ( ) ;
} ) ;
2022-04-02 08:11:17 +13:00
window . addEventListener ( "phx:close-search" , ( event ) => {
2022-09-08 03:53:36 +12:00
document . getElementById ( "close-search" ) . click ( ) ;
2022-04-02 08:11:17 +13:00
event . preventDefault ( ) ;
2022-09-08 03:53:36 +12:00
} ) ;
2022-03-30 18:07:17 +13:00
2022-03-21 17:43:24 +13:00
// connect if there are any LiveViews on the page
2022-09-08 03:53:36 +12:00
liveSocket . connect ( ) ;
2022-03-21 17:43:24 +13:00
// expose liveSocket on window for web console debug logs and latency simulation:
2022-09-13 12:35:59 +12:00
// liveSocket.disableDebug();
2022-03-21 17:43:24 +13:00
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
2022-09-08 03:53:36 +12:00
window . liveSocket = liveSocket ;
2023-01-30 16:21:50 +13:00
2024-04-03 13:24:23 +13:00
window . addEventListener ( "load" , function ( ) {
2023-01-30 16:21:50 +13:00
window . cookieconsent . initialise ( {
content : {
message :
"Hi, this website uses essential cookies for remembering your explicitly set preferences, if you opt-in. We do not use google analytics, but we do use plausible.io, an ethical google analytics alternative which does not use any cookies and collects no personal data." ,
link : null ,
} ,
type : "opt-in" ,
palette : {
popup : {
background : "#000" ,
} ,
button : {
background : "#f1d600" ,
} ,
} ,
} ) ;
} ) ;