finished_milestone (#279)

Co-authored-by: Adam Bem <adam.bem@zoho.eu>
Reviewed-on: #279
Co-authored-by: widlam <mikolaj.widla@gmail.com>
Co-committed-by: widlam <mikolaj.widla@gmail.com>
This commit is contained in:
2024-01-04 08:08:16 +01:00
committed by Mikolaj Widla
parent 971cc5f36a
commit f0dd41a86e
91 changed files with 2007 additions and 882 deletions

View File

@@ -1,4 +1,4 @@
FROM node:latest as build-stage
FROM node:20.9.0-bullseye-slim as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
@@ -7,7 +7,7 @@ RUN npm run build
FROM nginx:stable-alpine as production-stage
FROM nginx:stable-alpine3.17-slim as production-stage
RUN mkdir /app
RUN apk add --no-cache tzdata
@@ -20,7 +20,7 @@ EXPOSE 80
EXPOSE 443
FROM node:latest as dev
FROM node:20.9.0-bullseye-slim as dev
WORKDIR /app
COPY package*.json ./
RUN npm install

View File

@@ -13,6 +13,7 @@
"@codemirror/lang-xml": "^6.0.2",
"@codemirror/theme-one-dark": "^6.1.2",
"codemirror": "^6.0.1",
"thememirror": "^2.0.1",
"vue": "^3.3.4",
"vue-codemirror": "^6.1.1",
"vue-router": "^4.2.2"
@@ -3190,9 +3191,9 @@
}
},
"node_modules/normalize-package-data/node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"bin": {
"semver": "bin/semver"
@@ -3326,9 +3327,9 @@
}
},
"node_modules/npm-run-all/node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"bin": {
"semver": "bin/semver"
@@ -3616,9 +3617,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.24",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [
{
"type": "opencollective",
@@ -4305,6 +4306,16 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
"node_modules/thememirror": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/thememirror/-/thememirror-2.0.1.tgz",
"integrity": "sha512-d5i6FVvWWPkwrm4cHLI3t9AT1OrkAt7Ig8dtdYSofgF7C/eiyNuq6zQzSTusWTde3jpW9WLvA9J/fzNKMUsd0w==",
"peerDependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0"
}
},
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@@ -6994,9 +7005,9 @@
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true
}
}
@@ -7097,9 +7108,9 @@
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true
},
"shebang-command": {
@@ -7302,9 +7313,9 @@
"dev": true
},
"postcss": {
"version": "8.4.24",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"requires": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
@@ -7756,6 +7767,12 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
"thememirror": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/thememirror/-/thememirror-2.0.1.tgz",
"integrity": "sha512-d5i6FVvWWPkwrm4cHLI3t9AT1OrkAt7Ig8dtdYSofgF7C/eiyNuq6zQzSTusWTde3jpW9WLvA9J/fzNKMUsd0w==",
"requires": {}
},
"thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",

View File

@@ -17,6 +17,7 @@
"@codemirror/lang-xml": "^6.0.2",
"@codemirror/theme-one-dark": "^6.1.2",
"codemirror": "^6.0.1",
"thememirror": "^2.0.1",
"vue": "^3.3.4",
"vue-codemirror": "^6.1.1",
"vue-router": "^4.2.2"

View File

@@ -1,14 +1,53 @@
<script setup lang="ts">
import { RouterView } from 'vue-router';
import SidebarComponent from '@components/sidebar/SidebarComponent.vue';
import {onMounted, provide, ref } from 'vue';
const theme = ref( getTheme() );
provide('theme', theme );
onMounted( ()=> {
if (localStorage.theme)
selectThemeFromLocalStorage();
else if (browserPrefersDarkMode()) {
setDarkTheme();
}
})
function setDarkTheme() {
document.documentElement.classList.add('dark');
theme.value = "dark";
localStorage.setItem("theme", "dark");
}
function selectThemeFromLocalStorage() {
if (localStorage.theme == "dark")
document.documentElement.classList.add('dark');
else
document.documentElement.classList.remove('dark');
theme.value = localStorage.theme;
}
function browserPrefersDarkMode(){
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}
function setTheme(newTheme : string){
theme.value = newTheme;
}
function getTheme(){
return localStorage.theme;
}
</script>
<template>
<div id="layout" class="flex h-screen bg-gradient-to-r from-white to-sky-200 dark:from-slate-800 dark:to-indigo-950">
<SidebarComponent />
<div class="relative p-4 w-full m-4 bg-blue-50 dark:bg-gray-700 rounded-2xl overflow-hidden shadow-lg">
<div id="layout" class="font-sans flex h-screen bg-gradient-to-br from-sky-200 to-indigo-200 dark:from-sky-950 dark:to-indigo-950">
<SidebarComponent @theme:changed="setTheme" />
<div class="relative p-4 w-full m-4 ml-0 bg-blue-50 dark:bg-gray-700 rounded-2xl overflow-hidden shadow-lg">
<RouterView></RouterView>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -2,11 +2,11 @@
"universalInfo":
[
{
"category":"What is XPath",
"category":"What is XPath?",
"description":"XPath is a query language used for selecting nodes from XML and processing them. It may perform operations on strings, numbers and boolean values."
},
{
"category":"What's new in XPath?"
"category":"What's new in XPath"
}
],
"VersionDiffs":

View File

@@ -6,7 +6,7 @@
"description":"XSLT (Extensible Stylesheet Language Transformations) is a language for converting and manipulating XML data into various formats. It uses rules defined in stylesheets to transform XML documents into HTML, XML, or other text-based outputs."
},
{
"category":"What's differences between XSLT versions"
"category":"Differences between XSLT versions"
}
],
"VersionDiffs":

View File

@@ -0,0 +1,88 @@
<script setup lang="ts">
import { onBeforeUpdate, inject } from 'vue'
import { Codemirror } from 'vue-codemirror'
import { oneDark } from '@codemirror/theme-one-dark'
import { espresso } from 'thememirror';
import {xml} from '@codemirror/lang-xml'
import {json} from '@codemirror/lang-json'
import {html} from '@codemirror/lang-html'
function isDarkModeSet(){
return localStorage.theme == "dark";
}
const theme : string = inject('theme')! ;
const props= defineProps({
code : {
type: String,
required: true
},
config: {
type: Object,
required: true
},
})
const emit = defineEmits(
[
'update:updatedCode'
]
)
function dataUpdated(newData:String){
emit('update:updatedCode',newData)
}
function selectTheme() {
if (isDarkModeSet()) {
return oneDark;
}
else {
return espresso;
}
}
let extensions = parseExtensions();
function parseExtensions(){
return [
selectTheme(),
parseLanguage(props.config.language),
]
}
function parseLanguage(name: String){
switch(name.toUpperCase()){
case "JSON": {
return json();
}
case "HTML": {
return html();
}
default: {
return xml();
}
}
}
onBeforeUpdate( () => { extensions = parseExtensions(); } )
</script>
<template>
<div class="editor w-full h-full rounded-2xl overflow-x-auto">
<codemirror
:key="theme"
style="height: 100%; width: 100%; padding:1rem ; border-radius: 1rem; font-size: large;"
:model-value="code"
@update:model-value="dataUpdated"
:extensions="extensions"
:disabled="config.disabled"
/>
</div>
</template>

View File

@@ -45,5 +45,5 @@ function setDefault() {
</script>
<template>
<button class="tool-button" @click="setDefault()">Default {{ stylizedName }}</button>
<button class="tool-button" @click="setDefault()">Example {{ stylizedName }}</button>
</template>

View File

@@ -0,0 +1,5 @@
export interface TabData {
id: number;
name: string;
data: string;
}

View File

@@ -0,0 +1,46 @@
<script setup lang="ts">
import lightThemeIcon from '@/assets/light_theme.svg';
import darkThemeIcon from '@/assets/dark_theme.svg';
const emit = defineEmits([
"theme",
]);
function toDarkMode(){
localStorage.theme = "dark";
document.documentElement.classList.add('dark');
emit('theme',"dark");
}
function toLightMode(){
localStorage.theme = "light";
document.documentElement.classList.remove('dark');
emit('theme',"light");
}
</script>
<template>
<div class="mb-4 flex flex-row gap-8">
<button id="header__moon" @click="toDarkMode()" title="Switch to dark mode" class="w-full h-10 focus:outline-none focus:shadow-outline text-gray-500">
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="currentColor" d="M17.75,4.09L15.22,6.03L16.13,9.09L13.5,7.28L10.87,9.09L11.78,6.03L9.25,4.09L12.44,4L13.5,1L14.56,4L17.75,4.09M21.25,11L19.61,12.25L20.2,14.23L18.5,13.06L16.8,14.23L17.39,12.25L15.75,11L17.81,10.95L18.5,9L19.19,10.95L21.25,11M18.97,15.95C19.8,15.87 20.69,17.05 20.16,17.8C19.84,18.25 19.5,18.67 19.08,19.07C15.17,23 8.84,23 4.94,19.07C1.03,15.17 1.03,8.83 4.94,4.93C5.34,4.53 5.76,4.17 6.21,3.85C6.96,3.32 8.14,4.21 8.06,5.04C7.79,7.9 8.75,10.87 10.95,13.06C13.14,15.26 16.1,16.22 18.97,15.95M17.33,17.97C14.5,17.81 11.7,16.64 9.53,14.5C7.36,12.31 6.2,9.5 6.04,6.68C3.23,9.82 3.34,14.64 6.35,17.66C9.37,20.67 14.19,20.78 17.33,17.97Z" />
</svg>
</button>
<button id="header__indeterminate" @click="toLightMode()" title="Switch to light mode" class="w-full h-10 focus:outline-none focus:shadow-outline text-gray-500">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-sun" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="12" cy="12" r="4"></circle>
<path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7"></path>
</svg>
</button>
</div>
</template>

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
const props = defineProps(
{
operation: {type:String,required:true},
code: {type: String, required: true}
}
)
const emit = defineEmits([
'update:result'
])
function encode(){
switch(props.operation.toLowerCase()){
case "encode":{
emit('update:result', encodeURI(props.code) )
break;
}
case "decode":{
emit('update:result', decodeURI(props.code) )
break;
}
}
}
</script>
<template>
<button @click="encode()" class="tool-button">{{ props.operation }}</button>
</template>

View File

@@ -0,0 +1,39 @@
<script setup lang="ts">
const props = defineProps(
{
operationType: {type:String,required:true},
code: {type: String, required: true}
}
)
const emit = defineEmits([
'update:result',
'image:show'
])
function convert(){
console.log("works")
switch(props.operationType.toLowerCase()){
case "encode":{
emit('update:result', btoa(props.code) )
break;
}
case "decode":{
emit('update:result', atob(props.code) )
break;
}
case "show image":{
emit('image:show', props.code )
break;
}
}
}
</script>
<template>
<button @click="convert()" class="tool-button">{{ props.operationType }}</button>
</template>

View File

@@ -0,0 +1,114 @@
<script setup lang="ts">
import XMLButtonFormatterComponent from '@/components/formatter/XMLButtonFormatterComponent.vue';
import JsonButtonFormatterComponent from '@/components/formatter/JsonButtonFormatterComponent.vue';
import HtmlButtonFormatterComponent from '@/components/formatter/HtmlButtonFormatterComponent.vue';
import InsertTemplateComponent from '@components/common/InsertTemplateComponent.vue';
import CodeEditorComponent from '@/components/common/CodeEditorComponent.vue';
import { ref } from 'vue';
const data = ref('');
const inputFile = ref()
const errorOccurred = ref(false);
const successOccurred = ref(false);
const props = defineProps({
formatterLanguage: {
type: String,
required: true
}
})
function setTextFieldValue(newData: string) {
successOccurred.value = false;
errorOccurred.value = false;
data.value = newData;
}
function format(formatted: any) {
data.value = formatted.result;
}
function setErrorOccurred(occurred: boolean) {
errorOccurred.value = occurred;
successOccurred.value = !(occurred);
}
function setExample(data: string) {
inputFile.value.value = ''
setTextFieldValue(data)
}
function clear() {
data.value = '';
successOccurred.value = false;
errorOccurred.value = false;
inputFile.value.value = ''
}
function readFile(file : any) {
const reader = new FileReader()
reader.onloadend = () => {
var result = reader.result?.toString()
if (typeof result == "string")
setTextFieldValue(result);
}
reader.readAsText(file.target.files[0])
}
function highlightTextField() {
if (successOccurred.value)
return "text-field-success";
if (errorOccurred.value)
return "text-field-error";
return "";
}
</script>
<template>
<div id="layout" class="flex flex-col w-full h-full gap-4">
<div id="toolbar" class= "flex flex-col gap-4 items-center lg:flex-row place-content-between">
<span class="dark:text-slate-100"> {{ formatterLanguage }} Formatter</span>
<!-- XML Formatter Toolbar -->
<div v-if="formatterLanguage.toLowerCase() == 'xml'" class="flex flex-wrap gap-2 justify-center">
<div class="flex items-stretch w-64">
<input id="fileLoader" ref="inputFile" class="file-selector" type="file" accept=".xml,.xql,.xquery,.xslt,text/xml,text/plain" @change="readFile" />
</div>
<InsertTemplateComponent stylized-name="XML" @update:defaultData="(data: string) => setExample(data)"></InsertTemplateComponent>
<button class="tool-button" @click="clear()">Clear</button>
<XMLButtonFormatterComponent @update:error="setErrorOccurred" is-minimizer :xml="data" @update:result="format"></XMLButtonFormatterComponent>
<XMLButtonFormatterComponent @update:error="setErrorOccurred" :xml="data" @update:result="format"></XMLButtonFormatterComponent>
</div>
<!-- JSON Formatter Toolbar -->
<div v-if="formatterLanguage.toLowerCase() == 'json'" class="flex flex-wrap gap-2 justify-center">
<div class="flex items-stretch w-64">
<input id="fileLoader" ref="inputFile" class="file-selector" type="file" accept=".json,text/xml,text/plain,text/json,application/json" @change="readFile" />
</div>
<InsertTemplateComponent stylized-name="JSON" @update:defaultData="(data: string) => setExample(data)"></InsertTemplateComponent>
<button class="tool-button" @click="clear()">Clear</button>
<JsonButtonFormatterComponent isMinimizer :json="data" @update:result="format" @update:error="setErrorOccurred"></JsonButtonFormatterComponent>
<JsonButtonFormatterComponent :json="data" @update:result="format" @update:error="setErrorOccurred"></JsonButtonFormatterComponent>
</div>
<!-- HTML Formatter Toolbar -->
<div v-if="formatterLanguage.toLowerCase() == 'html'" class="flex flex-wrap gap-2 justify-center">
<div class="flex items-stretch w-64">
<input id="fileLoader" ref="inputFile" class="file-selector" type="file" accept=".xml,.html,.htm,text/xml,text/plain,text/html" @change="readFile" />
</div>
<InsertTemplateComponent stylized-name="HTML" @update:defaultData="setExample"></InsertTemplateComponent>
<button class="tool-button" @click="clear()">Clear</button>
<HtmlButtonFormatterComponent @update:result="format" @update:error="setErrorOccurred" :code="data" format-type="Minimize" />
<HtmlButtonFormatterComponent @update:result="format" @update:error="setErrorOccurred" :code="data" format-type="Prettify" />
<HtmlButtonFormatterComponent @update:result="format" @update:error="setErrorOccurred" :code="data" format-type="HTML -> XML" />
</div>
</div>
<CodeEditorComponent :class="highlightTextField()" @update:updated-code="setTextFieldValue" :code="data" :config="{disabled:false,language:formatterLanguage.toLowerCase()}" />
</div>
</template>

View File

@@ -1,13 +1,28 @@
<script setup lang="ts">
const props = defineProps(
{
formatType: {type:String,required:true},
code: {type:String,required:true},
formatType: {
type:String,
required:true
},
code: {
type:String,
required:true
},
isError: {
type:Boolean,
required:false
},
}
)
const emit = defineEmits([
'update:result',
'update:error'
])
function chooseType(formatType: String){
if (formatType == "XML Converter"){
if (formatType == "HTML -> XML"){
return "convert";
}
return formatType.toLowerCase();
@@ -24,7 +39,7 @@ function getTypeInfo(){
function createBody(){
return JSON.stringify({
"data": props.code,
"process": getTypeInfo(),
"processorData": getTypeInfo(),
"processor": "libxml",
"version": "1.0"
});
@@ -32,19 +47,16 @@ function createBody(){
const fetchLink = document.location.protocol + "//" + document.location.hostname + "/libxml/html/" + chooseType(props.formatType);
const emit = defineEmits([
'update:result'
])
function processResponse(formattedCode : any){
var result = formattedCode.result;
return result
}
function process(){
fetch(fetchLink, {body:createBody(), method: "POST"})
.then( response => response.json() )
.then( formattedCode => emit('update:result', processResponse(formattedCode) ) )
.then( formattedCode => processResponse(formattedCode) )
}
function processResponse(formattedCode : any){
emit('update:result', formattedCode )
emit("update:error", formattedCode.status == "ERR")
}
</script>

View File

@@ -5,12 +5,12 @@ const props = defineProps({
isMinimizer: {type: Boolean}
})
const emit = defineEmits(["update:result"])
const emit = defineEmits(["update:result", "update:error"])
function process() {
var request:Request = prepareRequest();
fetchRequest(request).then((data) => {
sendProcessedData(data);
sendProcessedData(data);
})
}
@@ -36,9 +36,11 @@ function prepareRequestBody():string {
async function fetchRequest(request: Request):Promise<JSON> {
var responseBody = await fetch(request)
.then(response => response.json())
.then(response => {
emit('update:error', response.status != 200)
return response.json()
})
.then((body) => body);
console.log(responseBody);
return responseBody;
}

View File

@@ -5,12 +5,15 @@ const props = defineProps({
isMinimizer: {type: Boolean}
})
const emit = defineEmits(["update:result"])
const emit = defineEmits([
'update:result',
'update:error'
])
function process() {
var request:Request = prepareRequest();
var request:Request = prepareRequest()
fetchRequest(request).then((data) => {
sendProcessedData(data);
sendProcessedData(data)
})
}
@@ -18,36 +21,39 @@ function prepareRequest():Request {
var request = new Request(prepareURL(), {
body: prepareRequestBody(),
method: "POST"
});
})
return request
}
function prepareURL(): string {
var mode = "prettify";
var mode = "prettify"
if (props.isMinimizer)
mode = "minimize";
return document.location.protocol + "//" + document.location.hostname + "/libxml/" + mode;
mode = "minimize"
return document.location.protocol + "//" + document.location.hostname + "/libxml/" + mode
}
function prepareRequestBody():string {
var requestBody = JSON.stringify({
"data": props.xml,
"process": "N/A",
"processorData": "N/A",
"processor": "libxml",
"version": "1.0"
});
return requestBody;
})
return requestBody
}
async function fetchRequest(request: Request):Promise<JSON> {
var responseBody = await fetch(request)
.then(response => response.json())
.then((body) => body);
return responseBody;
.then((body) => {
emit("update:error", body.status == "ERR")
return body
})
return responseBody
}
function sendProcessedData(data: JSON) {
emit("update:result", data);
emit("update:result", data)
}
</script>

View File

@@ -5,12 +5,35 @@
<template>
<div class="dark:text-slate-100">
<h1 class="text-3xl mb-4">Welcome to Release11 Tools</h1>
<h2 class="text-xl">Our toolset is split to three main categories:</h2>
<ol class="list-decimal ml-5">
<h2 class="text-xl">Our tool set is split to three main categories:</h2>
<ul class="list-decimal ml-5">
<li><strong>XML</strong> - containing various tools that allow to validate and transform any XML</li>
<li><strong>Formatter</strong> - containing tools for formatting text files in various formats</li>
<li><strong>REST</strong> - consist of Mock that allows to create mocked REST endpoint</li>
</ol>
<li><strong>Encoder</strong> - consist of encoders for Base64 and URL</li>
</ul>
<br/>
<h2 class="text-xl mt-2">XML - Tools:</h2>
<ul class="list-decimal ml-5">
<li><strong>XPath</strong> - This is tool that allows to parse XPath on selected XML</li>
<li><strong>XQuery</strong> - Allows to execute XQuery on provided XML file.</li>
<li><strong>XSD</strong> - Allows to validate XML against provided XSD schema.</li>
<li><strong>XSLT</strong> - Allows to transformate XML using XSLT transformate.</li>
</ul>
<h2 class="text-xl mt-2">Formatter - Tools:</h2>
<p>These are tools that allow to format or minimize files in bespoken formats.</p>
<h2 class="text-xl mt-2">REST - Tools:</h2>
<p>This is tools that allow to created mocked REST Service that can be used to test REST clients.</p>
<h2 class="text-xl mt-2">Encoder - Tools:</h2>
<ul class="list-decimal ml-5">
<li><strong>Base64</strong> - This is tool allowing to encode text to base64 and decode base64 to text or image.</li>
<li><strong>URL</strong> - This tool allow to encode string in the way that is used by browser when you put it in address bar.</li>
</ul>
</div>
</template>

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps(
{
name: {type: String, required: true}
}
)
</script>
<template>
<h3 class="text-lg font-medium mt-2">{{ name }}</h3>
<p><slot></slot></p>
</template>

View File

@@ -0,0 +1,21 @@
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps(
{
imgPath: {type: String, required: true},
label: {type: String, required: true}
}
)
</script>
<template>
<div class="flex flex-col gap-2 w-fit h-fit shadow-sm bg-slate-200 dark:bg-slate-800 p-2 rounded-md my-2 text-center self-center">
<img id="url_section" class="rounded-md" :src="imgPath" />
<label for="url_section" class="text-sm mb-0 text-slate-600 dark:text-slate-300">{{ label }}</label>
</div>
</template>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import { ref } from 'vue';
// const props = defineProps({
// version: {
// type: String,
// required: true
// },
// toolType: {
// type: String,
// required: true
// }
// })
const emit = defineEmits([
"update:visible"
])
const areTooltipsHidden = ref(true)
function toggleTooltips() {
areTooltipsHidden.value = !areTooltipsHidden.value;
emit("update:visible", !areTooltipsHidden.value)
}
</script>
<template>
<div :class="areTooltipsHidden ? 'w-fit' : 'w-5/12'" class="hidden xl:flex items-stretch p-2 flex-row rounded-xl shadow-lg bg-gradient-to-r from-blue-400 to-blue-300 dark:from-sky-600 dark:to-sky-800 ">
<button :class="{'mr-2' : !areTooltipsHidden }" class="text-xl w-6 dark:text-slate-100" @click="toggleTooltips()">
T<br/>o<br/>o<br/>l<br/>t<br/>i<br/>p<br/>s
</button>
<div id="content" :class="{'hidden' : areTooltipsHidden}" class="w-full flex flex-col gap-4 p-2 overflow-auto rounded-xl dark:text-white bg-indigo-50 dark:bg-gray-700" >
<slot></slot>
</div>
</div>
</template>

View File

@@ -0,0 +1,71 @@
<script setup lang="ts">
import ImgMan from './ImgMan.vue';
import ElementDescription from './ElementDescription.vue';
import urlSectionImg from '@assets/man/rest-mock/URL_section.png';
import statusSectionImg from '@assets/man/rest-mock/Status_section.png';
import bodySectionImg from '@assets/man/rest-mock/Body_section.png';
import headerSectionImg from '@assets/man/rest-mock/Header_section.png';
</script>
<template>
<div class="flex flex-col dark:text-slate-100 w-full h-full text-justify overflow-auto px-2">
<h2 class="text-2xl font-bold mt-4 mb-2">Description</h2>
<p><span class="font-medium">REST Mock</span> is a tool allowing to create temporary REST endpoint called REST Mock, that allows to test REST clients.</p>
<br>
<p>Its main functions are:</p>
<ul class="list-disc ml-5">
<li>Generating random URL for each user, one per user, with persistence for 24h.</li>
<li>Customizable HTTP response status code</li>
<li>Customizable response body and its content type</li>
<li>Customizable response headers</li>
<li>History of connections to generated endpoint with ability to look into request method, headers and body</li>
</ul>
<h2 class="text-2xl font-bold mt-4 mb-2">GUI elements</h2>
<p>This section describe how certain elements of the application work:</p>
<ElementDescription name="URL field">This field contains autogenerated URL for your REST Mock. All links are removed every 24h.</ElementDescription>
<ElementDescription name="'Save' button">Applies every change made on this page and saves it between sessions.</ElementDescription>
<ElementDescription name="Response Content Type field">Its just convenient way to set "Content-Type" header in the response.</ElementDescription>
<ElementDescription name="Response HTTP Status field">This field sets HTTP status that will be sent to client. Any valid HTTP status can be used, by default it's 200 OK.</ElementDescription>
<ElementDescription name="Response Body field">Here you can set response body. This is sophisticated text editor with syntax-highlighting based on detected language. It works both with JSON and XML data.</ElementDescription>
<ElementDescription name="Response Headers section">This section contains three default and unremovable headers ("Connection", "Date", "Keep-Alive") as well as empty field used to adding custom header. Adding header with empty value is possible, but every header has to have a name. When adding new header is possible, the "Add" button becomes active. New header is added after "Add" button is clicked.</ElementDescription>
<ElementDescription name="History">This section allows to look into history of requests sent to current REST Mock. Entries are sorted by descending by date (newer are first). If request has body, "Show body" button appears in its entry. Also there is option to look into every request's headers by clicking "Show Headers" button. For now, History doesn't refresh automatically. To refresh it, click small refresh button on the right-hand side.</ElementDescription>
<h2 class="text-2xl font-bold mt-4 mb-2">Example</h2>
<p>Let's say we want to create temporary endpoint that returns code 200 with body:</p>
<div class="w-fit p-2 my-2 bg-slate-200 dark:bg-slate-800 rounded-md self-center"><pre>{
"status": "completed",
"warnings": "none"
}</pre></div>
<p>Additionally we want to set some headers:</p>
<div class="w-fit p-2 my-2 bg-slate-200 dark:bg-slate-800 rounded-md self-center"><pre> Name Value
operation addition
priority urgent
</pre></div>
<p>In this example we will focus on the left-hand side panel first as it contains most of the settings. On the top you see autogenerated URL link to your REST Mock service and the "Save" button that saves every setting on this page.</p>
<ImgMan :imgPath="urlSectionImg" label="Screenshot of Rest Mock URL bar and Save button"></ImgMan>
<p>Next, down from REST Mock URL are two text fields. One for content type of a response and second for the HTTP status of a response. </p>
<ImgMan :imgPath="statusSectionImg" label='Screenshot of "Response Content Type" and "Response HTTP Status"'></ImgMan>
<p>These two fields are first things that we will set in this example. Out content type is JSON, so we will put "application/json" in "Response Content Type" field. The "Response HTTP Status" field we'll leave
unchanged for now.</p><br/>
<p>Next element of this tool is big "Response Body" text field. Here you can put data that you want your REST Mock to respond with. In this example we will put JSON from few lines above.</p>
<ImgMan :imgPath="bodySectionImg" label='Screenshot of Response Body field filled with example JSON'></ImgMan>
<p>Last thing that we have to set are headers. Those we can set in Headers section.</p>
<ImgMan :imgPath="headerSectionImg" label='Screenshot of Headers Section with added custom headers.'></ImgMan>
</div>
</template>
<style scoped></style>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import CodeEditorComponent from '../CodeEditorComponent.vue';
import CodeEditorComponent from '@/components/common/CodeEditorComponent.vue';
const props = defineProps(

View File

@@ -55,6 +55,7 @@ function addNewHeader(name : string, value : string){
<template>
<div class="flex flex-col gap-4">
<label>Response Headers</label>
<div class="flex flex-row gap-4">
<div class="w-full">Header name</div>
<div class="w-full">Header value</div>

View File

@@ -10,14 +10,14 @@ const props = defineProps(
</script>
<template>
<div class="w-full text-center text-white mt-2 flex flex-col gap-4 ">
<div class="w-full h-2/3 text-center dark:text-white mt-2 flex flex-col gap-4 overflow-auto">
<div class="flex flex-row gap-4">
<div class="w-full font-bold">Name</div>
<div class="w-full font-bold">Value</div>
</div >
<div class="flex flex-row gap-4" v-for="(value,name) in JSON.parse(data)" :key="name">
<div class="w-full overflow-hidden">{{ name }}</div>
<div class="w-full overflow-hidden">{{ value }}</div>
<div class="w-1/2 break-words">{{ name }}</div>
<div class="w-1/2 break-words">{{ value }}</div>
</div>
</div>
</template>

View File

@@ -34,8 +34,9 @@ function showHeaders(headers: object, index: number){
<template>
<div class="w-full xl:w-2/5 flex flex-none flex-col gap-y-4">
<HistoryRecords class="xl:h-1/3 overflow-y-scroll" @click:show-headers="showHeaders" @click:show-body="showBody"></HistoryRecords>
<div class="flex flex-1 flex-col xl:w-3/12 justify-items-stretch gap-y-4">
<label class="dark:text-white text-center"><span class="font-bold">Attention! </span>History doesn't refresh automatically! Use refresh button (⟳) on the right!</label>
<HistoryRecords class="xl:h-1/3 overflow-y-auto" @click:show-headers="showHeaders" @click:show-body="showBody"></HistoryRecords>
<BodyDetailComponent :content-type="currentContentType" :data="currentShownData" v-if="shownDetail == 'body' "></BodyDetailComponent>
<HeadersDetailComponent :data="currentShownData" v-if="shownDetail == 'headers' "></HeadersDetailComponent>
</div>

View File

@@ -45,12 +45,12 @@ function refreshHistory(){
</script>
<template>
<div>
<table class="text-white h-28 w-full text-center">
<div class="h-28 text-center text-grey-900 dark:text-white">
<table class="w-full">
<tr>
<th>Time</th>
<th>HTTP Method</th>
<th>HTTP Headers</th>
<th>Request <br>HTTP Method</th>
<th>Request Headers</th>
<th>Request Body</th>
<th class="text-2xl"><button @click="refreshHistory()"></button></th>
</tr>
@@ -60,7 +60,7 @@ function refreshHistory(){
<td> <button @click="showHeaders(item.headers, index)" class="underline">Show Headers</button> </td>
<td>
<button v-if="item.requestBody.length != 0" @click="showBody(item.requestBody, index, item.headers['content-type'])" class="underline">Show Body</button>
<span v-else>Empty Body</span>
<span v-else>Empty</span>
</td>
</tr>
</table>

View File

@@ -2,7 +2,7 @@
import {ref, type Ref} from 'vue';
import HeadersComponent from './HeadersComponent.vue';
import SaveComponent from './SaveComponent.vue';
import CodeEditorComponent from '../CodeEditorComponent.vue';
import CodeEditorComponent from '@/components/common/CodeEditorComponent.vue';
const clientUUID = ref('');
const host = window.location.protocol + "//" + window.location.hostname + "/mock";
@@ -44,31 +44,30 @@ function showUpdatedCode(newCode : string){
</script>
<template>
<div class="flex flex-col flex-none w-full xl:w-3/5 text-center dark:text-white gap-6 p-1">
<div class="flex flex-col flex-none w-full xl:w-3/5 text-center dark:text-white gap-3 p-1">
<div class="flex flex-col md:flex-row gap-4 items-center md:justify-stretch md:items-end">
<div class="flex flex-col w-full">
<label for="link">Link</label><br/>
<label for="link">REST Service URL</label>
<div class="p-2 w-full border-slate-400 border-2 rounded-lg">
<a class="underline" :href="mockMessageLink">{{ mockMessageLink }}</a>
</div>
</div>
<SaveComponent v-bind:message-data="messageData"></SaveComponent>
</div>
<div class="flex flex-col md:flex-row w-full gap-4">
<div class="w-full">
<label for="contentType">Content Type</label><br/>
<label for="contentType">Response Content Type</label><br/>
<input class="text-field" id="contentType" type="text" v-model="messageData.contentType"/>
</div>
<div class="w-full">
<label for="httpStatus">HttpStatus</label><br/>
<label for="httpStatus">Response HTTP Status</label><br/>
<input class="text-field" id="httpStatus" type="text" v-model="messageData.httpStatus"/>
</div>
</div>
<div class="flex text-left flex-col overflow-scroll h-3/4">
<label for="messageBody text-center">Body</label>
<div class="flex text-left flex-col overflow-auto gap-2 h-3/4">
<label class="text-center" for="messageBody text-center">Response Body</label>
<CodeEditorComponent
@update:updated-code="showUpdatedCode"
v-model="messageData.messageBody"

View File

@@ -15,7 +15,7 @@ const visible = ref('hidden');
const fetchLink = window.location.protocol + "//" + window.location.hostname + "/mock/api/mock";
function prepareAndSendData(){
if (props.messageData != null|| props.messageData != undefined ){
if (props.messageData != null || props.messageData != undefined ){
fetch(fetchLink, { method: "put", body:JSON.stringify(props.messageData), headers: { "Content-Type" : "application/json" }})
.then( response => response.text() )
.then( data => {message.value = data} )

View File

@@ -1,10 +1,16 @@
<script setup lang="ts">
import ThemeSwitcherComponent from '../common/ThemeSwitcherComponent.vue';
const emit = defineEmits([
"theme",
]);
</script>
<template>
<div class="flex flex-col gap-4 text-center font-thin text-slate-600 dark:text-slate-400 ">
<div class="flex flex-col gap-4 items-center text-center font-thin text-slate-600 dark:text-slate-400 ">
<ThemeSwitcherComponent @theme="(theme)=>emit('theme',theme)"></ThemeSwitcherComponent>
<div class="flex flex-col">
<a href="mailto:bugs@release11.com">Found a bug?</a>
<a href="#" class="hidden">Privacy Policy</a>

View File

@@ -8,15 +8,27 @@ import logoWhite from '@assets/logo_czarne.svg';
const logoR11 = ref( logoDark );
const emit = defineEmits([
'theme:changed'
])
function changeLogoForTheme(){
logoR11.value = isDarkModeSet() ? logoDark : logoWhite;
}
function isDarkModeSet(){
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
if (localStorage.theme)
return localStorage.theme == "dark";
else
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}
function changeTheme(theme:string){
changeLogoForTheme()
emit('theme:changed',theme);
}
onMounted( () => {
changeLogoForTheme();
})
@@ -29,8 +41,8 @@ onMounted( () => {
<a href="https://release11.com/">
<img :src="logoR11" class="w-72 h-16 p-2 pt-0"/>
</a>
<div class="flex basis-full flex-col font-medium items-center">
<sidebar-menu-element-component category-name="XML">
<div class="flex basis-full flex-col font-normal items-center">
<sidebar-menu-element-component category-name="Parser">
<SidebarToolLinkComponent path-to="/xml/xpath" element-content="XPath" />
<SidebarToolLinkComponent path-to="/xml/xquery" element-content="XQuery" />
<SidebarToolLinkComponent path-to="/xml/xsd" element-content="XSD" />
@@ -48,8 +60,14 @@ onMounted( () => {
<SidebarToolLinkComponent path-to="/rest/mock" element-content="Mock" />
</sidebar-menu-element-component>
<sidebar-menu-element-component category-name="Encoder">
<SidebarToolLinkComponent path-to="/encoder/base64" element-content="Base64" />
<SidebarToolLinkComponent path-to="/encoder/url" element-content="URL" />
</sidebar-menu-element-component>
</div>
<FooterComponent></FooterComponent>
<FooterComponent @theme="changeTheme"></FooterComponent>
</div>
</aside>
</template>

View File

@@ -18,10 +18,10 @@ const props = defineProps(
<template>
<div class="w-full mb-4 p-2 rounded-xl shadow-lg bg-gradient-to-r from-blue-400 to-blue-300 dark:from-sky-700 dark:to-sky-900">
<button @click="switchHiddenElement()" type="button" :class="[isActive ? 'rounded-lg' : 'rounded-lg']" class="w-full p-2 text-lg font-bold text-gray-900 transition duration-75 hover:bg-blue-100 dark:text-gray-100 dark:hover:bg-slate-600">
<button @click="switchHiddenElement()" type="button" :class="[isActive ? 'rounded-lg' : 'rounded-lg']" class="w-full p-1 text-lg font-normal text-gray-900 transition duration-75 hover:bg-blue-100 dark:text-gray-100 dark:hover:bg-slate-600">
<span class="flex-1 whitespace-nowrap">{{props.categoryName}}</span>
</button>
<div class="flex flex-col w-full py-2 bg-indigo-50 dark:bg-slate-800 rounded-xl font-thin overflow-hidden" :class="[isActive ? 'active' : 'hidden']">
<div class="flex flex-col w-full mt-2 py-2 bg-indigo-50 dark:bg-slate-800 rounded-xl font-light overflow-hidden" :class="[isActive ? 'active' : 'hidden']">
<slot></slot>
</div>
</div>

View File

@@ -17,6 +17,6 @@ const props = defineProps(
<style>
.router-link-active {
font-weight: 500;
font-weight: 600;
}
</style>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
const props = defineProps({
id: {
type: Number,
required: true
},
isActive: {
type: Boolean,
default: false,
required: false
}
})
const emit = defineEmits(["click:activate", "click:remove"])
function activate() {
emit("click:activate", props.id);
}
function remove() {
emit("click:remove", props.id);
}
</script>
<template>
<div :class=" isActive ? 'tab-active' : 'tab'" class="flex flex-row gap-3">
<button @click="activate" class="hover:brightness-110"><slot /></button>
<button @click="remove" class="hover:brightness-110 hover:bg-blue-100 hover:dark:bg-slate-400 hover:dark:text-black px-2 rounded-full">x</button>
</div>
</template>

View File

@@ -1,10 +1,8 @@
<script setup lang="ts">
import InsertTemplateComponent from '@components/common/InsertTemplateComponent.vue';
import InsertTemplateComponent from '@components/common/InsertTemplateComponent.vue'
import XMLButtonFormatterComponent from '@components/formatter/XMLButtonFormatterComponent.vue'
import { ref } from 'vue';
import CodeEditor from '../CodeEditorComponent.vue';
const data = ref('')
import { ref } from 'vue'
import CodeEditor from '@/components/common/CodeEditorComponent.vue'
const props = defineProps(
{
@@ -12,45 +10,60 @@ const props = defineProps(
data: {type: String},
}
)
const emit = defineEmits(['update'])
const emit = defineEmits(['update:modelValue'])
const data = ref('')
const inputFile = ref()
function sendValue() {
console.log("input works")
emit('update', data.value)
emit('update:modelValue', data.value)
}
function sendNewValue(newValue : string) {
data.value = newValue
emit('update', data.value)
}
function updateData(newData: string) {
data.value = newData;
sendValue();
function updateData(newData: string, clearFileSelector: boolean = true) {
data.value = newData
if (clearFileSelector)
inputFile.value.value = '';
sendValue()
}
function clear() {
updateData('');
updateData('')
}
function canBeFormatted() {
return props.stylizedName.toLowerCase() == 'xml' ||
props.stylizedName.toLowerCase() == 'xsd' ||
props.stylizedName.toLowerCase() == 'xslt';
props.stylizedName.toLowerCase() == 'xslt'
}
function readFile(file : any) {
const reader = new FileReader()
reader.onloadend = () => {
let result = reader.result?.toString()
if (typeof result == "string")
updateData(result, false);
}
reader.readAsText(file.target.files[0])
}
</script>
<template>
<div class="flex flex-col w-full h-1/2 lg:h-1/2 flex-none pr-4 pb-2">
<div class="flex flex-col w-full h-1/2 lg:h-1/2 flex-none xl:pr-2 2xl:pr-4 pb-2">
<div class="flex place-content-between w-full items-center">
<span class="dark:text-white">{{ stylizedName }}</span>
<div class="flex space-x-2 pb-2">
<InsertTemplateComponent :stylized-name="props.stylizedName" @update:default-data="(data: string) => updateData(data)"></InsertTemplateComponent>
<span class="dark:text-white mr-2">{{ stylizedName }}</span>
<div class="flex space-x-2 pb-2 overflow-x-auto">
<div class="flex items-stretch w-64">
<input id="fileLoader" ref="inputFile" class="file-selector" type="file" accept=".xml,.xql,.xquery,.xslt,text/xml,text/plain" @change="readFile" />
</div>
<InsertTemplateComponent :stylized-name="props.stylizedName" @update:default-data="updateData"></InsertTemplateComponent>
<XMLButtonFormatterComponent v-if="canBeFormatted()" :xml="data" @update:result="(data:any) => updateData(data.result)"></XMLButtonFormatterComponent>
<button class="tool-button" @click="clear">Clear</button>
</div>
</div>
<CodeEditor @update:updated-code="sendNewValue" v-model="data" :code="data" :config="{disabled:false, language:stylizedName}"></CodeEditor>
<CodeEditor @update:updated-code="updateData" v-model="data" :code="data" :config="{disabled:false, language:stylizedName}"></CodeEditor>
</div>
</template>

View File

@@ -1,13 +1,13 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import CodeEditor from '../CodeEditorComponent.vue';
import { type TabData } from '../common/TabData'
import CodeEditor from '@/components/common/CodeEditorComponent.vue';
const props = defineProps(
{
tool: {type: String, required: true},
xml: {type: String},
query: {type: String}
xml: {type: [String, Array<TabData>], required: true},
query: {type: String, required: true}
}
)
@@ -15,14 +15,21 @@ const emit = defineEmits(["update"]);
const result = ref('');
var enginesForCurrentTool = ref(["saxon", "xalan", "libxml"]);
let enginesForCurrentTool = ref(["saxon", "xalan", "libxml"]);
const allVersions = ["1.0", "2.0", "3.0", "3.1"];
var versionsForCurrentEngine = ref([""]);
let versionsForCurrentEngine = ref([""]);
const engine = ref('');
const version = ref('');
const errorOccurred = ref(false);
const successOccurred = ref(false);
interface XmlFile {
fileName: string;
fileData: string;
}
onMounted(() => {
changeAvailableEngines();
@@ -72,79 +79,147 @@ function selectDefaultEngine() {
}
function selectDefaultVersion() {
const lastVersion = versionsForCurrentEngine.value.length - 1
const lastVersion = versionsForCurrentEngine.value.length - 1;
version.value = versionsForCurrentEngine.value[lastVersion];
emitVersionChange();
}
function process() {
var request:Request = prepareRequest();
let request:Request = prepareRequest();
fetchRequest(request).then((data) => {
updateOutputField(data);
})
}
function updateOutputField(data: any) {
result.value = data.result
errorOccurred.value = data.status != "OK"
successOccurred.value = data.status == "OK"
}
function prepareRequest():Request {
var request = new Request(prepareURL(), {
body: prepareRequestBody(),
let request = new Request(prepareURL(), {
body: selectRequestBodyType(),
method: "POST"
});
return request
}
function prepareURL(): string {
const engineEndpoint = engine.value == "libxml" ? "libxml" : "java";
return document.location.protocol + "//" + document.location.hostname + "/" + engineEndpoint + "/" + props.tool;
const engineEndpoint = engine.value == "libxml" ? "libxml" : "java";
let tool = props.tool;
if (Array.isArray(props.xml) && props.xml.length > 1)
tool = "multiple/xslt";
return document.location.protocol + "//" + document.location.hostname + "/" + engineEndpoint + "/" + tool;
}
function prepareRequestBody():string {
var requestBody = JSON.stringify({
"data": props.xml,
"process": props.query,
function selectRequestBodyType() : string {
if (Array.isArray(props.xml) && props.xml.length > 1)
return prepareRequestBodyMultiXml();
else if (Array.isArray(props.xml))
return prepareRequestBodySingleXml(props.xml.at(0)!.data);
else
return prepareRequestBodySingleXml(props.xml!);
}
function prepareRequestBodySingleXml(data: string):string {
let requestBody = JSON.stringify({
"data": data,
"processorData": props.query,
"processor": engine.value,
"version": version.value
});
return requestBody;
}
async function fetchRequest(request: Request):Promise<JSON> {
var responseBody = await fetch(request)
.then(response => response.json())
.then((body) => body);
return responseBody;
function prepareRequestBodyMultiXml():string {
if (!Array.isArray(props.xml))
return "";
let xmlFilesArray = convertDataArray(props.xml);
let requestBody = JSON.stringify({
"data": xmlFilesArray,
"processorData": props.query,
"processor": engine.value,
"version": version.value
});
return requestBody;
}
function updateOutputField(data: any) {
result.value = data.result;
function convertDataArray(data: Array<TabData>) {
let result = new Array<XmlFile>;
data.forEach(element => {
let fileName = element.name;
if (!fileName.endsWith(".xml")) {
fileName += ".xml";
}
result.push({
fileName: fileName,
fileData: element.data,
});
});
return result;
}
async function fetchRequest(request: Request):Promise<JSON> {
let responseBody = await fetch(request)
.then(response => response.json())
.then((body) => body)
return responseBody
}
function clear() {
result.value = "";
result.value = ""
errorOccurred.value = false
successOccurred.value = false
}
function emitVersionChange() {
emit("update", version.value);
}
function isVersionSelectionAvailable() {
return !(versionsForCurrentEngine.value.length == 1 && versionsForCurrentEngine.value.at(0) == "N/A");
}
function highlightField() {
if (errorOccurred.value)
return "text-field-error";
if (successOccurred.value)
return "text-field-success";
return "";
}
</script>
<template>
<div class="flex flex-col flex-none w-full lg:w-1/2 h-1/3 lg:h-full items-center pb-2 pr-2">
<div class="flex flex-col flex-none w-full 2xl:w-1/2 h-1/3 2xl:h-full items-center pb-2 xl:pr-2">
<div class="flex place-content-between w-full items-center pb-2">
<span class="dark:text-white">Result:</span>
<div class="flex space-x-2">
<div class="flex space-x-2 overflow-x-auto">
<select v-model="engine" name="engine" @change="changeAvailableVersions()" class="px-3 rounded-full border border-slate-400 bg-white dark:text-slate-100 dark:bg-gray-600">
<option v-for="engine in enginesForCurrentTool" :value="engine">{{ engine }}</option>
</select>
<select v-model="version" name="version" @change="emitVersionChange()" class="px-3 rounded-full border border-slate-400 bg-white dark:text-slate-100 dark:bg-gray-600">
<select v-model="version" v-if="isVersionSelectionAvailable()" name="version" @change="emitVersionChange()" class="px-3 rounded-full border border-slate-400 bg-white dark:text-slate-100 dark:bg-gray-600">
<option v-for="version in versionsForCurrentEngine" :value="version">{{ version }}</option>
</select>
<button class="tool-button" @click="clear">Clear</button>
<button class="tool-button" @click="process">Process</button>
</div>
</div>
<div class="overflow-scroll h-full w-full">
<CodeEditor :code="result" :config="{disabled:true,language:tool}"></CodeEditor>
<div class="overflow-auto h-full w-full rounded-2xl" :class="highlightField()">
<CodeEditor :code="result" :config="{disabled:false,language:tool}"></CodeEditor>
</div>
</div>

View File

@@ -0,0 +1,153 @@
<script setup lang="ts">
import TabComponent from './TabComponent.vue'
import InsertTemplateComponent from '@components/common/InsertTemplateComponent.vue'
import XMLButtonFormatterComponent from '@components/formatter/XMLButtonFormatterComponent.vue'
import { type TabData } from '../common/TabData'
import { ref } from 'vue'
import CodeEditor from '@/components/common/CodeEditorComponent.vue';
const props = defineProps(
{
stylizedName: {type: String, required: true},
data: {type: Array<TabData>},
tabCountLimit: {type: Number, required: false, validator: (value) => typeof value == "number" && value > 0}
}
)
const emit = defineEmits(['update:modelValue'])
const newTabId = ref(0);
const activeTabId = ref(0);
const tabs = ref(new Array<TabData>);
tabs.value.push({
id: newTabId.value++,
name: "xml1.xml",
data: "",
})
const data = ref('')
const inputFile = ref()
function sendValue() {
emit('update:modelValue', tabs.value);
}
function updateData(newData: string) {
data.value = newData;
tabs.value.at(findIndexWithID(activeTabId.value))!.data = newData;
inputFile.value.value = '';
sendValue();
}
function clear() {
updateData('');
}
function canBeFormatted() {
return props.stylizedName.toLowerCase() == 'xml' ||
props.stylizedName.toLowerCase() == 'xsd' ||
props.stylizedName.toLowerCase() == 'xslt'
}
function readFile(file : any) {
const reader = new FileReader()
reader.onloadend = () => {
let result = reader.result!.toString();
let activeIndex = findIndexWithID(activeTabId.value);
let filePath = inputFile.value.value.split("\\");
let fileName = filePath.at(filePath.length - 1);
tabs.value.at(activeIndex)!.name = fileName;
updateData(result);
}
reader.readAsText(file.target.files[0]);
}
function changeActiveTab(id : number) {
let index = findIndexWithID(activeTabId.value);
let newIndex = findIndexWithID(id);
tabs.value.at(index)!.data = data.value;
activeTabId.value = id;
data.value = tabs.value.at(newIndex)!.data;
sendValue();
}
function addTab() {
if (isTabCountLimitAchieved())
return
tabs.value.push({
id: newTabId.value++,
name: "xml" + newTabId.value + ".xml",
data: ""
});
}
function isTabCountLimitAchieved() {
return props.tabCountLimit && tabs.value.length == props.tabCountLimit
}
function removeTab(id : number) {
if (tabs.value.length == 1)
return
let indexToRemove = findIndexWithID(id);
switchToExistingTab(indexToRemove);
tabs.value.splice(indexToRemove, 1);
}
function switchToExistingTab(indexToRemove: number) {
let activeIndex = findIndexWithID(activeTabId.value);
if (indexToRemove == activeIndex && activeIndex == 0)
changeActiveTab(tabs.value.at(1)!.id)
else if (indexToRemove == activeIndex)
changeActiveTab(tabs.value.at(0)!.id)
}
function findIndexWithID(id : number) : number {
for (let i = 0; tabs.value.length; i++)
if (tabs.value.at(i)!.id == id)
return i;
return -1;
}
</script>
<template>
<div class="flex flex-col w-full h-1/2 lg:h-1/2 flex-none xl:pr-2 2xl:pr-4 pb-2">
<div class="flex justify-between mb-2">
<div class="flex gap-2 overflow-x-auto">
<TabComponent @click:activate="changeActiveTab" @click:remove="removeTab" v-for="tab in tabs" :id="tab.id" :isActive="tab.id == activeTabId">{{ tab.name }}</TabComponent>
</div>
<div class="flex gap-2">
<div class="flex items-stretch w-64">
<input id="fileLoader" ref="inputFile" class="file-selector" type="file" accept=".xml,.xql,.xquery,.xslt,text/xml,text/plain" @change="readFile" />
</div>
<button :class="isTabCountLimitAchieved() ? 'inactive-button' : 'tool-button'" @click="addTab">New</button>
</div>
</div>
<div class="flex place-content-between w-full items-center">
<span class="dark:text-white mr-2">{{ stylizedName }}</span>
<div class="flex space-x-2 pb-2 overflow-x-auto">
<InsertTemplateComponent :stylized-name="props.stylizedName" @update:default-data="updateData"></InsertTemplateComponent>
<XMLButtonFormatterComponent v-if="canBeFormatted()" :xml="data" @update:result="(data:any) => updateData(data.result)"></XMLButtonFormatterComponent>
<button class="tool-button" @click="clear">Clear</button>
</div>
</div>
<CodeEditor @update:updated-code="updateData" v-model="data" :code="data" :config="{disabled:false, language:stylizedName}"></CodeEditor>
</div>
</template>

View File

@@ -13,7 +13,7 @@ function toggleTooltips() {
</script>
<template>
<div class="flex p-2 flex-col rounded-xl shadow-lg bg-gradient-to-r from-zinc-400 to-slate-400 dark:from-slate-600 dark:to-slate-700">
<div class="flex p-2 flex-col rounded-xl shadow-lg bg-gradient-to-r from-gray-300 to-slate-300 dark:from-slate-500 dark:to-slate-600">
<button :class="{ 'mb-2' : !isCategoryHidden }" class="dark:text-slate-100 hover:font-bold" @click="toggleTooltips()">{{ props.name }}</button>
<div id="content" :class="{'hidden' : isCategoryHidden}" class="flex flex-col gap-4 w-full h-fit p-2 rounded-xl dark:text-white bg-indigo-50 dark:bg-slate-800" >
<slot></slot>

View File

@@ -58,11 +58,11 @@ function toggleTooltips() {
</script>
<template>
<div :class="areTooltipsHidden ? 'w-fit' : 'w-4/12'" class="hidden 2xl:flex shrink-0 items-stretch p-2 flex-row rounded-xl shadow-lg bg-gradient-to-r from-blue-400 to-blue-300 dark:from-sky-600 dark:to-sky-800 ">
<div :class="areTooltipsHidden ? 'w-fit' : 'w-[26rem]'" class="hidden xl:flex shrink-0 items-stretch p-2 flex-row rounded-xl shadow-lg bg-gradient-to-r from-blue-400 to-blue-300 dark:from-sky-600 dark:to-sky-800 ">
<button :class="{'mr-2' : !areTooltipsHidden }" class="text-xl w-6 dark:text-slate-100" @click="toggleTooltips()">
T<br/>o<br/>o<br/>l<br/>t<br/>i<br/>p<br/>s
</button>
<div id="content" :class="{'hidden' : areTooltipsHidden}" class="w-full flex flex-col gap-4 p-2 overflow-scroll rounded-xl dark:text-white bg-indigo-50 dark:bg-slate-800" >
<div id="content" :class="{'hidden' : areTooltipsHidden}" class="w-full flex flex-col gap-4 p-2 overflow-auto rounded-xl dark:text-white bg-indigo-50 dark:bg-gray-700" >
<TooltipDiffsComponent :tool-name="toolType" :tool-version="props.version"></TooltipDiffsComponent>
<div class="w-full h-2"> </div>
<tooltipCategoryComponent v-for="category in selectTooltip()" :key="category.name" :name="category.name">

View File

@@ -12,19 +12,19 @@ const props = defineProps({
function getDiffEntry(toolVersion : String) : string[] {
if ( props.toolName == "xpath" ){
switch(toolVersion){
case "2.0" : {
return xpathDiffs.VersionDiffs[0].diffs
case "2.0" : {
return xpathDiffs.VersionDiffs[0].diffs
}
case "3.0" : {
return xpathDiffs.VersionDiffs[1].diffs
}
case "3.1" : {
return xpathDiffs.VersionDiffs[2].diffs
}
default: {
return xpathDiffs.VersionDiffs[2].diffs
}
}
case "3.0" : {
return xpathDiffs.VersionDiffs[1].diffs
}
case "3.1" : {
return xpathDiffs.VersionDiffs[2].diffs
}
default: {
return xpathDiffs.VersionDiffs[2].diffs
}
}
} else if (props.toolName == "xslt") {
return ["XSLT 2.0"].concat(xsltDiffs.VersionDiffs[0].diffs).concat(["XSLT 3.0"]).concat(xsltDiffs.VersionDiffs[1].diffs) ;
} else{
@@ -56,7 +56,7 @@ function getInfo(num : number ){
</span>
</TooltipCategoryComponent>
<TooltipCategoryComponent v-if="toolVersion !== '1.0'" :name="getInfo(1).category">
<TooltipCategoryComponent v-if="toolVersion !== '1.0'" :name="getInfo(1).category + ' ' + toolVersion + '?'">
<span v-for=" diff in getDiffEntry(toolVersion)" v-bind:key="diff" class=" text-justify" >
<div class="w-full h-4 text-center" v-if="diff.includes('XSLT')">
------------ {{ diff }} ------------

Binary file not shown.

Binary file not shown.

View File

@@ -12,6 +12,11 @@ const xsdTool = import("@views/XSDView.vue")
const xpathTool = import("@views/XPathView.vue")
const xqueryTool = import("@views/XQueryView.vue")
const base64Encoder = import("@views/Base64EncoderView.vue")
const urlEncoder = import("@views/UrlEncoderView.vue")
const restMockMan = import("@views/man/RestMockManView.vue")
const routes = [
{
path: '/',
@@ -57,6 +62,21 @@ const routes = [
path: '/rest/mock',
name: 'restmock',
component: () => restMock
},
{
path: '/encoder/base64',
name: 'base64encoder',
component: () => base64Encoder
},
{
path: '/encoder/url',
name: 'urlEncoder',
component: () => urlEncoder
},
{
path: '/man/rest-mock',
name: 'RestMockManView',
component: () => restMockMan
}
]

View File

@@ -2,14 +2,44 @@
@tailwind components;
@tailwind utilities;
@font-face {
font-family: "Raleway";
src: url("fonts/Raleway-Variable.ttf");
}
@font-face {
font-family: "Sono";
src: url("fonts/Sono-Variable.ttf");
}
.inactive-button {
@apply py-2 px-4 h-fit text-slate-400 border border-slate-400 rounded-full
@apply py-2 px-3 h-fit text-slate-400 border border-slate-400 rounded-full
}
.tool-button {
@apply hover:brightness-110 py-2 px-4 h-fit rounded-full bg-gradient-to-r from-blue-400 to-sky-300 dark:text-white dark:from-sky-600 dark:to-sky-800 hover:bg-blue-400
@apply hover:brightness-110 py-2 px-3 h-fit min-w-fit rounded-full bg-gradient-to-r from-blue-400 to-sky-300 dark:text-white dark:from-sky-600 dark:to-sky-800 hover:bg-blue-400
}
.text-field {
@apply w-full font-mono dark:text-slate-100 bg-slate-50 dark:bg-gray-600 border border-slate-400 p-2 rounded-lg
}
}
.file-selector {
@apply block file:border-none file:font-sans file:text-base file:hover:brightness-110 file:py-2 file:px-3 file:h-full file:w-fit file:rounded-full file:bg-gradient-to-r file:from-blue-400 file:to-sky-300 file:dark:text-white file:dark:from-sky-600 file:dark:to-sky-800 file:hover:bg-blue-400 w-fit rounded-full text-sm text-gray-900 border border-gray-300 cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
}
.text-field-error {
@apply shadow-[0px_0px_10px_0px_rgba(255,0,0,1)];
}
.text-field-success {
@apply shadow-[0px_0px_10px_0px_rgba(52,211,153,1)];
}
.tab {
@apply py-2 px-3 h-fit dark:text-slate-400 rounded-t-2xl border-t border-l border-r border-slate-400
}
.tab-active {
@apply py-2 px-3 h-fit text-slate-700 border-t border-l border-r border-slate-400 rounded-t-2xl bg-gradient-to-r from-blue-400 to-sky-300 dark:text-white dark:from-sky-600 dark:to-sky-800 hover:bg-blue-400
}

View File

@@ -0,0 +1,75 @@
<script setup lang="ts">
import CodeEditorComponent from '@/components/common/CodeEditorComponent.vue'
import EncoderButton from '@/components/encoder/EncoderButtonComponent.vue'
import { ref } from 'vue'
const data : any = ref("")
const imageData = ref("")
const DoshowImage = ref(false)
const inputImage = ref()
function setTextFieldValue(newData: string) {
data.value = newData.toString()
DoshowImage.value = false
}
function showImage(newImage : string){
imageData.value = "data:image/jpeg;base64,"+newImage
DoshowImage.value = true
}
function convertImageToBase64(file : any){
const reader = new FileReader()
reader.onloadend = () => {
data.value = reader.result?.toString().split(',')[1]
showImage(data.value)
}
reader.readAsDataURL(file.target.files[0])
}
function clear(){
data.value = ""
imageData.value = ""
DoshowImage.value = false
inputImage.value.value = null
}
</script>
<template>
<div id="layoutFull" class="w-full h-full flex flex-col xl:flex-row gap-4">
<div id="layoutLeft" class="flex flex-col w-full xl:w-1/2 h-1/3 xl:h-full gap-4">
<div class="w-full flex flex-row place-content-between items-center">
<label class="dark:text-white text-center">Base64 Encoder</label>
<div class="flex flex-row items-center gap-2 overflow-x-auto">
<button class="tool-button" @click="clear()">Clear</button>
<EncoderButton @image:show="showImage" :code="data" operation-type="Show Image"></EncoderButton>
<div class="w-2"></div>
<EncoderButton @update:result="setTextFieldValue" :code="data" operation-type="Encode"></EncoderButton>
<EncoderButton @update:result="setTextFieldValue" :code="data" operation-type="Decode"></EncoderButton>
</div>
</div>
<div id="codeEditor" class="w-full h-full xl:h-1/3 flex flex-col">
<CodeEditorComponent @update:updated-code="setTextFieldValue" :config="{language:'base64'}" :code="data"></CodeEditorComponent>
</div>
<div class="flex flex-row justify-center w-full">
<div class="flex flex-col items-center w-fit" id="imageImporter">
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" for="file_input">Upload image</label>
<input id="imageLoader" ref="inputImage" class="file-selector" accept=".gif, .jpg, .jpeg, .png, .webm, image/gif, image/jpeg, image/png, image/webm" type="file" @change="convertImageToBase64" />
</div>
</div>
</div>
<div id="layoutRight" class="w-full xl:w-1/2 min-h-[66%] xl:h-full">
<div class="border-2 rounded-lg border-gray-300 dark:border-gray-600 min-h-[50%]" v-on="DoshowImage">
<img :src="imageData"/>
</div>
</div>
</div>
</template>

View File

@@ -1,34 +1,8 @@
<script setup lang="ts">
import InsertTemplateComponent from '@components/common/InsertTemplateComponent.vue';
import CodeEditorComponent from '@/components/CodeEditorComponent.vue';
import { ref } from 'vue';
import HtmlButtonFormatterComponent from '@/components/formatter/HtmlButtonFormatterComponent.vue';
const html = ref('');
function clear() {
html.value = '';
}
function setTextFieldValue(data: string) {
html.value = data.toString()
}
import FormatterComponent from '@/components/formatter/FormatterComponent.vue';
</script>
<template>
<div id="layout" class="flex flex-col w-full h-full gap-4">
<div id="toolbar" class="flex flex-col gap-4 items-center lg:flex-row place-content-between">
<span class="dark:text-slate-100">HTML Formatter</span>
<div class="flex flex-wrap gap-2 justify-center">
<InsertTemplateComponent stylized-name="HTML" @update:defaultData="setTextFieldValue"></InsertTemplateComponent>
<button class="tool-button" @click="clear()">Clear</button>
<HtmlButtonFormatterComponent @update:result="setTextFieldValue" :code="html" format-type="Minimize" />
<HtmlButtonFormatterComponent @update:result="setTextFieldValue" :code="html" format-type="Prettify" />
<HtmlButtonFormatterComponent @update:result="setTextFieldValue" :code="html" format-type="XML Converter" />
</div>
</div>
<CodeEditorComponent @update:updated-code="setTextFieldValue" :code="html" :config="{disabled:false,language:'html'}" />
</div>
<FormatterComponent formatter-language="HTML"></FormatterComponent>
</template>

View File

@@ -1,37 +1,8 @@
<script setup lang="ts">
import CodeEditorComponent from '@/components/CodeEditorComponent.vue';
import JsonButtonFormatterComponent from '@/components/formatter/JsonButtonFormatterComponent.vue';
import InsertTemplateComponent from '@components/common/InsertTemplateComponent.vue';
import { ref } from 'vue';
const json = ref('');
function setTextFieldValue(data: string) {
json.value = data
}
function format(formattedXml: any) {
json.value = formattedXml.data;
}
function clear() {
json.value = '';
}
import FormatterComponent from '@/components/formatter/FormatterComponent.vue';
</script>
<template>
<div id="layout" class="flex flex-col w-full h-full gap-4">
<div id="toolbar" class= "flex flex-col gap-4 items-center lg:flex-row place-content-between">
<span class="dark:text-slate-100">JSON Formatter</span>
<div class="flex flex-wrap gap-2 justify-center">
<InsertTemplateComponent stylized-name="JSON" @update:defaultData="(data: string) => setTextFieldValue(data)"></InsertTemplateComponent>
<button class="tool-button" @click="clear()">Clear</button>
<JsonButtonFormatterComponent isMinimizer :json="json" @update:result="(data: any) => format(data)"></JsonButtonFormatterComponent>
<JsonButtonFormatterComponent :json="json" @update:result="(data: any) => format(data)"></JsonButtonFormatterComponent>
</div>
</div>
<CodeEditorComponent @update:updated-code="setTextFieldValue" :code="json" :config="{disabled:false,language:'json'}" />
</div>
<FormatterComponent formatter-language="JSON"></FormatterComponent>
</template>

View File

@@ -1,14 +1,29 @@
<script setup lang="ts">
import RestMockMessageComponent from '@components/mock/RestMockMessageComponent.vue'
import HistoryComponent from '@components/mock/HistoryComponent.vue'
import ManTooltipComponent from '@/components/man/ManTooltipComponent.vue';
import RestMockManComponent from '@/components/man/RestMockManComponent.vue';
import { ref } from 'vue';
const historyVisibility = ref(true);
function setHistoryVisibility(visibility : boolean) {
historyVisibility.value = !visibility;
}
</script>
<template>
<div class="flex flex-col xl:flex-row gap-6 w-full overflow-y-scroll overflow-x-hidden h-full">
<div class="flex flex-col xl:flex-row gap-6 w-full overflow-y-auto overflow-x-hidden h-full">
<RestMockMessageComponent></RestMockMessageComponent>
<HistoryComponent></HistoryComponent>
<HistoryComponent :class="{'hidden': !historyVisibility}"></HistoryComponent>
<ManTooltipComponent @update:visible="setHistoryVisibility">
<div class="mt-2">
<a class="tool-button" href="/man/rest-mock">Expand</a>
</div>
<RestMockManComponent></RestMockManComponent>
</ManTooltipComponent>
</div>
</template>

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
import { ref } from 'vue';
import EncodeUriButton from '@components/encoder/EncodeUriButtonComponent.vue'
import CodeEditorComponent from '@/components/common/CodeEditorComponent.vue';
const uri = ref("")
function setTextFieldValue(newVal : string){
console.log(newVal)
uri.value = newVal
}
function clear(){
uri.value = "";
}
</script>
<template>
<div class="flex flex-col w-full h-full gap-4" id="layout">
<div id="toolbar" class="flex flex-col gap-4 items-center lg:flex-row place-content-between">
<span class="dark:text-slate-100">URI Encoder</span>
<div class="flex flex-wrap gap-2 justify-center">
<button class="tool-button" @click="clear()">Clear</button>
<EncodeUriButton @update:result="setTextFieldValue" :code="uri" operation="Encode" />
<EncodeUriButton @update:result="setTextFieldValue" :code="uri" operation="Decode" />
</div>
</div>
<CodeEditorComponent @update:updated-code="setTextFieldValue" :code="uri" :config="{disabled:false,language:'uri'}" />
</div>
</template>

View File

@@ -16,11 +16,11 @@ function updateVersion(newVersion: string) {
</script>
<template>
<div id="layout" class="flex flex-col lg:flex-row w-full h-full">
<div class="flex flex-col lg:flex-row w-full lg:w-7/12 grow overflow-hide px-2">
<div class="flex flex-col w-full lg:w-1/2 h-2/3 lg:h-full flex-none items-center">
<xmlInputFieldComponent stylized-name="XML" :data="xml" @update="(data) => {xml = data}"></xmlInputFieldComponent>
<xmlInputFieldComponent stylized-name="XPath" :data="query" @update="(data) => {query = data}"></xmlInputFieldComponent>
<div id="layout" class="flex flex-row w-full h-full">
<div class="flex flex-col 2xl:flex-row w-full xl:w-7/12 grow overflow-hide xl:pr-2">
<div class="flex flex-col w-full 2xl:w-1/2 h-2/3 2xl:h-full flex-none items-center">
<xmlInputFieldComponent stylized-name="XML" v-model="xml"></xmlInputFieldComponent>
<xmlInputFieldComponent stylized-name="XPath" v-model="query"></xmlInputFieldComponent>
</div>
<xmlOutputFieldComponent tool="xpath" :xml="xml" :query="query" @update="(version) => updateVersion(version)"></xmlOutputFieldComponent>
</div>

View File

@@ -10,10 +10,10 @@ const query = ref('');
</script>
<template>
<div id="layout" class="flex flex-col lg:flex-row w-full h-full">
<div class="flex flex-col w-full lg:w-1/2 h-2/3 lg:h-full flex-none items-center">
<xmlInputFieldComponent stylized-name="XML" @update="(data) => {xml = data}"></xmlInputFieldComponent>
<xmlInputFieldComponent stylized-name="XQuery" @update="(data) => {query = data}"></xmlInputFieldComponent>
<div id="layout" class="flex flex-col 2xl:flex-row w-full h-full">
<div class="flex flex-col w-full 2xl:w-1/2 h-2/3 2xl:h-full flex-none items-center">
<xmlInputFieldComponent stylized-name="XML" v-model="xml"></xmlInputFieldComponent>
<xmlInputFieldComponent stylized-name="XQuery" v-model="query"></xmlInputFieldComponent>
</div>
<xmlOutputFieldComponent tool="xquery" :xml="xml" :query="query"></xmlOutputFieldComponent>
</div>

View File

@@ -10,10 +10,10 @@ const query = ref('');
</script>
<template>
<div id="layout" class="flex flex-col lg:flex-row w-full h-full">
<div class="flex flex-col w-full lg:w-1/2 h-2/3 lg:h-full flex-none items-center">
<xmlInputFieldComponent stylized-name="XML" @update="(data) => {xml = data}"></xmlInputFieldComponent>
<xmlInputFieldComponent stylized-name="XSD" @update="(data) => {query = data}"></xmlInputFieldComponent>
<div id="layout" class="flex flex-col 2xl:flex-row w-full h-full">
<div class="flex flex-col w-full 2xl:w-1/2 h-2/3 2xl:h-full flex-none items-center">
<xmlInputFieldComponent stylized-name="XML" v-model="xml"></xmlInputFieldComponent>
<xmlInputFieldComponent stylized-name="XSD" v-model="query"></xmlInputFieldComponent>
</div>
<xmlOutputFieldComponent tool="xsd" :xml="xml" :query="query"></xmlOutputFieldComponent>
</div>

View File

@@ -1,27 +1,31 @@
<script setup lang="ts">
import xmlInputFieldComponent from '@/components/xml/XmlInputFieldComponent.vue';
import xmlTabbedInputComponent from '@/components/xml/XmlTabbedInputComponent.vue';
import xmlOutputFieldComponent from '@/components/xml/XmlOutputFieldComponent.vue';
import TooltipComponent from '@/components/xml/tooltips/TooltipComponent.vue';
import { type TabData } from '@/components/common/TabData';
import { ref } from 'vue';
const xml = ref('');
const xml = ref(new Array<TabData>);
const query = ref('');
const version = ref('');
function updateVersion(newVersion: string) {
version.value = newVersion;
}
</script>
<template>
<div id="layout" class="flex flex-col lg:flex-row w-full h-full">
<div class="flex flex-col lg:flex-row w-full lg:w-7/12 grow overflow-hide px-2">
<div class="flex flex-col w-full lg:w-1/2 h-2/3 lg:h-full flex-none items-center">
<xmlInputFieldComponent stylized-name="XML" @update="(data) => {xml = data}"></xmlInputFieldComponent>
<xmlInputFieldComponent stylized-name="XSLT" @update="(data) => {query = data}"></xmlInputFieldComponent>
</div>
<xmlOutputFieldComponent tool="xslt" :xml="xml" :query="query" @update="(version) => updateVersion(version)"></xmlOutputFieldComponent>
<div id="layout" class="flex flex-row w-full h-full">
<div class="flex flex-col 2xl:flex-row w-full xl:w-7/12 grow overflow-hide pr-2">
<div class="flex flex-col w-full 2xl:w-1/2 h-2/3 2xl:h-full flex-none items-center">
<xmlTabbedInputComponent stylized-name="XML" :tab-count-limit="3" v-model="xml"></xmlTabbedInputComponent>
<xmlInputFieldComponent stylized-name="XSLT" :data="query" v-model="query"></xmlInputFieldComponent>
</div>
<xmlOutputFieldComponent tool="xslt" :xml="xml" :query="query" @update="updateVersion"></xmlOutputFieldComponent>
</div>
<TooltipComponent tool-type="xslt" :version="version"></TooltipComponent>
</div>

View File

@@ -1,37 +1,8 @@
<script setup lang="ts">
import XMLButtonFormatterComponent from '@/components/formatter/XMLButtonFormatterComponent.vue';
import InsertTemplateComponent from '@components/common/InsertTemplateComponent.vue';
import CodeEditorComponent from '@/components/CodeEditorComponent.vue';
import { ref } from 'vue';
const xml = ref('');
function setTextFieldValue(data: string) {
xml.value = data
}
function format(formattedXml: any) {
xml.value = formattedXml.result;
}
function clear() {
xml.value = '';
}
import FormatterComponent from '@/components/formatter/FormatterComponent.vue';
</script>
<template>
<div id="layout" class="flex flex-col w-full h-full gap-4">
<div id="toolbar" class= "flex flex-col gap-4 items-center lg:flex-row place-content-between">
<span class="dark:text-slate-100">XML Formatter</span>
<div class="flex flex-wrap gap-2 justify-center">
<InsertTemplateComponent stylized-name="XML" @update:defaultData="(data: string) => setTextFieldValue(data)"></InsertTemplateComponent>
<button class="tool-button" @click="clear()">Clear</button>
<XMLButtonFormatterComponent is-minimizer :xml="xml" @update:result="(data: any) => format(data)"></XMLButtonFormatterComponent>
<XMLButtonFormatterComponent :xml="xml" @update:result="(data: any) => format(data)"></XMLButtonFormatterComponent>
</div>
</div>
<CodeEditorComponent @update:updated-code="setTextFieldValue" :code="xml" :config="{disabled:false,language:'xml'}" />
</div>
<FormatterComponent formatter-language="XML"></FormatterComponent>
</template>

View File

@@ -0,0 +1,20 @@
<script lang="ts">
import RestMockManComponent from '@man/RestMockManComponent.vue'
export default {
name:"RestMockManView",
components: {RestMockManComponent}
}
</script>
<template>
<div class="flex flex-col h-full gap-4">
<div class="flex flex-row">
<a href="/rest/mock" class="tool-button">Back to REST Mock</a>
</div>
<RestMockManComponent></RestMockManComponent>
</div>
</template>

View File

@@ -6,6 +6,11 @@ export default {
],
theme: {
extend: {},
fontFamily: {
'sans': ['Raleway'],
'mono': ["Sono"],
}
},
darkMode: 'class',
plugins: [],
}

View File

@@ -3,6 +3,11 @@
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"lib": [
"DOM",
"ES2022",
"ESNext"
],
"composite": true,
"baseUrl": ".",
"paths": {

View File

@@ -35,6 +35,7 @@ export default defineConfig({
'@components': fileURLToPath(new URL('./src/components', import.meta.url)),
'@views': fileURLToPath(new URL('./src/views', import.meta.url)),
'@assets': fileURLToPath(new URL('./src/assets', import.meta.url)),
'@man': fileURLToPath(new URL('./src/components/man', import.meta.url)),
}
}
})