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 += ''; + } + + /** + * @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 @@ - - diff --git a/Frontend/tools/xmlFormatter.html b/Frontend/tools/xmlFormatter.html index c9419ea..a200e44 100644 --- a/Frontend/tools/xmlFormatter.html +++ b/Frontend/tools/xmlFormatter.html @@ -5,8 +5,9 @@ - + + @@ -62,20 +63,6 @@ - - diff --git a/Frontend/tools/xpath.html b/Frontend/tools/xpath.html index b7726d4..6a6fc3c 100644 --- a/Frontend/tools/xpath.html +++ b/Frontend/tools/xpath.html @@ -5,6 +5,7 @@ + @@ -17115,156 +17116,6 @@ - - \ No newline at end of file diff --git a/Frontend/tools/xsd.html b/Frontend/tools/xsd.html index 100756b..52e559a 100644 --- a/Frontend/tools/xsd.html +++ b/Frontend/tools/xsd.html @@ -6,6 +6,7 @@ + @@ -79,45 +80,6 @@ - - \ No newline at end of file diff --git a/Frontend/tools/xslt.html b/Frontend/tools/xslt.html index 7fd299b..e5dd2f0 100644 --- a/Frontend/tools/xslt.html +++ b/Frontend/tools/xslt.html @@ -6,6 +6,7 @@ + @@ -1147,93 +1148,6 @@ - - \ No newline at end of file