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)
  • This shoulndn't be the json_url but the schema_url.

    The json_url is software.cfg.json an the schema_url is the actually used to render the forum, so the validdation never occur (since the software.cfg.json can be used as json schema too (it will just consider everything as valid).

  • The line of the comment of json/schemaurl is bellow not at the getBaseUrl, see the fix on !383 (merged)

Please register or sign in to reply
.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