master #13
|
@ -124,14 +124,14 @@ window.customElements.define('d4s-boot-2', class extends HTMLElement {
|
||||||
}).then(token => {
|
}).then(token => {
|
||||||
console.log("Authorized. Token exp: " + this.expirationDate(this.parseJwt(token).exp))
|
console.log("Authorized. Token exp: " + this.expirationDate(this.parseJwt(token).exp))
|
||||||
//transform all queued requests to fetches
|
//transform all queued requests to fetches
|
||||||
console.log("All pending requests to promises")
|
//console.log("All pending requests to promises")
|
||||||
let promises = this.#queue.map(r => {
|
let promises = this.#queue.map(r => {
|
||||||
r.request.headers["Authorization"] = "Bearer " + token
|
r.request.headers["Authorization"] = "Bearer " + token
|
||||||
return r.resolve( fetch(r.url, r.request) )
|
return r.resolve( fetch(r.url, r.request) )
|
||||||
})
|
})
|
||||||
//clear queue
|
//clear queue
|
||||||
this.#queue = []
|
this.#queue = []
|
||||||
console.log("Resolving all fetches")
|
//console.log("Resolving all fetches")
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
|
|
||||||
}).catch(err => console.error("Unable to make calls: " + err)) // Sometimes throws: Unable to make calls: TypeError: Cannot read properties of undefined (reading 'split')
|
}).catch(err => console.error("Unable to make calls: " + err)) // Sometimes throws: Unable to make calls: TypeError: Cannot read properties of undefined (reading 'split')
|
||||||
|
@ -177,7 +177,7 @@ window.customElements.define('d4s-boot-2', class extends HTMLElement {
|
||||||
} else {
|
} else {
|
||||||
req.headers = { "Authorization" : null}
|
req.headers = { "Authorization" : null}
|
||||||
}
|
}
|
||||||
console.log("Queued request to url ", url)
|
//console.log("Queued request to url ", url)
|
||||||
this.#queue.push({ url : url, request : req, resolve : resolve, reject : reject})
|
this.#queue.push({ url : url, request : req, resolve : resolve, reject : reject})
|
||||||
})
|
})
|
||||||
return p
|
return p
|
||||||
|
@ -202,7 +202,7 @@ window.customElements.define('d4s-boot-2', class extends HTMLElement {
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
this.#config = json
|
this.#config = json
|
||||||
console.log("Keycloak uma2 configuration loaded")
|
//console.log("Keycloak uma2 configuration loaded")
|
||||||
resolve(true)
|
resolve(true)
|
||||||
})
|
})
|
||||||
.catch(err => reject("Failed to fetch uma2-configuration from server: " + err))
|
.catch(err => reject("Failed to fetch uma2-configuration from server: " + err))
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<script src="js/methodlistcontroller.js"></script>
|
||||||
|
<script src="js/methodeditorcontroller.js"></script>
|
||||||
|
<script src="js/inputwidgeteditorcontroller.js"></script>
|
||||||
|
<script src="js/outputwidgeteditorcontroller.js"></script>
|
||||||
|
<script src="js/executionformcontroller.js"></script>
|
||||||
|
<script src="js/inputwidgetcontroller.js"></script>
|
||||||
|
<script src="js/outputwidgetcontroller.js"></script>
|
||||||
|
<link href="css/common.css" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.cloud-dev.d4science.org/common/js/bss-min-1.2.6.js"></script>
|
||||||
|
<script src="../storage/d4s-storage.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="m-4">
|
||||||
|
<d4s-boot-2 clientid="https://next.dev.d4science.org" context="%2Fgcube%2Fdevsec%2FCCP" gateway="next.dev.d4science.org" redirect-url="http://localhost:8080/ccp/index.html" url="https://accounts.dev.d4science.org/auth"> <script src="https://cdn.dev.d4science.org/boot/d4s-boot.js"></script> </d4s-boot-2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<d4s-ccp-methodlist serviceurl="https://ccp.cloud-dev.d4science.org" allow-edit="true"></d4s-ccp-methodlist>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<d4s-ccp-methodeditor serviceurl="https://ccp.cloud-dev.d4science.org"></d4s-ccp-methodeditor>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<d4s-ccp-executionform serviceurl="https://ccp.cloud-dev.d4science.org"></d4s-ccp-executionform>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -55,8 +55,9 @@ class CCPExecutionForm extends HTMLElement{
|
||||||
render(){
|
render(){
|
||||||
this.#rootdoc.innerHTML = `
|
this.#rootdoc.innerHTML = `
|
||||||
<div>
|
<div>
|
||||||
|
<link rel="canonical" href="https://getbootstrap.com/docs/5.0/components/modal/">
|
||||||
<link rel="stylesheet" href="https://cdn.dev.d4science.org/ccp/css/common.css"></link>
|
<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">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" crossorigin="anonymous">
|
||||||
<style>
|
<style>
|
||||||
.ccp-execution-form{
|
.ccp-execution-form{
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -94,12 +95,12 @@ class CCPExecutionForm extends HTMLElement{
|
||||||
<h5>Outputs</h5>
|
<h5>Outputs</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-row ccp-outputs">
|
<div class="row ccp-outputs">
|
||||||
<div class="col form-group"></div>
|
<div class="col form-group"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<button id="execute_method_button" class="btn btn-info">Execute</button>
|
<button id="execute_method_button" class="btn btn-info">Execute</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,115 +2,135 @@ class CCPInputWidgetController extends HTMLElement {
|
||||||
|
|
||||||
#data = null;
|
#data = null;
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback() {
|
||||||
this.#data = JSON.parse(atob(this.getAttribute("input")))
|
this.#data = JSON.parse(atob(this.getAttribute("input")))
|
||||||
|
|
||||||
if(this.isChecklist()){
|
if (this.isChecklist()) {
|
||||||
|
|
||||||
const opts = this.#data.schema.enum.join(",")
|
const opts = this.#data.schema.enum.join(",")
|
||||||
this.innerHTML += `<d4s-ccp-input-checklist readonly="${this.#data.schema.readOnly}" options="${opts}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-checklist>`
|
this.innerHTML += `<d4s-ccp-input-checklist readonly="${this.#data.schema.readOnly}" options="${opts}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-checklist>`
|
||||||
|
this.querySelector("d4s-ccp-input-checklist").default = this.#data.schema.default
|
||||||
|
|
||||||
} else if(this.isEnum()){
|
} else if (this.isEnum()) {
|
||||||
|
|
||||||
const opts = this.#data.schema.enum.join(",")
|
const opts = this.#data.schema.enum.join(",")
|
||||||
this.innerHTML += `<d4s-ccp-input-enum readonly="${this.#data.schema.readOnly}" options="${opts}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-enum>`
|
this.innerHTML += `<d4s-ccp-input-enum readonly="${this.#data.schema.readOnly}" options="${opts}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-enum>`
|
||||||
}else if(this.isCode()){
|
this.querySelector("d4s-ccp-input-enum").default = this.#data.schema.default
|
||||||
this.innerHTML += `<d4s-ccp-input-textarea readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-textarea>`
|
|
||||||
|
} else if (this.isCode()) {
|
||||||
|
|
||||||
|
this.innerHTML += `<d4s-ccp-input-textarea readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-textarea>`
|
||||||
|
this.querySelector("d4s-ccp-input-textarea").default = this.#data.schema.default
|
||||||
|
|
||||||
|
} else if (this.isDateTime()) {
|
||||||
|
|
||||||
}else if(this.isDateTime()){
|
|
||||||
const t = this.#data.schema.format.toLowerCase() === "datetime" ?
|
const t = this.#data.schema.format.toLowerCase() === "datetime" ?
|
||||||
"datetime-local" : this.#data.schema.format.toLowerCase()
|
"datetime-local" : this.#data.schema.format.toLowerCase()
|
||||||
this.innerHTML += `<d4s-ccp-input-simple type="${t}" readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-simple>`
|
this.innerHTML += `<d4s-ccp-input-simple type="${t}" readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-simple>`
|
||||||
|
this.querySelector("d4s-ccp-input-simple").default = this.#data.schema.default
|
||||||
|
|
||||||
}else if(this.isFile()){
|
} else if (this.isFile()) {
|
||||||
this.innerHTML += `<d4s-ccp-input-file readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-file>`
|
|
||||||
|
|
||||||
}else if(this.isRemoteFile()){
|
this.innerHTML += `<d4s-ccp-input-file readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-file>`
|
||||||
this.innerHTML += `<d4s-ccp-input-remotefile readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-remotefile>`
|
this.querySelector("d4s-ccp-input-file").default = this.#data.schema.default
|
||||||
|
|
||||||
}else if(this.isSecret()){
|
} else if (this.isRemoteFile()) {
|
||||||
this.innerHTML += `<d4s-ccp-input-secret readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-secret>`
|
|
||||||
|
|
||||||
}else if(this.isBoolean()){
|
this.innerHTML += `<d4s-ccp-input-remotefile readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-remotefile>`
|
||||||
this.innerHTML += `<d4s-ccp-input-boolean readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-boolean>`
|
this.querySelector("d4s-ccp-input-remotefile").default = this.#data.schema.default
|
||||||
|
|
||||||
}else if(this.isNumber()){
|
} else if (this.isSecret()) {
|
||||||
this.innerHTML += `<d4s-ccp-input-simple type="number" readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-simple>`
|
|
||||||
|
this.innerHTML += `<d4s-ccp-input-secret readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs }" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-secret>`
|
||||||
|
this.querySelector("d4s-ccp-input-secret").default = this.#data.schema.default
|
||||||
|
|
||||||
|
} else if (this.isBoolean()) {
|
||||||
|
|
||||||
|
this.innerHTML += `<d4s-ccp-input-boolean readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-boolean>`
|
||||||
|
this.querySelector("d4s-ccp-input-boolean").default = this.#data.schema.default
|
||||||
|
|
||||||
|
} else if (this.isNumber()) {
|
||||||
|
|
||||||
|
this.innerHTML += `<d4s-ccp-input-simple type="number" readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-simple>`
|
||||||
|
this.querySelector("d4s-ccp-input-simple").default = this.#data.schema.default
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.innerHTML += `<d4s-ccp-input-simple readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-simple>`
|
||||||
|
this.querySelector("d4s-ccp-input-simple").default = this.#data.schema.default
|
||||||
|
|
||||||
}else{
|
|
||||||
this.innerHTML += `<d4s-ccp-input-simple readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-simple>`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set value(value){
|
set value(value) {
|
||||||
this.firstElementChild.value = value
|
this.firstElementChild.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(){
|
get value() {
|
||||||
return this.firstElementChild.value.length === 1 ?
|
return this.firstElementChild.value.length === 1 ?
|
||||||
this.firstElementChild.value[0] :
|
this.firstElementChild.value[0] :
|
||||||
this.firstElementChild.value
|
this.firstElementChild.value
|
||||||
}
|
}
|
||||||
|
|
||||||
get name(){
|
get name() {
|
||||||
return this.firstElementChild.name
|
return this.firstElementChild.name
|
||||||
}
|
}
|
||||||
|
|
||||||
isChecklist(){
|
isChecklist() {
|
||||||
return this.isEnum() && (this.#data.schema.format === "checklist")
|
return this.isEnum() && (this.#data.schema.format === "checklist")
|
||||||
}
|
}
|
||||||
|
|
||||||
isNumber(){
|
isNumber() {
|
||||||
return (this.#data.schema.type === "string") &&
|
return (this.#data.schema.type === "string") &&
|
||||||
("format" in this.#data.schema) &&
|
("format" in this.#data.schema) &&
|
||||||
(this.#data.schema.format != null) &&
|
(this.#data.schema.format != null) &&
|
||||||
(this.#data.schema.format.toLowerCase() === "number")
|
(this.#data.schema.format.toLowerCase() === "number")
|
||||||
}
|
}
|
||||||
|
|
||||||
isBoolean(){
|
isBoolean() {
|
||||||
return (this.#data.schema.type === "string") &&
|
return (this.#data.schema.type === "string") &&
|
||||||
("format" in this.#data.schema) &&
|
("format" in this.#data.schema) &&
|
||||||
(this.#data.schema.format != null) &&
|
(this.#data.schema.format != null) &&
|
||||||
(this.#data.schema.format.toLowerCase() === "boolean")
|
(this.#data.schema.format.toLowerCase() === "boolean")
|
||||||
}
|
}
|
||||||
|
|
||||||
isEnum(){
|
isEnum() {
|
||||||
return (this.#data.schema.type === "string") && ("enum" in this.#data.schema)
|
return (this.#data.schema.type === "string") && ("enum" in this.#data.schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
isSecret(){
|
isSecret() {
|
||||||
return (this.#data.schema.type === "string") &&
|
return (this.#data.schema.type === "string") &&
|
||||||
("format" in this.#data.schema) &&
|
("format" in this.#data.schema) &&
|
||||||
(this.#data.schema.format != null) &&
|
(this.#data.schema.format != null) &&
|
||||||
(this.#data.schema.format.toLowerCase() === "secret")
|
(this.#data.schema.format.toLowerCase() === "secret")
|
||||||
}
|
}
|
||||||
|
|
||||||
isCode(){
|
isCode() {
|
||||||
return (this.#data.schema.type === "string") &&
|
return (this.#data.schema.type === "string") &&
|
||||||
("format" in this.#data.schema) &&
|
("format" in this.#data.schema) &&
|
||||||
(this.#data.schema.format != null) &&
|
(this.#data.schema.format != null) &&
|
||||||
(this.#data.schema.format.toLowerCase() === "code")
|
(this.#data.schema.format.toLowerCase() === "code")
|
||||||
}
|
}
|
||||||
|
|
||||||
isFile(){
|
isFile() {
|
||||||
return (this.#data.schema.type === "string") &&
|
return (this.#data.schema.type === "string") &&
|
||||||
("format" in this.#data.schema) &&
|
("format" in this.#data.schema) &&
|
||||||
(this.#data.schema.format != null) &&
|
(this.#data.schema.format != null) &&
|
||||||
(this.#data.schema.format.toLowerCase() === "file")
|
(this.#data.schema.format.toLowerCase() === "file")
|
||||||
}
|
}
|
||||||
|
|
||||||
isRemoteFile(){
|
isRemoteFile() {
|
||||||
return (this.#data.schema.type === "string") &&
|
return (this.#data.schema.type === "string") &&
|
||||||
("format" in this.#data.schema) &&
|
("format" in this.#data.schema) &&
|
||||||
(this.#data.schema.format != null) &&
|
(this.#data.schema.format != null) &&
|
||||||
(this.#data.schema.format.toLowerCase() === "remotefile")
|
(this.#data.schema.format.toLowerCase() === "remotefile")
|
||||||
}
|
}
|
||||||
|
|
||||||
isDateTime(){
|
isDateTime() {
|
||||||
return (this.#data.schema.type === "string") &&
|
return (this.#data.schema.type === "string") &&
|
||||||
("format" in this.#data.schema) &&
|
("format" in this.#data.schema) &&
|
||||||
(this.#data.schema.format != null) &&
|
(this.#data.schema.format != null) &&
|
||||||
|
@ -119,7 +139,7 @@ class CCPInputWidgetController extends HTMLElement {
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input', CCPInputWidgetController);
|
window.customElements.define('d4s-ccp-input', CCPInputWidgetController);
|
||||||
|
|
||||||
class CCPBaseInputWidgetController extends HTMLElement{
|
class CCPBaseInputWidgetController extends HTMLElement {
|
||||||
#rootdoc = null;
|
#rootdoc = null;
|
||||||
#minOccurs = 1;
|
#minOccurs = 1;
|
||||||
#maxOccurs = 1;
|
#maxOccurs = 1;
|
||||||
|
@ -136,15 +156,16 @@ class CCPBaseInputWidgetController extends HTMLElement{
|
||||||
|
|
||||||
#count = 1
|
#count = 1
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.#rootdoc = this//this.attachShadow({ mode: "open" });
|
this.#rootdoc = this//this.attachShadow({ mode: "open" });
|
||||||
this.#name = this.getAttribute("name")
|
this.#name = this.getAttribute("name")
|
||||||
this.#title = this.getAttribute("title")
|
this.#title = this.getAttribute("title")
|
||||||
this.#description = this.getAttribute("description")
|
this.#description = this.getAttribute("description")
|
||||||
this.#default = this.getAttribute("default")
|
//this.#default = this.getAttribute("default")
|
||||||
this.#minOccurs = Number(this.getAttribute("minoccurs") ? this.getAttribute("minoccurs") : 1)
|
this.#minOccurs = Number(this.getAttribute("minoccurs") ? this.getAttribute("minoccurs") : 1)
|
||||||
this.#maxOccurs = Number(this.getAttribute("maxoccurs") ? this.getAttribute("maxoccurs") : 1)
|
this.#maxOccurs = Number(this.getAttribute("maxoccurs") ? this.getAttribute("maxoccurs") : 1)
|
||||||
|
this.#maxOccurs = Math.max(this.#minOccurs, this.#maxOccurs)
|
||||||
this.#readonly = this.getAttribute("readonly") === "true"
|
this.#readonly = this.getAttribute("readonly") === "true"
|
||||||
|
|
||||||
// coalesce all basic input types
|
// coalesce all basic input types
|
||||||
|
@ -153,93 +174,103 @@ class CCPBaseInputWidgetController extends HTMLElement{
|
||||||
// Handle enum case
|
// Handle enum case
|
||||||
this.#options = (this.getAttribute("options") ? this.getAttribute("options").split(",") : null)
|
this.#options = (this.getAttribute("options") ? this.getAttribute("options").split(",") : null)
|
||||||
|
|
||||||
this.value = Array(Math.max(this.#minOccurs,1)).fill(this.#default)
|
//this.value = Array(Math.max(this.#minOccurs, 1)).fill(this.#default)
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(v){
|
set default(def){
|
||||||
|
this.#default = def
|
||||||
|
const defarr = Array.isArray(def) ? def : [def]
|
||||||
|
const min = Math.max(this.#minOccurs, 1)
|
||||||
|
var v = []
|
||||||
|
for(let j=0; j < this.#maxOccurs; j++){
|
||||||
|
if(j < defarr.length){
|
||||||
|
v.push(defarr[j])
|
||||||
|
}else if(j < min){
|
||||||
|
v.push(defarr[defarr.length - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.value = v
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(v) {
|
||||||
this.#value = v
|
this.#value = v
|
||||||
this.render()
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
set value(v){
|
set value(v) {
|
||||||
const actual = Array.isArray(v) ? v : [v]
|
const actual = Array.isArray(v) ? v : [v]
|
||||||
if(actual.length < this.#minOccurs || actual.length > this.#maxOccurs){
|
if (actual.length < this.#minOccurs || actual.length > this.#maxOccurs) {
|
||||||
throw `Value with length ${v.length} does not respect bounds [${this.minOccurs},${this.maxOccurs}]`
|
throw `Value with length ${v.length} does not respect bounds [${this.minOccurs},${this.maxOccurs}]`
|
||||||
}
|
}
|
||||||
this.#value = actual
|
this.#value = actual
|
||||||
this.render()
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(){
|
get value() {
|
||||||
return this.#value
|
return this.#value
|
||||||
}
|
}
|
||||||
|
|
||||||
get rootdoc(){
|
get rootdoc() {
|
||||||
return this.#rootdoc
|
return this.#rootdoc
|
||||||
}
|
}
|
||||||
|
|
||||||
get required(){
|
get required() {
|
||||||
return this.#minOccurs > 0
|
return this.#minOccurs > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
get readonly(){
|
get readonly() {
|
||||||
return this.#readonly
|
return this.#readonly
|
||||||
}
|
}
|
||||||
|
|
||||||
isIncrementable(){
|
isIncrementable() {
|
||||||
return this.#value.length < this.#maxOccurs
|
return this.#value.length < this.#maxOccurs
|
||||||
}
|
}
|
||||||
|
|
||||||
isDecrementable(){
|
isDecrementable() {
|
||||||
return this.#value.length > this.#minOccurs && this.#value.length > 1
|
return this.#value.length > this.#minOccurs && this.#value.length > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
get count(){
|
get count() {
|
||||||
return this.#count
|
return this.#count
|
||||||
}
|
}
|
||||||
|
|
||||||
get name(){
|
get name() {
|
||||||
return this.#name
|
return this.#name
|
||||||
}
|
}
|
||||||
|
|
||||||
get title(){
|
get title() {
|
||||||
return this.#title
|
return this.#title
|
||||||
}
|
}
|
||||||
|
|
||||||
get description(){
|
get description() {
|
||||||
return this.#description
|
return this.#description
|
||||||
}
|
}
|
||||||
|
|
||||||
get default(){
|
get default() {
|
||||||
return this.#default
|
return this.#default
|
||||||
}
|
}
|
||||||
|
|
||||||
get options(){
|
get options() {
|
||||||
return this.#options
|
return this.#options
|
||||||
}
|
}
|
||||||
|
|
||||||
set default(v){
|
get type() {
|
||||||
this.#default = v
|
|
||||||
}
|
|
||||||
|
|
||||||
get type(){
|
|
||||||
return this.#type
|
return this.#type
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPlusMinus(){
|
renderPlusMinus() {
|
||||||
this.rootdoc.querySelector("div[name=plusminus]").innerHTML = `
|
this.rootdoc.querySelector("div[name=plusminus]").innerHTML = `
|
||||||
${this.isIncrementable() ? `<span name="plus" title="add one" class="badge text-success border-success btn">+</span>` : ``}
|
${this.isIncrementable() ? `<span name="plus" title="add one" class="badge text-success border-success btn">+</span>` : ``}
|
||||||
${this.isDecrementable() ? `<span name="minus" title="remove one" class="badge border border-danger text-danger btn">-</span>` : ``}
|
${this.isDecrementable() ? `<span name="minus" title="remove one" class="badge border border-danger text-danger btn">-</span>` : ``}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
renderContent(){
|
renderContent() {
|
||||||
this.rootdoc.querySelector("div[name=content]").innerHTML = this.content()
|
this.rootdoc.querySelector("div[name=content]").innerHTML = this.content()
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
this.rootdoc.innerHTML = `
|
this.rootdoc.innerHTML = `
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<div name="root" class="my-3 ccp-input-widget form-field row">
|
<div name="root" class="my-3 ccp-input-widget form-field row">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
|
@ -264,42 +295,44 @@ class CCPBaseInputWidgetController extends HTMLElement{
|
||||||
this.renderPlusMinus()
|
this.renderPlusMinus()
|
||||||
this.renderContent()
|
this.renderContent()
|
||||||
|
|
||||||
this.#rootdoc.querySelector("div[name=root]").addEventListener("click", ev=>{
|
this.#rootdoc.querySelector("div[name=root]").addEventListener("click", ev => {
|
||||||
const src = ev.target.getAttribute("name")
|
const src = ev.target.getAttribute("name")
|
||||||
if(src === "plus"){
|
if (src === "plus") {
|
||||||
this.#value.push(this.#default)
|
this.#value.push(this.#value[this.#value.length - 1])
|
||||||
this.renderPlusMinus()
|
this.renderPlusMinus()
|
||||||
this.renderContent()
|
this.renderContent()
|
||||||
}else if(src === "minus"){
|
ev.preventDefault()
|
||||||
|
} else if (src === "minus") {
|
||||||
this.#value.pop()
|
this.#value.pop()
|
||||||
this.renderPlusMinus()
|
this.renderPlusMinus()
|
||||||
this.renderContent()
|
this.renderContent()
|
||||||
|
ev.preventDefault()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CCPSimpleInputWidgetController extends CCPBaseInputWidgetController{
|
class CCPSimpleInputWidgetController extends CCPBaseInputWidgetController {
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback() {
|
||||||
this.rootdoc.addEventListener("input", ev=>{
|
this.rootdoc.addEventListener("input", ev => {
|
||||||
if(ev.target.getAttribute("name") === this.name){
|
if (ev.target.getAttribute("name") === this.name) {
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
this.value[index] = ev.target.value
|
this.value[index] = ev.target.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
content(){
|
content() {
|
||||||
if(this.value.length <= 1){
|
if (this.value.length <= 1) {
|
||||||
return `<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>`
|
return `<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>`
|
||||||
}
|
}
|
||||||
var out =
|
var out =
|
||||||
this.value.map((c,i)=>{
|
this.value.map((c, i) => {
|
||||||
return `
|
return `
|
||||||
<input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
<input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
||||||
`
|
`
|
||||||
|
@ -309,31 +342,35 @@ class CCPSimpleInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input-simple', CCPSimpleInputWidgetController);
|
window.customElements.define('d4s-ccp-input-simple', CCPSimpleInputWidgetController);
|
||||||
|
|
||||||
class CCPEnumInputWidgetController extends CCPBaseInputWidgetController{
|
class CCPEnumInputWidgetController extends CCPBaseInputWidgetController {
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback() {
|
||||||
this.rootdoc.addEventListener("change", ev=>{
|
this.rootdoc.addEventListener("change", ev => {
|
||||||
if(ev.target.getAttribute("name") === this.name){
|
if (ev.target.getAttribute("name") === this.name) {
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
this.value[index] = ev.target.value
|
this.value[index] = ev.target.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
content(){
|
content() {
|
||||||
if(this.value.length <= 1){
|
if (this.value.length <= 1) {
|
||||||
const opts = this.options.map(o=>`<option value="${o}" ${ this.value.length && o === this.value[0] ? 'selected' : ''}>${o}</option>`).join("\n")
|
const opts = this.options ?
|
||||||
|
this.options.map(o => `<option value="${o}" ${this.value.length && o === this.value[0] ? 'selected' : ''}>${o}</option>`).join("\n") :
|
||||||
|
''
|
||||||
return `<select data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'disabled' : ''} ${this.required ? 'required' : ''}>${opts}</select>`
|
return `<select data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'disabled' : ''} ${this.required ? 'required' : ''}>${opts}</select>`
|
||||||
}
|
}
|
||||||
var out =
|
var out =
|
||||||
this.value.map((c,i)=>{
|
this.value.map((v, i) => {
|
||||||
const opts = this.options.map(o=>`<option value="${o}" ${ this.value.length >= i && c === this.value[i] ? 'selected' : ''}>${o}</option>`).join("\n")
|
const opts = this.options ?
|
||||||
|
this.options.map(o => `<option value="${o}" ${o === v ? 'selected' : ''}>${o}</option>`).join("\n") :
|
||||||
|
''
|
||||||
return `
|
return `
|
||||||
<select data-index="${i}" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'disabled' : ''} ${this.required ? 'required' : ''}>${opts}</select>
|
<select data-index="${i}" value="${v}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'disabled' : ''} ${this.required ? 'required' : ''}>${opts}</select>
|
||||||
`
|
`
|
||||||
}).join("\n")
|
}).join("\n")
|
||||||
return out
|
return out
|
||||||
|
@ -341,34 +378,39 @@ class CCPEnumInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input-enum', CCPEnumInputWidgetController);
|
window.customElements.define('d4s-ccp-input-enum', CCPEnumInputWidgetController);
|
||||||
|
|
||||||
class CCPChecklistInputWidgetController extends CCPBaseInputWidgetController{
|
class CCPChecklistInputWidgetController extends CCPBaseInputWidgetController {
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback() {
|
||||||
this.rootdoc.addEventListener("change", ev=>{
|
this.rootdoc.addEventListener("change", ev => {
|
||||||
if(ev.target.getAttribute("name") === this.name){
|
if (ev.target.getAttribute("name") === this.name) {
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
|
|
||||||
const elems = Array.prototype.slice.call(ev.currentTarget.querySelectorAll(`input[name='${this.name}'][data-index='${index}']`))
|
const elems = Array.prototype.slice.call(ev.currentTarget.querySelectorAll(`input[name='${this.name}'][data-index='${index}']`))
|
||||||
|
|
||||||
this.value[index] = elems.filter(e=>e.checked).map(e=>e.value).join(",")
|
this.value[index] = elems.filter(e => e.checked).map(e => e.value).join(",")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
buildOpts(index, selections){
|
buildOpts(index, selections) {
|
||||||
return this.options.map(o=>`
|
return this.options ? this.options.map(o => `
|
||||||
<div class="form-check form-switch form-check-inline">
|
<div class="form-check form-switch form-check-inline">
|
||||||
<label>${o}</label>
|
<label>${o}</label>
|
||||||
<input data-index="${index}" class="form-check-input" type="checkbox" name="${this.name}" value="${o}" ${this.readonly ? 'readonly' : ''} ${selections.indexOf(o) > -1 ? 'checked' : ''}/></div>
|
<input data-index="${index}" class="form-check-input" type="checkbox" name="${this.name}" value="${o}" ${this.readonly ? 'readonly' : ''} ${selections.indexOf(o) > -1 ? 'checked' : ''}/></div>
|
||||||
`).join("\n")
|
`).join("\n") :
|
||||||
|
`
|
||||||
|
<div class="form-check form-switch form-check-inline">
|
||||||
|
<span class="muted text-danger">No options supplied</span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
content(){
|
content() {
|
||||||
if(this.value.length === 1){
|
if (this.value.length === 1) {
|
||||||
const opts = this.buildOpts(0, this.value.length ? this.value[0].split(",") : [])
|
const opts = this.buildOpts(0, this.value.length ? this.value[0].split(",") : [])
|
||||||
return `
|
return `
|
||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
|
@ -377,7 +419,7 @@ class CCPChecklistInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
var out =
|
var out =
|
||||||
this.value.map((c,i)=>{
|
this.value.map((c, i) => {
|
||||||
const opts = this.buildOpts(i, c.split(","))
|
const opts = this.buildOpts(i, c.split(","))
|
||||||
return `
|
return `
|
||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
|
@ -390,35 +432,34 @@ class CCPChecklistInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input-checklist', CCPChecklistInputWidgetController);
|
window.customElements.define('d4s-ccp-input-checklist', CCPChecklistInputWidgetController);
|
||||||
|
|
||||||
class CCPBooleanInputWidgetController extends CCPBaseInputWidgetController{
|
class CCPBooleanInputWidgetController extends CCPBaseInputWidgetController {
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback() {
|
||||||
this.value.forEach((v,i)=>{ this.value[i] = v === "" ? false : v})
|
this.rootdoc.addEventListener("input", ev => {
|
||||||
this.rootdoc.addEventListener("input", ev=>{
|
if (ev.target.getAttribute("name") === this.name) {
|
||||||
if(ev.target.getAttribute("name") === this.name){
|
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
this.value[index] = ev.target.checked
|
this.value[index] = ev.target.checked
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
content(){
|
content() {
|
||||||
if(this.value.length <= 1){
|
if (this.value.length <= 1) {
|
||||||
return `
|
return `
|
||||||
<div class="my-2 form-check form-switch form-check-inline">
|
<div class="my-2 form-check form-switch form-check-inline">
|
||||||
<input data-index="0" value="${this.value.length ? this.value[0] : false}" class="my-2 form-check-input" type="checkbox" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.value[0] == "true" ? 'checked' : ''} value="${ this.value[0]}"/>
|
<input data-index="0" value="${this.value.length ? this.value[0] : false}" class="my-2 form-check-input" type="checkbox" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.value[0] === "true" || this.value[0] === true ? 'checked' : ''} value="${this.value[0]}"/>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
var out =
|
var out =
|
||||||
this.value.map((c,i)=>{
|
this.value.map((c, i) => {
|
||||||
return `
|
return `
|
||||||
<div class="my-2 form-check form-switch form-check-inline">
|
<div class="my-2 form-check form-switch form-check-inline">
|
||||||
<input data-index="${i}" value="${c}" class="my-2 form-check-input" type="checkbox" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${c == "true" ? 'checked' : ''} value="${this.value[i]}"/>
|
<input data-index="${i}" value="${c}" class="my-2 form-check-input" type="checkbox" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${c === "true" || c === true ? 'checked' : ''}/>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}).join("\n")
|
}).join("\n")
|
||||||
|
@ -427,27 +468,27 @@ class CCPBooleanInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input-boolean', CCPBooleanInputWidgetController);
|
window.customElements.define('d4s-ccp-input-boolean', CCPBooleanInputWidgetController);
|
||||||
|
|
||||||
class CCPTextAreaWidgetController extends CCPBaseInputWidgetController{
|
class CCPTextAreaWidgetController extends CCPBaseInputWidgetController {
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback() {
|
||||||
this.rootdoc.addEventListener("input", ev=>{
|
this.rootdoc.addEventListener("input", ev => {
|
||||||
if(ev.target.getAttribute("name") === this.name){
|
if (ev.target.getAttribute("name") === this.name) {
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
this.value[index] = ev.target.value
|
this.value[index] = ev.target.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
content(){
|
content() {
|
||||||
if(this.value.length <= 1){
|
if (this.value.length <= 1) {
|
||||||
return `<textarea data-index="0" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${this.value.length ? this.value[0] : ''}</textarea>`
|
return `<textarea data-index="0" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${this.value.length ? this.value[0] : ''}</textarea>`
|
||||||
}
|
}
|
||||||
var out =
|
var out =
|
||||||
this.value.map((c,i)=>{
|
this.value.map((c, i) => {
|
||||||
return `
|
return `
|
||||||
<textarea name="${this.name}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${c}</textarea>
|
<textarea name="${this.name}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${c}</textarea>
|
||||||
`
|
`
|
||||||
|
@ -457,19 +498,19 @@ class CCPTextAreaWidgetController extends CCPBaseInputWidgetController{
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input-textarea', CCPTextAreaWidgetController);
|
window.customElements.define('d4s-ccp-input-textarea', CCPTextAreaWidgetController);
|
||||||
|
|
||||||
class CCPFileInputWidgetController extends CCPBaseInputWidgetController{
|
class CCPFileInputWidgetController extends CCPBaseInputWidgetController {
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback() {
|
||||||
this.rootdoc.addEventListener("change", ev=>{
|
this.rootdoc.addEventListener("change", ev => {
|
||||||
const tgt = ev.target
|
const tgt = ev.target
|
||||||
if(tgt.getAttribute("name") === this.name){
|
if (tgt.getAttribute("name") === this.name) {
|
||||||
const index = Number(tgt.getAttribute("data-index"))
|
const index = Number(tgt.getAttribute("data-index"))
|
||||||
const file = ev.target.files[0]
|
const file = ev.target.files[0]
|
||||||
if(file.size > 100*1024){
|
if (file.size > 100 * 1024) {
|
||||||
alert("This input allows only small files (100K). Use references instead ")
|
alert("This input allows only small files (100K). Use references instead ")
|
||||||
ev.stopPropagation()
|
ev.stopPropagation()
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
|
@ -477,32 +518,37 @@ class CCPFileInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
reader.addEventListener('load', ev=>{
|
reader.addEventListener('load', ev => {
|
||||||
let encoded = ev.target.result.toString().replace(/^data:(.*,)?/, '');
|
let encoded = ev.target.result.toString().replace(/^data:(.*,)?/, '');
|
||||||
if ((encoded.length % 4) > 0) {
|
if ((encoded.length % 4) > 0) {
|
||||||
encoded += '='.repeat(4 - (encoded.length % 4));
|
encoded += '='.repeat(4 - (encoded.length % 4));
|
||||||
}
|
}
|
||||||
this.value[index] = encoded
|
this.value[index] = encoded
|
||||||
this.querySelector("small[name=preview]").textContent = encoded.substr(0,5) + "..." + encoded.substr(encoded.length-5)
|
this.querySelector("small[name=preview]").textContent =
|
||||||
|
encoded.length > 15 ? encoded.substring(0, 5) + "..." + encoded.substring(encoded.length - 5) : encoded
|
||||||
|
const newev = new Event("input", { bubbles : true})
|
||||||
|
this.dispatchEvent(newev)
|
||||||
})
|
})
|
||||||
reader.readAsDataURL(file)
|
reader.readAsDataURL(file)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
content(){
|
content() {
|
||||||
if(this.value.length <= 1){
|
if (this.value.length <= 1) {
|
||||||
const v = this.value.length ? this.value[0] : ''
|
const v = this.value.length ? this.value[0] : ''
|
||||||
|
const preview = typeof(v) === "string" ? v.substring(0, 5) + "..." + v.substring(v.length - 5) : ""
|
||||||
return `
|
return `
|
||||||
<input type="file" data-index="0" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required && !v ? 'required' : ''} value="${v}"/>
|
<input type="file" data-index="0" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required && !v ? 'required' : ''} value="${v}"/>
|
||||||
<small name ="preview" class="form-text text-muted">${v.substr(0,5) + "..." + v.substr(v.length-5)}</small>
|
<small name ="preview" class="form-text text-muted">${preview}</small>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
var out =
|
var out =
|
||||||
this.value.map((c,i)=>{
|
this.value.map((c, i) => {
|
||||||
|
const preview = typeof(v) === "string" ? c.substring(0, 5) + "..." + c.substring(v.length - 5) : ""
|
||||||
return `
|
return `
|
||||||
<input type="file" data-index="${i}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''} value="${c}"/>
|
<input type="file" data-index="${i}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''} value="${c}"/>
|
||||||
<small name ="preview" class="form-text text-muted">${c.substr(0,5) + "..." + c.substr(s.length-5)}</small>
|
<small name ="preview" class="form-text text-muted">${preview}</small>
|
||||||
`
|
`
|
||||||
}).join("\n")
|
}).join("\n")
|
||||||
return out
|
return out
|
||||||
|
@ -510,81 +556,166 @@ class CCPFileInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input-file', CCPFileInputWidgetController);
|
window.customElements.define('d4s-ccp-input-file', CCPFileInputWidgetController);
|
||||||
|
|
||||||
class CCPRemoteFileInputWidgetController extends CCPBaseInputWidgetController{
|
class CCPRemoteFileInputWidgetController extends CCPBaseInputWidgetController {
|
||||||
|
|
||||||
constructor(){
|
#publicorprotected_dialog = null;
|
||||||
|
#target = null
|
||||||
|
#publiclink = null;
|
||||||
|
#protectedlink = null;
|
||||||
|
#index = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
async setPublicLink(l){
|
||||||
this.rootdoc.addEventListener("input", ev=>{
|
const b = document.querySelector("d4s-boot-2")
|
||||||
if(ev.target.getAttribute("name") === this.name){
|
const link = await b.secureFetch(l)
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
if(link.ok){
|
||||||
this.value[index] = ev.target.value
|
const result = await link.json()
|
||||||
|
this.#target.value = this.value[this.#index] = result
|
||||||
|
this.#publicorprotected_dialog.style.display = "none"
|
||||||
|
this.#publicorprotected_dialog.classList.remove("show")
|
||||||
|
this.#publiclink = this.#protectedlink = this.#target = this.#index = null
|
||||||
|
const newev = new Event("input", { bubbles : true})
|
||||||
|
this.dispatchEvent(newev)
|
||||||
|
}else{
|
||||||
|
alert("Unable to get public link for item")
|
||||||
|
this.#target.value = this.value[this.#index] = ""
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addToolContent(){
|
addToolContent() {
|
||||||
const iss = document.querySelector("d4s-boot-2").loginToken.iss;
|
const iss = document.querySelector("d4s-boot-2").loginToken.iss;
|
||||||
const addresses = {
|
const addresses = {
|
||||||
"https://accounts.dev.d4science.org/auth/realms/d4science" : "https://workspace-repository.dev.d4science.org/storagehub/workspace",
|
"https://accounts.dev.d4science.org/auth/realms/d4science": "https://workspace-repository.dev.d4science.org/storagehub/workspace",
|
||||||
"https://accounts.pre.d4science.org/auth/realms/d4science" : "https://pre.d4science.org/workspace",
|
"https://accounts.pre.d4science.org/auth/realms/d4science": "https://pre.d4science.org/workspace",
|
||||||
"https://accounts.d4science.org/auth/realms/d4science" : "https://api.d4science.org/workspace"
|
"https://accounts.d4science.org/auth/realms/d4science": "https://api.d4science.org/workspace"
|
||||||
};
|
};
|
||||||
this.rootdoc.querySelector("div[name=tools]").innerHTML += `
|
this.rootdoc.querySelector("div[name=tools]").innerHTML += `
|
||||||
<svg name="trigger" style="width:24;height:24;fill:#007bff; cursor:pointer" viewBox="0 -960 960 960">
|
<svg name="trigger" style="width:24;height:24;fill:#007bff; cursor:pointer" viewBox="0 -960 960 960">
|
||||||
<path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640H447l-80-80H160v480l96-320h684L837-217q-8 26-29.5 41.5T760-160H160Zm84-80h516l72-240H316l-72 240Zm0 0 72-240-72 240Zm-84-400v-80 80Z"/>
|
<path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640H447l-80-80H160v480l96-320h684L837-217q-8 26-29.5 41.5T760-160H160Zm84-80h516l72-240H316l-72 240Zm0 0 72-240-72 240Zm-84-400v-80 80Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
<div name="ws" class="d-none position-absolute shadow border border-primary bg-light p-2" style="left: 100%;z-index:1000; line-height:1.5rem;overflow:hidden;padding:5px;">
|
<div name="ws" class="d-none position-absolute shadow border border-primary bg-light p-2" style="right:0;z-index:1000; line-height:1.5rem;overflow:hidden;padding:5px;">
|
||||||
<div class="d-flex justify-content-between" style="border-bottom: solid 1px gray;">
|
<div class="mb-1" style="border-bottom: solid 1px gray;">
|
||||||
<h5 class="text-secondary">Access your workspace</h5>
|
<div class="d-flex justify-content-between m-0">
|
||||||
|
<h5 class="text-secondary m-0">Access your workspace</h5>
|
||||||
<span class="btn text-danger p-0" style="font-weight:bold" name="closebtn">x</span>
|
<span class="btn text-danger p-0" style="font-weight:bold" name="closebtn">x</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="min-width:350px; max-width:500px;overflow-x:hidden;text-wrap:nowrap;text-overflow: ellipsis;min-height:5rem; max-height:10rem;">
|
<small class="text-muted m-0">Select an item or drag and drop it to a proper input</small>
|
||||||
|
</div>
|
||||||
|
<div style="min-width:350px; max-width:500px;overflow:auto;etxt-wrap:nowrap;text-overflow: ellipsis;min-height:5rem; max-height:10rem;">
|
||||||
<d4s-storage-tree
|
<d4s-storage-tree
|
||||||
base-url="${addresses[iss]}"
|
base-url="${addresses[iss]}"
|
||||||
file-download-enabled="true"
|
file-download-enabled="true"
|
||||||
show-files="true"
|
show-files="true"
|
||||||
allow-drag="true"/>
|
allow-drag="true"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="d-flex justify-content-end mt-1 pt-1" style="border-top: solid 1px gray;">
|
||||||
|
<span class="btn btn-primary" name="selectbtn">SELECT</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal fade" style="background-color:rgba(0,0,0,0.3)" name="publicorprotected" 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 use the public link or the protected link (requires authentication and authorization).
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<span name="public" class="btn btn-info">Public link</span>
|
||||||
|
<span name="protected" class="btn btn-primary">Protected link</span>
|
||||||
|
<span name="cancel" class="btn btn-danger">Cancel</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
this.#publicorprotected_dialog = this.rootdoc.querySelector("div.modal[name=publicorprotected]")
|
||||||
|
this.#publicorprotected_dialog.addEventListener("click", ev => {
|
||||||
|
ev.stopPropagation()
|
||||||
|
ev.preventDefault()
|
||||||
|
const name = ev.target.getAttribute("name")
|
||||||
|
if(this.#index != null && this.#target != null){
|
||||||
|
if(name === "public") {
|
||||||
|
this.setPublicLink(this.#publiclink);
|
||||||
|
}
|
||||||
|
else if(name === "protected"){
|
||||||
|
this.#target.value = this.value[this.#index] = this.#protectedlink;
|
||||||
|
this.#publicorprotected_dialog.style.display = "none"
|
||||||
|
this.#publicorprotected_dialog.classList.remove("show")
|
||||||
|
this.#publiclink = this.#protectedlink = this.#target = this.#index = null
|
||||||
|
const newev = new Event("input", { bubbles : true})
|
||||||
|
this.dispatchEvent(newev)
|
||||||
|
}else{
|
||||||
|
this.#publicorprotected_dialog.style.display = "none"
|
||||||
|
this.#publicorprotected_dialog.classList.remove("show")
|
||||||
|
this.#publiclink = this.#protectedlink = this.#target = this.#index = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const ws = this.rootdoc.querySelector("div[name=ws]")
|
const ws = this.rootdoc.querySelector("div[name=ws]")
|
||||||
this.rootdoc.querySelector("svg[name=trigger]").addEventListener("click", ev=>{
|
const st = ws.querySelector("d4s-storage-tree")
|
||||||
|
this.addEventListener("click", ev=>{
|
||||||
|
ev.stopPropagation()
|
||||||
|
ev.preventDefault()
|
||||||
|
if (ev.target.getAttribute("name") == "selectbtn") {
|
||||||
|
this.#publicorprotected_dialog.style.display = "block"
|
||||||
|
this.#publicorprotected_dialog.classList.add("show")
|
||||||
|
this.#target = this.querySelector("input")
|
||||||
|
this.#index = 0
|
||||||
|
this.#publiclink = st.d4sWorkspace.getPublicLink(st.currentId)
|
||||||
|
this.#protectedlink = st.d4sWorkspace.getDownloadLink(st.currentId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.rootdoc.querySelector("svg[name=trigger]").addEventListener("click", ev => {
|
||||||
ws.classList.toggle("d-none")
|
ws.classList.toggle("d-none")
|
||||||
|
ev.preventDefault()
|
||||||
|
ev.stopPropagation()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.rootdoc.querySelector("span[name=closebtn]").addEventListener("click", ev=>{
|
this.rootdoc.querySelector("span[name=closebtn]").addEventListener("click", ev => {
|
||||||
ws.classList.add("d-none")
|
ws.classList.add("d-none")
|
||||||
|
ev.preventDefault()
|
||||||
|
ev.stopPropagation()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.addEventListener("dragover", ev=>{
|
this.addEventListener("dragover", ev => {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.addEventListener("drop", ev=>{
|
this.addEventListener("drop", ev => {
|
||||||
ev.stopPropagation()
|
ev.stopPropagation()
|
||||||
if(ev.target.getAttribute("name") == this.name && ev.target.getAttribute("data-index") != null){
|
if (ev.target.getAttribute("name") == this.name && ev.target.getAttribute("data-index") != null) {
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
this.#publicorprotected_dialog.style.display = "block"
|
||||||
ev.target.value = ev.dataTransfer.getData("text/plain+downloadlink")
|
this.#publicorprotected_dialog.classList.add("show")
|
||||||
this.value[index] = ev.target.value
|
this.#target = ev.target
|
||||||
|
this.#index = Number(ev.target.getAttribute("data-index"))
|
||||||
|
this.#publiclink = ev.dataTransfer.getData("text/plain+publiclink")
|
||||||
|
this.#protectedlink = ev.dataTransfer.getData("text/plain+downloadlink")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener("keydown", ev=>{
|
this.addEventListener("input", ev => {
|
||||||
if(ev.code == 'Escape' ) ws.classList.add("d-none");
|
ev.stopPropagation()
|
||||||
|
if (ev.target.getAttribute("name") == this.name && ev.target.getAttribute("data-index") != null) {
|
||||||
|
this.value[Number(ev.target.getAttribute("data-index"))] = ev.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener("keydown", ev => {
|
||||||
|
if (ev.code == 'Escape') ws.classList.add("d-none");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
content(){
|
content() {
|
||||||
this.addToolContent()
|
if(!this.readonly && !this.querySelector("div[name=ws]")) this.addToolContent();
|
||||||
if(this.value.length <= 1){
|
if (this.value.length <= 1) {
|
||||||
return `
|
return `
|
||||||
<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>`
|
<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>`
|
||||||
}
|
}
|
||||||
var out =
|
var out =
|
||||||
this.value.map((c,i)=>{
|
this.value.map((c, i) => {
|
||||||
return `
|
return `
|
||||||
<input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
<input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
||||||
`
|
`
|
||||||
|
@ -594,32 +725,32 @@ class CCPRemoteFileInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input-remotefile', CCPRemoteFileInputWidgetController);
|
window.customElements.define('d4s-ccp-input-remotefile', CCPRemoteFileInputWidgetController);
|
||||||
|
|
||||||
class CCPSecretInputWidgetController extends CCPBaseInputWidgetController{
|
class CCPSecretInputWidgetController extends CCPBaseInputWidgetController {
|
||||||
|
|
||||||
constructor(){
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback() {
|
||||||
this.rootdoc.addEventListener("input", ev=>{
|
this.rootdoc.addEventListener("input", ev => {
|
||||||
if(ev.target.getAttribute("name") === this.name){
|
if (ev.target.getAttribute("name") === this.name) {
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
this.value[index] = ev.target.value
|
this.value[index] = ev.target.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.rootdoc.addEventListener("click", ev=>{
|
this.rootdoc.addEventListener("click", ev => {
|
||||||
if(ev.target.getAttribute("name") === "password_toggle"){
|
if (ev.target.getAttribute("name") === "password_toggle") {
|
||||||
const index = Number(ev.target.getAttribute("data-index"))
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
const field = this.rootdoc.querySelector(`input[data-index='${index}']`)
|
const field = this.rootdoc.querySelector(`input[data-index='${index}']`)
|
||||||
if(field.type === "text") field.type="password";
|
if (field.type === "text") field.type = "password";
|
||||||
else field.type = "text";
|
else field.type = "text";
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
content(){
|
content() {
|
||||||
if(this.value.length <= 1){
|
if (this.value.length <= 1) {
|
||||||
return `
|
return `
|
||||||
<div style="position:relative">
|
<div style="position:relative">
|
||||||
<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="password" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="password" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
||||||
|
@ -628,7 +759,7 @@ class CCPSecretInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
var out =
|
var out =
|
||||||
this.value.map((c,i)=>{
|
this.value.map((c, i) => {
|
||||||
return `
|
return `
|
||||||
<div style="position:relative">
|
<div style="position:relative">
|
||||||
<input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="password" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
<input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="password" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
||||||
|
|
|
@ -23,7 +23,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
return this.#index
|
return this.#index
|
||||||
}
|
}
|
||||||
|
|
||||||
computeDefaultInputType(){
|
/*computeDefaultInputType(){
|
||||||
if(this.#input.schema.format === "secret"){
|
if(this.#input.schema.format === "secret"){
|
||||||
return "password"
|
return "password"
|
||||||
}else if(this.#input.schema.format === "date"){
|
}else if(this.#input.schema.format === "date"){
|
||||||
|
@ -34,14 +34,15 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
return "datetime-local"
|
return "datetime-local"
|
||||||
}
|
}
|
||||||
return "text"
|
return "text"
|
||||||
}
|
}*/
|
||||||
|
|
||||||
isSelectedFormat(fmt){
|
isSelectedFormat(fmt){
|
||||||
return this.#input.schema.format === fmt
|
return this.#input.schema.format === fmt
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDefaultByType(){
|
renderDefaultByType(){
|
||||||
if(this.#input.schema.format === "code"){
|
return `<d4s-ccp-input name="default" input="${btoa(JSON.stringify(this.#input))}"></d4s-ccp-input>`
|
||||||
|
/*if(this.#input.schema.format === "code"){
|
||||||
return `
|
return `
|
||||||
<textarea rows="5" name="default" class="form-control" placeholder="default">${this.#input.schema.default}</textarea>
|
<textarea rows="5" name="default" class="form-control" placeholder="default">${this.#input.schema.default}</textarea>
|
||||||
`
|
`
|
||||||
|
@ -49,7 +50,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
return `
|
return `
|
||||||
<input type="${this.computeDefaultInputType()}" value="${this.#input.schema.default}" name="default" class="form-control" placeholder="default"/>
|
<input type="${this.computeDefaultInputType()}" value="${this.#input.schema.default}" name="default" class="form-control" placeholder="default"/>
|
||||||
`
|
`
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDeleteButton(){
|
renderDeleteButton(){
|
||||||
|
@ -62,6 +63,8 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
this.#index = i
|
this.#index = i
|
||||||
this.#input = input
|
this.#input = input
|
||||||
this.#type = input.schema.enum ? "enum" : "string"
|
this.#type = input.schema.enum ? "enum" : "string"
|
||||||
|
const minOccurs = input.minOccurs = Number(input.minOccurs) ? Number(input.minOccurs) : 0
|
||||||
|
const maxOccurs = input.maxOccurs = Number(input.maxOccurs) ? Number(input.maxOccurs) : 0
|
||||||
this.innerHTML = `
|
this.innerHTML = `
|
||||||
<details ${ reopen ? 'open' : ''}>
|
<details ${ reopen ? 'open' : ''}>
|
||||||
<summary class="mb-3">
|
<summary class="mb-3">
|
||||||
|
@ -85,22 +88,22 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<div class="form-field" title="Type">
|
<div class="form-field" title="Type">
|
||||||
<label>Type</label>
|
<label>Type</label>
|
||||||
<select name="type" class="form-control" placeholder="type" value="${this.#type}" ${ input.id === 'ccpimage' ? 'readonly' : ''}>
|
<select name="type" class="form-control" placeholder="type" value="${this.#type}">
|
||||||
<option value="string" ${this.#type === "string" ? "selected" : ""}>String</option>
|
<option value="string" ${this.#type === "string" ? "selected" : ""}>String</option>
|
||||||
<option value="enum" ${this.#type === "enum" ? "selected" : ""}>Enum</option>
|
${ input.id === 'ccpimage' ? '' : `<option value="enum" ${this.#type === "enum" ? "selected" : ""}>Enum</option>` }
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-field" title="Minimum occurrences">
|
<div class="form-field" title="Minimum occurrences">
|
||||||
<label>Min occurs</label>
|
<label>Min occurs</label>
|
||||||
<input value="${input.minOccurs}" type="number" min="0" step="1" name="minOccurs" value="${input.minOccurs}" required="required" class="form-control" placeholder="minOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
<input value="${minOccurs}" type="number" min="0" step="1" name="minOccurs" value="${minOccurs}" required="required" class="form-control" placeholder="minOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-field" title="Maximum occurrences">
|
<div class="form-field" title="Maximum occurrences">
|
||||||
<label>Max occurs</label>
|
<label>Max occurs</label>
|
||||||
<input value="${input.maxOccurs}" type="number" min="0" step="1" name="maxOccurs" value="${input.maxOccurs}" required="required" class="form-control" placeholder="maxOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
<input value="${maxOccurs}" type="number" min="0" step="1" name="maxOccurs" value="${maxOccurs}" required="required" class="form-control" placeholder="maxOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col" title="Read only">
|
<div class="col" title="Read only">
|
||||||
|
@ -123,7 +126,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
<option value="boolean" ${this.isSelectedFormat('boolean') ? "selected" : ""}>True/False</option>
|
<option value="boolean" ${this.isSelectedFormat('boolean') ? "selected" : ""}>True/False</option>
|
||||||
<option value="code" ${this.isSelectedFormat('code') ? "selected" : ""}>Code</option>
|
<option value="code" ${this.isSelectedFormat('code') ? "selected" : ""}>Code</option>
|
||||||
<option value="file" ${this.isSelectedFormat('file') ? "selected" : ""}>File</option>
|
<option value="file" ${this.isSelectedFormat('file') ? "selected" : ""}>File</option>
|
||||||
<option value="remotefile" ${this.isSelectedFormat('remotefile') ? "selected" : ""}>Remote file</option>
|
<option value="remotefile" ${this.isSelectedFormat('remotefile') ? "selected" : ""}>Workspace file</option>
|
||||||
<option value="secret" ${this.isSelectedFormat('secret') ? "selected" : ""}>Secret</option>
|
<option value="secret" ${this.isSelectedFormat('secret') ? "selected" : ""}>Secret</option>
|
||||||
<option value="url" ${this.isSelectedFormat('url') ? "selected" : ""}>Url</option>
|
<option value="url" ${this.isSelectedFormat('url') ? "selected" : ""}>Url</option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -150,8 +153,9 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div name="input-default" class="mb-3">
|
<div name="input-default" class="mb-3">
|
||||||
<div class="form-field" title="Default value">
|
<label>Default value</label>
|
||||||
${this.renderDefaultByType()}
|
<div class="form-field border border-info px-2 py-1" style="background-color:#0dcaf022" title="Default value">
|
||||||
|
<div name="default-container">${this.renderDefaultByType()}</div>
|
||||||
<span style="user-select:none;position:relative;top:-1.6rem;float:right;cursor:pointer" name="password_toggle" class="${this.isSelectedFormat('secret') ? 'inline' : 'd-none'}">👁</span>
|
<span style="user-select:none;position:relative;top:-1.6rem;float:right;cursor:pointer" name="password_toggle" class="${this.isSelectedFormat('secret') ? 'inline' : 'd-none'}">👁</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -167,53 +171,63 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const defaultinp = this.querySelector("div[name='input-default']")
|
||||||
|
defaultinp.addEventListener("input", ev=>{
|
||||||
|
const inp = this.querySelector("d4s-ccp-input")
|
||||||
|
this.#input.schema.default = inp.value
|
||||||
|
})
|
||||||
|
|
||||||
|
defaultinp.addEventListener("change", ev=>{
|
||||||
|
const inp = this.querySelector("d4s-ccp-input")
|
||||||
|
this.#input.schema.default = inp.value
|
||||||
|
})
|
||||||
|
|
||||||
|
defaultinp.addEventListener("click", ev=>{
|
||||||
|
const src = ev.target.getAttribute("name")
|
||||||
|
if (src === "plus" || src === "minus") {
|
||||||
|
const inp = this.querySelector("d4s-ccp-input")
|
||||||
|
this.#input.schema.default = inp.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.addEventListener("input", ev=>{
|
this.addEventListener("input", ev=>{
|
||||||
const val = ev.target.value
|
const val = ev.target.value
|
||||||
const ename = ev.target.getAttribute("name")
|
const ename = ev.target.getAttribute("name")
|
||||||
|
const display = this.querySelector("div[name='default-container']")
|
||||||
if(ename === "id"){
|
if(ename === "id"){
|
||||||
this.#input.id = val
|
this.#input.id = val
|
||||||
}
|
}
|
||||||
else if(ename === "title"){
|
else if(ename === "title"){
|
||||||
this.#input.title = val
|
this.#input.title = val
|
||||||
|
display.innerHTML = this.renderDefaultByType()
|
||||||
}
|
}
|
||||||
else if(ename === "description"){
|
else if(ename === "description"){
|
||||||
this.#input.description = val
|
this.#input.description = val
|
||||||
|
display.innerHTML = this.renderDefaultByType()
|
||||||
}
|
}
|
||||||
else if(ename === "minOccurs"){
|
else if(ename === "minOccurs"){
|
||||||
this.#input.minOccurs = val
|
this.#input.minOccurs = Number(val) ? Number(val) : 0
|
||||||
|
display.innerHTML = this.renderDefaultByType()
|
||||||
}
|
}
|
||||||
else if(ename === "maxOccurs"){
|
else if(ename === "maxOccurs"){
|
||||||
this.#input.maxOccurs = val
|
this.#input.maxOccurs = Number(val) ? Number(val) : 0
|
||||||
|
display.innerHTML = this.renderDefaultByType()
|
||||||
}
|
}
|
||||||
else if(ename === "format"){
|
else if(ename === "format"){
|
||||||
this.#input.schema.format = val
|
this.#input.schema.format = val
|
||||||
//this.render(this.#input, this.#index, true)
|
display.innerHTML = this.renderDefaultByType()
|
||||||
/*this.querySelector("div[name=input-default] span[name=password_toggle]").classList.add("d-none")
|
|
||||||
this.querySelector("div[name=input-default] input[name=default]").type = ""
|
|
||||||
if(this.#input.schema.format === "secret"){
|
|
||||||
this.querySelector("div[name=input-default] input[name=default]").type = "password"
|
|
||||||
this.querySelector("div[name=input-default] span[name=password_toggle]").classList.remove("d-none")
|
|
||||||
}else if(this.#input.schema.format === "date"){
|
|
||||||
this.querySelector("div[name=input-default] input[name=default]").type = "date"
|
|
||||||
}else if(this.#input.schema.format === "time"){
|
|
||||||
this.querySelector("div[name=input-default] input[name=default]").type = "time"
|
|
||||||
}else if(this.#input.schema.format === "dateTime"){
|
|
||||||
this.querySelector("div[name=input-default] input[name=default]").type = "dateTime"
|
|
||||||
}else if(this.#input.schema.format === "file"){
|
|
||||||
this.querySelector("div[name=input-default] input[name=default]").type = "file"
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
else if(ename === "contentMediaType"){
|
else if(ename === "contentMediaType"){
|
||||||
this.#input.schema.contentMediaType = val
|
this.#input.schema.contentMediaType = val
|
||||||
|
display.innerHTML = this.renderDefaultByType()
|
||||||
}
|
}
|
||||||
else if(ename === "options"){
|
else if(ename === "options"){
|
||||||
this.#input.schema.enum = val.split(",")
|
this.#input.schema.enum = val.split(",")
|
||||||
}
|
display.innerHTML = this.renderDefaultByType()
|
||||||
else if(ename === "default"){
|
|
||||||
this.#input.schema.default = val
|
|
||||||
}
|
}
|
||||||
else if(ename === "readonly"){
|
else if(ename === "readonly"){
|
||||||
this.#input.schema.readOnly = ev.target.checked
|
this.#input.schema.readOnly = ev.target.checked
|
||||||
|
display.innerHTML = this.renderDefaultByType()
|
||||||
}
|
}
|
||||||
else if(ename === "type"){
|
else if(ename === "type"){
|
||||||
this.#type = ev.target.value
|
this.#type = ev.target.value
|
||||||
|
@ -226,9 +240,9 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
this.querySelector("div[name=string-input]").classList.remove("d-none")
|
this.querySelector("div[name=string-input]").classList.remove("d-none")
|
||||||
delete this.#input.schema['enum']
|
delete this.#input.schema['enum']
|
||||||
}
|
}
|
||||||
|
display.innerHTML = this.renderDefaultByType()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -220,9 +220,9 @@ class CCPMethodList extends HTMLElement{
|
||||||
const f = filter.toLowerCase()
|
const f = filter.toLowerCase()
|
||||||
this.#filtered = this.#data.filter(d=>{
|
this.#filtered = this.#data.filter(d=>{
|
||||||
return false ||
|
return false ||
|
||||||
(d.title.toLowerCase().indexOf(f) !== -1)||
|
(d.title && d.title.toLowerCase().indexOf(f) !== -1)||
|
||||||
(d.description.indexOf(f) !== -1) ||
|
(d.description && d.description.indexOf(f) !== -1) ||
|
||||||
(d.keywords.map(k=>k.toLowerCase()).filter(i=>i.indexOf(f) !== -1)).length
|
(Array.isArray(d.keywords) && d.keywords.map(k=>k.toLowerCase()).filter(i=>i.indexOf(f) !== -1)).length
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.groupBy()
|
this.groupBy()
|
||||||
|
|
|
@ -12,10 +12,13 @@
|
||||||
<h3>CDN sandbox</h3>
|
<h3>CDN sandbox</h3>
|
||||||
<p>Select the section you want to enter</p>
|
<p>Select the section you want to enter</p>
|
||||||
</header>
|
</header>
|
||||||
<div class="d-flex">
|
<div class="d-flex flex-column">
|
||||||
<div class="d-inline-block">
|
<div class="d-inline-block">
|
||||||
<a href="storage/index.html">D4S Workspace</a> (OIDC protected)
|
<a href="storage/index.html">D4S Workspace</a> (OIDC protected)
|
||||||
</div>
|
</div>
|
||||||
|
<div class="d-inline-block">
|
||||||
|
<a href="ccp/index.html">CCP</a> (OIDC protected)
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,7 @@ class D4SStorageHtmlElement extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
let linkElem = document.createElement('link');
|
|
||||||
linkElem.setAttribute('rel', 'stylesheet');
|
|
||||||
linkElem.setAttribute('href', 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css');
|
|
||||||
this.shadowRoot.appendChild(linkElem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get srcBaseURL() {
|
get srcBaseURL() {
|
||||||
|
@ -98,7 +95,7 @@ class D4SStorageToolbar extends D4SStorageHtmlElement {
|
||||||
|
|
||||||
class D4SStorageTree extends D4SStorageHtmlElement {
|
class D4SStorageTree extends D4SStorageHtmlElement {
|
||||||
|
|
||||||
static tree_event_name = "d4s-toolbar";
|
static tree_event_name = "d4s-tree";
|
||||||
static folder_data_event_name = "d4s-folder-data";
|
static folder_data_event_name = "d4s-folder-data";
|
||||||
|
|
||||||
static dataid_attr = 'data-id';
|
static dataid_attr = 'data-id';
|
||||||
|
@ -226,8 +223,10 @@ class D4SStorageTree extends D4SStorageHtmlElement {
|
||||||
|
|
||||||
const div = document.createElement('div')
|
const div = document.createElement('div')
|
||||||
div.innerHTML = /*css*/`
|
div.innerHTML = /*css*/`
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<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>
|
<style>
|
||||||
.selected > span {
|
.selected > .li-title {
|
||||||
background-color: ${this.#selectedbgcolor};
|
background-color: ${this.#selectedbgcolor};
|
||||||
}
|
}
|
||||||
ul.root {
|
ul.root {
|
||||||
|
@ -238,6 +237,12 @@ class D4SStorageTree extends D4SStorageHtmlElement {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding-left: 0.6em;
|
padding-left: 0.6em;
|
||||||
}
|
}
|
||||||
|
li{
|
||||||
|
color: #444444;
|
||||||
|
}
|
||||||
|
li:hover{
|
||||||
|
color: #555555;
|
||||||
|
}
|
||||||
li {
|
li {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -378,7 +383,7 @@ class D4SStorageTree extends D4SStorageHtmlElement {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent(
|
new CustomEvent(
|
||||||
D4SStorageTree.tree_event_name,
|
D4SStorageTree.tree_event_name,
|
||||||
{detail: {id: id}}
|
{detail: {id: id, bubbles: true}}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -417,15 +422,43 @@ class D4SStorageTree extends D4SStorageHtmlElement {
|
||||||
data
|
data
|
||||||
.filter(item => this.showFiles || item['@class'].includes('FolderItem'))
|
.filter(item => this.showFiles || item['@class'].includes('FolderItem'))
|
||||||
.forEach(item => {
|
.forEach(item => {
|
||||||
ul.appendChild(this.createListItem(item.title, item.id, item['@class'], parentId));
|
ul.appendChild(this.createListItem(item, parentId));
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
ul.appendChild(this.createListItem(data.displayName ? data.displayName : data.title, data.id, data['@class'], parentId));
|
ul.appendChild(this.createListItem(data, parentId));
|
||||||
}
|
}
|
||||||
parentElement.appendChild(ul);
|
parentElement.appendChild(ul);
|
||||||
}
|
}
|
||||||
|
|
||||||
createListItem(label, id, type, parentId) {
|
getIconByMIME(mime){
|
||||||
|
if(mime.startsWith("image")) return "image";
|
||||||
|
if(mime.startsWith("application/pdf")) return "picture_as_pdf";
|
||||||
|
if(mime.startsWith("application/zip")) return "archive";
|
||||||
|
if(mime.startsWith("application/xml")) return "code";
|
||||||
|
if(mime.match(/tar|compressed|gzip|gz|tgz/)) return 'archive';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIconByExtension(name){
|
||||||
|
const tks = name.split(".")
|
||||||
|
if(tks.length === 0) return null;
|
||||||
|
const ext = tks[tks.length - 1]
|
||||||
|
if(ext.match(/java$|py$|^r$|^c$|ccp|lua|jl|^sh$|json|xml|xsl|xslt|md$|xq$|xqm$/i)) return "code";
|
||||||
|
if(ext.match(/js$/i)) return "javascript";
|
||||||
|
if(ext.match(/html$|css$|php$/i)) return ext;
|
||||||
|
if(ext.match(/kml$/i)) return "place";
|
||||||
|
if(ext.match(/ics$/i)) return "event";
|
||||||
|
if(ext.match(/csv$|xls$|ods$/i)) return "assessment";
|
||||||
|
if(ext.match(/txt$|odt$|rtf$|doc$|docx$/i)) return "description";
|
||||||
|
if(ext.match(/ppt$|odp$/i)) return "slideshow";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
createListItem(item, parentId) {
|
||||||
|
const label = item.displayName ? item.displayName : item.title
|
||||||
|
const id = item.id
|
||||||
|
const type = item["@class"]
|
||||||
|
const mime = item.content && item.content.mimeType ? item.content.mimeType : null
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.setAttribute(D4SStorageTree.dataname_attr, label);
|
li.setAttribute(D4SStorageTree.dataname_attr, label);
|
||||||
li.setAttribute(D4SStorageTree.dataid_attr, id);
|
li.setAttribute(D4SStorageTree.dataid_attr, id);
|
||||||
|
@ -434,14 +467,30 @@ class D4SStorageTree extends D4SStorageHtmlElement {
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
li.setAttribute(D4SStorageTree.parentid_attr, parentId);
|
li.setAttribute(D4SStorageTree.parentid_attr, parentId);
|
||||||
}
|
}
|
||||||
const icon = type.includes('FolderItem') || type.includes("SharedFolder")? "folder.svg" : "file-earmark.svg"
|
//const icon = type.includes('FolderItem') || type.includes("SharedFolder")? "folder.svg" : "file-earmark.svg"
|
||||||
|
var icon = "insert_drive_file";
|
||||||
|
if(type.includes('FolderItem')){
|
||||||
|
icon = "folder"
|
||||||
|
}else if(type.includes("SharedFolder")){
|
||||||
|
icon = "folder_shared"
|
||||||
|
}else{
|
||||||
|
const im = mime ? this.getIconByMIME(mime) : null
|
||||||
|
if(im) icon = im;
|
||||||
|
else{
|
||||||
|
const ie = this.getIconByExtension(label)
|
||||||
|
if(ie) icon = ie;
|
||||||
|
}
|
||||||
|
}
|
||||||
li.innerHTML = `
|
li.innerHTML = `
|
||||||
<img class="px-1" src="${this.srcBaseURL}/img/${icon}"</img>
|
<div class="li-title d-flex gap-1">
|
||||||
|
<span class="material-icons">${icon}</span>
|
||||||
<span>${label}</span>
|
<span>${label}</span>
|
||||||
|
</div>
|
||||||
`
|
`
|
||||||
|
|
||||||
li.addEventListener('click', (ev) => {
|
li.addEventListener('click', (ev) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault()
|
||||||
this.select(ev.currentTarget.getAttribute(D4SStorageTree.dataid_attr));
|
this.select(ev.currentTarget.getAttribute(D4SStorageTree.dataid_attr));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -668,6 +717,7 @@ class D4SStorageFolder extends D4SStorageHtmlElement {
|
||||||
const isFolder = item['@class'].includes('FolderItem')
|
const isFolder = item['@class'].includes('FolderItem')
|
||||||
filename.addEventListener('click', (ev) => {
|
filename.addEventListener('click', (ev) => {
|
||||||
ev.stopPropagation()
|
ev.stopPropagation()
|
||||||
|
ev.preventDefault()
|
||||||
if (isFolder) {
|
if (isFolder) {
|
||||||
const span = ev.currentTarget.querySelector(':nth-child(2)')
|
const span = ev.currentTarget.querySelector(':nth-child(2)')
|
||||||
if (span) {
|
if (span) {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<body class="m-4">
|
<body class="m-4">
|
||||||
<d4s-boot-2 context="%2Fgcube%2Fdevsec%2FdevVRE"
|
<d4s-boot-2 context="%2Fgcube%2Fdevsec%2FdevVRE"
|
||||||
gateway="next.d4science.org"
|
gateway="next.dev.d4science.org"
|
||||||
redirect-url="https://cdn.dev.d4science.org/storage/"
|
redirect-url="https://cdn.dev.d4science.org/storage/"
|
||||||
url="https://accounts.dev.d4science.org/auth">
|
url="https://accounts.dev.d4science.org/auth">
|
||||||
<!-- redirect-url="http://localhost:8080/storage/" -->
|
<!-- redirect-url="http://localhost:8080/storage/" -->
|
||||||
|
|
Loading…
Reference in New Issue