Commit 6c4baa94 authored by Romain Courteaud's avatar Romain Courteaud

slapos_jio: rewrite JSON editor

* jslint
* drop duplicated loopEventListener
* use rjs.onEvent method to reduce the number of created listeners
* use domsugar to make the code more readable
* start to use mutex
parent 1d4a4b51
......@@ -10,6 +10,7 @@
<script src="URI.js" type="text/javascript"></script>
<script src="jquery.js" type="text/javascript"></script>
<script src="vkbeautify.js" type="text/javascript"></script>
<script src="domsugar.js" type="text/javascript"></script>
<script src="gadget_erp5_page_slap_parameter_form.js" type="text/javascript"></script>
<link href="gadget_erp5_page_slap_parameter_form.css" rel="stylesheet" type="text/css"/>
</head>
......
......@@ -238,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>971.50899.9801.15428</string> </value>
<value> <string>998.21971.45138.37495</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -256,7 +256,7 @@
</tuple>
<state>
<tuple>
<float>1542813409.18</float>
<float>1649668690.43</float>
<string>UTC</string>
</tuple>
</state>
......
/*jslint nomen: true, maxlen: 200, indent: 2*/
/*global rJS, console, window, document, RSVP, btoa, atob, $, XMLSerializer, jQuery, URI, vkbeautify */
/*jslint nomen: true, maxlen: 200, indent: 2, unparam: true*/
/*global rJS, console, window, document, RSVP, btoa, atob, $, XMLSerializer,
jQuery, URI, vkbeautify, domsugar, Boolean */
(function (window, document, rJS, $, XMLSerializer, jQuery, vkbeautify) {
(function (window, document, rJS, $, XMLSerializer, jQuery, vkbeautify,
loopEventListener, domsugar, Boolean) {
"use strict";
var gk = rJS(window);
var DISPLAY_JSON_FORM = 'display_json_form',
DISPLAY_RAW_XML = 'display_raw_xml';
function jsonDictToParameterXML(json) {
var parameter_id,
xml_output = $($.parseXML('<?xml version="1.0" encoding="utf-8" ?><instance />'));
xml_output = $($.parseXML('<?xml version="1.0" encoding="UTF-8" ?>\n<instance />'));
// Used by serialisation XML
for (parameter_id in json) {
if (json.hasOwnProperty(parameter_id)) {
......@@ -25,7 +28,7 @@
}
function jsonDictToParameterJSONInXML(json) {
var xml_output = $($.parseXML('<?xml version="1.0" encoding="utf-8" ?><instance />'));
var xml_output = $($.parseXML('<?xml version="1.0" encoding="UTF-8" ?>\n<instance />'));
// Used by serialisation XML
$('instance', xml_output).append(
$('<parameter />', xml_output)
......@@ -37,133 +40,68 @@
);
}
function loopEventListener(target, type, useCapture, callback,
prevent_default) {
//////////////////////////
// Infinite event listener (promise is never resolved)
// eventListener is removed when promise is cancelled/rejected
//////////////////////////
var handle_event_callback,
callback_promise;
if (prevent_default === undefined) {
prevent_default = true;
}
function cancelResolver() {
if ((callback_promise !== undefined) &&
(typeof callback_promise.cancel === "function")) {
callback_promise.cancel();
}
}
function canceller() {
if (handle_event_callback !== undefined) {
target.removeEventListener(type, handle_event_callback, useCapture);
}
cancelResolver();
}
function itsANonResolvableTrap(resolve, reject) {
var result;
handle_event_callback = function (evt) {
if (prevent_default) {
evt.stopPropagation();
evt.preventDefault();
}
cancelResolver();
try {
result = callback(evt);
} catch (e) {
result = RSVP.reject(e);
}
callback_promise = result;
new RSVP.Queue()
.push(function () {
return result;
})
.push(undefined, function (error) {
if (!(error instanceof RSVP.CancellationError)) {
canceller();
reject(error);
}
});
};
target.addEventListener(type, handle_event_callback, useCapture);
}
return new RSVP.Promise(itsANonResolvableTrap, canceller);
}
function render_selection(json_field, default_value) {
var input = document.createElement("select"),
option = document.createElement("option"),
option_index,
optionz;
input.size = 1;
option.value = "";
if (default_value === undefined) {
option.selected = true;
}
input.appendChild(option);
var option_list = [domsugar('option', {
value: "",
selected: (default_value === undefined)
})],
option_index;
for (option_index in json_field['enum']) {
if (json_field['enum'].hasOwnProperty(option_index)) {
optionz = document.createElement("option");
optionz.value = json_field['enum'][option_index];
optionz.textContent = json_field['enum'][option_index];
if (json_field['enum'][option_index] === default_value) {
optionz.selected = true;
}
input.appendChild(optionz);
option_list.push(domsugar('option', {
value: json_field['enum'][option_index],
text: json_field['enum'][option_index],
selected: (json_field['enum'][option_index] === default_value)
}));
}
}
return input;
return domsugar('select', {
size: 1
}, option_list);
}
function render_selection_oneof(json_field, default_value) {
var input = document.createElement("select"),
option = document.createElement("option"),
optionz;
input.size = 1;
option.value = "";
if (default_value === undefined) {
option.selected = true;
}
input.appendChild(option);
json_field.oneOf.forEach(function (element, index) {
var option_list = [domsugar('option', {
value: "",
selected: (default_value === undefined)
})];
json_field.oneOf.forEach(function (element) {
if ((element['const'] !== undefined) && (element.title !== undefined)) {
var value;
if ((json_field.type == 'array') || (json_field.type == 'object')) {
if ((json_field.type === 'array') || (json_field.type === 'object')) {
// Support for unusual types
value = JSON.stringify(element['const']);
} else {
value = element['const'];
}
optionz = document.createElement("option");
optionz.value = value;
optionz.textContent = element.title;
if (value === default_value) {
optionz.selected = true;
}
input.appendChild(optionz);
option_list.push(domsugar('option', {
value: value,
text: element.title,
selected: (value === default_value)
}));
}
});
return input;
return domsugar('select', {
size: 1
}, option_list);
}
function render_textarea(json_field, default_value, data_format) {
var input = document.createElement("textarea");
var value = '';
if (default_value !== undefined) {
if (default_value instanceof Array) {
input.value = default_value.join("\n");
value = default_value.join("\n");
} else {
input.value = default_value;
value = default_value;
}
}
input["data-format"] = data_format;
return input;
return domsugar('textarea', {
value: value,
"data-format": data_format
});
}
function render_field(json_field, default_value) {
......@@ -195,23 +133,27 @@
return render_textarea(json_field, default_value, "string");
}
var input = document.createElement("input");
var value,
type;
if (default_value !== undefined) {
input.value = default_value;
value = default_value;
}
if (json_field.type === "integer") {
input.type = "number";
type = "number";
} else if (json_field.type === "number") {
input.type = "number";
type = "number";
} else if (json_field.type === "hidden") {
input.type = "hidden";
type = "hidden";
} else {
input.type = "text";
type = "text";
}
return input;
return domsugar('input', {
value: value,
type: type
});
}
function render_subform(json_field, default_dict, root, path, restricted) {
......@@ -219,7 +161,6 @@
key,
div,
label,
close_span,
input,
default_value,
default_used_list = [],
......@@ -238,54 +179,56 @@
if (json_field.patternProperties !== undefined) {
if (json_field.patternProperties['.*'] !== undefined) {
div = document.createElement("div");
div.setAttribute("class", "subfield");
div.title = json_field.description;
div = domsugar("div", {
"class": "subfield",
title: json_field.description
});
if (restricted !== true) {
div_input = document.createElement("div");
div_input.setAttribute("class", "input");
input = document.createElement("input");
input.type = "text";
// Name is only meaningfull to automate tests
input.name = "ADD" + path;
div_input.appendChild(input);
input = document.createElement("button");
input.value = btoa(JSON.stringify(json_field.patternProperties['.*']));
input.setAttribute("class", "add-sub-form");
input.type = "button";
input.name = path;
input.textContent = "+";
div_input.appendChild(input);
div_input = domsugar("div", {
"class": "input"
}, [
domsugar('input', {
type: "text",
// Name is only meaningfull to automate tests
name: "ADD" + path
}),
domsugar('button', {
value: btoa(JSON.stringify(json_field.patternProperties['.*'])),
"class": "add-sub-form",
type: "button",
name: path,
text: "+"
})
]);
div.appendChild(div_input);
}
for (default_value in default_dict) {
if (default_dict.hasOwnProperty(default_value)) {
default_div = document.createElement("div");
default_div.setAttribute("class", "slapos-parameter-dict-key");
label = document.createElement("label");
label.textContent = default_value;
label.setAttribute("class", "slapos-parameter-dict-key");
close_span = document.createElement("span");
close_span.textContent = "×";
close_span.setAttribute("class", "bt_close CLOSE" + path + "/" + default_value);
close_span.setAttribute("title", "Remove this parameter section.");
label.appendChild(close_span);
default_div.appendChild(label);
default_div = render_subform(
default_div = domsugar("div", {
"class": "slapos-parameter-dict-key"
}, [
domsugar('label', {
text: default_value,
'class': "slapos-parameter-dict-key"
}, [
domsugar('span', {
text: "×",
"class": "bt_close CLOSE" + path + "/" + default_value,
title: "Remove this parameter section."
})
])
]);
div.appendChild(render_subform(
json_field.patternProperties['.*'],
default_dict[default_value],
default_div,
path + "/" + default_value,
restricted
);
div.appendChild(default_div);
));
}
}
root.appendChild(div);
......@@ -337,15 +280,15 @@
if (default_used_list.indexOf(key) < 0) {
div = document.createElement("div");
div.title = key;
if (typeof default_dict[key] === 'object') {
div_input = document.createElement("div");
div_input.setAttribute("class", "input");
label.setAttribute("class", "slapos-parameter-dict-key");
div_input = render_subform({},
default_dict[key],
div_input,
path + "/" + key,
restricted);
if (typeof default_dict[key] === 'object') {
div_input = document.createElement("div");
div_input.setAttribute("class", "input");
label.setAttribute("class", "slapos-parameter-dict-key");
div_input = render_subform({},
default_dict[key],
div_input,
path + "/" + key,
restricted);
} else if (restricted === true) {
div_input = document.createElement("div");
div_input.setAttribute("class", "input");
......@@ -433,10 +376,6 @@
return multi_level_dict;
}
function validateForm(gadget, json_url) {
return gadget.processValidation(json_url);
}
function collapseParameter(element) {
$(element).parent().children("div").toggle(300);
if ($(element).hasClass("slapos-parameter-dict-key-colapse")) {
......@@ -453,21 +392,21 @@
return false;
}
function addSubForm(element) {
function addSubForm(gadget, element) {
var subform_json = JSON.parse(atob(element.value)),
input_text = element.parentNode.querySelector("input[type='text']"),
div = document.createElement("div"),
label;
div;
if (input_text.value === "") {
return false;
}
div.setAttribute("class", "slapos-parameter-dict-key");
label = document.createElement("label");
label.textContent = input_text.value;
label.setAttribute("class", "slapos-parameter-dict-key");
div.appendChild(label);
div = domsugar('div', {
'class': "slapos-parameter-dict-key"
}, [domsugar('label', {
'class': "slapos-parameter-dict-key",
text: input_text.value
})]);
div = render_subform(subform_json, {}, div, element.name + "/" + input_text.value);
......@@ -477,54 +416,6 @@
return div;
}
function loadEventList(gadget) {
var g = gadget,
field_list = g.element.querySelectorAll(".slapos-parameter"),
button_list = g.element.querySelectorAll('button.add-sub-form'),
label_list = g.element.querySelectorAll('label.slapos-parameter-dict-key'),
close_list = g.element.querySelectorAll(".bt_close"),
i,
promise_list = [];
for (i = 0; i < field_list.length; i = i + 1) {
promise_list.push(loopEventListener(
field_list[i],
'change',
false,
validateForm.bind(g, g, g.options.value.parameter.json_url)
));
}
for (i = 0; i < button_list.length; i = i + 1) {
promise_list.push(loopEventListener(
button_list[i],
'click',
false,
addSubForm.bind(g, button_list[i])
));
}
for (i = 0; i < label_list.length; i = i + 1) {
promise_list.push(loopEventListener(
label_list[i],
'click',
false,
collapseParameter.bind(g, label_list[i])
));
}
for (i = 0; i < close_list.length; i = i + 1) {
promise_list.push(loopEventListener(
close_list[i],
'click',
false,
removeSubParameter.bind(g, close_list [i])
));
}
return RSVP.all(promise_list);
}
function getSoftwareTypeFromForm(element) {
var input = element.querySelector(".slapos-software-type");
......@@ -543,7 +434,7 @@
return "";
}
/*
function getSchemaUrlFromForm(element) {
var input = element.querySelector(".parameter_schema_url");
......@@ -552,130 +443,140 @@
}
return "";
}
*/
gk.declareMethod("loadJSONSchema", function (url, serialisation) {
return this.getDeclaredGadget('loadschema')
.push(function (gadget) {
return gadget.loadJSONSchema(url, serialisation);
});
})
function showParameterForm(g) {
var e = g.element.getElementsByTagName('select')[0],
to_hide = g.element.querySelector("button.slapos-show-form"),
to_show = g.element.querySelector("button.slapos-show-raw-parameter");
.declareMethod("validateJSON", function (base_url, schema_url, generated_json) {
return this.getDeclaredGadget('loadschema')
.push(function (gadget) {
return gadget.validateJSON(base_url, schema_url, generated_json);
});
})
if (e === undefined) {
throw new Error("Select not found.");
}
.declareMethod("getBaseUrl", function (url) {
return this.getDeclaredGadget('loadschema')
.push(function (gadget) {
return gadget.getBaseUrl(url);
});
})
.declareMethod("loadSoftwareJSON", function (url) {
return this.getDeclaredGadget('loadschema')
.push(function (gadget) {
return gadget.loadSoftwareJSON(url);
});
})
$(to_hide).addClass("hidden-button");
$(to_show).removeClass("hidden-button");
.declareMethod('processValidation', function (json_url) {
var g = this,
software_type = getSoftwareTypeFromForm(g.element),
json_dict = getFormValuesAsJSONDict(g.element),
schema_url = getSchemaUrlFromForm(g.element),
serialisation_type = getSerialisationTypeFromForm(g.element);
return g.changeState({
display_step: DISPLAY_JSON_FORM,
softwareindex: e.selectedOptions[0]["data-id"],
// Force refresh in any case
render_timestamp: new Date().getTime()
});
}
if (software_type === "") {
if (g.options.value.parameter.shared) {
throw new Error("The software type is not part of the json (" + software_type + " as slave)");
}
throw new Error("The software type is not part of the json (" + software_type + ")");
}
function showRawParameter(g) {
var e = g.element.querySelector("button.slapos-show-raw-parameter"),
to_show = g.element.querySelector("button.slapos-show-form");
return g.getBaseUrl(json_url)
.push(function (base_url) {
return g.validateJSON(base_url, json_url, json_dict);
})
.push(function (validation) {
var error_index,
parameter_hash_input = g.element.querySelectorAll('.parameter_hash_output')[0],
field_name,
div,
divm,
missing_index,
missing_field_name,
xml_output;
$(g.element.querySelectorAll("span.error")).each(function (i, span) {
span.textContent = "";
});
$(e).addClass("hidden-button");
$(to_show).removeClass("hidden-button");
$(g.element.querySelectorAll("div.error-input")).each(function (i, div) {
div.setAttribute("class", "");
});
if (serialisation_type === "json-in-xml") {
xml_output = jsonDictToParameterJSONInXML(json_dict);
} else {
xml_output = jsonDictToParameterXML(json_dict);
}
parameter_hash_input.value = btoa(xml_output);
g.options.value.parameter.parameter_hash = btoa(xml_output);
// console.log(parameter_hash_input.value);
// console.log(xml_output);
if (validation.valid) {
return xml_output;
}
for (error_index in validation.errors) {
if (validation.errors.hasOwnProperty(error_index)) {
field_name = validation.errors[error_index].dataPath;
div = $(".slapos-parameter[name='/" + field_name + "']")[0].parentNode;
div.setAttribute("class", "slapos-parameter error-input");
div.querySelector("span.error").textContent = validation.errors[error_index].message;
}
}
return g.changeState({
display_step: DISPLAY_RAW_XML,
// Force refresh in any case
render_timestamp: new Date().getTime()
});
}
for (missing_index in validation.missing) {
if (validation.missing.hasOwnProperty(missing_index)) {
missing_field_name = validation.missing[missing_index].dataPath;
divm = $('.slapos-parameter[name=/' + missing_field_name + "']")[0].parentNode;
divm.setAttribute("class", "error-input");
divm.querySelector("span.error").textContent = validation.missing[missing_index].message;
}
}
return "ERROR";
});
})
function updateParameterForm(g) {
var e = g.element.getElementsByTagName('select')[0],
parameter_shared = g.element.querySelector('input.parameter_shared');
if (e === undefined) {
throw new Error("Select not found.");
}
.declareMethod('renderParameterForm', function (json_url, default_dict, restricted_parameter, serialisation) {
parameter_shared.value = e.selectedOptions[0]["data-shared"];
return g.changeState({
softwareindex: e.selectedOptions[0]["data-id"],
// Force refresh in any case
render_timestamp: new Date().getTime()
});
}
var g = this;
return g.loadJSONSchema(json_url, serialisation)
.push(function (json) {
var fieldset_list = g.element.querySelectorAll('fieldset'),
fieldset = document.createElement("fieldset");
/////////////////////////////////////////////////////
// check the form validity
/////////////////////////////////////////////////////
function checkValidity(g) {
var json_url = g.state.json_url,
software_type = getSoftwareTypeFromForm(g.element),
json_dict = getFormValuesAsJSONDict(g.element),
// schema_url = getSchemaUrlFromForm(g.element),
serialisation_type = getSerialisationTypeFromForm(g.element);
if (software_type === "") {
if (g.state.shared) {
throw new Error("The software type is not part of the json (" + software_type + " as slave)");
}
throw new Error("The software type is not part of the json (" + software_type + ")");
}
fieldset = render_subform(json, default_dict, fieldset, undefined, restricted_parameter);
$(fieldset_list[1]).replaceWith(fieldset);
return fieldset_list;
return g.getBaseUrl(json_url)
.push(function (base_url) {
return g.validateJSON(base_url, json_url, json_dict);
})
.push(function (validation) {
var error_index,
parameter_hash_input = g.element.querySelectorAll('.parameter_hash_output')[0],
field_name,
div,
divm,
missing_index,
missing_field_name,
xml_output;
$(g.element.querySelectorAll("span.error")).each(function (i, span) {
span.textContent = "";
});
})
.declareMethod('renderFailoverTextArea', function (content, error) {
var g = this,
div = document.createElement("div"),
div_error = document.createElement("div"),
span_error = document.createElement("span"),
detail_error = document.createElement("details"),
detail_error_summary = document.createElement("summary"),
detail_error_span = document.createElement("span"),
textarea = document.createElement("textarea"),
fieldset = document.createElement("fieldset"),
fieldset_list = g.element.querySelectorAll('fieldset'),
show_raw_button = g.element.querySelector("button.slapos-show-raw-parameter"),
show_form_button = g.element.querySelector("button.slapos-show-form");
$(g.element.querySelectorAll("div.error-input")).each(function (i, div) {
div.setAttribute("class", "");
});
if (serialisation_type === "json-in-xml") {
xml_output = jsonDictToParameterJSONInXML(json_dict);
} else {
xml_output = jsonDictToParameterXML(json_dict);
}
parameter_hash_input.value = btoa(xml_output);
// g.options.value.parameter.parameter_hash = btoa(xml_output);
// console.log(parameter_hash_input.value);
// console.log(xml_output);
if (validation.valid) {
return xml_output;
}
for (error_index in validation.errors) {
if (validation.errors.hasOwnProperty(error_index)) {
field_name = validation.errors[error_index].dataPath;
div = $(".slapos-parameter[name='/" + field_name + "']")[0].parentNode;
div.setAttribute("class", "slapos-parameter error-input");
div.querySelector("span.error").textContent = validation.errors[error_index].message;
}
}
for (missing_index in validation.missing) {
if (validation.missing.hasOwnProperty(missing_index)) {
missing_field_name = validation.missing[missing_index].dataPath;
divm = $('.slapos-parameter[name=/' + missing_field_name + "']")[0].parentNode;
divm.setAttribute("class", "error-input");
divm.querySelector("span.error").textContent = validation.missing[missing_index].message;
}
}
return "ERROR";
});
}
/////////////////////////////////////////////////////
// main render display functions
/////////////////////////////////////////////////////
function renderDisplayRawXml(g, error_text) {
var fieldset,
fieldset_list = g.element.querySelectorAll('fieldset'),
div_error,
show_raw_button = g.element.querySelector("button.slapos-show-raw-parameter"),
show_form_button = g.element.querySelector("button.slapos-show-form");
if (error_text) {
if (show_raw_button !== null) {
$(show_raw_button).addClass("hidden-button");
}
......@@ -684,437 +585,443 @@
$(show_form_button).removeClass("hidden-button");
}
div.setAttribute("class", "field");
textarea.setAttribute("rows", "10");
textarea.setAttribute("cols", "80");
textarea.setAttribute("name", "text_content");
textarea.textContent = content;
span_error.setAttribute("class", "error");
span_error.textContent = "Parameter form is not available, use the textarea above for edit the instance parameters.";
detail_error_summary.textContent = "More information..."
detail_error_span.setAttribute("class", "error_msg");
detail_error_span.textContent = error;
detail_error.appendChild(detail_error_summary);
detail_error.appendChild(detail_error_span);
div_error.setAttribute("class", "error");
div.appendChild(textarea);
div_error.appendChild(span_error);
div_error.appendChild(detail_error);
fieldset.appendChild(div);
fieldset.appendChild(div_error);
// Do not hide the Software type, let the user edit it.
//fieldset_list[0].innerHTML = '';
$(fieldset_list[1]).replaceWith(fieldset);
fieldset_list[2].innerHTML = '';
return fieldset;
})
.declareMethod('renderRawParameterTextArea', function (content) {
var g = this,
div = document.createElement("div"),
div_error = document.createElement("div"),
textarea = document.createElement("textarea"),
fieldset = document.createElement("fieldset"),
fieldset_list = g.element.querySelectorAll('fieldset');
div.setAttribute("class", "field");
textarea.setAttribute("rows", "10");
textarea.setAttribute("cols", "80");
textarea.setAttribute("name", "text_content");
textarea.textContent = content;
div.appendChild(textarea);
div.appendChild(textarea);
fieldset.appendChild(div);
fieldset.appendChild(div_error);
$(fieldset_list[1]).replaceWith(fieldset);
fieldset_list[2].innerHTML = '';
return fieldset;
})
.declareMethod('render', function (options) {
var gadget = this,
to_hide = gadget.element.querySelector("button.slapos-show-form"),
to_show = gadget.element.querySelector("button.slapos-show-raw-parameter"),
softwaretype,
json_url = options.value.parameter.json_url;
div_error = domsugar('div', {
'class': 'error'
}, [
domsugar('span', {
'class': 'error',
text: "Parameter form is not available, use the textarea above for edit the instance parameters."
}),
domsugar('details', [
domsugar('summary', {
text: "More information..."
}),
domsugar('span', {
'class': 'error_msg',
text: error_text
})
])
]);
} else {
div_error = domsugar('div');
}
fieldset = domsugar('fieldset', [
domsugar('div', {
'class': 'field'
}, [
domsugar('textarea', {
rows: "10",
cols: "80",
name: "text_content",
text: g.state.parameter_xml
})
]),
// div error
div_error
]);
gadget.options = options;
// Do not hide the Software type, let the user edit it.
$(fieldset_list[1]).replaceWith(fieldset);
fieldset_list[2].innerHTML = '';
if (options.value.parameter.parameter_hash !== undefined) {
// A JSON where provided via gadgetfield
options.value.parameter.parameter_xml = atob(options.value.parameter.parameter_hash);
}
return fieldset;
}
if (json_url === undefined) {
throw new Error("undefined json_url");
}
function renderDisplayJsonForm(gadget) {
var serialisation = gadget.state.serialisation,
json_url = gadget.state.json_url,
parameter_xml = gadget.state.parameter_xml,
restricted_softwaretype = gadget.state.restricted_softwaretype,
restricted_parameter = gadget.state.restricted_parameter,
shared = gadget.state.shared,
softwaretype = gadget.state.softwaretype,
softwareindex = gadget.state.softwareindex,
to_hide = gadget.element.querySelector("button.slapos-show-form"),
to_show = gadget.element.querySelector("button.slapos-show-raw-parameter");
if (json_url === undefined) {
throw new Error("undefined json_url");
}
if (to_hide !== null) {
$(to_hide).addClass("hidden-button");
}
if (to_hide !== null) {
$(to_hide).addClass("hidden-button");
}
if (to_show !== null) {
$(to_show).removeClass("hidden-button");
}
return gadget.loadSoftwareJSON(json_url)
.push(function (json) {
var option_index,
option,
option_selected = options.value.parameter.softwaretype,
option_selected_index = options.value.parameter.softwaretypeindex,
restricted_softwaretype = options.value.parameter.restricted_softwaretype,
input = gadget.element.querySelector('select.slapos-software-type'),
parameter_shared = gadget.element.querySelector('input.parameter_shared'),
parameter_schema_url = gadget.element.querySelector('input.parameter_schema_url'),
s_input = gadget.element.querySelector('input.slapos-serialisation-type'),
selection_option_list = [],
lowest_index = 999,
lowest_option_index;
if (input.children.length === 0) {
if (option_selected === undefined) {
// search by the lowest index
for (option_index in json['software-type']) {
if (json['software-type'].hasOwnProperty(option_index)) {
if (json['software-type'][option_index].index === undefined) {
json['software-type'][option_index].index = 999;
}
if (to_show !== null) {
$(to_show).removeClass("hidden-button");
}
return gadget.loadSoftwareJSON(json_url)
.push(function (json) {
var option_index,
option,
option_selected = softwaretype,
option_selected_index = softwareindex,
input = gadget.element.querySelector('select.slapos-software-type'),
parameter_shared = gadget.element.querySelector('input.parameter_shared'),
parameter_schema_url = gadget.element.querySelector('input.parameter_schema_url'),
s_input = gadget.element.querySelector('input.slapos-serialisation-type'),
selection_option_list = [],
lowest_index = 999,
lowest_option_index;
if (input.children.length === 0) {
if (option_selected === undefined) {
// search by the lowest index
for (option_index in json['software-type']) {
if (json['software-type'].hasOwnProperty(option_index)) {
if (json['software-type'][option_index].index === undefined) {
json['software-type'][option_index].index = 999;
}
if (json['software-type'][option_index].index < lowest_index) {
lowest_index = json['software-type'][option_index].index;
lowest_option_index = option_index;
}
if (json['software-type'][option_index].index < lowest_index) {
lowest_index = json['software-type'][option_index].index;
lowest_option_index = option_index;
}
}
}
}
for (option_index in json['software-type']) {
if (json['software-type'].hasOwnProperty(option_index)) {
option = document.createElement("option");
if (json['software-type'][option_index]['software-type'] !== undefined) {
option.value = json['software-type'][option_index]['software-type'];
} else {
option.value = option_index;
}
for (option_index in json['software-type']) {
if (json['software-type'].hasOwnProperty(option_index)) {
option = document.createElement("option");
if (json['software-type'][option_index]['software-type'] !== undefined) {
option.value = json['software-type'][option_index]['software-type'];
} else {
option.value = option_index;
}
option['data-id'] = option_index;
option.textContent = json['software-type'][option_index].title;
if (json['software-type'][option_index].index) {
option['data-index'] = json['software-type'][option_index].index;
} else {
option['data-index'] = 999;
}
option['data-id'] = option_index;
option.textContent = json['software-type'][option_index].title;
if (json['software-type'][option_index].index) {
option['data-index'] = json['software-type'][option_index].index;
if (option_index === lowest_option_index) {
option_selected = option.value;
option.selected = true;
option_selected_index = option_index;
if (json['software-type'][option_index].shared === true) {
parameter_shared.value = true;
} else {
option['data-index'] = 999;
parameter_shared.value = false;
}
if (option_index === lowest_option_index) {
option_selected = option.value;
option.selected = true;
option_selected_index = option_index;
if (json['software-type'][option_index].shared === true) {
parameter_shared.value = true;
} else {
parameter_shared.value = false;
}
if (options.value.parameter.shared === undefined) {
options.value.parameter.shared = parameter_shared.value;
}
if (shared === undefined) {
shared = parameter_shared.value;
}
}
if (json['software-type'][option_index].shared === undefined) {
json['software-type'][option_index].shared = false;
}
if (json['software-type'][option_index].shared === undefined) {
json['software-type'][option_index].shared = false;
}
option['data-shared'] = json['software-type'][option_index].shared;
option['data-shared'] = json['software-type'][option_index].shared;
if ((option_selected_index === undefined) &&
if ((option_selected_index === undefined) &&
(option.value === option_selected) &&
(options.value.parameter.shared == json['software-type'][option_index].shared)) {
option.selected = true;
option_selected_index = option_index;
if (json['software-type'][option_index].shared === true) {
parameter_shared.value = true;
} else {
parameter_shared.value = false;
}
(Boolean(shared) === Boolean(json['software-type'][option_index].shared))) {
option.selected = true;
option_selected_index = option_index;
if (json['software-type'][option_index].shared === true) {
parameter_shared.value = true;
} else {
parameter_shared.value = false;
}
}
if (restricted_softwaretype === true) {
if (option.value === options.value.parameter.softwaretype) {
if (options.value.parameter.shared == json['software-type'][option_index].shared) {
selection_option_list.push(option);
}
if (restricted_softwaretype === true) {
if (option.value === softwaretype) {
if (Boolean(shared) === Boolean(json['software-type'][option_index].shared)) {
selection_option_list.push(option);
}
} else {
selection_option_list.push(option);
}
} else {
selection_option_list.push(option);
}
}
}
selection_option_list.sort(function (a, b) {
return a["data-index"] - b["data-index"];
});
}
for (option_index in selection_option_list) {
selection_option_list.sort(function (a, b) {
return a["data-index"] - b["data-index"];
});
for (option_index in selection_option_list) {
if (selection_option_list.hasOwnProperty(option_index)) {
input.appendChild(selection_option_list[option_index]);
}
}
if (softwaretype === undefined) {
softwaretype = option_selected;
}
if (input.children.length === 0) {
if (options.value.parameter.shared) {
throw new Error("The software type is not part of the json (" + softwaretype + " as slave)");
}
throw new Error("The software type is not part of the json (" + softwaretype + ")");
}
if (json['software-type'][option_selected_index] === undefined) {
throw new Error("The sotware type is not part of the json (" + softwaretype + ")");
if (softwaretype === undefined) {
softwaretype = option_selected;
}
if (input.children.length === 0) {
if (shared) {
throw new Error("The software type is not part of the json (" + softwaretype + " as slave)");
}
throw new Error("The software type is not part of the json (" + softwaretype + ")");
}
if (json['software-type'][option_selected_index] === undefined) {
throw new Error("The sotware type is not part of the json (" + softwaretype + ")");
}
if (json['software-type'][option_selected_index].serialisation !== undefined) {
s_input.value = json['software-type'][option_selected_index].serialisation;
options.serialisation = json['software-type'][option_selected_index].serialisation;
if (json['software-type'][option_selected_index].serialisation !== undefined) {
s_input.value = json['software-type'][option_selected_index].serialisation;
serialisation = json['software-type'][option_selected_index].serialisation;
} else {
s_input.value = json.serialisation;
serialisation = json.serialisation;
}
// Save current schema on the field
parameter_schema_url.value = json['software-type'][option_selected_index].request;
return parameter_schema_url.value;
})
.push(function (parameter_json_schema_url) {
var parameter_dict = {}, parameter_list, json_url_uri, prefix, parameter_entry;
if (parameter_xml !== undefined) {
if (serialisation === "json-in-xml") {
parameter_list = jQuery.parseXML(
parameter_xml
).querySelectorAll("parameter");
if (parameter_list.length > 1) {
throw new Error("The current parameter should contains only _ parameter (json-in-xml).");
}
parameter_entry = jQuery.parseXML(
parameter_xml
).querySelector("parameter[id='_']");
if (parameter_entry !== null) {
parameter_dict = JSON.parse(parameter_entry.textContent);
} else if (parameter_list.length === 1) {
throw new Error(
"The current parameter should contains only _ parameter (json-in-xml)."
);
}
} else if (["", "xml"].indexOf(serialisation) >= 0) {
parameter_entry = jQuery.parseXML(
parameter_xml
).querySelector("parameter[id='_']");
if (parameter_entry !== null) {
throw new Error("The current parameter values should NOT contains _ parameter (xml).");
}
$(jQuery.parseXML(parameter_xml)
.querySelectorAll("parameter"))
.each(function (key, p) {
parameter_dict[p.id] = p.textContent;
});
} else {
s_input.value = json.serialisation;
options.serialisation = json.serialisation;
throw new Error("Unknown serialisation: " + serialisation);
}
}
// Save current schema on the field
parameter_schema_url.value = json['software-type'][option_selected_index].request;
if (URI(parameter_json_schema_url).protocol() === "") {
// URL is relative, turn into absolute
json_url_uri = URI(json_url);
prefix = json_url_uri.path().split("/");
prefix.pop();
prefix = json_url.split(json_url_uri.path())[0] + prefix.join("/");
parameter_json_schema_url = prefix + "/" + parameter_json_schema_url;
}
return gadget.loadJSONSchema(parameter_json_schema_url, serialisation)
.push(function (json) {
var fieldset_list = gadget.element.querySelectorAll('fieldset'),
fieldset = document.createElement("fieldset");
fieldset = render_subform(json, parameter_dict, fieldset, undefined, restricted_parameter);
$(fieldset_list[1]).replaceWith(fieldset);
return fieldset_list;
});
})
.push(function () {
var i, div_list = gadget.element.querySelectorAll('.slapos-parameter-dict-key > div'),
label_list = gadget.element.querySelectorAll('label.slapos-parameter-dict-key');
return parameter_schema_url.value;
})
.push(function (parameter_json_schema_url) {
var parameter_dict = {}, parameter_list, json_url_uri, prefix, parameter_entry,
restricted_parameter = options.value.parameter.restricted_parameter;
if (options.value.parameter.parameter_xml !== undefined) {
if (options.serialisation === "json-in-xml") {
parameter_list = jQuery.parseXML(
options.value.parameter.parameter_xml)
.querySelectorAll("parameter")
if (parameter_list.length > 1) {
throw new Error("The current parameter should contains only _ parameter (json-in-xml).")
}
parameter_entry = jQuery.parseXML(
options.value.parameter.parameter_xml
).querySelector("parameter[id='_']");
if (parameter_entry !== null) {
parameter_dict = JSON.parse(parameter_entry.textContent);
} else if (parameter_list.length == 1) {
throw new Error(
"The current parameter should contains only _ parameter (json-in-xml).")
}
} else if (["", "xml"].indexOf(options.serialisation) >= 0) {
parameter_entry = jQuery.parseXML(
options.value.parameter.parameter_xml
).querySelector("parameter[id='_']");
if (parameter_entry !== null) {
throw new Error("The current parameter values should NOT contains _ parameter (xml).");
}
$(jQuery.parseXML(options.value.parameter.parameter_xml)
.querySelectorAll("parameter"))
.each(function (key, p) {
parameter_dict[p.id] = p.textContent;
});
} else {
throw new Error("Unknown serialisation: " + options.serialisation);
}
}
// console.log("Collapse paramaters");
if (URI(parameter_json_schema_url).protocol() === "") {
// URL is relative, turn into absolute
json_url_uri = URI(options.value.parameter.json_url);
prefix = json_url_uri.path().split("/");
prefix.pop();
prefix = options.value.parameter.json_url.split(json_url_uri.path())[0] + prefix.join("/");
parameter_json_schema_url = prefix + "/" + parameter_json_schema_url;
}
return gadget.renderParameterForm(parameter_json_schema_url,
parameter_dict, restricted_parameter,
options.serialisation);
})
.push(function () {
var i, div_list = gadget.element.querySelectorAll('.slapos-parameter-dict-key > div'),
label_list = gadget.element.querySelectorAll('label.slapos-parameter-dict-key');
for (i = 0; i < div_list.length; i = i + 1) {
$(div_list[i]).hide();
}
// console.log("Collapse paramaters");
for (i = 0; i < label_list.length; i = i + 1) {
$(label_list[i]).addClass("slapos-parameter-dict-key-colapse");
}
})
for (i = 0; i < div_list.length; i = i + 1) {
$(div_list[i]).hide();
}
.fail(function (error) {
console.warn(error);
console.log(error.stack);
return renderDisplayRawXml(gadget, error.toString());
});
}
for (i = 0; i < label_list.length; i = i + 1) {
$(label_list[i]).addClass("slapos-parameter-dict-key-colapse");
}
return gadget;
})
/////////////////////////////////////////////////////
// Gadget methods
/////////////////////////////////////////////////////
rJS(window)
.setState({
display_step: DISPLAY_JSON_FORM
})
.declareMethod("loadJSONSchema", function (url, serialisation) {
return this.getDeclaredGadget('loadschema')
.push(function (gadget) {
/* console.log("FINISHED TO RENDER, RETURNING THE GADGET"); */
return loadEventList(gadget);
})
.fail(function (error) {
var parameter_xml = '';
console.warn(error);
console.log(error.stack);
if (gadget.options.value.parameter.parameter_hash !== undefined) {
parameter_xml = atob(gadget.options.value.parameter.parameter_hash);
}
return gadget.renderFailoverTextArea(parameter_xml, error.toString())
.push(function () {
error = undefined;
return loadEventList(gadget);
});
return gadget.loadJSONSchema(url, serialisation);
});
})
.declareService(function () {
var g = this,
element = g.element.getElementsByTagName('select')[0];
if (element === undefined) {
return true;
}
.declareMethod("validateJSON", function (base_url, schema_url, generated_json) {
return this.getDeclaredGadget('loadschema')
.push(function (gadget) {
return gadget.validateJSON(base_url, schema_url, generated_json);
});
})
function updateParameterForm(evt) {
var e = g.element.getElementsByTagName('select')[0],
parameter_shared = g.element.querySelector('input.parameter_shared');
.declareMethod("getBaseUrl", function (url) {
return this.getDeclaredGadget('loadschema')
.push(function (gadget) {
return gadget.getBaseUrl(url);
});
})
.declareMethod("loadSoftwareJSON", function (url) {
return this.getDeclaredGadget('loadschema')
.push(function (gadget) {
return gadget.loadSoftwareJSON(url);
});
})
if (e === undefined) {
throw new Error("Select not found.");
}
.declareMethod('render', function (options) {
var parameter_hash = options.value.parameter.parameter_hash,
// XXX Do we directly get parameter_xml parameter?
parameter_xml = options.value.parameter.parameter_xml;
g.options.value.parameter.softwaretype = e.value;
g.options.value.parameter.softwaretypeindex = e.selectedOptions[0]["data-id"];
parameter_shared.value = e.selectedOptions[0]["data-shared"];
return g.render(g.options)
.push(function () {
return loadEventList(g);
});
if (parameter_hash !== undefined) {
// A JSON where provided via gadgetfield
parameter_xml = atob(parameter_hash);
}
return loopEventListener(
element,
'change',
false,
updateParameterForm.bind(g)
);
return this.changeState({
// Not used parameters
// editable: options.editable,
// hidden: options.hidden,
// key: options.key,
serialisation: options.serialisation,
json_url: options.value.parameter.json_url,
parameter_xml: parameter_xml,
restricted_softwaretype: options.value.parameter.restricted_softwaretype,
restricted_parameter: options.value.parameter.restricted_parameter,
shared: options.value.parameter.shared,
softwaretype: options.value.parameter.softwaretype,
softwareindex: options.value.parameter.softwareindex,
// Force refresh in any case
render_timestamp: new Date().getTime()
});
})
.declareService(function () {
var g = this,
element = g.element.querySelector("button.slapos-show-raw-parameter");
if (element === undefined) {
return true;
.onStateChange(function () {
if (this.state.display_step === DISPLAY_JSON_FORM) {
return renderDisplayJsonForm(this);
}
if (this.state.display_step === DISPLAY_RAW_XML) {
return renderDisplayRawXml(this);
}
throw new Error('Unhandled display step: ' + this.state.display_step);
})
function showRawParameter(evt) {
var e = g.element.querySelector("button.slapos-show-raw-parameter"),
to_show = g.element.querySelector("button.slapos-show-form"),
parameter_xml;
if (g.options.value.parameter.parameter_hash !== undefined) {
parameter_xml = atob(g.options.value.parameter.parameter_hash);
}
.onEvent("change", function (evt) {
var gadget = this,
software_type_element = gadget.element.getElementsByTagName('select')[0];
$(e).addClass("hidden-button");
$(to_show).removeClass("hidden-button");
if (evt.target === software_type_element) {
return updateParameterForm(gadget);
}
return g.renderRawParameterTextArea(parameter_xml)
.push(function () {
return loadEventList(g);
});
if (evt.target.className.indexOf("slapos-parameter") !== -1) {
// getContent is protected by a mutex which prevent
// onchangestate to be called in parallel
return gadget.getContent();
}
return loopEventListener(
element,
'click',
false,
showRawParameter.bind(g)
);
})
}, false, false)
.onEvent("click", function (evt) {
// Only handle click on BUTTON element
var gadget = this,
queue,
tag_name = evt.target.tagName;
.declareService(function () {
var g = this,
element = g.element.querySelector("button.slapos-show-form");
if ((tag_name === 'LABEL') &&
(evt.target.className.indexOf("slapos-parameter-dict-key") !== -1)) {
return collapseParameter(evt.target);
}
function showParameterForm(evt) {
var e = g.element.getElementsByTagName('select')[0],
to_hide = g.element.querySelector("button.slapos-show-form"),
to_show = g.element.querySelector("button.slapos-show-raw-parameter"),
text_content = g.element.querySelector('textarea[name=text_content]');
if (evt.target.className.indexOf("bt_close") !== -1) {
return removeSubParameter(evt.target);
}
if (e === undefined) {
throw new Error("Select not found.");
}
if (tag_name === 'BUTTON') {
// Disable any button. It must be managed by this gadget
evt.preventDefault();
}
$(to_hide).addClass("hidden-button");
$(to_show).removeClass("hidden-button");
// Always get content to ensure the possible displayed form
// is checked and content propagated to the gadget state value
queue = gadget.getContent();
g.options.value.parameter.softwaretype = e.value;
g.options.value.parameter.softwaretypeindex = e.selectedOptions[0]["data-id"];
g.options.value.parameter.parameter_xml = text_content.value;
g.options.value.parameter.parameter_hash = btoa(text_content.value);
return g.render(g.options)
if ((tag_name === 'BUTTON') &&
(evt.target.className.indexOf("slapos-show-form") !== -1)) {
return queue
.push(function () {
return loadEventList(g);
return showParameterForm(gadget);
});
}
if ((tag_name === 'BUTTON') &&
(evt.target.className.indexOf("slapos-show-raw-parameter") !== -1)) {
return queue
.push(function () {
return showRawParameter(gadget);
});
}
return loopEventListener(
element,
'click',
false,
showParameterForm.bind(g)
);
})
.declareService(function () {
return loadEventList(this);
})
if ((tag_name === 'BUTTON') &&
(evt.target.className.indexOf("add-sub-form") !== -1)) {
return queue
.push(function () {
return addSubForm(gadget, evt.target);
});
}
}, false, false)
.declareMethod('getContent', function () {
var gadget = this,
content_dict = {};
content_dict = {};
return gadget.getElement()
.push(function (element) {
var text_content = element.querySelector('textarea[name=text_content]'),
software_type = element.querySelector('select[name=software_type]'),
shared = element.querySelector('input[name=shared]');
software_type = element.querySelector('select[name=software_type]'),
shared = element.querySelector('input[name=shared]');
if (software_type !== null) {
gadget.state.softwaretype = software_type.value;
content_dict.software_type = software_type.value;
}
if ((shared !== null) && (shared.value === "true")) {
gadget.state.shared = 1;
content_dict.shared = 1;
}
if (text_content !== null) {
return text_content.value;
}
return gadget.processValidation(gadget.options.value.parameter.json_url);
return checkValidity(gadget);
})
.push(function (xml_result) {
// Update gadget state
gadget.state.parameter_xml = xml_result;
content_dict.text_content = xml_result;
return content_dict;
})
.fail(function (e) {
return {};
});
});
}, {mutex: 'statechange'});
//.declareService(function () {
// var gadget = this;
......@@ -1133,4 +1040,5 @@
// });
//});
}(window, document, rJS, $, XMLSerializer, jQuery, vkbeautify));
\ No newline at end of file
}(window, document, rJS, $, XMLSerializer, jQuery, vkbeautify,
rJS.loopEventListener, domsugar, Boolean));
\ No newline at end of file
......@@ -280,7 +280,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>999.23324.48841.24558</string> </value>
<value> <string>999.42820.18181.40192</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -298,7 +298,7 @@
</tuple>
<state>
<tuple>
<float>1649686520.32</float>
<float>1650370742.0</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -124,13 +124,13 @@
<tr>
<td>assertValue</td>
<td>//textarea[@name="text_content"]</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;instance&gt;&lt;/instance&gt;</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br>&lt;instance/&gt;</td>
</tr>
<tr>
<td>type</td>
<td>//textarea[@name="text_content"]</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;instance&gt;&lt;parameter id=&quot;ram-size&quot;&gt;1024&lt;/parameter&gt;&lt;/instance&gt;</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;&lt;instance&gt;&lt;parameter id=&quot;ram-size&quot;&gt;1024&lt;/parameter&gt;&lt;/instance&gt;</td>
</tr>
<tr>
......@@ -202,7 +202,7 @@
<tr>
<td>assertValue</td>
<td>//textarea[@name="text_content"]</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;instance&gt;&lt;parameter id=&quot;ram-size&quot;&gt;1024&lt;/parameter&gt;&lt;/instance&gt;</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;&lt;instance&gt;&lt;parameter id=&quot;ram-size&quot;&gt;1024&lt;/parameter&gt;&lt;/instance&gt;</td>
</tr>
<tr>
......@@ -221,7 +221,7 @@
<tr>
<td>type</td>
<td>//textarea[@name="text_content"]</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;instance&gt;&lt;parameter id=&quot;_&quot;&gt;{&quot;kvm-partition-dict&quot;: {&quot;T&quot;: {&quot;ram-size&quot;: 2048}}}&lt;/parameter&gt;&lt;/instance&gt;</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;instance&gt;&lt;parameter id=&quot;_&quot;&gt;{&quot;kvm-partition-dict&quot;: {&quot;T&quot;: {&quot;ram-size&quot;: 2048}}}&lt;/parameter&gt;&lt;/instance&gt;</td>
</tr>
<tal:block metal:use-macro="here/Zuite_SlapOSCommonTemplate/macros/click_proceed" />
......
......@@ -125,13 +125,13 @@
<tr>
<td>assertValue</td>
<td>//textarea[@name="text_content"]</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;instance&gt;&lt;/instance&gt;</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br>&lt;instance/&gt;</td>
</tr>
<tr>
<td>type</td>
<td>//textarea[@name="text_content"]</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;instance&gt;&lt;parameter id=&quot;ram-size&quot;&gt;1024&lt;/parameter&gt;&lt;/instance&gt;</td>
<td>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;&lt;instance&gt;&lt;parameter id=&quot;ram-size&quot;&gt;1024&lt;/parameter&gt;&lt;/instance&gt;</td>
</tr>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment