278 lines
7.5 KiB
Plaintext
278 lines
7.5 KiB
Plaintext
def postJson(url, dataObject)
|
|
fetch `${url}` with method:'POST', body:dataObject as JSON, headers:{content-type:'application/json'}
|
|
return result
|
|
end
|
|
|
|
def getJsonUrlAsObject(url)
|
|
fetch `${url}` with method:'GET', headers:{content-type:'application/json'}
|
|
return result as Object
|
|
end
|
|
|
|
def countdownSeconds(el, sec)
|
|
set i to sec
|
|
set _s to el's textContent
|
|
repeat until i is 0
|
|
put `${_s} (${i}s)` into el
|
|
decrement i by 1
|
|
wait 1s
|
|
end
|
|
end
|
|
|
|
def setCheckboxes(el, option)
|
|
repeat for e in (<input[type='checkbox'].multiselect/> in el)
|
|
if option == 'all'
|
|
set e's @checked to 'true'
|
|
set e.checked to true
|
|
else if option == 'none'
|
|
remove @checked from e
|
|
set e.checked to false
|
|
else if option == 'invert'
|
|
if e.checked
|
|
toggle [@checked='false'] on e
|
|
set e.checked to false
|
|
else
|
|
toggle [@checked='true'] on e
|
|
set e.checked to true
|
|
end
|
|
else if option == 'toggle'
|
|
toggle [@checked='true'] on e
|
|
if e's @checked set e.checked to true else set e.checked to false end
|
|
end
|
|
end
|
|
end
|
|
|
|
behavior trCheckboxSelect
|
|
on click
|
|
document.getSelection().removeAllRanges()
|
|
if not event.shiftKey
|
|
take .select-tr-element from <tr/> in closest <table/> for me
|
|
call setCheckboxes(me, 'toggle') unless event.target.tagName.toLowerCase() === 'a'
|
|
else
|
|
document.getSelection().removeAllRanges()
|
|
get first .select-tr-element in closest <table/>
|
|
if it
|
|
set toggleTo to 'none'
|
|
if checked of first .multiselect in it
|
|
set toggleTo to 'all'
|
|
end
|
|
set selectedTrElement to it
|
|
if it.rowIndex < my.rowIndex
|
|
repeat while selectedTrElement.nextElementSibling
|
|
set selectedTrElement to selectedTrElement.nextElementSibling
|
|
call setCheckboxes(selectedTrElement, toggleTo)
|
|
if selectedTrElement is me
|
|
break
|
|
end
|
|
end
|
|
else
|
|
repeat while selectedTrElement.previousElementSibling
|
|
call setCheckboxes(selectedTrElement, toggleTo)
|
|
if selectedTrElement is me
|
|
break
|
|
end
|
|
set selectedTrElement to selectedTrElement.previousElementSibling
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
behavior buttonCheckHtmxResponse
|
|
on htmx:afterRequest from closest <form/> to me
|
|
if (closest <form/> to me) != (event.target) exit end
|
|
set :_v to my textContent unless :_v
|
|
if event.detail.successful then
|
|
put `👍` into me
|
|
else
|
|
put `🤖 An error occured` into me
|
|
end
|
|
wait 1s
|
|
put :_v into me
|
|
set :_v to null
|
|
end
|
|
end
|
|
|
|
behavior confirmButton
|
|
init set :inner to my.innerHTML end
|
|
on every click
|
|
halt the event
|
|
end
|
|
on click[event.detail==1] from me queue none
|
|
set x to 3
|
|
repeat until x == 0
|
|
put `Confirm ${x}x` into me
|
|
wait for a click or 1500ms
|
|
if the result's type is 'click'
|
|
decrement x
|
|
else
|
|
put :inner into me
|
|
exit
|
|
end
|
|
end
|
|
put :inner into me
|
|
trigger confirmedButton
|
|
end
|
|
end
|
|
|
|
behavior inlineHtmxRename
|
|
init
|
|
set :_textContent to my.textContent
|
|
end
|
|
|
|
on click halt the event end
|
|
|
|
on htmx:afterRequest
|
|
if event.detail.successful == true
|
|
set :_textContent to my.textContent
|
|
end
|
|
set my.textContent to :_textContent
|
|
end
|
|
|
|
on htmx:confirm(issueRequest)
|
|
halt the event
|
|
call confirm(`${:_textContent} to ${my.textContent}?`)
|
|
if not result set my.textContent to :_textContent else issueRequest() end
|
|
end
|
|
|
|
on blur
|
|
if my.textContent == '' set my.textContent to :_textContent then exit end
|
|
if my.textContent == :_textContent exit end
|
|
set @hx-vals to `{"${my @data-patch-parameter}": "${my.textContent}"}`
|
|
trigger editContent on me
|
|
end
|
|
|
|
on keydown[keyCode == 13]
|
|
me.blur()
|
|
halt the event
|
|
end
|
|
end
|
|
|
|
behavior tresorToggle
|
|
def setUnlocked
|
|
get #vault-unlock-pin
|
|
add @disabled to it
|
|
set its @placeholder to 'Tresor is unlocked'
|
|
set #vault-unlock's textContent to '🔓'
|
|
end
|
|
def setLocked
|
|
get #vault-unlock-pin
|
|
remove @disabled from it
|
|
set its @placeholder to 'Tresor password'
|
|
set #vault-unlock's textContent to '🔐'
|
|
end
|
|
def noTresor
|
|
get #vault-unlock-pin
|
|
add @disabled to it
|
|
set its @placeholder to 'No tresor available'
|
|
set #vault-unlock's textContent to '⛔'
|
|
end
|
|
init
|
|
if window.vault.isUnlocked()
|
|
call setUnlocked()
|
|
else
|
|
if #vault-unlock's @data-tresor != ""
|
|
call setLocked()
|
|
else
|
|
call noTresor()
|
|
end
|
|
end
|
|
end
|
|
on profileUpdate from body
|
|
exit unless #vault-unlock's @data-tresor == ""
|
|
set #vault-unlock's @data-tresor to (value of event.detail)
|
|
call setLocked()
|
|
end
|
|
on keydown[keyCode == 13] from #vault-unlock-pin
|
|
trigger click on #vault-unlock unless #vault-unlock-pin's value is empty
|
|
end
|
|
on click from #vault-unlock
|
|
halt the event
|
|
if not window.vault.isUnlocked()
|
|
exit unless value of #vault-unlock-pin
|
|
call JSON.parse(#vault-unlock's @data-tresor) set keyData to the result
|
|
call VaultUnlockPrivateKey(value of #vault-unlock-pin, keyData)
|
|
call setUnlocked()
|
|
else
|
|
call window.vault.lock()
|
|
call setLocked()
|
|
end
|
|
set value of #vault-unlock-pin to ''
|
|
on exception(error)
|
|
trigger notification(
|
|
title: 'Tresor error',
|
|
level: 'validationError',
|
|
message: 'Could not unlock tresor, check your PIN',
|
|
duration: 3000,
|
|
locations: ['vault-unlock-pin']
|
|
)
|
|
end
|
|
end
|
|
|
|
behavior bodydefault
|
|
on htmx:wsError or htmx:wsClose
|
|
set #ws-indicator's textContent to '⭕'
|
|
end
|
|
|
|
on keydown
|
|
exit unless window.vault.isUnlocked()
|
|
if navigator.platform.toUpperCase().indexOf('MAC') >= 0
|
|
set ctrlOrCmd to event.metaKey
|
|
else
|
|
set ctrlOrCmd to event.ctrlKey
|
|
end
|
|
if (event.key is "F5" or (ctrlOrCmd and event.key.toLowerCase() === "r")) or ((ctrlOrCmd and event.shiftKey and event.key.toLowerCase() === "r") or (event.shiftKey and e.key === "F5"))
|
|
trigger notification(
|
|
title: 'Unlocked session',
|
|
level: 'user',
|
|
message: 'Preventing window reload due to unlocked session',
|
|
duration: 2000,
|
|
locations: []
|
|
)
|
|
halt the event
|
|
end
|
|
end
|
|
|
|
on htmx:responseError
|
|
set status to event.detail.xhr.status
|
|
if status >= 500
|
|
trigger notification(title: 'Server error', level: 'error', message: 'The server could not handle the given request', duration: 10000)
|
|
else if status == 404
|
|
trigger notification(title: 'Not found', level: 'error', message: `Route not found: ${event.detail.xhr.responseURL}`, duration: 3000)
|
|
end
|
|
end
|
|
|
|
on htmx:configRequest
|
|
if window.vault.isUnlocked()
|
|
repeat for p in event.detail.parameters
|
|
log p
|
|
end
|
|
end
|
|
end
|
|
|
|
on htmx:beforeRequest
|
|
remove @aria-invalid from <[aria-invalid]/>
|
|
end
|
|
end
|
|
|
|
behavior objectFilters(submitForm)
|
|
on click from <button[name=_filters]/> in me
|
|
halt the event
|
|
put '' into .generated-filters in me
|
|
|
|
repeat for btn in (<button[name=_filters]/> in me)
|
|
if (btn is event.target and btn does not match .active) or (btn is not event.target and btn matches .active)
|
|
render #filter-item with (value: btn's value)
|
|
then put the result at the end of .generated-filters in me
|
|
end
|
|
end
|
|
|
|
if length of <.generated-filters input/> is 0
|
|
render #filter-item with (value: '')
|
|
then put the result at the end of .generated-filters in me
|
|
end
|
|
|
|
trigger submit on submitForm unless submitForm == ''
|
|
end
|
|
end
|