class CCPInputWidgetController extends HTMLElement { #input = null; #renderer = null; constructor(){ super() this.#input = JSON.parse(this.getAttribute("input")) this.#renderer = Renderer.instance(this.#input) } connectedCallback(){ console.log("Widget connected") this.innerHTML = this.render() this.#renderer.connectedCallback(this) } render(){ return this.#renderer.render() } get name(){ return this.#renderer.name } get value(){ return this.#renderer.getValue(this) } } window.customElements.define('d4s-ccp-input', CCPInputWidgetController); class Renderer{ #input = null; constructor(input){ this.#input = input } connectedCallback(controller){ } get schema(){ return this.#input.schema } get name(){ return this.#input.id } get title(){ return this.#input.title } get description(){ return this.#input.description } get required(){ return this.#input.minOccurs > 0 } static instance(input){ if(this.isEnum(input)){ return new EnumInputRenderer(input) } if(this.isCode(input)){ return new CodeInputRenderer(input) } if(this.isDateTime(input)){ return new DateTimeInputRenderer(input) } if(this.isSecret(input)){ return new SecretInputRenderer(input) } if(this.isFile(input)){ return new FileInputRenderer(input) } return new SimpleInputRenderer(input) } static isEnum(input){ return (input.schema.type === "string") && ("enum" in input.schema) } static isSecret(input){ return (input.schema.type === "string") && ("format" in input.schema) && (input.schema.format != null) && (input.schema.format.toLowerCase() === "secret") } static isCode(input){ return (input.schema.type === "string") && ("format" in input.schema) && (input.schema.format != null) && (input.schema.format.toLowerCase() === "code") } static isFile(input){ return (input.schema.type === "string") && ("format" in input.schema) && (input.schema.format != null) && (input.schema.format.toLowerCase() === "file") } static isDateTime(input){ return (input.schema.type === "string") && ("format" in input.schema) && (input.schema.format != null) && (["date", "time", "datetime"].indexOf(input.schema.format.toLowerCase()) !== -1) } } class SimpleInputRenderer extends Renderer{ #html = null; constructor(input){ super(input) } getValue(parent){ return parent.querySelector("input").value } render(){ let required = this.required ? 'required="required"' : "" let readonly = this.readOnly ? 'readonly="readOnly"' : "" this.#html = `
👁
` return this.#html } } class FileInputRenderer extends Renderer{ #html = null; #content = null; constructor(input){ super(input) } connectedCallback(controller){ controller.querySelector(`input[name=${this.name}]`).addEventListener("change", ev=>{ const tgt = ev.target const ename = tgt.getAttribute("name") if(ename === this.name){ const file = ev.target.files[0] if(file.type !== this.schema.contentMediaType){ alert("Unsupported media type. Must be " + this.schema.contentMediaType) ev.stopPropagation() ev.preventDefault() tgt.value = null return false } if(file.size > 100*1024){ alert("This input allows only small files (100K). Use references instead ") ev.stopPropagation() ev.preventDefault() tgt.value = null return false } const reader = new FileReader() reader.addEventListener('load', ev=>{ let encoded = ev.target.result.toString().replace(/^data:(.*,)?/, ''); if ((encoded.length % 4) > 0) { encoded += '='.repeat(4 - (encoded.length % 4)); } this.#content = encoded }) reader.readAsDataURL(file) } }) } getValue(parent){ return this.#content } render(){ let required = this.required ? 'required="required"' : "" let readonly = this.readOnly ? 'readonly="readOnly"' : "" this.#html = `
` return this.#html } } class SecretInputRenderer extends Renderer{ #html = null; constructor(input){ super(input) } getValue(parent){ return parent.querySelector("input").value } connectedCallback(controller){ controller.addEventListener("click", ev=>{ const ename = ev.target.getAttribute("name") if(ename === "password_toggle"){ const w = controller.querySelector("div.ccp-input-widget input") w.type = (w.type === "password" ? "" : "password") ev.preventDefault() } }) } render(){ let required = this.required ? 'required="required"' : "" let readonly = this.readOnly ? 'readonly="readOnly"' : "" this.#html = `
👁
` return this.#html } } class DateTimeInputRenderer extends Renderer{ #html = null; constructor(input){ super(input) } getValue(parent){ return parent.querySelector("input").value } render(){ let required = this.required ? 'required="required"' : "" let readonly = this.schema.readOnly ? 'readonly="readOnly"' : "" let t = this.schema.format.toLowerCase() === "datetime" ? "datetime-local" : this.schema.format.toLowerCase() this.#html = `
` return this.#html } } class EnumInputRenderer extends Renderer{ #html = null; constructor(input){ super(input) } getValue(parent){ return parent.querySelector("select").value } render(){ let options = this.schema.enum.map(e => { return e === this.schema.default ? `` : `` }) let required = this.required ? 'required="required"' : "" let readonly = this.schema.readOnly ? 'readonly="readOnly"' : "" this.#html = `
` return this.#html } } class CodeInputRenderer extends Renderer{ #html = null; #codemirror = null; constructor(input){ super(input) } getValue(parent){ return parent.querySelector("textarea").textContent } connectedCallback(controller){ /*const ta = controller.querySelector("textarea") const opts = { lineNumbers: true, indentUnit: 4, matchBrackets: true, mode: this.schema.contentMediaType, readOnly : this.schema.readOnly ? true : false } this.#codemirror = CodeMirror.fromTextArea(ta, opts) this.#codemirror.setValue(this.schema.default) this.#codemirror.refresh()*/ } render(){ let required = this.required ? 'required="required"' : "" let readonly = this.schema.readOnly ? 'readonly="readOnly"' : "" this.#html = `
` return this.#html } }