🎨 Add theme support
- Move root (var) style properties to `/assets/css/themes.scss` - Improve mobile navigation - Create section component for the collapsable sections. - Create logo component, so color can be changed. - Add settings page - Add option to select theme color - Add option to select theme background - Add option to enable/disable multi-colored frames. - Add settings to VueX Store - Persist VueX store in LocalStorage.
This commit is contained in:
387
pages/index.vue
387
pages/index.vue
@@ -1,196 +1,183 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
|
||||
<fieldset class="request" ref="request">
|
||||
<legend v-on:click="collapse">Request ↕</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="method">Method</label>
|
||||
<select v-model="method">
|
||||
<option>GET</option>
|
||||
<option>POST</option>
|
||||
<option>PUT</option>
|
||||
<option>DELETE</option>
|
||||
<option>OPTIONS</option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
<input type="url" v-bind:class="{ error: urlNotValid }" v-model="url" v-on:keyup.enter="sendRequest">
|
||||
</li>
|
||||
<li>
|
||||
<label for="path">Path</label>
|
||||
<input v-model="path" v-on:keyup.enter="sendRequest">
|
||||
</li>
|
||||
<li>
|
||||
<label for="action"> </label>
|
||||
<button v-bind:class="{ disabled: urlNotValid }" name="action" @click="sendRequest">Send</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="reqbody" v-if="method === 'POST' || method === 'PUT'">
|
||||
<legend v-on:click="collapse">Request Body ↕</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label>Content Type</label>
|
||||
<select v-model="contentType">
|
||||
<option>application/json</option>
|
||||
<option>www-form/urlencoded</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
<ol v-for="(param, index) in bodyParams">
|
||||
<li>
|
||||
<label :for="'bparam'+index">Key {{index + 1}}</label>
|
||||
<input :name="'bparam'+index" v-model="param.key">
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'bvalue'+index">Value {{index + 1}}</label>
|
||||
<input :name="'bvalue'+index" v-model="param.value">
|
||||
</li>
|
||||
<li>
|
||||
<label for="request"> </label>
|
||||
<button name="request" @click="removeRequestBodyParam(index)">Remove</button>
|
||||
</li>
|
||||
</ol>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="addrequest">Action</label>
|
||||
<button name="addrequest" @click="addRequestBodyParam">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="request">Parameter List</label>
|
||||
<textarea name="request" rows="1" readonly>{{rawRequestBody || '(add at least one parameter)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="authentication hidden">
|
||||
<legend v-on:click="collapse">Authentication ↕</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="auth">Authentication Type</label>
|
||||
<select v-model="auth">
|
||||
<option>None</option>
|
||||
<option>Basic</option>
|
||||
<option>Bearer Token</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="auth === 'Basic'">
|
||||
<li>
|
||||
<label for="http_basic_user">User</label>
|
||||
<input v-model="httpUser">
|
||||
</li>
|
||||
<li>
|
||||
<label for="http_basic_passwd">Password</label>
|
||||
<input v-model="httpPassword" type="password">
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="auth === 'Bearer Token'">
|
||||
<li>
|
||||
<label for="bearer_token">Token</label>
|
||||
<input v-model="bearerToken">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="parameters hidden">
|
||||
<legend v-on:click="collapse">Parameters ↕</legend>
|
||||
<div class="collapsible">
|
||||
<ol v-for="(param, index) in params">
|
||||
<li>
|
||||
<label :for="'param'+index">Key {{index + 1}}</label>
|
||||
<input :name="'param'+index" v-model="param.key">
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'value'+index">Value {{index + 1}}</label>
|
||||
<input :name="'value'+index" v-model="param.value">
|
||||
</li>
|
||||
<li>
|
||||
<label for="param"> </label>
|
||||
<button name="param" @click="removeRequestParam(index)">Remove</button>
|
||||
</li>
|
||||
</ol>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="add">Action</label>
|
||||
<button name="add" @click="addRequestParam">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="request">Parameter List</label>
|
||||
<textarea name="request" rows="1" readonly>{{queryString || '(add at least one parameter)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="response" id="response" ref="response">
|
||||
<legend v-on:click="collapse">Response ↕</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="status">status</label>
|
||||
<input name="status" type="text" readonly :value="response.status || '(waiting to send request)'">
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(value, key) in response.headers">
|
||||
<li>
|
||||
<label for="value">{{key}}</label>
|
||||
<input name="value" :value="value" readonly>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="body">response</label>
|
||||
<textarea name="body" rows="10" readonly>{{response.body || '(waiting to send request)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="history">
|
||||
<legend v-on:click="collapse">History ↕</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<button v-bind:class="{ disabled: noHistoryToClear }" v-on:click="clearHistory">Clear History</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="entry in history">
|
||||
<li>
|
||||
<label for="time">Time</label>
|
||||
<input name="time" type="text" readonly :value="entry.time">
|
||||
</li>
|
||||
<li>
|
||||
<label for="name">Method</label>
|
||||
<input name="name" type="text" readonly :value="entry.method">
|
||||
</li>
|
||||
<li>
|
||||
<label for="name">URL</label>
|
||||
<input name="name" type="text" readonly :value="entry.url">
|
||||
</li>
|
||||
<li>
|
||||
<label for="name">Path</label>
|
||||
<input name="name" type="text" readonly :value="entry.path">
|
||||
</li>
|
||||
<li>
|
||||
<label for="delete"> </label>
|
||||
<button name="delete" @click="deleteHistory(entry)">Delete</button>
|
||||
</li>
|
||||
<li>
|
||||
<label for="use"> </label>
|
||||
<button name="use" @click="useHistory(entry)">Use</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<pw-section class="blue" label="Request" ref="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="method">Method</label>
|
||||
<select id="method" v-model="method">
|
||||
<option>GET</option>
|
||||
<option>POST</option>
|
||||
<option>PUT</option>
|
||||
<option>DELETE</option>
|
||||
<option>OPTIONS</option>
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
<input id="url" type="url" v-bind:class="{ error: urlNotValid }" v-model="url" v-on:keyup.enter="sendRequest">
|
||||
</li>
|
||||
<li>
|
||||
<label for="path">Path</label>
|
||||
<input id="path" v-model="path" v-on:keyup.enter="sendRequest">
|
||||
</li>
|
||||
<li>
|
||||
<label for="action"> </label>
|
||||
<button id="action" v-bind:class="{ disabled: urlNotValid }" name="action" @click="sendRequest">Send</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="blue-dark" label="Request Body" v-if="method === 'POST' || method === 'PUT'">
|
||||
<ul>
|
||||
<li>
|
||||
<label>Content Type</label>
|
||||
<select v-model="contentType">
|
||||
<option>application/json</option>
|
||||
<option>www-form/urlencoded</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
<ol v-for="(param, index) in bodyParams">
|
||||
<li>
|
||||
<label :for="'bparam'+index">Key {{index + 1}}</label>
|
||||
<input :name="'bparam'+index" v-model="param.key">
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'bvalue'+index">Value {{index + 1}}</label>
|
||||
<input :name="'bvalue'+index" v-model="param.value">
|
||||
</li>
|
||||
<li>
|
||||
<label for="request"> </label>
|
||||
<button name="request" @click="removeRequestBodyParam(index)">Remove</button>
|
||||
</li>
|
||||
</ol>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="addrequest">Action</label>
|
||||
<button name="addrequest" @click="addRequestBodyParam">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="request">Parameter List</label>
|
||||
<textarea name="request" rows="1" readonly>{{rawRequestBody || '(add at least one parameter)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="green" label="Authentication" collapsed>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="auth">Authentication Type</label>
|
||||
<select v-model="auth">
|
||||
<option>None</option>
|
||||
<option>Basic</option>
|
||||
<option>Bearer Token</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="auth === 'Basic'">
|
||||
<li>
|
||||
<label for="http_basic_user">User</label>
|
||||
<input v-model="httpUser">
|
||||
</li>
|
||||
<li>
|
||||
<label for="http_basic_passwd">Password</label>
|
||||
<input v-model="httpPassword" type="password">
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="auth === 'Bearer Token'">
|
||||
<li>
|
||||
<label for="bearer_token">Token</label>
|
||||
<input v-model="bearerToken">
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="cyan" label="Parameters" collapsed>
|
||||
<ol v-for="(param, index) in params">
|
||||
<li>
|
||||
<label :for="'param'+index">Key {{index + 1}}</label>
|
||||
<input :name="'param'+index" v-model="param.key">
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'value'+index">Value {{index + 1}}</label>
|
||||
<input :name="'value'+index" v-model="param.value">
|
||||
</li>
|
||||
<li>
|
||||
<label for="param"> </label>
|
||||
<button name="param" @click="removeRequestParam(index)">Remove</button>
|
||||
</li>
|
||||
</ol>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="add">Action</label>
|
||||
<button name="add" @click="addRequestParam">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="request">Parameter List</label>
|
||||
<textarea name="request" rows="1" readonly>{{queryString || '(add at least one parameter)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="purple" label="Response" id="response" ref="response">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="status">status</label>
|
||||
<input name="status" type="text" readonly :value="response.status || '(waiting to send request)'">
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(value, key) in response.headers">
|
||||
<li>
|
||||
<label for="value">{{key}}</label>
|
||||
<input name="value" :value="value" readonly>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="body">response</label>
|
||||
<textarea name="body" rows="10" readonly>{{response.body || '(waiting to send request)'}}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="gray" label="History">
|
||||
<ul>
|
||||
<li>
|
||||
<button v-bind:class="{ disabled: noHistoryToClear }" v-on:click="clearHistory">Clear History</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="entry in history">
|
||||
<li>
|
||||
<label for="time">Time</label>
|
||||
<input name="time" type="text" readonly :value="entry.time">
|
||||
</li>
|
||||
<li>
|
||||
<label for="name">Method</label>
|
||||
<input name="name" type="text" readonly :value="entry.method">
|
||||
</li>
|
||||
<li>
|
||||
<label for="name">URL</label>
|
||||
<input name="name" type="text" readonly :value="entry.url">
|
||||
</li>
|
||||
<li>
|
||||
<label for="name">Path</label>
|
||||
<input name="name" type="text" readonly :value="entry.path">
|
||||
</li>
|
||||
<li>
|
||||
<label for="delete"> </label>
|
||||
<button name="delete" @click="deleteHistory(entry)">Delete</button>
|
||||
</li>
|
||||
<li>
|
||||
<label for="use"> </label>
|
||||
<button name="use" @click="useHistory(entry)">Use</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@@ -208,7 +195,13 @@
|
||||
return headerMap
|
||||
};
|
||||
|
||||
import section from "../components/section";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
'pw-section': section
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
method: 'GET',
|
||||
@@ -295,16 +288,10 @@
|
||||
this.method = method
|
||||
this.url = url
|
||||
this.path = path
|
||||
this.$refs.request.scrollIntoView({
|
||||
this.$refs.request.$el.scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
})
|
||||
},
|
||||
collapse({
|
||||
target
|
||||
}) {
|
||||
const el = target.parentNode.className
|
||||
document.getElementsByClassName(el)[0].classList.toggle('hidden')
|
||||
},
|
||||
sendRequest() {
|
||||
if (this.urlNotValid) {
|
||||
alert('Please check the formatting of the URL')
|
||||
@@ -318,10 +305,10 @@
|
||||
path: this.path
|
||||
}, ...this.history]
|
||||
window.localStorage.setItem('history', JSON.stringify(this.history))
|
||||
if (this.$refs.response.classList.contains('hidden')) {
|
||||
this.$refs.response.classList.toggle('hidden')
|
||||
if (this.$refs.response.$el.classList.contains('hidden')) {
|
||||
this.$refs.response.$el.classList.toggle('hidden')
|
||||
}
|
||||
this.$refs.response.scrollIntoView({
|
||||
this.$refs.response.$el.scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
})
|
||||
this.response.status = 'Fetching...'
|
||||
|
||||
@@ -1,3 +1,126 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
|
||||
</template>
|
||||
<pw-section class="blue" label="Theme">
|
||||
<ul>
|
||||
<li>
|
||||
<h3>Background</h3>
|
||||
<div class="backgrounds">
|
||||
<span v-for="theme in themes" :key="theme.class" @click="applyTheme(theme.class)">
|
||||
<swatch :color="theme.color" :name="theme.name" :class="{ vibrant: theme.vibrant }" :active="settings.THEME_CLASS === theme.class"></swatch>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<br><br>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
<h3>Color</h3>
|
||||
<div class="colors">
|
||||
<span v-for="entry in colors" :key="entry.color"
|
||||
@click.prevent="setActiveColor(entry.color, entry.vibrant)">
|
||||
<swatch
|
||||
:color="entry.color"
|
||||
:name="entry.name"
|
||||
:class="{ vibrant: entry.vibrant }"
|
||||
:active="settings.THEME_COLOR === entry.color.toUpperCase()" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<input id="disableFrameColors" type="checkbox"
|
||||
:checked="!settings.DISABLE_FRAME_COLORS"
|
||||
@change="toggleSetting('DISABLE_FRAME_COLORS')">
|
||||
<label for="disableFrameColors">Enable multi-colored frames</label>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import section from "../components/section";
|
||||
import swatch from "../components/settings/swatch";
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
// NOTE:: You need to first set the CSS for your theme in /assets/css/themes.scss
|
||||
// You should copy the existing light theme as a template and then just
|
||||
// set the relevant values.
|
||||
themes: [
|
||||
{ "color": "#121212", "name": "Dark (Default)", "class": "" },
|
||||
{ "color": "#DFDFDF", "name": "Light", "vibrant": true, "class": "light" }
|
||||
],
|
||||
|
||||
// You can define a new color here! It will simply store the color value.
|
||||
colors: [
|
||||
// If the color is vibrant, black is used as the active foreground color.
|
||||
{ "color": "#51ff0d", "name":"Lime (Default)", "vibrant": true },
|
||||
{ "color": "#FFC107", "name":"Yellow", "vibrant": true },
|
||||
{ "color": "#E91E63", "name":"Pink", "vibrant": false },
|
||||
{ "color": "#e74c3c", "name":"Red", "vibrant": false },
|
||||
{ "color": "#9b59b6", "name":"Purple", "vibrant": false },
|
||||
{ "color": "#2980b9", "name":"Blue", "vibrant": false },
|
||||
],
|
||||
settings: {
|
||||
THEME_CLASS: this.$store.state.postwoman.settings.THEME_CLASS || '',
|
||||
THEME_COLOR: '',
|
||||
THEME_COLOR_VIBRANT: true,
|
||||
|
||||
DISABLE_FRAME_COLORS: this.$store.state.postwoman.settings.DISABLE_FRAME_COLORS || false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
'pw-section': section,
|
||||
'swatch': swatch
|
||||
},
|
||||
|
||||
methods: {
|
||||
applyTheme (name) {
|
||||
this.applySetting('THEME_CLASS', name);
|
||||
document.documentElement.className = name;
|
||||
},
|
||||
|
||||
setActiveColor (color, vibrant) {
|
||||
// By default, the color is vibrant.
|
||||
if(vibrant == null) vibrant = true;
|
||||
|
||||
document.documentElement.style.setProperty('--ac-color', color);
|
||||
document.documentElement.style.setProperty('--act-color', vibrant ? '#121212' : '#fff');
|
||||
|
||||
this.applySetting('THEME_COLOR', color.toUpperCase());
|
||||
this.applySetting('THEME_COLOR_VIBRANT', vibrant);
|
||||
},
|
||||
|
||||
getActiveColor () {
|
||||
// This strips extra spaces and # signs from the strings.
|
||||
const strip = (str) => str.replace(/#/g, '').replace(/ /g, '');
|
||||
|
||||
return `#${strip(window.getComputedStyle(document.documentElement).getPropertyValue('--ac-color')).toUpperCase()}`;
|
||||
},
|
||||
|
||||
applySetting (key, value) {
|
||||
this.settings[key] = value;
|
||||
this.$store.commit('postwoman/applySetting', [key, value]);
|
||||
},
|
||||
|
||||
toggleSetting (key) {
|
||||
this.settings[key] = !this.settings[key];
|
||||
this.$store.commit('postwoman/applySetting', [key, this.settings[key]]);
|
||||
}
|
||||
},
|
||||
|
||||
beforeMount () {
|
||||
this.settings.THEME_COLOR = this.getActiveColor();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,50 +1,44 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
|
||||
<fieldset class="request" ref="request">
|
||||
<legend v-on:click="collapse">Request ↕</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
<input type="url" :class="{ error: !urlValid }" v-model="url" @keyup.enter="toggleConnection">
|
||||
</li>
|
||||
<li class="no-grow">
|
||||
<label for="action"> </label>
|
||||
<button class="action" :class="{ disabled: !urlValid }" name="action" @click="toggleConnection">{{ toggleConnectionVerb }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<pw-section class="blue" label="Request" ref="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
<input type="url" :class="{ error: !urlValid }" v-model="url" @keyup.enter="toggleConnection">
|
||||
</li>
|
||||
<li class="no-grow">
|
||||
<label for="action"> </label>
|
||||
<button class="action" :class="{ disabled: !urlValid }" name="action" @click="toggleConnection">{{ toggleConnectionVerb }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<fieldset class="response" id="response" ref="response">
|
||||
<legend v-on:click="collapse">Communication ↕</legend>
|
||||
<div class="collapsible">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="body">Log</label>
|
||||
<div name="body" class="body" readonly>
|
||||
<pw-section class="purple" label="Communication" id="response" ref="response">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="body">Log</label>
|
||||
<div name="body" class="body" readonly>
|
||||
<span v-if="communication.log">
|
||||
<span v-for="logEntry in communication.log" :style="{ color: logEntry.color }">{{ getSourcePrefix(logEntry.source) }} {{ logEntry.payload }}</span>
|
||||
</span>
|
||||
<span v-else>(Waiting for connection...)</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<span v-else>(Waiting for connection...)</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label for="status">Message</label>
|
||||
<input name="status" type="text" v-model="communication.input" :readonly="!connectionState" @keyup.enter="sendMessage">
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="status">Message</label>
|
||||
<input name="status" type="text" v-model="communication.input" :readonly="!connectionState" @keyup.enter="sendMessage">
|
||||
</li>
|
||||
|
||||
<li class="no-grow">
|
||||
<label for="send"> </label>
|
||||
<button class="action" name="send" :class="{ disabled: !connectionState }" @click="sendMessage">Send</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<li class="no-grow">
|
||||
<label for="send"> </label>
|
||||
<button class="action" name="send" :class="{ disabled: !connectionState }" @click="sendMessage">Send</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -61,7 +55,7 @@
|
||||
padding: 8px 16px;
|
||||
width: calc(100% - 8px);
|
||||
border-radius: 4px;
|
||||
background-color: #000;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
height: 300px;
|
||||
overflow: auto;
|
||||
@@ -80,146 +74,152 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import section from "../components/section";
|
||||
|
||||
export default {
|
||||
|
||||
data () {
|
||||
return {
|
||||
connectionState: false,
|
||||
url: "wss://echo.websocket.org",
|
||||
socket: null,
|
||||
components: {
|
||||
'pw-section': section
|
||||
},
|
||||
|
||||
communication: {
|
||||
log: null,
|
||||
input: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
connectionState: false,
|
||||
url: "wss://echo.websocket.org",
|
||||
socket: null,
|
||||
|
||||
computed: {
|
||||
toggleConnectionVerb () {
|
||||
return !this.connectionState ? "Connect" : "Disconnect";
|
||||
},
|
||||
|
||||
urlValid () {
|
||||
const pattern = new RegExp('^(wss?:\\/\\/)?' +
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' +
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
|
||||
'(\\?[;&a-z\\d%_.~+=-]*)?' +
|
||||
'(\\#[-a-z\\d_]*)?$', 'i');
|
||||
return pattern.test(this.url);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleConnection () {
|
||||
// If it is connecting:
|
||||
if(!this.connectionState) return this.connect();
|
||||
|
||||
// Otherwise, it's disconnecting.
|
||||
else return this.disconnect();
|
||||
},
|
||||
|
||||
connect () {
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: `Connecting to ${this.url}...`,
|
||||
source: 'info',
|
||||
color: 'lime'
|
||||
}
|
||||
];
|
||||
|
||||
try {
|
||||
this.socket = new WebSocket(this.url);
|
||||
|
||||
this.socket.onopen = (event) => {
|
||||
this.connectionState = true;
|
||||
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: `Connected to ${this.url}.`,
|
||||
source: 'info',
|
||||
color: 'lime'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
this.socket.onerror = (event) => {
|
||||
this.handleError();
|
||||
};
|
||||
|
||||
this.socket.onclose = (event) => {
|
||||
this.connectionState = false;
|
||||
|
||||
this.communication.log.push({
|
||||
payload: `Disconnected from ${this.url}.`,
|
||||
source: 'info',
|
||||
color: 'red'
|
||||
});
|
||||
};
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
this.communication.log.push({
|
||||
payload: event.data,
|
||||
source: 'server'
|
||||
});
|
||||
}
|
||||
}catch(ex){
|
||||
this.handleError(ex);
|
||||
}
|
||||
},
|
||||
|
||||
disconnect () {
|
||||
if(this.socket != null) this.socket.close();
|
||||
},
|
||||
|
||||
handleError (error) {
|
||||
this.disconnect();
|
||||
this.connectionState = false;
|
||||
|
||||
this.communication.log.push({
|
||||
payload: `An error has occurred.`,
|
||||
source: 'info',
|
||||
color: 'red'
|
||||
});
|
||||
if(error != null) this.communication.log.push({
|
||||
payload: error,
|
||||
source: 'info',
|
||||
color: 'red'
|
||||
});
|
||||
},
|
||||
|
||||
sendMessage () {
|
||||
const message = this.communication.input;
|
||||
|
||||
this.socket.send(message);
|
||||
this.communication.log.push({
|
||||
payload: message,
|
||||
source: 'client'
|
||||
});
|
||||
|
||||
this.communication.input = "";
|
||||
},
|
||||
|
||||
collapse({target}) {
|
||||
const el = target.parentNode.className;
|
||||
document.getElementsByClassName(el)[0].classList.toggle('hidden');
|
||||
},
|
||||
|
||||
getSourcePrefix(source){
|
||||
const sourceEmojis = {
|
||||
// Source used for info messages.
|
||||
'info': 'ℹ️ [INFO]:\t',
|
||||
// Source used for client to server messages.
|
||||
'client': '👽 [SENT]:\t',
|
||||
// Source used for server to client messages.
|
||||
'server': '📥 [RECEIVED]:\t'
|
||||
};
|
||||
|
||||
if (Object.keys(sourceEmojis).includes(source)) return sourceEmojis[source];
|
||||
return '';
|
||||
communication: {
|
||||
log: null,
|
||||
input: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
toggleConnectionVerb () {
|
||||
return !this.connectionState ? "Connect" : "Disconnect";
|
||||
},
|
||||
|
||||
urlValid () {
|
||||
const pattern = new RegExp('^(wss?:\\/\\/)?' +
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' +
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
|
||||
'(\\?[;&a-z\\d%_.~+=-]*)?' +
|
||||
'(\\#[-a-z\\d_]*)?$', 'i');
|
||||
return pattern.test(this.url);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleConnection () {
|
||||
// If it is connecting:
|
||||
if(!this.connectionState) return this.connect();
|
||||
|
||||
// Otherwise, it's disconnecting.
|
||||
else return this.disconnect();
|
||||
},
|
||||
|
||||
connect () {
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: `Connecting to ${this.url}...`,
|
||||
source: 'info',
|
||||
color: 'lime'
|
||||
}
|
||||
];
|
||||
|
||||
try {
|
||||
this.socket = new WebSocket(this.url);
|
||||
|
||||
this.socket.onopen = (event) => {
|
||||
this.connectionState = true;
|
||||
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: `Connected to ${this.url}.`,
|
||||
source: 'info',
|
||||
color: 'lime'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
this.socket.onerror = (event) => {
|
||||
this.handleError();
|
||||
};
|
||||
|
||||
this.socket.onclose = (event) => {
|
||||
this.connectionState = false;
|
||||
|
||||
this.communication.log.push({
|
||||
payload: `Disconnected from ${this.url}.`,
|
||||
source: 'info',
|
||||
color: 'red'
|
||||
});
|
||||
};
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
this.communication.log.push({
|
||||
payload: event.data,
|
||||
source: 'server'
|
||||
});
|
||||
}
|
||||
}catch(ex){
|
||||
this.handleError(ex);
|
||||
}
|
||||
},
|
||||
|
||||
disconnect () {
|
||||
if(this.socket != null) this.socket.close();
|
||||
},
|
||||
|
||||
handleError (error) {
|
||||
this.disconnect();
|
||||
this.connectionState = false;
|
||||
|
||||
this.communication.log.push({
|
||||
payload: `An error has occurred.`,
|
||||
source: 'info',
|
||||
color: 'red'
|
||||
});
|
||||
if(error != null) this.communication.log.push({
|
||||
payload: error,
|
||||
source: 'info',
|
||||
color: 'red'
|
||||
});
|
||||
},
|
||||
|
||||
sendMessage () {
|
||||
const message = this.communication.input;
|
||||
|
||||
this.socket.send(message);
|
||||
this.communication.log.push({
|
||||
payload: message,
|
||||
source: 'client'
|
||||
});
|
||||
|
||||
this.communication.input = "";
|
||||
},
|
||||
|
||||
collapse({target}) {
|
||||
const el = target.parentNode.className;
|
||||
document.getElementsByClassName(el)[0].classList.toggle('hidden');
|
||||
},
|
||||
|
||||
getSourcePrefix(source){
|
||||
const sourceEmojis = {
|
||||
// Source used for info messages.
|
||||
'info': 'ℹ️ [INFO]:\t',
|
||||
// Source used for client to server messages.
|
||||
'client': '👽 [SENT]:\t',
|
||||
// Source used for server to client messages.
|
||||
'server': '📥 [RECEIVED]:\t'
|
||||
};
|
||||
|
||||
if (Object.keys(sourceEmojis).includes(source)) return sourceEmojis[source];
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user