diff --git a/Frontend/assets/scripts/frame.js b/Frontend/assets/scripts/frame.js
index c1c72f7..00f61c6 100644
--- a/Frontend/assets/scripts/frame.js
+++ b/Frontend/assets/scripts/frame.js
@@ -1,20 +1,5 @@
const tools = new Map();
-/**
- * This functions imports other js file. I hate this solution, but other didn't work.
- *
- * @function
- * @name importScript
- * @kind function
- * @param {any} url
- * @returns {void}
- */
-function importScript(url) {
- var script = document.createElement("script");
- script.src = url;
- document.head.appendChild(script);
-}
-
/**
* Get address of Mock Services
*
diff --git a/Frontend/assets/scripts/tools/json.js b/Frontend/assets/scripts/tools/json.js
deleted file mode 100644
index 1f5f09c..0000000
--- a/Frontend/assets/scripts/tools/json.js
+++ /dev/null
@@ -1,72 +0,0 @@
-function formatAndValidateJson(errorElement) {
- const input = document.querySelector('#jsonBlock');
- const processInfo = document.getElementById(errorElement);
-
- const address = window.location.protocol + "//" + window.location.hostname + ":" + 8081 + "/json/formatting"
-
- fetch(address, {
- method: 'POST',
- body: input.textContent
- })
- .then(async (response) => {
- const promise = response.json();
- if (!response.ok) {
- throw Error(await promise);
- }
-
- return promise;
- })
- .then((data) => {
- input.innerText = data.data;
- processInfo.innerText = "";
- hljs.highlightElement(input);
-
- processInfo.innerHTML = "Computed in " + data.time + "ms";
- })
- .catch((error) => {
- processInfo.innerHTML = "" + error.data + "";
- console.error('Error:', error);
- });
-}
-
-function minimizeJson(errorElement) {
- const input = document.querySelector('#jsonBlock');
- const processInfo = document.getElementById(errorElement);
-
- const address = window.location.protocol + "//" + window.location.hostname + ":" + 8081 + "/json/minimize"
-
- fetch(address, {
- method: 'POST',
- body: input.textContent
- })
- .then(async (response) => {
- const promise = response.json();
- if (!response.ok) {
- throw Error(await promise);
- }
-
- return promise;
- })
- .then((data) => {
- input.innerText = data.data;
- processInfo.innerText = "";
- hljs.highlightElement(input);
-
- processInfo.innerHTML = "Computed in " + data.time + "ms";
- })
- .catch((error) => {
- processInfo.innerHTML = "" + error.data + "";
- console.error('Error:', error);
- });
-}
-
-function clearJsonData() {
- const input = document.querySelector('#jsonBlock');
- input.textContent = "";
-}
-
-function insertDefaultJson() {
- const input = document.querySelector('#jsonBlock');
- input.textContent = "{\"enter\": \"your\", \"json\": \"here\"}";
- hljs.highlightElement(input);
-}
\ No newline at end of file
diff --git a/Frontend/assets/scripts/tools/jsonFormatter.js b/Frontend/assets/scripts/tools/jsonFormatter.js
new file mode 100644
index 0000000..64f7609
--- /dev/null
+++ b/Frontend/assets/scripts/tools/jsonFormatter.js
@@ -0,0 +1,250 @@
+
+
+const mergeHTMLPlugin = (function () {
+ 'use strict';
+
+ var originalStream;
+
+ /**
+ * @param {string} value
+ * @returns {string}
+ */
+ function escapeHTML(value) {
+ return value
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+
+ /* plugin itself */
+
+ /** @type {HLJSPlugin} */
+ const mergeHTMLPlugin = {
+ // preserve the original HTML token stream
+ "before:highlightElement": ({ el }) => {
+ originalStream = nodeStream(el);
+ },
+ // merge it afterwards with the highlighted token stream
+ "after:highlightElement": ({ el, result, text }) => {
+ if (!originalStream.length) {
+ return;
+ }
+
+ const resultNode = document.createElement('div');
+ resultNode.innerHTML = result.value;
+ result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
+ el.innerHTML = result.value;
+ }
+ };
+
+ /**
+ * @param {Node} node
+ */
+ function tag(node) {
+ return node.nodeName.toLowerCase();
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function nodeStream(node) {
+ /** @type Event[] */
+ const result = [];
+ (function _nodeStream(node, offset) {
+ for (let child = node.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType === 3) {
+ offset += child.nodeValue.length;
+ } else if (child.nodeType === 1) {
+ result.push({
+ event: 'start',
+ offset: offset,
+ node: child
+ });
+ offset = _nodeStream(child, offset);
+
+ if (!tag(child).match(/br|hr|img|input/)) {
+ result.push({
+ event: 'stop',
+ offset: offset,
+ node: child
+ });
+ }
+ }
+ }
+ return offset;
+ })(node, 0);
+ return result;
+ }
+
+ /**
+ * @param {any} original - the original stream
+ * @param {any} highlighted - stream of the highlighted source
+ * @param {string} value - the original source itself
+ */
+ function mergeStreams(original, highlighted, value) {
+ let processed = 0;
+ let result = '';
+ const nodeStack = [];
+
+ function selectStream() {
+ if (!original.length || !highlighted.length) {
+ return original.length ? original : highlighted;
+ }
+ if (original[0].offset !== highlighted[0].offset) {
+ return (original[0].offset < highlighted[0].offset) ? original : highlighted;
+ }
+
+ return highlighted[0].event === 'start' ? original : highlighted;
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function open(node) {
+ /** @param {Attr} attr */
+ function attributeString(attr) {
+ return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
+ }
+
+ // @ts-ignore
+ result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('')
+ + '>';
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function close(node) {
+ result += '' + tag(node) + '>';
+ }
+
+ /**
+ * @param {Event} event
+ */
+ function render(event) {
+ (event.event === 'start' ? open : close)(event.node);
+ }
+
+ while (original.length || highlighted.length) {
+ let stream = selectStream();
+ result += escapeHTML(value.substring(processed, stream[0].offset));
+ processed = stream[0].offset;
+ if (stream === original) {
+ /*
+ On any opening or closing tag of the original markup we first close
+ the entire highlighted node stack, then render the original tag along
+ with all the following original tags at the same offset and then
+ reopen all the tags on the highlighted stack.
+ */
+ nodeStack.reverse().forEach(close);
+ do {
+ render(stream.splice(0, 1)[0]);
+ stream = selectStream();
+ } while (stream === original && stream.length && stream[0].offset === processed);
+ nodeStack.reverse().forEach(open);
+ } else {
+ if (stream[0].event === 'start') {
+ nodeStack.push(stream[0].node);
+ } else {
+ nodeStack.pop();
+ }
+ render(stream.splice(0, 1)[0]);
+ }
+ }
+ return result + escapeHTML(value.substr(processed));
+ }
+
+ return mergeHTMLPlugin;
+
+}());
+
+function formatAndValidateJson(errorElement) {
+ const input = document.querySelector('#jsonBlock');
+ const processInfo = document.getElementById(errorElement);
+
+ const address = window.location.protocol + "//" + window.location.hostname + ":" + 8081 + "/json/formatting"
+
+ fetch(address, {
+ method: 'POST',
+ body: input.textContent
+ })
+ .then(async (response) => {
+ const promise = response.json();
+ if (!response.ok) {
+ throw Error(await promise);
+ }
+
+ return promise;
+ })
+ .then((data) => {
+ input.innerText = data.data;
+ processInfo.innerText = "";
+ hljs.highlightElement(input);
+
+ processInfo.innerHTML = "Computed in " + data.time + "ms";
+ })
+ .catch((error) => {
+ processInfo.innerHTML = "" + error.data + "";
+ console.error('Error:', error);
+ });
+}
+
+function minimizeJson(errorElement) {
+ const input = document.querySelector('#jsonBlock');
+ const processInfo = document.getElementById(errorElement);
+
+ const address = window.location.protocol + "//" + window.location.hostname + ":" + 8081 + "/json/minimize"
+
+ fetch(address, {
+ method: 'POST',
+ body: input.textContent
+ })
+ .then(async (response) => {
+ const promise = response.json();
+ if (!response.ok) {
+ throw Error(await promise);
+ }
+
+ return promise;
+ })
+ .then((data) => {
+ input.innerText = data.data;
+ processInfo.innerText = "";
+ hljs.highlightElement(input);
+
+ processInfo.innerHTML = "Computed in " + data.time + "ms";
+ })
+ .catch((error) => {
+ processInfo.innerHTML = "" + error.data + "";
+ console.error('Error:', error);
+ });
+}
+
+function clearJsonData() {
+ const input = document.querySelector('#jsonBlock');
+ input.textContent = "";
+}
+
+function insertDefaultJson() {
+ const input = document.querySelector('#jsonBlock');
+ input.textContent = "{\"enter\": \"your\", \"json\": \"here\"}";
+ hljs.highlightElement(input);
+}
+
+/**
+ * This function is executed after the page is loaded.
+ *
+ * @function
+ * @name init
+ * @kind function
+ */
+function init() {
+ // Make sure that only plain text is pasted
+ configurePastingInElement("jsonBlock");
+
+ hljs.addPlugin(mergeHTMLPlugin);
+}
+
diff --git a/Frontend/assets/scripts/tools/xmlFormatter.js b/Frontend/assets/scripts/tools/xmlFormatter.js
new file mode 100644
index 0000000..fef7d28
--- /dev/null
+++ b/Frontend/assets/scripts/tools/xmlFormatter.js
@@ -0,0 +1,34 @@
+/**
+ * This function is executed after the page is loaded.
+ *
+ * @function
+ * @name init
+ * @kind function
+ */
+function init() {
+ configurePastingInElement("xmlArea");
+}
+
+/**
+ * Function returns processor that will be used to transform XML.
+ * This solution allows to use one function for sending request from every tool
+ *
+ * @function
+ * @name getProcessor
+ * @kind function
+ */
+function getProcessor() {
+ return "libxml";
+}
+
+/**
+ * Function returns version of XML processor that will be used to transform XML.
+ * This solution allows to use one function for sending request from every tool
+ *
+ * @function
+ * @name getVersion
+ * @kind function
+ */
+function getVersion() {
+ return "1.0"
+}
\ No newline at end of file
diff --git a/Frontend/assets/scripts/tools/xpath.js b/Frontend/assets/scripts/tools/xpath.js
new file mode 100644
index 0000000..ab5bff6
--- /dev/null
+++ b/Frontend/assets/scripts/tools/xpath.js
@@ -0,0 +1,174 @@
+
+/**
+ * The `processVersionSelector()` function is responsible for updating the display of the web page
+ * based on the selected processor and version.
+ *
+ * @function
+ * @name processVersionSelector
+ * @kind function
+ * @returns {void}
+ */
+function processVersionSelector() {
+ var processor = getProcessor();
+ var hideableOptions = document.getElementsByClassName("hideable");
+ for (let i = 0; i < hideableOptions.length; i++) {
+ hideableOptions[i].style = "display: none;";
+ }
+ if (processor == "xalan" || processor == "libxml") {
+ var xalanOptions = document.getElementsByClassName("xalan");
+ for (let i = 0; i < xalanOptions.length; i++) {
+ xalanOptions[i].style = "";
+ }
+ document.getElementById("versions").selectedIndex = 0;
+ }
+ else {
+ var saxonOptions = document.getElementsByClassName("saxon");
+ for (let i = 0; i < saxonOptions.length; i++) {
+ saxonOptions[i].style = "";
+ }
+ document.getElementById("versions").selectedIndex = 3;
+
+ }
+ processTooltip();
+
+}
+
+/**
+ * The `processTooltip()` function is responsible for updating the display of the tooltip based on the selected version of the processor.
+ * It shows or hides different sections of the tooltip based on the selected version.
+ * It also handles the click event on the form and updates the tooltip accordingly.
+ *
+ * @function
+ * @name processTooltip
+ * @kind function
+ */
+function processTooltip() {
+ var filter = "collapse" + getVersion();
+ var collList;
+
+
+ if (filter == "collapse3.0") {
+ document.getElementById("tooltipFunctionInfo").innerText = "XPath 3.0 functions";
+ hideList(document.getElementsByName("collapse10"));
+ hideList(document.getElementsByName("collapse20"));
+ showList(document.getElementsByName("collapse30"));
+ hideList(document.getElementsByName("collapse31"));
+
+ } else if (filter == "collapse3.1") {
+ document.getElementById("tooltipFunctionInfo").innerText = "XPath 3.1 functions";
+ hideList(document.getElementsByName("collapse10"));
+ hideList(document.getElementsByName("collapse20"));
+ hideList(document.getElementsByName("collapse30"));
+ showList(document.getElementsByName("collapse31"));
+ } else if (filter == "collapse2.0") {
+ document.getElementById("tooltipFunctionInfo").innerText = "XPath 2.0 functions";
+ hideList(document.getElementsByName("collapse10"));
+ showList(document.getElementsByName("collapse20"));
+ hideList(document.getElementsByName("collapse30"));
+ hideList(document.getElementsByName("collapse31"));
+ } else {
+ document.getElementById("tooltipFunctionInfo").innerText = "XPath 1.0 functions";
+ showList(document.getElementsByName("collapse10"));
+ hideList(document.getElementsByName("collapse20"));
+ hideList(document.getElementsByName("collapse30"));
+ hideList(document.getElementsByName("collapse31"));
+
+ }
+}
+
+
+
+/**
+ * This function is executed after the page is loaded.
+ *
+ * @function
+ * @name init
+ * @kind function
+ */
+function init() {
+
+ // Make sure that only plain text is pasted
+ configurePastingInElement("xmlArea");
+ configurePastingInElement("transformArea");
+
+ //Handle clicks in whole form and set info in tooltip
+ setDefaultContent(document.getElementById("xmlArea"), 'Insert XML here');
+ setDefaultContent(document.getElementById("transformArea"), 'Insert XPath expression here');
+
+ processVersionSelector();
+ processTooltip();
+ tool.addEventListener('change', event => {
+ //Check if script was called from textarea or selector
+ var targetID = event.target.getAttribute('id');
+ if (targetID == "processors") {
+ processVersionSelector();
+ processTooltip();
+ }
+ else if (targetID == "versions") {
+ processTooltip();
+ }
+
+
+ })
+ tool.addEventListener('click', event => {
+ //Check if script was called from textarea or selector
+ var targetID = event.target.getAttribute('id');
+ if (targetID !== "xmlArea" && targetID !== "transformArea") {
+ return;
+ }
+ processTooltip();
+
+ })
+ tool.addEventListener('change', event => {
+ //Check if script was called from textarea or selector
+ var targetID = event.target.getAttribute('id');
+ if (targetID !== "xmlArea" && targetID !== "transformArea") {
+ return;
+ }
+ processTooltip();
+ })
+
+ var triggerList = document.getElementsByClassName("collapseTrigger");
+ for (i = 0; i < triggerList.length; i++) {
+
+ triggerList[i].addEventListener("click", function () {
+ var collapsible = this.parentElement;
+ if (this.tagName == "A") {
+ var collapsibleData = this.nextElementSibling;
+ } else {
+ var collapsibleData = this.parentElement.nextElementSibling;
+
+ }
+
+
+ if (collapsibleData.style.maxHeight > "0px") {
+ collapsibleData.style.maxHeight = "0px";
+
+ this.classList.toggle("active", false);
+ if (!this.classList.contains("collapsibleMini")) {
+ collapsible.classList.toggle("active", false);
+ }
+
+ var subLists1 = collapsibleData.getElementsByClassName("content");
+ var subLists2 = collapsibleData.getElementsByClassName("active");
+ for (j = 0; j < subLists1.length; j++) {
+ subLists1[j].style.maxHeight = "0px";
+ }
+ for (j = 0; j < subLists2.length; j++) {
+ subLists2[j].classList.toggle("active", false);
+ }
+ } else {
+ collapsibleData.style.maxHeight = (collapsibleData.scrollHeight) + "px";
+
+ this.classList.toggle("active", true);
+ if (!this.classList.contains("collapsibleMini")) {
+ collapsible.classList.toggle("active", true);
+ } else {
+ var parentContent = this.closest(".content");
+ parentContent.style.maxHeight = (parentContent.scrollHeight + collapsibleData.scrollHeight) + "px";
+ }
+ }
+ });
+ }
+}
+
diff --git a/Frontend/assets/scripts/tools/xsd.js b/Frontend/assets/scripts/tools/xsd.js
new file mode 100644
index 0000000..62e5612
--- /dev/null
+++ b/Frontend/assets/scripts/tools/xsd.js
@@ -0,0 +1,51 @@
+/**
+ * This function is executed after the page is loaded.
+ *
+ * @function
+ * @name init
+ * @kind function
+ */
+function init() {
+ // Make sure that only plain text is pasted
+ configurePastingInElement("xmlArea");
+ configurePastingInElement("transformArea");
+
+ //Handle clicks in whole form and set info in tooltip
+ setDefaultContent(document.getElementById("xmlArea"), 'Insert XML here');
+ setDefaultContent(document.getElementById("transformArea"), 'Insert XSD here');
+
+ // refreshTooltip();
+ processTooltip();
+ tool.addEventListener('click', event => {
+ //Check if script was called from textarea or selector
+ var targetID = event.target.getAttribute('id');
+ if (targetID !== "processors" && targetID !== "xmlArea" && targetID !== "transformArea" && targetID !== "versions") {
+ return;
+ }
+
+ processTooltip();
+
+ })
+}
+
+/**
+ * The `processTooltip()` function is responsible for updating the display of the tooltip based on the selected version of the processor.
+ * It shows or hides different sections of the tooltip based on the selected version.
+ * It also handles the click event on the form and updates the tooltip accordingly.
+ *
+ * @function
+ * @name processTooltip
+ * @kind function
+ */
+function processTooltip() {
+
+ if (getProcessor() == "xalan") {
+ document.getElementById("tooltipFunctionInfo").innerText = "XSLT 1.0 functions";
+ document.getElementById("processorTooltipInfo").innerText = "Supports XSLT 1.0";
+ hideList(document.getElementsByName("collapse30"));
+ } else {
+ document.getElementById("tooltipFunctionInfo").innerText = "XSLT 1.0, 2.0 & 3.0 functions";
+ document.getElementById("processorTooltipInfo").innerText = "Supports XSLT up to 3.0";
+ showList(document.getElementsByName("collapse30"));
+ }
+}
\ No newline at end of file
diff --git a/Frontend/assets/scripts/tools/xslt.js b/Frontend/assets/scripts/tools/xslt.js
new file mode 100644
index 0000000..4e61cb1
--- /dev/null
+++ b/Frontend/assets/scripts/tools/xslt.js
@@ -0,0 +1,100 @@
+/**
+ * The `processTooltip()` function is responsible for updating the display of the tooltip based on the selected version of the processor.
+ * It shows or hides different sections of the tooltip based on the selected version.
+ * It also handles the click event on the form and updates the tooltip accordingly.
+ *
+ * @function
+ * @name processTooltip
+ * @kind function
+ */
+function processTooltip() {
+
+ if (getProcessor() == "xalan" || getProcessor() == "libxml") {
+ document.getElementById("tooltipFunctionInfo").innerText = "XSLT 1.0 functions";
+ document.getElementById("processorTooltipInfo").innerText = "Supports XSLT 1.0";
+ hideList(document.getElementsByName("collapse30"));
+ } else {
+ document.getElementById("tooltipFunctionInfo").innerText = "XSLT 1.0, 2.0 & 3.0 functions";
+ document.getElementById("processorTooltipInfo").innerText = "Supports XSLT up to 3.0";
+ showList(document.getElementsByName("collapse30"));
+ }
+}
+
+
+/**
+ * This function is executed after the page is loaded.
+ *
+ * @function
+ * @name init
+ * @kind function
+ */
+function init() {
+ // Make sure that only plain text is pasted
+ configurePastingInElement("xmlArea");
+ configurePastingInElement("transformArea");
+
+ //Handle clicks in whole form and set info in tooltip
+ setDefaultContent(document.getElementById("xmlArea"), 'Insert XML here');
+ setDefaultContent(document.getElementById("transformArea"), 'Insert XSLT here');
+
+ // refreshTooltip();
+ processTooltip();
+ tool.addEventListener('click', event => {
+ //Check if script was called from textarea or selector
+ var targetID = event.target.getAttribute('id');
+ if (targetID !== "processors" && targetID !== "xmlArea" && targetID !== "transformArea" && targetID !== "versions") {
+ return;
+ }
+
+ processTooltip();
+ })
+
+ tool.addEventListener('change', event => {
+ //Check if script was called from textarea or selector
+ var targetID = event.target.getAttribute('id');
+ if (targetID !== "processors") {
+ return;
+ }
+
+ processTooltip();
+
+ })
+
+ var triggerList = document.getElementsByClassName("collapseTrigger");
+ for (i = 0; i < triggerList.length; i++) {
+
+ triggerList[i].addEventListener("click", function () {
+
+ var collapsible = this.parentElement;
+ var collapsibleData = this.nextElementSibling;
+ if (collapsibleData.style.maxHeight > "0px") {
+ collapsibleData.style.maxHeight = "0px";
+
+ this.classList.toggle("active", false);
+ if (!this.classList.contains("collapsibleMini")) {
+ collapsible.classList.toggle("active", false);
+ }
+
+ var subLists1 = collapsibleData.getElementsByClassName("content");
+ var subLists2 = collapsibleData.getElementsByClassName("active");
+ for (j = 0; j < subLists1.length; j++) {
+ subLists1[j].style.maxHeight = "0px";
+ }
+ for (j = 0; j < subLists2.length; j++) {
+ subLists2[j].classList.toggle("active", false);
+ }
+ } else {
+ collapsibleData.style.maxHeight = (collapsibleData.scrollHeight) + "px";
+
+ this.classList.toggle("active", true);
+ if (!this.classList.contains("collapsibleMini")) {
+ collapsible.classList.toggle("active", true);
+ } else {
+ var parentContent = this.closest(".content");
+ parentContent.style.maxHeight = (parentContent.scrollHeight + collapsibleData.scrollHeight) + "px";
+ }
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/Frontend/tools/jsonFormatter.html b/Frontend/tools/jsonFormatter.html
index 300ff95..8081f02 100644
--- a/Frontend/tools/jsonFormatter.html
+++ b/Frontend/tools/jsonFormatter.html
@@ -7,10 +7,9 @@
-
-
+
+
-
@@ -61,175 +60,5 @@
-
-