class ArgumentForm extends window.HTMLElement {
getQueryParams () {
return new URLSearchParams(window.location.search.substring(1))
}
setup (json, callback) {
this.setAttribute('class', 'action-arguments')
this.constructTemplate()
this.domTitle.innerText = json.title
this.domIcon.innerHTML = json.icon
this.createDomFormArguments(json.arguments)
this.domBtnStart.onclick = () => {
for (const arg of this.argInputs) {
if (!arg.validity.valid) {
return
}
}
const argvs = this.getArgumentValues()
callback(argvs)
this.remove()
}
this.domBtnCancel.onclick = () => {
this.clearBookmark()
this.remove()
}
}
getArgumentValues () {
const ret = []
for (const arg of this.argInputs) {
if (arg.type === 'checkbox') {
if (arg.checked) {
arg.value = '1'
} else {
arg.value = '0'
}
}
if (arg.name === "") {
continue
}
ret.push({
name: arg.name,
value: arg.value
})
}
return ret
}
constructTemplate () {
const tpl = document.getElementById('tplArgumentForm')
const content = tpl.content.cloneNode(true)
this.appendChild(content)
this.domTitle = this.querySelector('h2')
this.domIcon = this.querySelector('span.icon')
this.domWrapper = this.querySelector('.wrapper')
this.domArgs = this.querySelector('.arguments')
this.domBtnStart = this.querySelector('[name=start]')
this.domBtnCancel = this.querySelector('[name=cancel]')
}
createDomFormArguments (args) {
this.argInputs = []
for (const arg of args) {
this.domArgs.appendChild(this.createDomLabel(arg))
this.domArgs.appendChild(this.createDomSuggestions(arg))
this.domArgs.appendChild(this.createDomInput(arg))
this.domArgs.appendChild(this.createDomDescription(arg))
}
}
createDomLabel (arg) {
const domLbl = document.createElement('label')
const lastChar = arg.title.charAt(arg.title.length - 1)
if (lastChar === '?' || lastChar === '.' || lastChar === ':') {
domLbl.innerHTML = arg.title
} else {
domLbl.innerHTML = arg.title + ':'
}
domLbl.setAttribute('for', arg.name)
return domLbl
}
createDomSuggestions (arg) {
if (typeof arg.suggestions !== 'object' || arg.suggestions.length === 0) {
return document.createElement('span')
}
const ret = document.createElement('datalist')
ret.setAttribute('id', arg.name + '-choices')
for (const suggestion of Object.keys(arg.suggestions)) {
const opt = document.createElement('option')
opt.setAttribute('value', suggestion)
if (typeof arg.suggestions[suggestion] !== 'undefined' && arg.suggestions[suggestion].length > 0) {
opt.innerText = arg.suggestions[suggestion]
}
ret.appendChild(opt)
}
return ret
}
createDomInput (arg) {
let domEl = null
if (arg.choices.length > 0 && (arg.type === 'select' || arg.type === '')) {
domEl = document.createElement('select')
// select/choice elements don't get an onchange/validation because theoretically
// the user should only select from a dropdown of valid options. The choices are
// riggeriously checked on StartAction anyway. ValidateArgumentType is only
// meant for showing simple warnings in the UI before running.
for (const choice of arg.choices) {
domEl.appendChild(this.createSelectOption(choice))
}
} else {
switch (arg.type) {
case 'html':
domEl = document.createElement('div')
domEl.innerHTML = arg.defaultValue
return domEl
case 'confirmation':
this.domBtnStart.disabled = true
domEl = document.createElement('input')
domEl.setAttribute('type', 'checkbox')
domEl.onchange = () => {
this.domBtnStart.disabled = false
domEl.disabled = true
}
break
case 'raw_string_multiline':
domEl = document.createElement('textarea')
domEl.setAttribute('rows', '5')
domEl.style.resize = 'vertical'
break
case 'datetime':
domEl = document.createElement('input')
domEl.setAttribute('type', 'datetime-local')
domEl.setAttribute('step', '1')
break
case 'checkbox':
domEl = document.createElement('input')
domEl.setAttribute('type', 'checkbox')
domEl.setAttribute('name', arg.name)
domEl.setAttribute('value', '1')
break
case 'password':
case 'email':
domEl = document.createElement('input')
domEl.setAttribute('type', arg.type)
break
default:
domEl = document.createElement('input')
if (arg.type.startsWith('regex:')) {
domEl.setAttribute('pattern', arg.type.replace('regex:', ''))
}
domEl.onchange = () => {
formatValidation(domEl, arg)
}
}
}
domEl.name = arg.name
// Use query parameter value if available
const params = this.getQueryParams()
const paramValue = params.get(arg.name)
if (paramValue !== null) {
domEl.value = paramValue
} else {
domEl.value = arg.defaultValue
}
// update the URL when a parameter is changed
domEl.addEventListener('change', this.updateUrlWithArg)
if (typeof arg.suggestions === 'object' && Object.keys(arg.suggestions).length > 0) {
domEl.setAttribute('list', arg.name + '-choices')
}
this.argInputs.push(domEl)
return domEl
}
async formatValidation (domEl, arg) {
const validateArgumentTypeArgs = {
value: domEl.value,
type: arg.type
}
const validation = await window.validateArgumentType(validateArgumentTypeArgs)
if (validation.valid) {
domEl.setCustomValidity('')
} else {
domEl.setCustomValidity(validation.description)
}
}
updateUrlWithArg (ev) {
if (!ev.target.name) {
return
}
const url = new URL(window.location.href)
if (ev.target.type === 'password') {
return
}
// copy the parameter value
url.searchParams.set(ev.target.name, ev.target.value)
// Update the URL without reloading the page
window.history.replaceState({}, '', url.toString())
}
createDomDescription (arg) {
const domArgumentDescription = document.createElement('span')
domArgumentDescription.classList.add('argument-description')
domArgumentDescription.innerHTML = arg.description
return domArgumentDescription
}
createSelectOption (choice) {
const domEl = document.createElement('option')
domEl.setAttribute('value', choice.value)
domEl.innerText = choice.title
return domEl
}
clearBookmark () {
// remove the action from the URL
window.history.replaceState({
path: window.location.pathname
}, '', window.location.pathname)
}
}
window.customElements.define('argument-form', ArgumentForm)