2023-03-07 11:43:28 +01:00
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 : [ ] ,
2023-03-07 12:26:30 +01:00
inputs : { } ,
2023-03-07 11:43:28 +01:00
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" > < / l i n k >
< 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 - toolbar - header {
display : inline - flex ;
align - items : center ;
gap : 5 px ;
padding : . 2 rem ;
}
. ccp - toolbar - right {
position : absolute ;
right : 1 rem ;
}
. ccp - toolbar - button {
font - weight : bold ;
padding : . 3 rem ;
line - height : . 8 rem ;
cursor : pointer ;
}
. ccp - toolbar - button svg {
fill : white ;
width : 24 px ;
height : 24 px ;
pointer - events : none ;
}
. ccp - option {
background - color : # eeffff ;
color : # 0099 CC ;
border : solid 1 px # 0099 CC ;
}
. plexiglass {
position : absolute ;
width : 100 % ;
height : 100 % ;
background - color : rgba ( 0 , 0 , 0 , . 3 ) ;
z - index : 10 ;
}
ul . author _list , ul . context _list {
list - style : none ;
display : flex ;
flex - direction : row ;
gap : 2 px ;
padding - left : 0 ;
font - size : small ;
font - weight : 300 ;
}
li . author _list _item {
display : inline - block ;
padding : 0.25 em 0.4 em ;
line - height : 1 ;
text - align : center ;
white - space : nowrap ;
vertical - align : baseline ;
border - radius : 0.25 rem ;
background - color : # eeffff ;
color : # 9900 CC ;
border : solid 1 px # 9900 CC ;
}
li . context _list _item {
display : inline - block ;
padding : 0.25 em 0.4 em ;
line - height : 1 ;
text - align : center ;
white - space : nowrap ;
vertical - align : baseline ;
border - radius : 0.25 rem ;
background - color : # eeffff ;
color : # 0099 CC ;
border : solid 1 px # 0099 CC ;
}
< / s t y l e >
`
# 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" / >
< / s v g >
`
# 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" / >
< / s v g >
`
# 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" / >
< / s v g >
`
# 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" / >
< / s v g >
`
constructor ( ) {
super ( ) ;
this . # boot = document . querySelector ( "d4s-boot-2" )
this . # rootdoc = this . attachShadow ( { "mode" : "open" } )
this . # serviceurl = this . getAttribute ( "serviceurl" )
this . initMethod ( )
this . fetchInfrastructures ( )
}
connectedCallback ( ) {
}
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 = [ ]
2023-03-07 12:26:30 +01:00
this . # tmp _inputs = [
{
2023-03-07 12:38:24 +01:00
id : "ccpimage" ,
2023-03-07 12:26:30 +01:00
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" ,
2023-03-07 12:40:03 +01:00
contentMediaType : "text/plain" ,
2023-03-07 12:26:30 +01:00
default : "" ,
readonly : true ,
}
}
]
2023-03-07 11:43:28 +01:00
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
}
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 .
< / d i v >
< div class = "modal-footer" >
< button name = "clone" type = "button" class = "btn btn-info" > Clone < / b u t t o n >
< button name = "edit" type = "button" class = "btn btn-primary" > Edit < / b u t t o n >
< / d i v >
< / d i v >
< / d i v >
< / d i v >
< 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" > < / c i r c l e >
< / s v g >
< / d i v >
$ { 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 } < / s p a n >
< / d i v >
< div class = "ccp-toolbar-right" >
$ { this . renderSaveButton ( ) }
$ { this . renderResetButton ( ) }
$ { this . # isupdate ? this . renderDeleteButton ( ) : "" }
< / d i v >
< / d i v >
< / d i v >
< div class = "card-body" >
< div class = "mb-3 row" >
$ { this . renderAuthors ( ) }
< / d i v >
< div class = "mb-3 row" >
$ { this . renderContexts ( ) }
< / d i v >
< div class = "mb-3 row" >
< div class = "col" >
< label class = "form-label" > Title < / l a b e l >
< input name = "title" class = "form-control" type = "text" required = "required" value = "${this.#current.title}" / >
< / d i v >
< div class = "col" >
< label class = "form-label" > Version < / l a b e l >
< input name = "version" class = "form-control" type = "text" required = "required" value = "${this.#current.version}" / >
< / d i v >
< / d i v >
< div class = "mb-3" >
< label class = "form-label" > Description < / l a b e l >
< input name = "description" class = "form-control" type = "text" required = "required" value = "${this.#current.description}" / >
< / d i v >
< div class = "mb-3" >
< label class = "form-label" > Keywords < / l a b e l >
< input name = "keyword-input" class = "form-control" type = "text" / >
< div name = "keyword-list" class = "form-text" >
$ { this . renderKeywords ( ) }
< / d i v >
< / d i v >
2023-03-07 12:01:04 +01:00
< div name = "infrastructures" class = "mb-3" >
2023-03-07 11:43:28 +01:00
< label class = "form-label" > Compatible Infrastrucures < / l a b e l >
< select name = "infrastructure-input" class = "form-control" >
$ { this . renderInfrastructureOptions ( ) }
< / s e l e c t >
< div name = "infrastructure-list" class = "form-text" >
$ { this . renderInfrastructures ( ) }
< / d i v >
< / d i v >
< / d i v >
< / d i v >
< details class = "card" >
< summary class = "card-header" >
< div class = "ccp-toolbar-header" >
< div >
< span class = "mr-2" > Inputs < / s p a n >
< / d i v >
< div class = "ccp-toolbar-right" >
$ { this . renderPlusButton ( "add-input" ) }
< / d i v >
< / d i v >
< / s u m m a r y >
< div class = "card-body" name = "input-list" >
< / d i v >
< / d e t a i l s >
< details class = "card" >
< summary class = "card-header" name = "output-buttons" >
< div class = "ccp-toolbar-header" >
< div >
< span class = "mr-2" > Outputs < / s p a n >
< / d i v >
< div class = "ccp-toolbar-right" >
$ { this . renderStandardOutputButtons ( ) }
$ { this . renderPlusButton ( "add-output" ) }
< / d i v >
< / d i v >
< / s u m m a r y >
< div class = "card-body" name = "output-list" >
< d4s - ccp - output - editor > < / d 4 s - c c p - o u t p u t - e d i t o r >
< / d i v >
< / d i v >
< details class = "card" name = "script-list" >
< summary class = "card-header" >
< div class = "ccp-toolbar-header" >
< div >
< span > Scripts < / s p a n >
< select name = "script-selector" style = "border:none; padding:.3rem" >
< option value = "" > < / o p t i o n >
< option value = "deploy-script" > Deploy < / o p t i o n >
< option value = "execute-script" > Execute < / o p t i o n >
< option value = "undeploy-script" > Undeploy < / o p t i o n >
< / s e l e c t >
< / d i v >
< / d i v >
< / s u m m a r y >
< div class = "card-body" name = "input_container" >
$ { this . renderScripts ( ) }
< / d i v >
< / d i v >
< / d i v >
< / d i v >
`
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 ( ) }
< / u l >
`
}
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 ( "" ) }
< / u l >
`
}
renderSaveButton ( ) {
return `
< button title = "Save" name = "save" class = "btn btn-primary ccp-toolbar-button" >
$ { this . # disc _icon }
< / b u t t o n >
`
}
renderDeleteButton ( ) {
return `
< button title = "Delete" name = "delete" class = "btn btn-danger ccp-toolbar-button" >
$ { this . # delete _icon }
< / b u t t o n >
`
}
renderResetButton ( ) {
return `
< button name = "reset" title = "Reset" class = "btn btn-primary ccp-toolbar-button" >
< svg viewBox = "0 0 24 24" >
$ { this . # erase _icon }
< / s v g >
< / b u t t o n >
`
}
renderPlusButton ( name ) {
return `
< button name = "${name}" title = "Add" name = "reset" class = "btn btn-primary ccp-toolbar-button" >
$ { this . # plus _icon }
< / b u t t o n >
`
}
renderStandardOutputButtons ( ) {
return `
< button name = "add-stdout" title = "Add stdout" name = "reset" class = "btn btn-success ccp-toolbar-button" >
$ { this . # output _icon }
< / b u t t o n >
< button name = "add-stderr" title = "Add stderr" name = "reset" class = "btn btn-danger ccp-toolbar-button" >
$ { this . # output _icon }
< / b u t t o n >
`
}
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" >
< span > $ { k } < / s p a n >
< span class = "btn text-danger ccp-toolbar-button" name = "delete-keyword" data - index = "${i}" > x < / s p a n >
< / d i v >
`
} ) . join ( "\n" )
} else {
return ""
}
}
reRenderInfrastructures ( ) {
this . # rootdoc . querySelector ( "select[name=infrastructure-input]" ) . innerHTML = this . renderInfrastructureOptions ( )
2023-03-07 11:53:11 +01:00
this . # rootdoc . querySelector ( "div[name=infrastructure-list]" ) . innerHTML = this . renderInfrastructures ( )
2023-03-07 11:43:28 +01:00
}
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 > < / o p t i o n >
$ {
available . map ( infra => {
return `
< option value = "${infra.id}" title = "${infra.description}" > $ { infra . name } < / o p t i o n >
`
} ) . join ( "\n" )
}
`
}
2023-03-07 11:53:11 +01:00
renderInfrastructures ( ) {
2023-03-07 11:43:28 +01:00
return this . # current . links . filter ( l => l . rel === "compatibleWith" ) . map ( ( l , i ) => {
return `
< div class = "ccp-option badge ccp-keyword" >
< span > $ { l . title } < / s p a n >
2023-03-07 12:23:20 +01:00
< span class = "btn text-danger ccp-toolbar-button" name = "delete-infrastructure" data - index = "${i}" > x < / s p a n >
2023-03-07 11:43:28 +01:00
< / d i v >
`
} ) . 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 ) => {
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 } < / t e x t a r e a >
`
}
) . join ( "\n" )
}
}
window . customElements . define ( 'd4s-ccp-methodeditor' , CCPMethodEditorController ) ;