class CCPMethodEditorController extends HTMLElement{ #boot; #rootdoc; #serviceurl = "https://nubis1.int.d4science.net:8080" #runtimes; #locked = false; #tmp_inputs = [] #tmp_outputs = [] #current = null; #method_template = { id : "", title : "New Method", description : "New empty method", version : "1.0.0", jobControlOptions : "async-execute", inputs : {}, outputs : {}, additionalParameters : { parameters : [ { name : "deploy-script", value : [ ` - hosts: localhost tasks: - name: hookup instance add_host: name: "{ instancename }" ansible_connection: lxd groupname: "targets" ` ] }, { name : "execute-script", value : [ ` - hosts: localhost tasks: - name: hookup instance add_host: name: "{ instancename }" ansible_connection: lxd groupname: "targets" ` ] }, { name : "fetch-output-script", value : [ ` - hosts: localhost tasks: - name: hookup instance add_host: name: "{ instancename }" ansible_connection: lxd groupname: "targets" ` ] }, { name : "undeploy-script", value : [ ` - hosts: localhost tasks: - name: hookup instance add_host: name: "{ instancename }" ansible_connection: lxd groupname: "targets" ` ] }, { name : "cancel-script", value : [ ` - hosts: localhost tasks: - name: hookup instance add_host: name: "{ instancename }" ansible_connection: lxd groupname: "targets" ` ] } ] }, links : [] } #style = ` ` #erase_icon = ` ` #plus_icon = ` ` #disc_icon = ` ` #output_icon = ` ` #ansible_icon = ` ` constructor(){ super(); this.#boot = document.querySelector("d4s-boot-2") this.#rootdoc = this.attachShadow({ "mode" : "open"}) this.fetchRuntimes() } connectedCallback(){ } fetchRuntimes(){ this.#boot.secureFetch(this.#serviceurl + "/runtimes"). then(resp=>{ if(resp.status !== 200) throw "Unable to fetch runtimes"; return resp.json() }).then(data=>{ this.#runtimes = data this.render() }).catch(err=>{ alert(err) }) } saveCurrent(){ if(this.#current != null){ this.adoptTemporaries() console.log(this.#current) } } cloneMethod(method){ if(this.#locked) return; this.lockRender() this.#boot.secureFetch(this.#serviceurl + "/processes/" + method).then( (resp)=>{ if(resp.status === 200){ return resp.json() }else throw "Error retrieving process" } ).then(data=>{ this.#current = data this.#current.id = "" this.#tmp_inputs = Object.keys(this.#current.inputs).map(k=>this.#current.inputs[k]) this.#tmp_outputs = Object.keys(this.#current.outputs).map(k=>this.#current.outputs[k]) this.unlockRender() }).catch(err=>{ this.unlockRender() }) } adoptTemporaries(){ this.#current.inputs = {} this.#tmp_inputs.forEach(t=>{ this.#current.inputs[t.id] = t }) this.#current.outputs = {} this.#tmp_outputs.forEach(t=>{ this.#current.outputs[t.id] = t }) } getInputAt(i){ return this.#tmp_inputs[i] } deleteTmpInputAt(i){ this.#tmp_inputs.splice(i,1) } deleteTmpOutputAt(i){ this.#tmp_outputs.splice(i,1) } lockRender(){ this.#locked = true this.#rootdoc.querySelector(".plexiglass").classList.toggle("d-none") } unlockRender(){ this.#rootdoc.querySelector(".plexiglass").classList.toggle("d-none") this.render() this.#locked = false } render(){ if(this.#current == null){ this.#current = JSON.parse(JSON.stringify(this.#method_template)) this.#current.id = Math.random(10e6)|0 } this.#rootdoc.innerHTML = `
${this.#style}
${this.#current.title} ${this.renderSaveButton()} ${this.renderResetButton()}
${this.renderKeywords()}
${this.renderRuntimes()}
Inputs ${this.renderPlusButton("add-input")}
Outputs ${this.renderStandardOutputButtons()} ${this.renderPlusButton("add-output")}
Scripts ${ this.renderAnsibleIcon() }
${this.renderScripts()}
` this.renderInputs() this.renderOutputs() this.#rootdoc.addEventListener("drop", ev=>{ if(ev.dataTransfer && ev.dataTransfer.getData('text/plain+ccpmethod')){ const id = ev.dataTransfer.getData('text/plain+ccpmethod') ev.preventDefault() ev.stopPropagation() this.cloneMethod(id) } }) this.#rootdoc.addEventListener("dragover", ev=>{ ev.preventDefault() }) this.#rootdoc.querySelector("button[name=reset]").addEventListener("click", ev=>{ this.#current = this.#method_template this.#tmp_inputs = [] this.#tmp_outputs = [] this.render() ev.preventDefault() ev.stopPropagation() }) this.#rootdoc.querySelector("button[name=save]").addEventListener("click", ev=>{ ev.preventDefault() ev.stopPropagation() this.saveCurrent() }) this.#rootdoc.querySelector("input[name=keyword-input]").addEventListener("keypress", ev=>{ if(ev.key === "Enter" || ev.which === 13){ ev.preventDefault() ev.stopPropagation() const val = ev.target.value ev.target.value = null if(!this.#current.keywords){ this.#current.keywords = [val] }else{ this.#current.keywords.push(val) } this.reRenderKeywords() } }) this.#rootdoc.querySelector("div[name=keyword-list]").addEventListener("click", ev=>{ ev.preventDefault() ev.stopPropagation() if(ev.target.getAttribute('name') === "delete-keyword"){ const index = ev.target.getAttribute("data-index") this.#current.keywords.splice(index, 1) this.reRenderKeywords() this.#rootdoc.querySelector("input[name=keyword-input]").focus() } }) this.#rootdoc.querySelector("div[name=runtimes]").addEventListener("change", ev=>{ if(ev.target.getAttribute("name") === "runtime-input" && ev.target.value){ ev.preventDefault() ev.stopPropagation() const id = ev.target.value const display = ev.target.options[ev.target.selectedIndex].text let link = { rel : "compatibleWith", title : display, href : "runtimes/" + id } if(!this.#current.links){ this.#current.links = [link] }else{ this.#current.links.push(link) } this.reRenderRuntimes(ev.currentTarget) } }) this.#rootdoc.querySelector("div[name=runtime-list]").addEventListener("click", ev=>{ ev.preventDefault() ev.stopPropagation() if(ev.target.getAttribute('name') === "delete-runtime"){ const index = ev.target.getAttribute("data-index") this.#current.links.splice(index, 1) this.reRenderRuntimes() } }) this.#rootdoc.querySelector("button[name=add-input]").addEventListener("click", ev=>{ ev.preventDefault() ev.stopPropagation() this.#tmp_inputs.push( { id : "new_input", title : "New input", description : "A new input field", minOccurs : 1, maxOccurs : 1, schema : { type : "string", format : null, contentMediaType : null, default : "" } } ) this.renderInputs() }) this.#rootdoc.querySelector("div[name=input-list]").addEventListener("click", ev=>{ const evname = ev.target.getAttribute('name') if(evname === "delete-input"){ ev.preventDefault() ev.stopPropagation() const index = Number(ev.target.getAttribute("data-index")) console.log("deleting input at index", index) this.deleteTmpInputAt(index) this.renderInputs() } }) this.#rootdoc.querySelector("button[name=add-output]").addEventListener("click", ev=>{ ev.preventDefault() ev.stopPropagation() this.#tmp_outputs.push( { id : "new_output", title : "New ouput", description : "A new output field", minOccurs : 1, maxOccurs : 1, schema : { type : "string", contentMediaType : null } } ) this.renderOutputs() }) this.#rootdoc.querySelector("button[name=add-stdout]").addEventListener("click", ev=>{ ev.preventDefault() ev.stopPropagation() this.#tmp_outputs.push( { id : "stdout", title : "Standard output", description : "Standard output channel", minOccurs : 1, maxOccurs : 1, schema : { type : "string", contentMediaType : "text/plain" } } ) this.renderOutputs() }) this.#rootdoc.querySelector("button[name=add-stderr]").addEventListener("click", ev=>{ ev.preventDefault() ev.stopPropagation() this.#tmp_outputs.push( { id : "stderr", title : "Standard error", description : "Standard error channel", minOccurs : 1, maxOccurs : 1, schema : { type : "string", contentMediaType : "text/plain" } } ) this.renderOutputs() }) this.#rootdoc.querySelector("div[name=output-list]").addEventListener("click", ev=>{ const evname = ev.target.getAttribute('name') if(evname === "delete-output"){ ev.preventDefault() ev.stopPropagation() const index = Number(ev.target.getAttribute("data-index")) console.log("deleting output at index", index) this.deleteTmpOutputAt(index) this.renderOutputs() } }) this.#rootdoc.querySelector("select[name=script-selector]").addEventListener("change", ev=>{ ev.preventDefault() ev.stopPropagation() const scriptname = ev.target.value const areas = Array.prototype.slice.call(this.#rootdoc.querySelectorAll("textarea.script-area")) areas.forEach(a=>{ if(a.getAttribute("name") === scriptname) a.classList.remove("d-none"); else a.classList.add("d-none") }) }) this.#rootdoc.querySelector("details[name=script-list]").addEventListener("input", ev=>{ ev.preventDefault() ev.stopPropagation() const scriptname = ev.target.getAttribute("name") const script = this.#current.additionalParameters.parameters.filter(p=>p.name === scriptname)[0] if(script) script.value[0] = ev.target.value; }) } renderAnsibleIcon(){ return `
${ this.#ansible_icon }
` } renderSaveButton(){ return ` ` } renderResetButton(){ return ` ` } renderPlusButton(name){ return ` ` } renderStandardOutputButtons(){ return ` ` } reRenderKeywords(){ this.#rootdoc.querySelector("div[name=keyword-list]").innerHTML = this.renderKeywords() } renderKeywords(){ if(this.#current.keywords){ return this.#current.keywords.map((k,i) => { return `
${k} x
` }).join("\n") }else{ return "" } } reRenderRuntimes(){ this.#rootdoc.querySelector("select[name=runtime-input]").innerHTML = this.renderRuntimeOptions() this.#rootdoc.querySelector("div[name=runtime-list]").innerHTML = this.renderRuntimes() } renderRuntimeOptions(){ const selectedrts = this.#current.links.filter(l=>l.rel === "compatibleWith").map(r=>r.href.split("/")[1]) const available = this.#runtimes.filter(r=>{ return selectedrts.indexOf(r.id) === -1 }) return ` ${ available.map(rt=>{ return ` ` }).join("\n") } ` } renderRuntimes(){ return this.#current.links.filter(l=>l.rel === "compatibleWith").map((l, i)=>{ return `
${l.title} x
` }).join("\n") } renderInputs(){ const parent = this.#rootdoc.querySelector("div[name=input-list]") parent.innerHTML = "" return this.#tmp_inputs.map((inp, i)=>{ const c = document.createElement("d4s-ccp-input-editor"); parent.appendChild(c) c.render(inp, i) }) } renderOutputs(){ const parent = this.#rootdoc.querySelector("div[name=output-list]") parent.innerHTML = "" return this.#tmp_outputs.map((outp, i)=>{ const c = document.createElement("d4s-ccp-output-editor"); parent.appendChild(c) c.render(outp, i) }) } renderScripts(){ return this.#current.additionalParameters.parameters.map( (script, i) => `` ).join("\n") } } window.customElements.define('d4s-ccp-methodeditor', CCPMethodEditorController);