js/jsonform.ext.js

/**
* Extending JSONform with custom fields
* https://github.com/jsonform/jsonform/wiki
* @file jsonform.ext.js
* @sample
* {
// The template describes the HTML that the field will generate.
// It uses Underscore.js templates.
template: '<div><div id="<%=node.id%>"><%=value%></div></div>',

// Set the inputfield flag when the field is a real input field
// that produces a value. Set the array flag when it creates an
// array of fields. Both flags are mutually exclusive.
// Do not set any of these flags for containers and other types
// of fields.
inputfield: (true || false || undefined),
array: (true || false || undefined),

// Most real input fields should set this flag that wraps the
// generated content into the HTML code needed to report errors
fieldtemplate: (true || false),

// Return the root element created by the field
// (el is the DOM element whose id is node.id,
// this function is only useful when el is not the root
// element in the field's template)
getElement: function (el) {
    // Adjust the following based on your template. In this example
    // there is an additional <div> so we need to go one level up.
    return $(el).parent().get(0);
},

// This is where you can complete the data that will be used
// to run the template string
onBeforeRender: function (data, node) {},

// This is where you can enhance the generated HTML by adding
// event handlers if needed
onInsert: function (evt, node) {}
};
*/

const JSONFormExt = {};
JSONFormExt.htmlentities = function(str){
    var element = document.createElement('div');
    element.textContent = str;
    return element.innerHTML.replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}

JSONFormExt.init = function() {
    console.log("JSONFormExt.init loaded");
    // JSONForm.fieldTypes['html'] = {
    //     template : '<div class="form-group"><div id="<%=node.id%>"><%=elt.content%></div></div>',
    //     inputfield :false,
    //     array:false,
    //     getElement: function (el) {
    //         // el is the place with node.id
    //         // Adjust the following based on your template. In this example
    //         // there is an additional <div> so we need to go one level up.
    //         return $(el).parent().get(0);
    //     },
    //     onBeforeRender: function (data, node) {},
    //     onInsert: function (evt, node) {}
    // };
    JSONForm.fieldTypes['html'] = {
        template : '<div class="form-group"><%=elt.content || elt.value%></div>',
    };
    JSONForm.fieldTypes['hr'] = {
        template : '<div class="form-group"><hr /></div>',
    };
    JSONForm.fieldTypes['info'] = {
        template : `<div class="form-group blockBox infoBlock withIcon" ><%=elt.content || elt.value%></div>`,
    };
    JSONForm.fieldTypes['warn'] = {
        template : `<div class="form-group blockBox attentionBlock withIcon" ><%=elt.content || elt.value%></div>`,
    };
    JSONForm.fieldTypes['error'] = {
        template : `<div class="form-group blockBox errorBlock withIcon" ><%=elt.content || elt.value%></div>`,
    };

    JSONForm.fieldTypes['selectencoding'] = {...{}, ...JSONForm.fieldTypes.select};
    JSONForm.fieldTypes['selectencoding'].onBeforeRender = function(data, node) {
            node.options = [
                "", 
                "utf8", 
                "utf16le",
                "UTF-16", 
                "UTF-16BE",
                "ascii", 
                "ISO-8859-1", 
                "ISO-8859-16", 
                "koi8-r", 
                "koi8-u", 
                "koi8-ru", 
                "koi8-t", 
                "Shift_JIS",
                "Windows-31j",
                "Windows932",
                "EUC-JP",
                "GB2312",
                "GBK",
                "GB18030",
                "Windows936",
                "EUC-CN",
                "KS_C_5601",
                "Windows949",
                "EUC-KR",
                "Big5",
                "Big5-HKSCS",
                "Windows950"
            ]
        };



    JSONForm.fieldTypes['combobox'] = {
        "template": `<div><input type="search" class='form-control<%= (fieldHtmlClass ? " " + fieldHtmlClass : "") %>'name="<%= node.name %>" value="<%= escape(value) %>" id="<%= id %>" aria-label="<%= node.title ? escape(node.title) : node.name %>"<%= (node.disabled? " disabled" : "")%><%= (node.readOnly ? " readonly='readonly'" : "") %><%= (node.schemaElement && (node.schemaElement.step > 0 || node.schemaElement.step == "any") ? " step='" + node.schemaElement.step + "'" : "") %><%= (node.schemaElement && node.schemaElement.minLength ? " minlength='" + node.schemaElement.minLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.maxLength ? " maxlength='" + node.schemaElement.maxLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.required && (node.schemaElement.type !== "boolean") ? " required='required'" : "") %><%= (node.placeholder? " placeholder=" + '"' + escape(node.placeholder) + '"' : "")%> list="<%= id %>_datalist" /><datalist id="<%= id %>_datalist"><%=datalist%></datalist></div>`,
        "fieldtemplate": true,
        "inputfield": true,
        onBeforeRender: function (data, node) {
            data.datalist = ""
            if (!node.schemaElement?.enum?.length) return;
            const options = []
            for (let item of node.schemaElement.enum) {
                options.push(`<option value="${JSONFormExt.htmlentities(item)}">`)
            }
            data.datalist = options.join("")
        },
    }

    /**
     * Add tag elements.
     * Schema must be in 'object' type
     */
    JSONForm.fieldTypes['tag'] = {
        "template": `<input type="text" class='jsonform-tags form-control<%= (fieldHtmlClass ? " " + fieldHtmlClass : "") %>'name="<%= node.name %>" value="<%= escape(JSON.stringify(node.value)) %>" id="<%= id %>" aria-label="<%= node.title ? escape(node.title) : node.name %>"<%= (node.disabled? " disabled" : "")%><%= (node.readOnly ? " readonly='readonly'" : "") %><%= (node.schemaElement && (node.schemaElement.step > 0 || node.schemaElement.step == "any") ? " step='" + node.schemaElement.step + "'" : "") %><%= (node.schemaElement && node.schemaElement.minLength ? " minlength='" + node.schemaElement.minLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.maxLength ? " maxlength='" + node.schemaElement.maxLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.required && (node.schemaElement.type !== "boolean") ? " required='required'" : "") %><%= (node.placeholder? " placeholder=" + '"' + escape(node.placeholder) + '"' : "")%> />`,
        "fieldtemplate": true,
        "inputfield": true,
        onInsert: function (evt, node) {
            const $elm = $(node.el).find(".jsonform-tags");
            const whitelist = node.formElement.whitelist || node.schemaElement.enum || []
            const enforceWhitelist = node.formElement.enforceWhitelist || false;
            const maxTags = node.formElement.maxTags || Infinity;
            const mode = node.formElement.mode; // select

            //console.log("Preparing tagify for elm", $elm[0]);
            const tagify = new Tagify($elm[0], {
                dropdown: {
                    highlightFirst: true
                },
                whitelist: whitelist, // predefined values
                enforceWhitelist: enforceWhitelist, // only allow values from the whitelist
                maxTags:maxTags,
                mode:mode,
                originalInputValueFormat: (valuesArr)=> {
                    const result = [];
                    for (let val of valuesArr) {
                        result.push(val.value)
                    }
                    return JSON.stringify(result);
                }
            });
            $elm.data("tagify", tagify);
            console.log("initialized tag elm : ", $elm);
            console.log("node : ", node);
        },
        // getFormValuesHook : function(originalValues) {
        //     console.log("getFormValuesHook() custom getValue's this", this);
        //     console.log("getFormValuesHook() original value is", originalValues);
        //     const thisValue = originalValues[this.key];
        //     const valObj = JSON.parse(thisValue || "[]");
        //     const result = []
        //     for (let val of valObj) {
        //         result.push(val.value)
        //     }
        //     originalValues[this.key] = result;
        //     console.log("getFormValuesHook(), about to return", originalValues);
        //     return originalValues;
        // }

    };
    JSONForm.fieldTypes['selectfile'] = {
        "template": `<input type="text" class='jsonform-selectfile form-control<%= (fieldHtmlClass ? " " + fieldHtmlClass : "") %>'name="<%= node.name %>" value="<%= escape(value) %>" id="<%= id %>" accept="<%= node.formElement.accept %>" aria-label="<%= node.title ? escape(node.title) : node.name %>"<%= (node.disabled? " disabled" : "")%><%= (node.readOnly ? " readonly='readonly'" : "") %><%= (node.schemaElement && (node.schemaElement.step > 0 || node.schemaElement.step == "any") ? " step='" + node.schemaElement.step + "'" : "") %><%= (node.schemaElement && node.schemaElement.minLength ? " minlength='" + node.schemaElement.minLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.maxLength ? " maxlength='" + node.schemaElement.maxLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.required && (node.schemaElement.type !== "boolean") ? " required='required'" : "") %><%= (node.placeholder? " placeholder=" + '"' + escape(node.placeholder) + '"' : "")%> />`,
        "fieldtemplate": true,
        "inputfield": true,
        onInsert: function (evt, node) {
            const $elm = $(node.el).find(".jsonform-selectfile");
            $elm.attr("data-renderdvfield", "this");
            var accept = node.formElement.accept || "*";
            $elm.attr("accept", accept);
            if (node.formElement.multiple) $elm.attr("multiple", "multiple");
            if (node.formElement.directory) $elm.attr("nwdirectory", "nwdirectory");
            if (node.formElement.nwsaveas) $elm.attr("nwsaveas", "nwsaveas");
           
            const dvFields = new DVField();
            dvFields.renderAll($elm);
        }
    };
    JSONForm.fieldTypes['selectdir'] = {
        "template": `<input type="text" class='jsonform-selectfile form-control<%= (fieldHtmlClass ? " " + fieldHtmlClass : "") %>'name="<%= node.name %>" value="<%= escape(value) %>" id="<%= id %>" accept="<%= node.formElement.accept %>" aria-label="<%= node.title ? escape(node.title) : node.name %>"<%= (node.disabled? " disabled" : "")%><%= (node.readOnly ? " readonly='readonly'" : "") %><%= (node.schemaElement && (node.schemaElement.step > 0 || node.schemaElement.step == "any") ? " step='" + node.schemaElement.step + "'" : "") %><%= (node.schemaElement && node.schemaElement.minLength ? " minlength='" + node.schemaElement.minLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.maxLength ? " maxlength='" + node.schemaElement.maxLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.required && (node.schemaElement.type !== "boolean") ? " required='required'" : "") %><%= (node.placeholder? " placeholder=" + '"' + escape(node.placeholder) + '"' : "")%> />`,
        "fieldtemplate": true,
        "inputfield": true,
        onInsert: function (evt, node) {
            const $elm = $(node.el).find(".jsonform-selectfile");
            $elm.attr("data-renderdvfield", "this");
            var accept = node.formElement.accept || "*";
            $elm.attr("accept", accept);
            if (node.formElement.multiple) $elm.attr("multiple", "multiple");
            if (node.formElement.nwsaveas) $elm.attr("nwsaveas", "nwsaveas");
            $elm.attr("nwdirectory", true);

            const dvFields = new DVField();
            dvFields.renderAll($elm);
        }
    };
    // patch for ace field.
    // load ace if ace is not defined
    const aceOnInsert = JSONForm.fieldTypes['ace'].onInsert;
    JSONForm.fieldTypes['ace'].onInsert = async function(evt, node) {
        if (!window.ace) {
            await common.loadDomScript("modules/ace/src-min-noconflict/ace.js");
        }
        aceOnInsert.call(this, evt, node);
    }

    // todo
    // select file / folder field
    JSONForm.fieldTypes['selectfileorfolder'] = {
        "template": `<input type="text" class='jsonform-selectfile form-control<%= (fieldHtmlClass ? " " + fieldHtmlClass : "") %>'name="<%= node.name %>" value="<%= escape(value) %>" id="<%= id %>" accept="<%= node.formElement.accept %>" aria-label="<%= node.title ? escape(node.title) : node.name %>"<%= (node.disabled? " disabled" : "")%><%= (node.readOnly ? " readonly='readonly'" : "") %><%= (node.schemaElement && (node.schemaElement.step > 0 || node.schemaElement.step == "any") ? " step='" + node.schemaElement.step + "'" : "") %><%= (node.schemaElement && node.schemaElement.minLength ? " minlength='" + node.schemaElement.minLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.maxLength ? " maxlength='" + node.schemaElement.maxLength + "'" : "") %><%= (node.schemaElement && node.schemaElement.required && (node.schemaElement.type !== "boolean") ? " required='required'" : "") %><%= (node.placeholder? " placeholder=" + '"' + escape(node.placeholder) + '"' : "")%> />`,
        "fieldtemplate": true,
        "inputfield": true,
        onInsert: function (evt, node) {
            const $elm = $(node.el).find(".jsonform-selectfile");
            $elm.attr("data-renderdvfield", "this");
            var accept = node.formElement.accept || "*";
            $elm.attr("accept", accept);
            if (node.formElement.multiple) $elm.attr("multiple", "multiple");
            if (node.formElement.directory) $elm.attr("nwdirectory", "nwdirectory");
            if (node.formElement.nwsaveas) $elm.attr("nwsaveas", "nwsaveas");
           
            const dvFields = new DVField();
            dvFields.renderAll($elm);
        }
    };

        


    JSONForm.fieldTypes.fieldset.template = `<fieldset class="form-group jsonform-error-<%= keydash %> <% if (elt.expandable) { %>expandable<% } %> <%= elt.htmlClass?elt.htmlClass:"" %>" <% if (id) { %> id="<%= id %>"<% } %>><% if (node.title || node.legend) { %><legend role="treeitem" aria-expanded="false"><%= node.title || node.legend %></legend><% } %><% if (elt.expandable) { %><div class="form-group"><% } %><div class="fieldset-content"><%= children %></div><% if (elt.expandable) { %></div><% } %></fieldset>`

}