blob: b6eebb592ab226c6efe55cd48257743370662c0e [file]
// Copyright (c) 2018 Chandan B N. All rights reserved.
/* jshint esversion: 6 */
/* jshint browser:true */
/* jshint unused: false */
/* globals csrfToken */
/* globals ace */
/* globals JSONEditor */
/* globals pugRender */
/* globals textUtil */
/* globals schemaName */
/* globals wysihtml5 */
/* globals wysihtml5ParserRules */
/* globals wysihtml5ParserRules */
/* globals allowAjax */
/* globals docSchema */
/* globals custom_validators */
/* globals initJSON */
/* globals postUrl */
/* globals getChanges */
/* globals postURL */
/* globals idpath */
var infoMsg = document.getElementById('infoMsg');
var errMsg = document.getElementById('errMsg');
var save1 = document.getElementById('save1');
var save2 = document.getElementById('save2');
var editorLabel = document.getElementById('editorLabel');
var iconTheme = 'vgi-';
var starting_value = {};
var sourceEditor;
JSONEditor.defaults.languages.en.error_oneOf = "Please fill in the required fields *";
JSONEditor.AbstractEditor.prototype.showStar = function () {
return this.isRequired() && !(this.schema.readOnly || this.schema.readonly || this.schema.template)
}
JSONEditor.AbstractEditor.prototype.addLinks = function () {
/* Add links */
if (!this.no_link_holder) {
this.link_holder = this.theme.getLinksHolder()
/* if description element exists, insert the link before */
if (typeof this.description !== 'undefined') this.description.parentNode.insertBefore(this.link_holder, this.description)
/* otherwise just insert link at bottom of container */
else this.container.appendChild(this.link_holder)
if (this.schema.links) {
for (let i = 0; i < this.schema.links.length; i++) {
var link = this.schema.links[i];
//todo: refactor
var style = null;
if(link.class) {
style=link.class;
delete link.class;
}
var h = this.getLink(link);
if(style) {
h.setAttribute('class', style);
h.removeAttribute('style')
}
if(link.style) {
h.setAttribute('style', link.style);
}
if(link.title) {
h.setAttribute('title', link.title);
}
if(link.target === false) {
h.removeAttribute('target')
} else {
h.setAttribute('target', link.target)
}
if(link.onclick) {
h.setAttribute('onclick', link.onclick);
}
if(link.place == "container" && this.container) {
this.container.appendChild(h);
} else if (link.place == "header" && this.header) {
this.header.appendChild(h);
} else {
this.addLink(h)
}
}
}
}
}
JSONEditor.defaults.resolvers.unshift(function (schema) {
if (schema.type === "string" && schema.format === "radio") {
return "radio";
}
});
JSONEditor.defaults.templates.custom = function () {
return {
compile: function (template) {
return function (context) {
// context contains values of watched fields.
// XXX: careful with remote JSON schemas.
var ret = eval(template);
return ret;
};
}
};
};
JSONEditor.defaults.editors.array = class mystring extends JSONEditor.defaults.editors.array {
build() {
super.build();
if (this.header) {
if(this.options.class) {
this.header.className = 'lbl ' + this.options.class;
}
if(this.showStar()){
this.header.className = this.header.className + ' req';
}
}
}
/* move the delete button next to object title */
_createDeleteButton (i, holder) {
var r = super._createDeleteButton(i, holder);
r.setAttribute('class', 'sbn vgi-cancel');
r.innerHTML = '';
r.parentNode.setAttribute("vg","obj-del");
if(!this.options.disable_array_add) {
r.parentNode.parentNode.setAttribute("vg","array-obj");
}
return r;
}
}
JSONEditor.defaults.editors.table = class mystring extends JSONEditor.defaults.editors.table {
build() {
super.build();
if (this.header) {
if(this.options.class) {
this.header.className = 'lbl ' + this.options.class;
}
if(this.showStar()){
this.header.className = this.header.className + ' req';
}
}
}
}
JSONEditor.defaults.editors.object = class mystring extends JSONEditor.defaults.editors.object {
build() {
super.build();
if (this.title) {
if(this.options.class) {
this.title.className = this.title.className + ' ' + this.options.class;
}
if(this.showStar()){
this.title.className = this.title.className + ' req';
}
}
if(this.container && this.options.containerClass) {
this.container.className = this.container.className + ' ' + this.options.containerClass;
}
}
getValue () {
if (!this.dependenciesFulfilled) {
return undefined
}
const result = this.value;
const isEmpty = obj => typeof obj === 'undefined' || obj === '' ||
(
obj === Object(obj) &&
Object.keys(obj).length === 0 &&
obj.constructor === Object
) || (Array.isArray(obj) && obj.length === 0)
if (result && (this.jsoneditor.options.remove_empty_properties || this.options.remove_empty_properties)) {
Object.keys(result).forEach(key => {
var req = (Array.isArray(this.schema.required)) && this.schema.required.includes(key);
if (isEmpty(result[key]) && !req) {
delete result[key]
}
})
}
return result
}
}
JSONEditor.defaults.editors.number = class mystring extends JSONEditor.defaults.editors.number {
build() {
super.build();
if(this.label && this.options.class) {
this.label.className = this.label.className + ' ' + this.options.class;
if(this.showStar()){
this.label.className = this.label.className + ' req';
}
}
if(this.options.formClass) {
this.control.className = this.control.className + ' ' + this.options.formClass;
}
//Use html5 datalist to show examples
if(this.schema.examples && this.schema.examples.length > 0){
var dlist = document.createElement('datalist');
dlist.setAttribute('id', this.path + '-datalist');
var eg = this.schema.examples;
for(var i = 0; i< eg.length; i++) {
var v = document.createElement('option');
v.setAttribute('value', eg[i]);
dlist.appendChild(v);
}
this.input.setAttribute('list', this.path + '-datalist');
this.input.type='search';
this.container.appendChild(dlist);
}
}
}
JSONEditor.defaults.editors.string = class mystring extends JSONEditor.defaults.editors.string {
build() {
super.build();
if(this.label && this.options.class) {
this.label.className = this.label.className + ' ' + this.options.class;
if(this.showStar()){
this.label.className = this.label.className + ' req';
}
}
if(this.options.formClass) {
this.control.className = this.control.className + ' ' + this.options.formClass;
}
//Use html5 datalist to show examples
if(this.schema.examples && this.schema.examples.length > 0){
var dlist = document.createElement('datalist');
dlist.setAttribute('id', this.path + '-datalist');
var eg = this.schema.examples;
for(var i = 0; i< eg.length; i++) {
var v = document.createElement('option');
v.setAttribute('value', eg[i]);
dlist.appendChild(v);
}
this.input.setAttribute('list', this.path + '-datalist');
this.input.type='search';
this.container.appendChild(dlist);
}
}
};
//use for unique Idenifiers for an element's id attributes
var uid = 1;
JSONEditor.defaults.editors.radio = class radio extends JSONEditor.AbstractEditor {
setValue(value, initial) {
value = this.typecast(value || '');
// Sanitize value before setting it
var sanitized = value;
if (this.schema.enum.indexOf(sanitized) < 0) {
sanitized = this.schema.enum[0];
}
if (this.value === sanitized) {
return;
}
var self = this;
for (var input in this.inputs) {
if (input === sanitized) {
this.inputs[input].checked = true;
self.value = sanitized;
self.jsoneditor.notifyWatchers(self.path);
return false;
}
}
}
register() {
super.register();
if (!this.inputs) return;
for (var i = 0; i < this.inputs.length; i++) {
this.inputs[i].setAttribute('name', this.formname);
}
}
unregister(){
super.unregister();
if (!this.inputs) return;
for (var i = 0; i < this.inputs.length; i++) {
this.inputs[i].removeAttribute('name');
}
}
getNumColumns() {
var longest_text = this.getTitle().length;
for (var i = 0; i < this.schema.enum.length; i++) {
longest_text = Math.max(longest_text, this.schema.enum[i].length + 4);
}
return Math.min(12, Math.max(longest_text / 7, 2));
}
typecast(value) {
if (this.schema.type === "boolean") {
return !!value;
} else if (this.schema.type === "number") {
return 1 * value;
} else if (this.schema.type === "integer") {
return Math.floor(value * 1);
} else {
return "" + value;
}
}
getValue() {
return this.value;
}
removeProperty() {
super.removeProperty();
for (var i = 0; i < this.inputs.length; i++) {
this.inputs[i].style.display = 'none';
}
if (this.description) this.description.style.display = 'none';
this.theme.disableLabel(this.label);
}
addProperty() {
super.addProperty();
for (var i = 0; i < this.inputs.length; i++) {
this.inputs[i].style.display = '';
}
if (this.description) this.description.style.display = '';
this.theme.enableLabel(this.label);
}
sanitize(value) {
if (this.schema.type === "number") {
return 1 * value;
} else if (this.schema.type === "integer") {
return Math.floor(value * 1);
} else {
return "" + value;
}
}
build() {
var self = this,
i;
if (!this.options.compact) {
this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
}
if(this.label && this.options.class) {
this.label.className = this.label.className + ' ' + this.options.class;
if(this.showStar()){
this.label.className = this.label.className + ' req';
}
}
if (this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
this.select_options = {};
this.select_values = {};
var e = this.schema.enum || [];
var options = [];
for (i = 0; i < e.length; i++) {
// If the sanitized value is different from the enum value, don't include it
if (this.sanitize(e[i]) !== e[i]) continue;
options.push(e[i] + "");
this.select_values[e[i] + ""] = e[i];
}
this.input_type = 'radiogroup';
this.inputs = {};
this.controls = {};
for (i = 0; i < options.length; i++) {
this.inputs[options[i]] = this.theme.getRadio();
this.inputs[options[i]].setAttribute('value', options[i]);
this.inputs[options[i]].setAttribute('name', this.formname);
var xid = uid++;
this.inputs[options[i]].setAttribute('id', xid);
var label = this.theme.getRadioLabel((this.schema.options && this.schema.options.enum_titles && this.schema.options.enum_titles[i]) ?
this.schema.options.enum_titles[i] :
options[i]);
label.setAttribute('for', xid);
var rdicon = null;
if(this.options.icons && this.options.icons[options[i]]) {
rdicon = this.options.icons[options[i]];
} else if (iconMap[options[i]]) {
rdicon = iconMap[options[i]];
}
label.setAttribute('class', 'lbl' + (rdicon ? ' ' + iconTheme + rdicon : ''));
this.controls[options[i]] = this.theme.getFormControl(this.inputs[options[i]], label);
}
this.control = this.theme.getRadioGroupHolder(this.controls, this.label, this.description);
this.container.appendChild(this.control);
this.control.addEventListener('change', function (e) {
e.preventDefault();
e.stopPropagation();
var val = e.target.value;
var sanitized = val;
if (self.schema.enum.indexOf(val) === -1) {
sanitized = self.schema.enum[0];
}
self.value = sanitized;
if (self.parent) self.parent.onChildEditorChange(self);
else self.jsoneditor.onChange();
self.jsoneditor.notifyWatchers(self.path);
});
}
enable() {
if (!this.always_disabled) {
var opts = Object.keys(this.inputs);
for (var i = 0; i < opts.length; i++) {
this.inputs[opts[i]].disabled = false;
}
}
super.enable();
}
disable() {
//console.log(this.inputs);
var opts = Object.keys(this.inputs);
for (var i = 0; i < opts.length; i++) {
this.inputs[opts[i]].disabled = true;
}
super.disable();
}
destroy() {
if (this.label) this.label.parentNode.removeChild(this.label);
if (this.description) this.description.parentNode.removeChild(this.description);
for (var i = 0; i < this.inputs.length; i++) {
this.inputs[i].parentNode.removeChild(this.inputs[i]);
}
super.destroy();
}
};
function tzOffset(x) {
var offset = new Date(x).getTimezoneOffset(),
o = Math.abs(offset);
return (offset < 0 ? "+" : "-") + ("00" + Math.floor(o / 60)).slice(-2) + ":" + ("00" + (o % 60)).slice(-2);
}
// The time is displayed/set in local times in the input,
// but setValue, getValue use UTC. JSON output will be in UTC.
const localTZ = (new Date).toLocaleString("en", {timeZoneName: "short"}).split(" ").pop();
JSONEditor.defaults.editors.dateTime = class dateTime extends JSONEditor.defaults.editors.string{
getValue() {
if (this.value && this.value.length > 0) {
if (this.value.match(/^\d{4}-\d{2}-\d{2}T[\d\:\.]+$/)) {
this.value = this.value + tzOffset(this.value);
}
var d = new Date(this.value);
if (d instanceof Date && !isNaN(d.getTime())) {
return d.toISOString();
} else {
return this.value;
}
} else {
return "";
}
}
setValue(val) {
if (val && this.value.match(/^\d{4}-\d{2}-\d{2}T[\d\:\.]+$/)) {
val = val + tzOffset();
}
var d = new Date(val);
if (d instanceof Date && !isNaN(d.getTime()) && d.getTime() > 0) {
var x = new Date((d.getTime() - (d.getTimezoneOffset() * 60000)));
this.value =
this.input.value = x.toJSON().slice(0, 16);
} else {
this.value = this.input.value = "";
}
this.jsoneditor.notifyWatchers(this.path);
}
build() {
this.schema.format = "datetime-local";
super.build();
this.input.className = "txt";
this.input.setAttribute("tz", localTZ);
}
};
JSONEditor.defaults.editors.taglist = class taglist extends JSONEditor.defaults.editors.string {
getValue() {
if(this.tagify && this.tagify.value) {
return this.tagify.value.map(item => item.value);
} else {
return [];
}
}
setValue(val) {
if (val instanceof Array) {
this.tagify.removeAllTags();
this.tagify.addTags(val);
} else {
this.tagify.addTags(val.split(','));
}
this.onChange(true);
}
build() {
this.schema.format = "taglist";
super.build();
//console.log('list'+ this.schema.items.examples);
this.tagify = new Tagify(this.input, {
whitelist: this.schema.items.enum ? this.schema.items.enum : (this.schema.items.examples ? this.schema.items.examples : []),
enforceWhitelist: this.schema.items.enum ? true : false,
maxTags: this.schema.maxItems ? this.schema.maxItems : 512,
dropdown: {
maxItems: 40, // <- mixumum allowed rendered suggestions
classname: "tags-look", // <- custom classname for this dropdown, so it could be targeted
enabled: 0, // <- show suggestions on focus
closeOnSelect: false // <- do not hide the suggestions dropdown once an item has been selected
}
})
if(this.options && this.options.inputAttributes && this.options.inputAttributes.placeholder) {
this.input.setAttribute('placeholder', this.options.inputAttributes.placeholder)
}
}
};
JSONEditor.defaults.editors.simplehtml = class simplehtml extends JSONEditor.defaults.editors.string {
getValue() {
var ret = super.getValue();
if(this.wysLoaded) {
ret = this.wys.getValue();
}
return ret;
}
setValue (value,initial,from_template) {
super.setValue(value,initial,from_template);
if (this.wysLoaded) {
// get current value from HTML editor
var priorVal = this.wys.getValue();
// set the new value (and let HTML editor perform DOM sanitization)
this.wys.setValue(value);
// get the new value from the HTML editor
var currentVal = this.wys.getValue();
// if they are different trigger a change event.
if(priorVal != currentVal) {
this.input.value = currentVal;
this.onChange(true);
}
} else {
}
}
build() {
this.schema.format = this.format = 'hidden';
// this.schema.format = "simplehtml";
super.build();
if(this.label && this.options.class) {
this.label.className = this.label.className + ' ' + this.options.class;
if(this.showStar()){
this.label.className = this.label.className + ' req';
}
}
this.control.className = 'simplehtml form-control bor';
this.toolbar = document.createElement('div');
this.toolbar.innerHTML = '<div class="toolbar"><div><span class="btg indent"><a class="sbn vgi-bold" data-wysihtml5-command="bold" title="Bold CTRL+B" href="javascript:;" unselectable="on"></a><a class="sbn vgi-italic" data-wysihtml5-command="italic" title="Italic CTRL+I" href="javascript:;" unselectable="on"></a><a class="sbn vgi-underline" data-wysihtml5-command="underline" title="Underline CTRL+U" href="javascript:;" unselectable="on"></a><a class="sbn vgi-highlight" data-wysihtml5-command="bgColorStyle" title="highlight" color="#666699" data-wysihtml5-command-value="#effa66" href="javascript:;" unselectable="on"></a><!-- <a class="fbn icn strikethrough" data-wysihtml5-command="strike" title="Strike"></a>--></span><span class="btg indent"><a class="sbn vgi-p" data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="p" title="paragraph style" href="javascript:;" unselectable="on"></a><a class="sbn vgi-h1" data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="h1" title="Heading 1" href="javascript:;" unselectable="on"></a><a class="sbn vgi-h2" data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="h2" title="Heading 2" href="javascript:;" unselectable="on"></a><a class="sbn vgi-h3" data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="h3" title="Heading 3" href="javascript:;" unselectable="on"></a><a class="sbn vgi-noformat" data-wysihtml5-command="formatBlock" data-wysihtml5-command-blank-value="true" unselectable="on" title="Clear styles" href="javascript:;"></a></span><span class="btg indent"><a class="sbn vgi-link" data-wysihtml5-command="createLink" title="Hyperlink" href="javascript:;" unselectable="on"></a><a class="sbn vgi-unlink" data-wysihtml5-command="removeLink" title="Unlink" href="javascript:;" unselectable="on"></a><a class="sbn vgi-pic" data-wysihtml5-command="insertImage" title="Insert image" href="javascript:;" unselectable="on"></a><a class="sbn vgi-console" data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="tt" title="Code text" href="javascript:;" unselectable="on"></a><a class="sbn vgi-quote" data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="blockquote" title="Block quote" href="javascript:;" unselectable="on"></a><a class="sbn vgi-table" data-wysihtml5-command="createTable" title="Insert Table" href="javascript:;" unselectable="on"></a></span><span class="btg indent"><a class="sbn vgi-bullet" data-wysihtml5-command="insertUnorderedList" title="Bulletted list" href="javascript:;" unselectable="on"></a><a class="sbn vgi-numbered" data-wysihtml5-command="insertOrderedList" title="Numbered list" href="javascript:;" unselectable="on"></a></span><span class="btg indent"><a class="sbn vgi-undo" data-wysihtml5-command="undo" title="Undo" href="javascript:;" unselectable="on"></a><a class="sbn vgi-redo" data-wysihtml5-command="redo" title="Redo" href="javascript:;" unselectable="on"></a><a class="sbn vgi-markup" data-wysihtml5-action="change_view" title="HTML source view" href="javascript:;" unselectable="on"></a></span><span class="btg indent" data-wysihtml5-hiddentools="table" style="display: none;"><a class="sbn vgi-add-row-top" data-wysihtml5-command="addTableCells" data-wysihtml5-command-value="above" title="Insert row above" href="javascript:;" unselectable="on"></a><a class="sbn vgi-add-row-down" data-wysihtml5-command="addTableCells" data-wysihtml5-command-value="below" title="Insert row below" href="javascript:;" unselectable="on"></a><a class="sbn vgi-add-col-left" data-wysihtml5-command="addTableCells" data-wysihtml5-command-value="before" title="Insert column before" href="javascript:;" unselectable="on"></a><a class="sbn vgi-add-col-right" data-wysihtml5-command="addTableCells" data-wysihtml5-command-value="after" title="Insert column after" href="javascript:;" unselectable="on"></a><a class="sbn vgi-row-red" data-wysihtml5-command="deleteTableCells" data-wysihtml5-command-value="row" title="Delete row" href="javascript:;" unselectable="on"></a><a class="sbn vgi-col-red" data-wysihtml5-command="deleteTableCells" data-wysihtml5-command-value="column" title="Delete column" href="javascript:;" unselectable="on"></a></span></div><div data-wysihtml5-dialog="createLink" style="display: none;"><label class="lbl sml vgi-link">Link: </label><input class="vgi-text" size="90" data-wysihtml5-dialog-field="href" value="https://" title="URL"><a class="btn vgi-ext" onclick="window.open(this.previousElementSibling.value)">Open</a><a class="btn indent vgi-ok" data-wysihtml5-dialog-action="save">OK</a><a class="btn vgi-cancel" data-wysihtml5-dialog-action="cancel">Cancel</a></div><div data-wysihtml5-dialog="insertImage" style="display: none;"><label class="lbl vgi-link">URL</label><input class="vgi-txt" data-wysihtml5-dialog-field="src" size="50" value="https://"><label class="lbl">or</label><label class="btn vgi-folder" title="Browse for local images to insert">Insert Image ..<input class="hid" type="file" onchange="loadimg.call(this, event)" accept="image/*"></label><a class="btn indent vgi-ok" data-wysihtml5-dialog-action="save">OK</a><a class="btn vgi-cancel" data-wysihtml5-dialog-action="cancel">Cancel</a></div><div data-wysihtml5-dialog="createTable" style="display: none;"><label class="vgi-table lbl">Rows: </label><input class="txt" type="text" data-wysihtml5-dialog-field="rows"><label class="lbl">Cols: </label><input class="txt" type="text" data-wysihtml5-dialog-field="cols"><a class="btn vgi-ok indent" data-wysihtml5-dialog-action="save">OK</a><a class="btn vgi-cancel" data-wysihtml5-dialog-action="cancel">Cancel</a></div></div>'
//document.getElementById('commentTemplate')?.getElementsByClassName('toolbar')[0].cloneNode(true);
this.contentDiv = document.createElement('div');
this.contentDiv.className = 'pur ht4 fil';
if (this.toolbar) {
this.toolbar.className = 'fil shd wht stk toolbar';
this.input.parentNode.insertBefore(this.toolbar, this.input);
}
this.input.parentNode.appendChild(this.contentDiv);
}
afterInputReady () {
var self = this, options;
//console.log('called after input ready' + this.input.value);
var WYS = this.wys = new wysihtml5.Editor(this.contentDiv, {
toolbar: this.toolbar,
parserRules: wysihtml5ParserRules,
showToolbarAfterInit: false,
});
this.wys.on('load', function() {
self.wys.setValue(self.input.value);
var sa = self.wys.getValue();
if(sa != self.input.value) {
self.input.value = sa;
//console.log('Changed on setting input');
//self.is_dirty = true;
//self.onChange(true);
}
//console.log('Loaded');
self.wysLoaded = true;
});
this.wys.on('change', function() {
self.value = self.input.value = self.wys.getValue();
self.is_dirty = true;
self.onChange(true);
//console.log('Changed' + JSON.stringify(this))
});
this.wys.on("dragleave", function(event) {
event.preventDefault();
event.stopPropagation();
});
this.wys.on("drop", function(event) {
event.preventDefault();
event.stopPropagation();
var dt = event.dataTransfer;
var files = dt.files;
var reader = new FileReader();
reader.onload = function (e) {
//var data = this.result;
self.wys.composer.commands.exec('insertImage',e.target.result);
self.value = self.input.value = self.wys.getValue();
self.is_dirty = true;
self.onChange(true);
};
reader.readAsDataURL( files[0] );
});
this.wys.on("dragover", function(event) {
event.preventDefault();
event.stopPropagation();
this.addClass('dragging');
});
}
showValidationErrors(errs) {
var self = this;
if(this.jsoneditor.options.show_errors === "always") {}
else if(!this.is_dirty && this.previous_error_setting===this.jsoneditor.options.show_errors) return;
this.previous_error_setting = this.jsoneditor.options.show_errors;
var messages = [];
errs.forEach(i => {
if(i.path === self.path) {
messages.push(i.message);
}
});
if(messages.length) {
this.theme.addInputError(this.control, messages.join('. ')+'.');
}
else {
this.theme.removeInputError(this.control);
}
}
};
// Instruct the json-editor to use the custom datetime-editor.
JSONEditor.defaults.resolvers.unshift(function (schema) {
if (schema.type === "string" && schema.format === "datetime") {
return "dateTime";
}
});
JSONEditor.defaults.resolvers.unshift(function (schema) {
if (schema.type === "array" && schema.format === "taglist") {
return "taglist";
}
});
JSONEditor.defaults.resolvers.unshift(function (schema) {
if (schema.type === "string" && schema.format === "simplehtml") {
return "simplehtml";
}
});
/*
JSONEditor.defaults.editors.upload =
JSONEditor.defaults.editors.upload.extend({
build: function () {
this._super();
this.uploader.className = "fbn";
var a = document.createElement('a');
a.className = 'fbn icn download';
a.target = "_blank";
this.control.replaceChild(a, this.label);
this.control.appendChild(this.preview);
this.label = this.title = a;
},
setValue: function (val) {
if (this.value !== val) {
this.title.href = window.location + '/file/' + encodeURIComponent(val);
this.title.textContent = val;
this._super(val);
}
},
refreshPreview: function () {
if (this.last_preview === this.preview_value) return;
this.last_preview = this.preview_value;
this.preview.innerHTML = '';
if (!this.preview_value) return;
var self = this;
var mime = this.preview_value.match(/^data:([^;,]+)[;,]/);
if (mime) mime = mime[1];
if (!mime) mime = 'unknown';
var file = this.uploader.files[0];
this.preview.textContent = textUtil.fileSize(file.size);
var uploadButton = this.getButton('Upload', 'upload', 'Upload');
uploadButton.className ='btn icn indent sfe save';
this.preview.appendChild(uploadButton);
uploadButton.addEventListener('click', function (event) {
event.preventDefault();
uploadButton.setAttribute("disabled", "disabled");
self.theme.removeInputError(self.uploader);
if (self.theme.getProgressBar) {
self.progressBar = self.theme.getProgressBar();
self.preview.appendChild(self.progressBar);
}
self.jsoneditor.options.upload(self.path, file, {
success: function (url) {
self.setValue(url);
if (self.parent) self.parent.onChildEditorChange(self);
else self.jsoneditor.onChange();
if (self.progressBar) self.preview.removeChild(self.progressBar);
uploadButton.textContent = 'Done';
uploadButton.setAttribute('value', 'Done');
uploadButton.setAttribute('disabled', true);
},
failure: function (error) {
self.theme.addInputError(self.uploader, error);
if (self.progressBar) self.preview.removeChild(self.progressBar);
uploadButton.removeAttribute("disabled");
uploadButton.textContent = "Upload";
},
updateProgress: function (progress) {
if (self.progressBar) {
if (progress) self.theme.updateProgressBar(self.progressBar, progress);
else self.theme.updateProgressBarUnknown(self.progressBar);
}
}
});
});
}
});
*/
var iconMapping = {
collapse: 'down',
expand: 'add',
delete: 'cancel',
edit: 'edit',
add: 'add',
cancel: 'cancel',
save: 'save',
moveup: 'up',
movedown: 'down'
};
JSONEditor.defaults.iconlibs.vgi = class CustomIconLib extends JSONEditor.AbstractIconLib {
constructor () {
super();
this.mapping = iconMapping;
this.icon_prefix = 'vgi-';
}
}
JSONEditor.defaults.options.iconlib = 'vgi';
JSONEditor.defaults.themes.customTheme = class customTheme extends JSONEditor.AbstractTheme{
getBlockLink() {
var link = document.createElement('a');
return link;
}
getLinksHolder() {
var el = document.createElement('span');
return el;
}
getDescription (text) {
var el = document.createElement('summary');
return el;
}
getFormControl(label, input, description, infoText) {
var el = super.getFormControl(label, input, description, infoText);
if(input.type =='text')
input.className = 'txt';
return el;
}
getFormInputLabel(text) {
var el = super.getFormInputLabel(text);
el.className = 'lbl' + (iconMap[text]? ' ' + iconTheme + iconMap[text] : '');
return el;
}
/* getIndentedPanel() {
var el = super.getIndentedPanel();
// el.classList.add("indent");
return el;
}*/
getHeader(text) {
var el = document.createElement('span');
if (typeof text === "string") {
el.textContent = text;
if(iconMap[text])
el.className = iconTheme + iconMap[text];
} else {
if(iconMap[text.textContent])
text.className = iconTheme + iconMap[text.textContent];
el.appendChild(text);
}
return el;
}
getTable() {
var el = super.getTable();
el.className = 'tbl';
return el;
}
getTableHeaderCell (text) {
const el = document.createElement('b')
if(this.options && this.options.class) {
el.className = this.options.class
} else if(iconMap[text]) {
el.className = iconTheme + iconMap[text];
}
el.textContent = text == ' ' ? '': text;
const t = document.createElement('th');
t.appendChild(el);
return t
}
getButton(text, icon, title) {
var el = document.createElement('button');
el.type = 'button';
el.className = 'btn';
this.setButtonText(el,text,icon,title);
return el;
}
addInputError(input, text) {
try {
input.setCustomValidity(text);
input.onfocus = function(){ this.reportValidity(); }
input.oninput = function(){ this.setAttribute("novalidate", true); this.setCustomValidity('')}
} catch(e) {}
input.style.boxShadow = "0px 0px 0px 2px rgba(252, 114, 114, 0.33)";
input.style.border = "1px solid coral";
if(text && text != 'Value required.') {
if (!input.errmsg) {
var group = this.closest(input, '.form-control');
/* var label = group.getElementsByClassName('lbl');
if(label && label[0]){
group = label[0];
}*/
input.errmsg = document.createElement('div');
//input.errmsg.setAttribute('class', 'lbl tred indent');
input.errmsg.style = input.errmsg.style || {};
group.appendChild(input.errmsg);
} else {
input.errmsg.style.display = 'block';
}
input.errmsg.setAttribute('class', 'lbl tred indent');
input.errmsg.textContent = '';
input.errmsg.appendChild(document.createTextNode(' ' + text));
}
if(input.errmsg) {
input.errmsg.setAttribute('class', 'lbl tred indent');
}
}
removeInputError(input) {
try{
input.setCustomValidity('');
} catch(e) {}
input.style.border = '';
input.style.boxShadow = '';
if (input.errmsg) input.errmsg.style.display = 'none';
}
getRadio() {
var el = this.getFormInputField('radio');
return el;
}
getRadioGroupHolder(controls, label, description) {
var el = document.createElement('div');
var radioGroup = document.createElement('div');
radioGroup.className = 'rdg';
if (label) {
el.appendChild(label);
}
el.appendChild(radioGroup);
for (var i in controls) {
if (!controls.hasOwnProperty(i)) continue;
radioGroup.appendChild(controls[i]);
}
if (description) el.appendChild(description);
return el;
}
getRadioLabel(text) {
var el = this.getFormInputLabel(text);
return el;
}
getProgressBar() {
var max = 100,
start = 0;
var progressBar = document.createElement('progress');
progressBar.setAttribute('max', max);
progressBar.setAttribute('value', start);
return progressBar;
}
updateProgressBar(progressBar, progress) {
if (!progressBar) return;
progressBar.setAttribute('value', progress);
}
updateProgressBarUnknown(progressBar) {
if (!progressBar) return;
progressBar.removeAttribute('value');
}
/*getSelectInput(options) {
var select = document.createElement('select');
//select.className = 'txt';
if(options) this.setSelectOptions(select, options);
return select;
}*/
setGridColumnSize(el, size) {
el.className = el.className + ' col s' + size;
}
getSwitcher (options) {
const switcher = this.getSelectInput(options, false);
switcher.classList.add('je-switcher');
if(! /^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
switcher.classList.add('rdg');
switcher.setAttribute('size',2);
}
return switcher
}
};
JSONEditor.defaults.themes.customTheme.rules = {};
if (typeof(custom_validators) !== 'undefined'){
JSONEditor.defaults.custom_validators = custom_validators;
}
var docEditorOptions = {
// Enable fetching schemas via ajax
ajax: allowAjax,
theme: 'customTheme',
show_errors: 'always',
disable_collapse: true,
disable_array_reorder: true,
disable_properties: true,
disable_edit_json: true,
disable_array_delete_last_row: true,
disable_array_delete_all_rows: true,
input_width: '3em',
input_height: '4em',
template: 'custom',
prompt_before_delete: false,
ajaxBase: ajaxBase,
schema: docSchema,
// Seed the form with a starting value
//starting_value: {},
// Disable additional properties
//no_additional_properties: false,
// Require all properties by default
//required_by_default: false,
remove_empty_properties: true
//display_required_only: true
};
var docEditor;
/*
function hashChange() {
if(window.location.hash) {
var hash = window.location.hash.substring(1);
if((hash) && document.getElementById(hash+'Tab')) {
selected = hash+'Tab';
var t = document.getElementById(selected);
t.checked = true;
var event = new Event('change');
t.dispatchEvent(event);
}
}
}
window.onhashchange = hashChange;
*/
var selected = "editorTab";
if (typeof(defaultTab) !== 'undefined') {
selected = defaultTab;
} else {
if(window.location.hash) {
var hash = window.location.hash.substring(1);
if((hash) && document.getElementById(hash+'Tab')){
selected = hash+'Tab';
}
}
}
var insync = false;
function Tabs(tabGroupId, tabOpts, primary) {
var elem = document.getElementById(tabGroupId);
var tg = {
changeIndex: [],
primary: primary,
tabOpts: tabOpts,
tabId: [],
element: elem,
tabs: elem.getElementsByClassName("tab"),
};
for (var i = 0; i < tg.tabs.length; i++) {
var tab = tg.tabs[i];
//console.log(tab.nextElementSibling);
tab.nextElementSibling._tgIndex = tab._tgIndex = i;
if(tab.id){
tg.tabId[i] = tab.id;
}
tg.changeIndex[i]=0;
tab.addEventListener('change', function(event){
//console.log('CLicked ' + JSON.stringify( event) + ' ; ' + event.target);
tg.select(event, event.target);
});
}
tg.changeIndex[0] = 1;
tg.select = function (event, elem) {
//errMsg.textContent = "";
var selected = elem._tgIndex;
//Does the tab need an update?
//console.log('===CLICK====' + selected + ' cI ' + tg.changeIndex);
if (tg.changeIndex[selected] < Math.max(...tg.changeIndex)) {
var val = tg.getValue();
//console.log('VAL = ' + val);
if (val) {
if(selected != primary){
//console.log(' Then Setting ' + selected);
tg.setValue(selected, val);
tg.changeIndex[selected] = tg.changeIndex[primary];
}
} else {
event.preventDefault();
return;
}
}
window.location.hash = tg.tabId[selected].replace(/Tab$/,'');
},
tg.getValue = function (i) {
if(i == undefined) {
var maxi = 0;
for (var m = 0; m < tg.tabs.length; m++) {
if (tg.changeIndex[m] > tg.changeIndex[maxi]) {
maxi = m;
}
}
var src = tg.tabId[maxi];
//console.log('Most current = ' + maxi);
if (src && tg.tabOpts[src].validate) {
//console.log('Validating ' + src );
var ret = tg.tabOpts[src].validate();
//console.log(' Got = ' + ret)
if(ret == -1) {
//event.preventDefault();
return undefined;
}
}
var val = tg.getValue(maxi);
var primaryChanged = false;
if(maxi != primary) {
//console.log(' First Setting ' + primary);
tg.setValue(primary, val);
primaryChanged = true;
tg.changeIndex[primary] = tg.changeIndex[maxi];
var primaryOpts = tg.tabOpts[tg.tabId[primary]];
if (primaryOpts && primaryOpts.validate) {
//console.log('validating Primary');
primaryOpts.validate();
}
}
if (!primaryChanged) {
return val;
}
i = primary;
}
//console.log('geting tab '+i);
if(tg.tabOpts[tg.tabId[i]] && tg.tabOpts[tg.tabId[i]].getValue) {
return tg.tabOpts[tg.tabId[i]].getValue();
} else {
return undefined;
}
}
tg.setValue = function (index, val) {
//console.log('Seting tab '+index+ ' + ' + tg.tabId[index] + "="+JSON.stringify(tg.tabOpts));
if(tg.tabOpts[tg.tabId[index]] && tg.tabOpts[tg.tabId[index]].setValue) {
//console.log('tab INDEX',tg.tabId);
return tg.tabOpts[tg.tabId[index]].setValue(val);
}
}
tg.focus = function(index) {
if (!insync) {
if(tg.tabs[index]) {
tg.tabs[index].checked = true;
tg.tabs[index].dispatchEvent(new Event('change'));
} else {
console.log('no tab');
}
}
}
tg.change = function(index) {
if (!insync) {
tg.changeIndex[index]++;
errMsg.textContent = '';
editorLabel.className = "lbl";
infoMsg.textContent = 'Edited';
var nid = getDocID();
document.title = '• ' + (nid ? nid : 'Vulnogram');
if (document.getElementById("save1")) {
save2.className = "btn sfe gap save";
save1.className = "fbn sfe save";
}
//console.log('Inc '+ tg.tabId[index] + ' is ' + tg.changeIndex[index]);
}
}
return tg;
};
var originalTitle = document.title;
var changes = true;
if (document.getElementById('remove')) {
document.getElementById('remove').addEventListener('click', function () {
//var e = docEditor.getValue();
if (confirm('Delete this ' + originalTitle + '?')) {
fetch("", {
method: 'DELETE',
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*',
'CSRF-Token': csrfToken
},
}).then(function (response) {
if (response.status == 200) {
infoMsg.textContent = "Deleted ";
errMsg.textContent = "";
window.location = "./";
} else {
showAlert("Error " + response.statusText);
errMsg.textContent = "Error " + response.statusText;
infoMsg.textContent = "";
}
});
}
});
}
if (document.getElementById('save1') && document.getElementById('save2')) {
document.getElementById('save1').addEventListener('click', save);
document.getElementById('save2').addEventListener('click', save);
document.getElementById('save2').removeAttribute("style");
}
function scroll2Err(x) {
var path = x.getAttribute('e_path');
mainTabGroup.focus(0);
if(path) {
if(!path.startsWith('root.')) {
path = 'root.' + path;
}
var ee = docEditor.getEditor(path);
if(ee && ee.container) {
var stkH = document.getElementById("vgHead").offsetHeight;
ee.container.style["scroll-margin-top"] = (stkH + 40) + "px";
ee.container.scrollIntoView({behavior:"smooth"});
x.closest('details').removeAttribute('open');
}
}
}
function showJSONerrors(errors) {
errList.textContent="";
for (i = 0;i < errors.length; i++) {
var e = errors[i];
var showLabel = undefined;
var ee = docEditor.getEditor('root.'+e.path);
if (ee && ee.header && ee.header.innerText) {
showLabel = ee.header.innerText;
}
var a = document.createElement('a');
a.setAttribute('class', 'vgi-alert')
a.setAttribute('e_path', e.path);
a.setAttribute('onclick', 'scroll2Err(this)');
a.textContent = (showLabel && showLabel.trim() ? showLabel : e.path) + ": " + e.message;
errList.appendChild(a);
errList.appendChild(document.createElement('br'))
}
errCount.className = 'indent bdg';
errPop.className = 'popup';
errCount.innerText = errors.length;
editorLabel.className = "red lbl";
}
function hideJSONerrors() {
errCount.innerText = "";
errPop.className = 'hid';
errList.textContent = "";
editorLabel.className = "lbl";
}
var defaultTabs = {
editorTab: {
setValue: function (val) {
insync = true;
docEditor.setValue(val);
insync = false;
},
getValue: function () {
return docEditor.getValue();
},
validate: function (x) {
var errors = [];
if (x) {
errors = docEditor.validate(x);
} else {
errors = docEditor.validate();
}
if (typeof(errorFilter) !== 'undefined'){
errors = errorFilter(errors);
}
if (errors.length > 0) {
docEditor.setOption('show_errors', 'always');
showJSONerrors(errors);
return 0;
} else {
hideJSONerrors();
return 1;
}
}
},
sourceTab: {
setValue: function (val) {
if (sourceEditor == undefined) {
ace.config.set('basePath', '/js/')
sourceEditor = ace.edit("output");
sourceEditor.getSession().setMode("ace/mode/json");
sourceEditor.getSession().on('change', function () {
mainTabGroup.change(1);
});
sourceEditor.setOptions({
maxLines: 480,
wrap: true
});
sourceEditor.$blockScrolling = Infinity;
}
insync = true;
sourceEditor.getSession().setValue(JSON.stringify(val, null, 2));
sourceEditor.clearSelection();
insync = false;
},
validate: function () {
try {
var hasError = false;
var firsterror = null;
var annotations = sourceEditor.getSession().getAnnotations();
for (var l in annotations) {
var annotation = annotations[l];
if (annotation.type === "error") {
hasError = true;
firsterror = annotation;
//console.log('SOurce error'+annotation);
break;
}
}
if (!hasError) {
return 1;
} else {
sourceEditor.moveCursorTo(firsterror.row, firsterror.column, false);
sourceEditor.clearSelection();
showAlert('Please fix error: ' + firsterror.text);
errMsg.textContent = 'Please fix error: ' + firsterror.text;
document.getElementById("sourceTab").checked = true;
return -1;
}
} catch (err) {
showAlert(err.message);
errMsg.textContent = err.message;
document.getElementById("sourceTab").checked = true;
return -1;
} finally {}
},
getValue: function () {
return JSON.parse(sourceEditor.getSession().getValue());
}
}
};
if(typeof additionalTabs !== 'undefined') {
Object.assign(defaultTabs, additionalTabs);
}
var mainTabGroup = new Tabs('mainTabGroup', defaultTabs, 0);
function loadJSON(res, id, message, editorOptions) {
// workaround for JSON Editor issue with clearing arrays
// https://github.com/jdorn/json-editor/issues/617
if (docEditor) {
docEditor.destroy();
}
docEditor = new JSONEditor(document.getElementById('docEditor'), editorOptions ? editorOptions : docEditorOptions);
docEditor.on('ready', async function () {
await docEditor.root.setValue(res, true);
infoMsg.textContent = message ? message : '';
//errMsg.textContent = "";
if(id) {
document.title = id;
} else {
var nid = getDocID();
document.title = nid ? nid : 'Vulnogram';
}
if (document.getElementById("save1")) {
save2.className = "btn sfe gap";
save1.className = "fbn sfe";
}
if (message) {
selected = "editorTab";
}
docEditor.watch('root', function(){
mainTabGroup.change(0);
});
docEditor.on('change', async function(){
var errors = [];
if(docEditor.validation_results && docEditor.validation_results.length > 0) {
if (typeof(errorFilter) !== 'undefined'){
errors = errorFilter(docEditor.validation_results);
} else {
errors = docEditor.validation_results;
}
}
if(errors.length > 0) {
showJSONerrors(errors);
} else {
hideJSONerrors();
}
});
editorLabel.className = "lbl";
postUrl = getDocID() ? './' + getDocID() : "./new";
document.getElementById(selected).checked = true;
var event = new Event('change');
//document.getElementById(selected).dispatchEvent(event);
setTimeout(function (){
document.getElementById(selected).dispatchEvent(event);
}, 50);
});
}
function save(e, onSuccess) {
var j = mainTabGroup.getValue();
if (!j){
return;
}
infoMsg.textContent = "Saving...";
fetch(postUrl ? postUrl : '', {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
'CSRF-Token': csrfToken
},
redirect: 'error',
body: JSON.stringify(j),
})
.then(function (response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(function (res) {
if (res.type == "go") {
window.location.href = res.to;
} else if (res.type == "err") {
showAlert(res.msg);
errMsg.textContent = res.msg;
infoMsg.textContent = "";
} else if (res.type == "saved") {
infoMsg.textContent = "Saved";
errMsg.textContent = "";
document.title = originalTitle;
// turn button to normal, indicate nothing to save,
// but do not disable it.
if (document.getElementById("save1")) {
save2.className = "btn sfe gap";
save1.className = "fbn sfe";
}
getChanges(getDocID());
if (onSuccess)
onSuccess()
}
changes = 0;
})
.catch(function (error) {
showAlert(error + ' Try reloading the page.');
errMsg.textContent = error + ' Try reloading the page.';
});
// This is a trick for brower auto completion to work
document.getElementById('docEditor').submit();
}
function getDocID() {
var idEditor = docEditor.getEditor('root.' + idpath);
if (idEditor) {
var val = idEditor.getValue();
if (val) {
return val;
} else {
return null;
}
}
}
function copyText(element) {
if (document.selection) {
var range = document.body.createTextRange();
range.moveToElementText(element);
range.select();
document.execCommand("copy");
document.selection.empty();
infoMsg.textContent = 'Copied JSON to clipboard';
} else if (window.getSelection) {
var mrange = document.createRange();
mrange.selectNode(element);
window.getSelection().removeAllRanges();
window.getSelection().addRange(mrange);
document.execCommand("copy");
window.getSelection().removeAllRanges();
infoMsg.textContent = 'Copied JSON to clipboard';
}
}
function importFile(event, elem) {
var file = document.getElementById("importJSON");
file.click();
}
function loadFile(event, elem) {
var file = elem.files[0];
if (file) {
var reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = function (evt) {
loadJSON(JSON.parse(evt.target.result), null, "Imported file");
};
reader.onerror = function (evt) {
showAlert("Error reading file");
errMsg.textContent = "Error reading file";
};
}
}
function downloadFile(event, link) {
var j = mainTabGroup.getValue();
if (!j){
event.preventDefault();
alert('JSON Validation Failure: Fix the errors before downloading')
return false;
}
var file = new File([textUtil.getMITREJSON(textUtil.reduceJSON(j))], getDocID() + '.json', {
type: "text/plain",
lastModified: new Date()
});
link.href = URL.createObjectURL(file);
link.download = file.name;
// trick to get autocomplete work
document.getElementById('docEditor').submit();
}
function downloadText(element, link) {
var j = mainTabGroup.getValue();
if (!j){
event.preventDefault();
alert('JSON Validation Failure: Fix the errors before downloading.')
return false;
}
var file = new File([element.textContent], getDocID() + '.json', {
type: "text/plain",
lastModified: new Date()
});
link.href = URL.createObjectURL(file);
link.download = file.name;
}
function downloadHtml(title, element, link) {
var file = new File([
'<html><head><title>'
+ title
+ '</title><style>body{font-family:"Helvetica"; margin:3em}</style><body>'
+ element.innerHTML
+ '</body></html>'
], getDocID() + '.html', {
type: "text/html",
lastModified: new Date()
});
link.href = URL.createObjectURL(file);
link.download = file.name;
}
function showAlert(msg, smallmsg, timer, showCancel) {
errMsg.textContent="";
infoMsg.textContent="";
if (showCancel) {
document.getElementById("alertCancel").style.display = "inline-block";
} else {
var temp1 = document.getElementById("alertOk");
temp1.setAttribute("onclick", "document.getElementById('alertDialog').close();");
document.getElementById("alertCancel").style.display = "none";
}
document.getElementById("alertMessage").innerText = msg;
if (smallmsg)
document.getElementById("smallAlert").innerText = smallmsg;
else
document.getElementById("smallAlert").innerText = " ";
if (!document.getElementById("alertDialog").hasAttribute("open"))
document.getElementById("alertDialog").showModal();
if (timer)
setTimeout(function () {
document.getElementById("alertDialog").close();
}, timer);
}