removed version 2 of some methodeditor and cleared reference to cache

This commit is contained in:
dcore94 2023-03-29 12:32:20 +02:00
parent 76d527215b
commit 1447716a8c
5 changed files with 71 additions and 1105 deletions

View File

@ -1,59 +0,0 @@
<div>
<link rel="stylesheet" href="https://cdn.dev.d4science.org/ccp/css/common.css"></link>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
</style>
<template id="EXECUTION_FORM_TEMPLATE">
<div class="ccp-execution-form" name="execution_form">
<h5 class="ccp-method-title"></h5>
<p class="description font-italic font-weight-lighter"></p>
<div name="plexiglass" class="ccp-invisible">
<span name="status"></span>
<span class="fas fa-spinner fa-spin"></span>
</div>
<form name="execution_form" class="d-flex flex-column gap-3" style="gap:5px">
<div class="card">
<div class="card-header">
<h5>Inputs</h5>
</div>
<div class="card-body ccp-inputs">
<div class="card-body">
<div class="form-group"></div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5>Outputs</h5>
</div>
<div class="card-body">
<div class="form-row ccp-outputs">
<div class="col form-group"></div>
</div>
</div>
</div>
<!--div class="card">
<div class="card-header">
<h5>Runtimes <span alt="refresh" title="refresh" style="cursor:pointer" name="refresh-runtimes" class="text-info">&#8634;</span></h5>
</div>
<div class="card-body">
<div class="ccp-runtimes">
<div class="form-row">
<select class="form-control">
</select>
</div>
</div>
</div>
</div-->
<button id="execute_method_button" class="btn btn-info">Execute</button>
</form>
</div>
</template>
<template id="EXECUTION_FORM_EMPTY_TEMPLATE">
<div name="execution_form">
<i style="padding:3rem">Select a method</i>
</div>
</template>
<div name="execution_form"></div>
</div>

View File

@ -50,7 +50,7 @@ class CCPInfrastructureList extends HTMLElement{
fetchInfrastructures(){
const prom1 = this.#boot.secureFetch(this.#serviceurl + "/infrastructures").
then(resp=>{
if(resp.status !== 200) throw "Unable to fetch infrastructure cache";
if(resp.status !== 200) throw "Unable to fetch infrastructures";
return resp.json()
}).then(data=>{
this.#infrastructures = data
@ -152,7 +152,7 @@ class CCPInfrastructureList extends HTMLElement{
}else{
this.#socket.send("ping")
}
}, 30000)
}, 30000)
}
}

View File

@ -4,7 +4,7 @@ class CCPMethodEditorController extends HTMLElement{
#rootdoc;
#serviceurl;
#runtimes;
#infrastructures;
#locked = false;
#isupdate = false;
@ -55,44 +55,20 @@ class CCPMethodEditorController extends HTMLElement{
position: relative;
}
.ccp-toolbar-header {
display: inline-flex;
align-items: center;
gap: 5px;
padding: .2rem;
}
.ccp-toolbar-right {
position:absolute;
right: 1rem;
}
.ccp-toolbar-button {
font-weight: bold;
padding:.3rem;
line-height:.8rem;
cursor: pointer;
}
.ccp-toolbar-button svg {
fill: white;
width:24px;
height:24px;
pointer-events: none;
}
.ccp-option {
background-color:#eeffff;
color:#0099CC;
border:solid 1px #0099CC;
display: inline-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 1rem;
font-size: 1rem;
}
.plexiglass{
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,.3);
z-index: 10;
.ccp-option > .btn {
padding: 0;
}
ul.author_list, ul.context_list{
@ -162,30 +138,31 @@ class CCPMethodEditorController extends HTMLElement{
this.#rootdoc = this.attachShadow({ "mode" : "open"})
this.#serviceurl = this.getAttribute("serviceurl")
this.initMethod()
this.fetchRuntimes()
this.fetchInfrastructures()
this.connectNewEditRequest()
}
connectedCallback(){
}
isRuntimeRegistered(rt){
return (this.#runtimes.filter(r=>r.id === rt.id)).length > 0
connectNewEditRequest(){
document.addEventListener("neweditrequest", ev=>{
this.cloneOrEditMethod(ev.detail)
})
}
fetchRuntimes(){
this.#boot.secureFetch(this.#serviceurl + "/infrastructures/cache").
isInfrastructureRegistered(infra){
return (this.#infrastructures.filter(i=>i.id === infra.id)).length > 0
}
fetchInfrastructures(){
this.#boot.secureFetch(this.#serviceurl + "/infrastructures").
then(resp=>{
if(resp.status !== 200) throw "Unable to fetch runtimes";
if(resp.status !== 200) throw "Unable to fetch infrastructures";
return resp.json()
}).then(data=>{
this.#runtimes = []
//merge all runtimes
data.forEach( i => {
i.available_runtimes.forEach(r => {
if(!this.isRuntimeRegistered(r)){ this.#runtimes.push(r)}
})
})
this.#infrastructures = data
this.render()
}).catch(err=>{
alert(err)
@ -252,7 +229,22 @@ class CCPMethodEditorController extends HTMLElement{
this.#current = JSON.parse(JSON.stringify(this.#method_template))
this.#current.id = Math.abs((Math.random() * 10e11)|0)
this.#current.metadata = []
this.#tmp_inputs = []
this.#tmp_inputs = [
{
id : "ccpimage",
title : "Runtime",
description : "The image of the runtime to use for method execution. This depends on the infrastructure specific protocol for interacting with registries.",
minOccurs : 1,
maxOccurs : 1,
schema : {
type : "string",
format : "url",
contentMediaType : "text/plain",
default : "",
readonly : true,
}
}
]
this.#tmp_outputs = []
}
@ -344,6 +336,7 @@ class CCPMethodEditorController extends HTMLElement{
this.#rootdoc.querySelector(".plexiglass").classList.toggle("d-none")
this.render()
this.#locked = false
this.parentElement.scrollIntoViewIfNeeded()
}
render(){
@ -411,13 +404,13 @@ class CCPMethodEditorController extends HTMLElement{
${this.renderKeywords()}
</div>
</div>
<div name="runtimes" class="mb-3">
<label class="form-label">Compatible Runtimes</label>
<select name="runtime-input" class="form-control">
${this.renderRuntimeOptions()}
<div name="infrastructures" class="mb-3">
<label class="form-label">Compatible Infrastrucures</label>
<select name="infrastructure-input" class="form-control">
${this.renderInfrastructureOptions()}
</select>
<div name="runtime-list" class="form-text">
${this.renderRuntimes()}
<div name="infrastructure-list" class="form-text">
${this.renderInfrastructures()}
</div>
</div>
</div>
@ -458,7 +451,6 @@ class CCPMethodEditorController extends HTMLElement{
<div>
<span>Scripts</span>
<select name="script-selector" style="border:none; padding:.3rem">
<option value=""></option>
<option value="deploy-script">Deploy</option>
<option value="execute-script">Execute</option>
<option value="undeploy-script">Undeploy</option>
@ -567,31 +559,31 @@ class CCPMethodEditorController extends HTMLElement{
}
})
this.#rootdoc.querySelector("div[name=runtimes]").addEventListener("change", ev=>{
if(ev.target.getAttribute("name") === "runtime-input" && ev.target.value){
this.#rootdoc.querySelector("div[name=infrastructures]").addEventListener("change", ev=>{
if(ev.target.getAttribute("name") === "infrastructure-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
rel : "compatibleWith", title : display, href : "infrastructures/" + id
}
if(!this.#current.links){
this.#current.links = [link]
}else{
this.#current.links.push(link)
}
this.reRenderRuntimes(ev.currentTarget)
this.reRenderInfrastructures(ev.currentTarget)
}
})
this.#rootdoc.querySelector("div[name=runtime-list]").addEventListener("click", ev=>{
this.#rootdoc.querySelector("div[name=infrastructure-list]").addEventListener("click", ev=>{
ev.preventDefault()
ev.stopPropagation()
if(ev.target.getAttribute('name') === "delete-runtime"){
if(ev.target.getAttribute('name') === "delete-infrastructure"){
const index = ev.target.getAttribute("data-index")
this.#current.links.splice(index, 1)
this.reRenderRuntimes()
this.reRenderInfrastructures()
}
})
@ -811,7 +803,7 @@ class CCPMethodEditorController extends HTMLElement{
if(this.#current.keywords){
return this.#current.keywords.map((k,i) => {
return `
<div class="ccp-option badge ccp-keyword">
<div class="ccp-option badge ccp-keyword" title="${k}" alt="${k}">
<span>${k}</span>
<span class="btn text-danger ccp-toolbar-button" name="delete-keyword" data-index="${i}">x</span>
</div>
@ -822,32 +814,32 @@ class CCPMethodEditorController extends HTMLElement{
}
}
reRenderRuntimes(){
this.#rootdoc.querySelector("select[name=runtime-input]").innerHTML = this.renderRuntimeOptions()
this.#rootdoc.querySelector("div[name=runtime-list]").innerHTML = this.renderRuntimes()
reRenderInfrastructures(){
this.#rootdoc.querySelector("select[name=infrastructure-input]").innerHTML = this.renderInfrastructureOptions()
this.#rootdoc.querySelector("div[name=infrastructure-list]").innerHTML = this.renderInfrastructures()
}
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 })
renderInfrastructureOptions(){
const selectedinfras = this.#current.links.filter(l=>l.rel === "compatibleWith").map(i=>i.href.split("/")[1])
const available = this.#infrastructures.filter(i=>{ return selectedinfras.indexOf(i.id) === -1 })
return `
<option></option>
${
available.map(rt=>{
available.map(infra=>{
return `
<option value="${rt.id}">${rt.name}</option>
<option value="${infra.id}" title="${infra.description}">${infra.name}</option>
`
}).join("\n")
}
`
}
renderRuntimes(){
renderInfrastructures(){
return this.#current.links.filter(l=>l.rel === "compatibleWith").map((l, i)=>{
return `
<div class="ccp-option badge ccp-keyword">
<div class="ccp-option badge ccp-keyword" title="${l.title}" alt="${l.title}">
<span>${l.title}</span>
<span class="btn text-danger ccp-toolbar-button" name="delete-runtime" data-index="${i}">x</span>
<span class="btn text-danger ccp-toolbar-button" name="delete-infrastructure" data-index="${i}">x</span>
</div>
`
}).join("\n")
@ -874,11 +866,12 @@ class CCPMethodEditorController extends HTMLElement{
}
renderScripts(){
const val = 'deploy-script'
return this.#current.additionalParameters.parameters.map(
(script, i) => {
let code = script.value.length ? script.value.join("\r\n") : ""
return `
<textarea rows="5" class="script-area form-control d-none" name="${script.name}">${code}</textarea>
return `
<textarea rows="5" class="script-area form-control ${script.name === val ? '' : 'd-none'}" name="${script.name}">${code}</textarea>
`
}
).join("\n")

View File

@ -1,881 +0,0 @@
class CCPMethodEditorController extends HTMLElement{
#boot;
#rootdoc;
#serviceurl;
#infrastructures;
#locked = false;
#isupdate = false;
#cloneornew_dialog = null;
#dragging_method = null;
#tmp_inputs = []
#tmp_outputs = []
#current = null;
#method_template = {
id : "",
title : "New Method",
description : "New empty method",
version : "1.0.0",
jobControlOptions : "async-execute",
metadata : [],
inputs : {},
outputs : {},
additionalParameters : {
parameters : [
{
name : "deploy-script",
value : []
},
{
name : "execute-script",
value : []
},
{
name : "undeploy-script",
value : []
}
]
},
links : []
}
#scripts = `
<link rel="canonical" href="https://getbootstrap.com/docs/5.0/components/modal/">
`
#style = `
<link rel="stylesheet" href="https://cdn.dev.d4science.org/ccp/css/common.css"></link>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<style>
.ccp-method-editor {
position: relative;
}
.ccp-option {
background-color:#eeffff;
color:#0099CC;
border:solid 1px #0099CC;
display: inline-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 1rem;
font-size: 1rem;
}
.ccp-option > .btn {
padding: 0;
}
ul.author_list, ul.context_list{
list-style: none;
display: flex;
flex-direction: row;
gap:2px;
padding-left: 0;
font-size: small;
font-weight: 300;
}
li.author_list_item{
display: inline-block;
padding: 0.25em 0.4em;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
background-color: #eeffff;
color: #9900CC;
border: solid 1px #9900CC;
}
li.context_list_item{
display: inline-block;
padding: 0.25em 0.4em;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
background-color: #eeffff;
color: #0099CC;
border: solid 1px #0099CC;
}
</style>
`
#erase_icon = `
<svg viewBox="0 0 24 24">
<path d="M16.24,3.56L21.19,8.5C21.97,9.29 21.97,10.55 21.19,11.34L12,20.53C10.44,22.09 7.91,22.09 6.34,20.53L2.81,17C2.03,16.21 2.03,14.95 2.81,14.16L13.41,3.56C14.2,2.78 15.46,2.78 16.24,3.56M4.22,15.58L7.76,19.11C8.54,19.9 9.8,19.9 10.59,19.11L14.12,15.58L9.17,10.63L4.22,15.58Z" />
`
#plus_icon = `
<svg viewBox="0 0 24 24">
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
</svg>
`
#disc_icon = `
<svg viewBox="0 0 24 24">
<path d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
</svg>
`
#output_icon = `
<svg viewBox="0 0 24 24">
<path d="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" />
</svg>
`
#delete_icon = `
<svg viewBox="0 0 24 24">
<path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" />
</svg>
`
constructor(){
super();
this.#boot = document.querySelector("d4s-boot-2")
this.#rootdoc = this.attachShadow({ "mode" : "open"})
this.#serviceurl = this.getAttribute("serviceurl")
this.initMethod()
this.fetchInfrastructures()
this.connectNewEditRequest()
}
connectedCallback(){
}
connectNewEditRequest(){
document.addEventListener("neweditrequest", ev=>{
this.cloneOrEditMethod(ev.detail)
})
}
isInfrastructureRegistered(infra){
return (this.#infrastructures.filter(i=>i.id === infra.id)).length > 0
}
fetchInfrastructures(){
this.#boot.secureFetch(this.#serviceurl + "/infrastructures").
then(resp=>{
if(resp.status !== 200) throw "Unable to fetch infrastructures";
return resp.json()
}).then(data=>{
this.#infrastructures = data
this.render()
}).catch(err=>{
alert(err)
})
}
saveMethod(){
if(this.#locked) return;
if(this.#current != null){
this.adoptTemporaries()
const text = `Confirm ${this.#isupdate ? "updating" : "creation"} of ${this.#current.title} version ${this.#current.version}`
if(window.confirm(text)){
this.lockRender()
const url = this.#serviceurl + "/methods"
const args = {
body : JSON.stringify(this.#current),
method : this.#isupdate ? "PUT" : "POST",
headers : {"Content-type" : "application/json"}
}
this.#boot.secureFetch(url, args).then(
(resp)=>{
if(resp.status === 201 || resp.status === 204){
return resp.text()
}else throw "Error saving process: " + resp.status
}).then(data=>{
if(!this.#isupdate){
this.resetMethod()
}
this.unlockRender()
}).catch(err=>{
alert(err)
this.unlockRender()
})
}
}
}
deleteMethod(){
if(this.#locked) return;
if(this.#current != null){
const text = `Confirm deletion of ${this.#current.title} version ${this.#current.version}`
if(window.confirm(text)){
this.lockRender()
const url = this.#serviceurl + "/methods/" + this.#current.id
const args = {
method : "DELETE"
}
this.#boot.secureFetch(url, args).then(
(resp)=>{
if(resp.status === 404 || resp.status === 204){
return null
}else throw "Error deleting method: " + resp.status
}).then(data=>{
this.unlockRender()
}).catch(err=>{
alert(err)
this.unlockRender()
})
}
}
}
initMethod(){
this.#current = JSON.parse(JSON.stringify(this.#method_template))
this.#current.id = Math.abs((Math.random() * 10e11)|0)
this.#current.metadata = []
this.#tmp_inputs = [
{
id : "ccpimage",
title : "Runtime",
description : "The image of the runtime to use for method execution. This depends on the infrastructure specific protocol for interacting with registries.",
minOccurs : 1,
maxOccurs : 1,
schema : {
type : "string",
format : "url",
contentMediaType : "text/plain",
default : "",
readonly : true,
}
}
]
this.#tmp_outputs = []
}
resetMethod(){
this.initMethod()
this.render()
}
cloneOrEditMethod(method){
const subject = this.#boot.subject
const matchingauthors = method.metadata ? method.metadata.filter(md=>md.role === "author" && md.href.endsWith("/" + subject)).length : 0
if(matchingauthors === 0) this.cloneMethod(method.id);
else{
this.#dragging_method = method.id
this.#cloneornew_dialog.style.display = "block"
this.#cloneornew_dialog.classList.add("show")
}
}
cloneMethod(method){
if(this.#locked) return;
this.lockRender()
this.#boot.secureFetch(this.#serviceurl + "/methods/" + method + "/clone").then(
(resp)=>{
if(resp.status === 200){
return resp.json()
}else throw "Error retrieving process: " + resp.status
}
).then(data=>{
this.#current = data
this.#isupdate = false
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()
})
}
editMethod(method){
if(this.#locked) return;
this.lockRender()
this.#boot.secureFetch(this.#serviceurl + "/methods/" + method + "/updatable").then(
(resp)=>{
if(resp.status === 200){
return resp.json()
}else throw "Error retrieving process: " + resp.status
}
).then(data=>{
this.#current = data
this.#isupdate = true
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
this.parentElement.scrollIntoViewIfNeeded()
}
render(){
this.#rootdoc.innerHTML = `
<!-- Modal -->
<div class="modal fade" style="background-color:rgba(0,0,0,0.3)" id="cloneornew" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content shadow-lg border-primary">
<div class="modal-body">
Choose whether you want to clone this method or edit it.
</div>
<div class="modal-footer">
<button name="clone" type="button" class="btn btn-info">Clone</button>
<button name="edit" type="button" class="btn btn-primary">Edit</button>
</div>
</div>
</div>
</div>
<div class="ccp-method-editor">
<div class="d-none plexiglass">
<svg class="spinner" viewBox="0 0 66 66">
<circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
</svg>
</div>
${this.#style}
<div class="ccp-method-editor-form">
<div class="card">
<div class="card-header">
<div class="ccp-toolbar-header">
<div>
<span name="header" class="mr-2">${this.#current.title}</span>
</div>
<div class="ccp-toolbar-right">
${this.renderSaveButton()}
${this.renderResetButton()}
${ this.#isupdate ? this.renderDeleteButton() : "" }
</div>
</div>
</div>
<div class="card-body">
<div class="mb-3 row">
${this.renderAuthors()}
</div>
<div class="mb-3 row">
${this.renderContexts()}
</div>
<div class="mb-3 row">
<div class="col">
<label class="form-label">Title</label>
<input name="title" class="form-control" type="text" required="required" value="${this.#current.title}"/>
</div>
<div class="col">
<label class="form-label">Version</label>
<input name="version" class="form-control" type="text" required="required" value="${this.#current.version}"/>
</div>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<input name="description" class="form-control" type="text" required="required" value="${this.#current.description}"/>
</div>
<div class="mb-3">
<label class="form-label">Keywords</label>
<input name="keyword-input" class="form-control" type="text"/>
<div name="keyword-list" class="form-text">
${this.renderKeywords()}
</div>
</div>
<div name="infrastructures" class="mb-3">
<label class="form-label">Compatible Infrastrucures</label>
<select name="infrastructure-input" class="form-control">
${this.renderInfrastructureOptions()}
</select>
<div name="infrastructure-list" class="form-text">
${this.renderInfrastructures()}
</div>
</div>
</div>
</div>
<details class="card">
<summary class="card-header">
<div class="ccp-toolbar-header">
<div>
<span class="mr-2">Inputs</span>
</div>
<div class="ccp-toolbar-right">
${this.renderPlusButton("add-input")}
</div>
</div>
</summary>
<div class="card-body" name="input-list">
</div>
</details>
<details class="card">
<summary class="card-header" name="output-buttons">
<div class="ccp-toolbar-header">
<div>
<span class="mr-2">Outputs</span>
</div>
<div class="ccp-toolbar-right">
${this.renderStandardOutputButtons()}
${this.renderPlusButton("add-output")}
</div>
</div>
</summary>
<div class="card-body" name="output-list">
<d4s-ccp-output-editor></d4s-ccp-output-editor>
</div>
</div>
<details class="card" name="script-list">
<summary class="card-header">
<div class="ccp-toolbar-header">
<div>
<span>Scripts</span>
<select name="script-selector" style="border:none; padding:.3rem">
<option value="deploy-script">Deploy</option>
<option value="execute-script">Execute</option>
<option value="undeploy-script">Undeploy</option>
</select>
</div>
</div>
</summary>
<div class="card-body" name="input_container">
${this.renderScripts()}
</div>
</div>
</div>
</div>
`
this.renderInputs()
this.renderOutputs()
this.#cloneornew_dialog = this.#rootdoc.querySelector("#cloneornew")
this.#rootdoc.querySelector("#cloneornew button[name=clone]").addEventListener("click", ev=>{
this.#cloneornew_dialog.classList.remove("show")
this.#cloneornew_dialog.style.display = "none"
this.cloneMethod(this.#dragging_method)
this.#dragging_method = null
})
this.#rootdoc.querySelector("#cloneornew button[name=edit]").addEventListener("click", ev=>{
this.#cloneornew_dialog.classList.remove("show")
this.#cloneornew_dialog.style.display = "none"
this.editMethod(this.#dragging_method)
this.#dragging_method = null
})
this.#rootdoc.querySelector("input[name=title]").addEventListener("input", ev=>{
this.#current.title = ev.currentTarget.value
this.#rootdoc.querySelector("span[name=header]").innerText = this.#current.title
})
this.#rootdoc.querySelector("input[name=version]").addEventListener("input", ev=>{
this.#current.version = ev.currentTarget.value
})
this.#rootdoc.querySelector("input[name=description]").addEventListener("input", ev=>{
this.#current.description = ev.currentTarget.value
})
this.#rootdoc.addEventListener("drop", ev=>{
if(ev.dataTransfer && ev.dataTransfer.getData('text/plain+ccpmethod')){
const method = JSON.parse(ev.dataTransfer.getData('application/json+ccpmethod'))
ev.stopImmediatePropagation()
ev.preventDefault()
ev.stopPropagation()
this.cloneOrEditMethod(method)
}
})
this.#rootdoc.addEventListener("dragover", ev=>{
ev.preventDefault()
})
this.#rootdoc.querySelector("button[name=reset]").addEventListener("click", ev=>{
if(window.confirm("All unsaved data will be lost. Proceed?")){
this.resetMethod()
}
ev.preventDefault()
ev.stopPropagation()
})
this.#rootdoc.querySelector("button[name=save]").addEventListener("click", ev=>{
ev.preventDefault()
ev.stopPropagation()
this.saveMethod()
})
if(this.#isupdate){
this.#rootdoc.querySelector("button[name=delete]").addEventListener("click", ev=>{
ev.preventDefault()
ev.stopPropagation()
this.deleteMethod()
})
}
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=infrastructures]").addEventListener("change", ev=>{
if(ev.target.getAttribute("name") === "infrastructure-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 : "infrastructures/" + id
}
if(!this.#current.links){
this.#current.links = [link]
}else{
this.#current.links.push(link)
}
this.reRenderInfrastructures(ev.currentTarget)
}
})
this.#rootdoc.querySelector("div[name=infrastructure-list]").addEventListener("click", ev=>{
ev.preventDefault()
ev.stopPropagation()
if(ev.target.getAttribute('name') === "delete-infrastructure"){
const index = ev.target.getAttribute("data-index")
this.#current.links.splice(index, 1)
this.reRenderInfrastructures()
}
})
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 : "text/plain",
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,
metadata : [
{
"role" : "file",
"title" : "newoutput.txt",
"href" : "newoutput.txt"
}
],
schema : {
type : "string",
contentMediaType : "text/plain"
}
}
)
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,
metadata : [
{
"role" : "file",
"title" : "stdout",
"href" : "stdout"
}
],
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,
metadata : [
{
"role" : "file",
"title" : "stderr",
"href" : "stderr"
}
],
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 = ev.target.value.split(/\r?\n/);
})
}
renderAuthors(){
return `
<ul class="author_list">
${ this.#current.metadata.
filter(md=>md.role === "author").
map(a=>`<li class="author_list_item" title="${a.title}">${a.title}</li>`).join() }
</ul>
`
}
renderContexts(){
return `
<ul class="context_list">
${ this.#current.metadata.
filter(md=>md.role === "context").
map(c=>`<li class="context_list_item" title="${c.title}">${c.title}</li>`).join("") }
</ul>
`
}
renderSaveButton(){
return `
<button title="Save" name="save" class="btn btn-primary ccp-toolbar-button">
${this.#disc_icon}
</button>
`
}
renderDeleteButton(){
return `
<button title="Delete" name="delete" class="btn btn-danger ccp-toolbar-button">
${this.#delete_icon}
</button>
`
}
renderResetButton(){
return `
<button name="reset" title="Reset" class="btn btn-primary ccp-toolbar-button">
<svg viewBox="0 0 24 24">
${this.#erase_icon}
</svg>
</button>
`
}
renderPlusButton(name){
return `
<button name="${name}" title="Add" name="reset" class="btn btn-primary ccp-toolbar-button">
${this.#plus_icon}
</button>
`
}
renderStandardOutputButtons(){
return `
<button name="add-stdout" title="Add stdout" name="reset" class="btn btn-success ccp-toolbar-button">
${this.#output_icon}
</button>
<button name="add-stderr" title="Add stderr" name="reset" class="btn btn-danger ccp-toolbar-button">
${this.#output_icon}
</button>
`
}
reRenderKeywords(){
this.#rootdoc.querySelector("div[name=keyword-list]").innerHTML = this.renderKeywords()
}
renderKeywords(){
if(this.#current.keywords){
return this.#current.keywords.map((k,i) => {
return `
<div class="ccp-option badge ccp-keyword" title="${k}" alt="${k}">
<span>${k}</span>
<span class="btn text-danger ccp-toolbar-button" name="delete-keyword" data-index="${i}">x</span>
</div>
`
}).join("\n")
}else{
return ""
}
}
reRenderInfrastructures(){
this.#rootdoc.querySelector("select[name=infrastructure-input]").innerHTML = this.renderInfrastructureOptions()
this.#rootdoc.querySelector("div[name=infrastructure-list]").innerHTML = this.renderInfrastructures()
}
renderInfrastructureOptions(){
const selectedinfras = this.#current.links.filter(l=>l.rel === "compatibleWith").map(i=>i.href.split("/")[1])
const available = this.#infrastructures.filter(i=>{ return selectedinfras.indexOf(i.id) === -1 })
return `
<option></option>
${
available.map(infra=>{
return `
<option value="${infra.id}" title="${infra.description}">${infra.name}</option>
`
}).join("\n")
}
`
}
renderInfrastructures(){
return this.#current.links.filter(l=>l.rel === "compatibleWith").map((l, i)=>{
return `
<div class="ccp-option badge ccp-keyword" title="${l.title}" alt="${l.title}">
<span>${l.title}</span>
<span class="btn text-danger ccp-toolbar-button" name="delete-infrastructure" data-index="${i}">x</span>
</div>
`
}).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(){
const val = 'deploy-script'
return this.#current.additionalParameters.parameters.map(
(script, i) => {
let code = script.value.length ? script.value.join("\r\n") : ""
return `
<textarea rows="5" class="script-area form-control ${script.name === val ? '' : 'd-none'}" name="${script.name}">${code}</textarea>
`
}
).join("\n")
}
}
window.customElements.define('d4s-ccp-methodeditor', CCPMethodEditorController);

View File

@ -1,87 +0,0 @@
<div>
<style>
.process_container{
display: flex;
flex-direction:column;
}
input[name=search]{
display: block;
padding: 0.375rem 0.75rem;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
input:focus{
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgb(0 123 255 / 25%);
}
ul.process_list{
list-style: none;
padding-left: 0;
color: #495057;
display: flex;
flex-direction: column;
gap: .25rem;
user-select: none;
}
li.process_list_item{
display: flex;
flex-direction: column;
gap: 0.25rem;
cursor: pointer;
border: solid 1px rgba(0,0,0,.3);
padding: 0.3rem;
border-radius: 0.3rem;
box-shadow: #333333 1px 1px 4px;
background: #eeeeee;
transition: background .3s;
}
li.process_list_item:hover{
background: #ffffff;
}
ul.keyword_list{
list-style: none;
display: flex;
flex-direction: row;
gap:2px;
padding-left: 0;
font-size: x-small;
font-weight: 300;
}
li.keyword_list_item{
display: inline-block;
padding: 0.25em 0.4em;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
background-color: #eeffff;
color: #0099CC;
border: solid 1px #0099CC;
}
.process_list_item_header{
margin: 0;
}
</style>
<template id="PROCESS_LIST_TEMPLATE">
<ul class="process_list" name="process_list">
<li class="process_list_item" draggable="true">
<h4 class="process_list_item_header"></h4>
<ul name="keywords" class="keyword_list">
<li class="keyword_list_item"></li>
</ul>
<i></i>
</li>
</ul>
</template>
<div class="process_container">
<input name="search" type="text"/>
<ul name="process_list"></ul>
</div>
</div>