ArgumentForm.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. class ArgumentForm extends window.HTMLElement {
  2. getQueryParams () {
  3. return new URLSearchParams(window.location.search.substring(1))
  4. }
  5. setup (json, callback) {
  6. this.setAttribute('class', 'action-arguments')
  7. this.constructTemplate()
  8. this.domTitle.innerText = json.title
  9. this.domIcon.innerHTML = json.icon
  10. this.createDomFormArguments(json.arguments)
  11. this.domBtnStart.onclick = () => {
  12. for (const arg of this.argInputs) {
  13. if (!arg.validity.valid) {
  14. return
  15. }
  16. }
  17. const argvs = this.getArgumentValues()
  18. callback(argvs)
  19. this.remove()
  20. }
  21. this.domBtnCancel.onclick = () => {
  22. this.clearBookmark()
  23. this.remove()
  24. }
  25. }
  26. getArgumentValues () {
  27. const ret = []
  28. for (const arg of this.argInputs) {
  29. if (arg.type === 'checkbox') {
  30. if (arg.checked) {
  31. arg.value = '1'
  32. } else {
  33. arg.value = '0'
  34. }
  35. }
  36. if (arg.name === '') {
  37. continue
  38. }
  39. ret.push({
  40. name: arg.name,
  41. value: arg.value
  42. })
  43. }
  44. return ret
  45. }
  46. constructTemplate () {
  47. const tpl = document.getElementById('tplArgumentForm')
  48. const content = tpl.content.cloneNode(true)
  49. this.appendChild(content)
  50. this.domTitle = this.querySelector('h2')
  51. this.domIcon = this.querySelector('span.icon')
  52. this.domWrapper = this.querySelector('.wrapper')
  53. this.domArgs = this.querySelector('.arguments')
  54. this.domBtnStart = this.querySelector('[name=start]')
  55. this.domBtnCancel = this.querySelector('[name=cancel]')
  56. }
  57. createDomFormArguments (args) {
  58. this.argInputs = []
  59. for (const arg of args) {
  60. this.domArgs.appendChild(this.createDomLabel(arg))
  61. this.domArgs.appendChild(this.createDomSuggestions(arg))
  62. this.domArgs.appendChild(this.createDomInput(arg))
  63. this.domArgs.appendChild(this.createDomDescription(arg))
  64. }
  65. }
  66. createDomLabel (arg) {
  67. const domLbl = document.createElement('label')
  68. const lastChar = arg.title.charAt(arg.title.length - 1)
  69. if (lastChar === '?' || lastChar === '.' || lastChar === ':') {
  70. domLbl.innerHTML = arg.title
  71. } else {
  72. domLbl.innerHTML = arg.title + ':'
  73. }
  74. domLbl.setAttribute('for', arg.name)
  75. return domLbl
  76. }
  77. createDomSuggestions (arg) {
  78. if (typeof arg.suggestions !== 'object' || arg.suggestions.length === 0) {
  79. return document.createElement('span')
  80. }
  81. const ret = document.createElement('datalist')
  82. ret.setAttribute('id', arg.name + '-choices')
  83. for (const suggestion of Object.keys(arg.suggestions)) {
  84. const opt = document.createElement('option')
  85. opt.setAttribute('value', suggestion)
  86. if (typeof arg.suggestions[suggestion] !== 'undefined' && arg.suggestions[suggestion].length > 0) {
  87. opt.innerText = arg.suggestions[suggestion]
  88. }
  89. ret.appendChild(opt)
  90. }
  91. return ret
  92. }
  93. createDomInput (arg) {
  94. let domEl = null
  95. if (arg.choices.length > 0 && (arg.type === 'select' || arg.type === '')) {
  96. domEl = document.createElement('select')
  97. // select/choice elements don't get an onchange/validation because theoretically
  98. // the user should only select from a dropdown of valid options. The choices are
  99. // riggeriously checked on StartAction anyway. ValidateArgumentType is only
  100. // meant for showing simple warnings in the UI before running.
  101. for (const choice of arg.choices) {
  102. domEl.appendChild(this.createSelectOption(choice))
  103. }
  104. } else {
  105. switch (arg.type) {
  106. case 'html':
  107. domEl = document.createElement('div')
  108. domEl.innerHTML = arg.defaultValue
  109. return domEl
  110. case 'confirmation':
  111. this.domBtnStart.disabled = true
  112. domEl = document.createElement('input')
  113. domEl.setAttribute('type', 'checkbox')
  114. domEl.onchange = () => {
  115. this.domBtnStart.disabled = false
  116. domEl.disabled = true
  117. }
  118. break
  119. case 'raw_string_multiline':
  120. domEl = document.createElement('textarea')
  121. domEl.setAttribute('rows', '5')
  122. domEl.style.resize = 'vertical'
  123. break
  124. case 'datetime':
  125. domEl = document.createElement('input')
  126. domEl.setAttribute('type', 'datetime-local')
  127. domEl.setAttribute('step', '1')
  128. break
  129. case 'checkbox':
  130. domEl = document.createElement('input')
  131. domEl.setAttribute('type', 'checkbox')
  132. domEl.setAttribute('name', arg.name)
  133. domEl.setAttribute('value', '1')
  134. break
  135. case 'password':
  136. case 'email':
  137. domEl = document.createElement('input')
  138. domEl.setAttribute('type', arg.type)
  139. break
  140. default:
  141. domEl = document.createElement('input')
  142. if (arg.type.startsWith('regex:')) {
  143. domEl.setAttribute('pattern', arg.type.replace('regex:', ''))
  144. }
  145. domEl.onchange = () => {
  146. const validateArgumentTypeArgs = {
  147. value: domEl.value,
  148. type: arg.type
  149. }
  150. window.fetch(window.restBaseUrl + 'ValidateArgumentType', {
  151. method: 'POST',
  152. headers: {
  153. 'Content-Type': 'application/json'
  154. },
  155. body: JSON.stringify(validateArgumentTypeArgs)
  156. }).then((res) => {
  157. if (res.ok) {
  158. return res.json()
  159. } else {
  160. throw new Error(res.statusText)
  161. }
  162. }).then((json) => {
  163. if (json.valid) {
  164. domEl.setCustomValidity('')
  165. } else {
  166. domEl.setCustomValidity(json.description)
  167. }
  168. })
  169. }
  170. }
  171. }
  172. domEl.name = arg.name
  173. // Use query parameter value if available
  174. const params = this.getQueryParams()
  175. const paramValue = params.get(arg.name)
  176. if (paramValue !== null) {
  177. domEl.value = paramValue
  178. } else {
  179. domEl.value = arg.defaultValue
  180. }
  181. // update the URL when a parameter is changed
  182. domEl.addEventListener('change', this.updateUrlWithArg)
  183. if (typeof arg.suggestions === 'object' && Object.keys(arg.suggestions).length > 0) {
  184. domEl.setAttribute('list', arg.name + '-choices')
  185. }
  186. this.argInputs.push(domEl)
  187. return domEl
  188. }
  189. updateUrlWithArg (ev) {
  190. if (!ev.target.name) {
  191. return
  192. }
  193. const url = new URL(window.location.href)
  194. if (ev.target.type === 'password') {
  195. return
  196. }
  197. // copy the parameter value
  198. url.searchParams.set(ev.target.name, ev.target.value)
  199. // Update the URL without reloading the page
  200. window.history.replaceState({}, '', url.toString())
  201. }
  202. createDomDescription (arg) {
  203. const domArgumentDescription = document.createElement('span')
  204. domArgumentDescription.classList.add('argument-description')
  205. domArgumentDescription.innerHTML = arg.description
  206. return domArgumentDescription
  207. }
  208. createSelectOption (choice) {
  209. const domEl = document.createElement('option')
  210. domEl.setAttribute('value', choice.value)
  211. domEl.innerText = choice.title
  212. return domEl
  213. }
  214. clearBookmark () {
  215. // remove the action from the URL
  216. window.history.replaceState({
  217. path: window.location.pathname
  218. }, '', window.location.pathname)
  219. }
  220. }
  221. window.customElements.define('argument-form', ArgumentForm)