diff --git a/html/src/app.js b/html/src/app.js
index d07889eb..4e47e273 100644
--- a/html/src/app.js
+++ b/html/src/app.js
@@ -5814,39 +5814,44 @@ speechSynthesis.getVoices();
order: 'descending'
}
},
- pageSize: 10,
- paginationProps: {
- small: true,
- layout: 'sizes,prev,pager,next,total',
- pageSizes: [
- 10,
- 25,
- 50,
- 100
- ]
- }
+ layout: 'table'
};
$app.data.VRCPlusIconsTable = {};
$app.data.inviteMessageTable = {
+ visible: false,
data: [],
tableProps: {
stripe: true,
size: 'mini'
- }
+ },
+ layout: 'table'
};
$app.data.inviteResponseMessageTable = {
+ visible: false,
data: [],
tableProps: {
stripe: true,
size: 'mini'
- }
+ },
+ layout: 'table'
};
$app.data.inviteRequestMessageTable = {
+ visible: false,
data: [],
tableProps: {
stripe: true,
size: 'mini'
- }
+ },
+ layout: 'table'
+ };
+ $app.data.inviteRequestResponseMessageTable = {
+ visible: false,
+ data: [],
+ tableProps: {
+ stripe: true,
+ size: 'mini'
+ },
+ layout: 'table'
};
$app.data.visits = 0;
$app.data.openVR = configRepository.getBool('openVR');
@@ -6911,29 +6916,6 @@ speechSynthesis.getVoices();
this.showBioDialog();
} else if (command === 'Logout') {
this.logout();
- } else if (command === 'Message') {
- this.$prompt('Enter a message', 'Send Message', {
- distinguishCancelAndClose: true,
- confirmButtonText: 'Send',
- cancelButtonText: 'Cancel',
- inputPattern: /\S+/,
- inputErrorMessage: 'Message is required',
- callback: (action, instance) => {
- if (action === 'confirm' &&
- instance.inputValue) {
- API.sendNotification({
- receiverUserId: D.id,
- type: 'message',
- message: instance.inputValue,
- seen: false,
- details: '{}'
- }).then((args) => {
- this.$message('Message sent');
- return args;
- });
- }
- }
- });
} else if (command === 'Request Invite') {
API.sendNotification({
receiverUserId: D.id,
@@ -8123,7 +8105,8 @@ speechSynthesis.getVoices();
method: 'DELETE'
}).then((json) => {
var args = {
- json
+ json,
+ userIcon
};
return args;
});
@@ -8198,14 +8181,24 @@ speechSynthesis.getVoices();
$app.inviteMessageTable.data = [];
$app.inviteResponseMessageTable.data = [];
$app.inviteRequestMessageTable.data = [];
+ $app.inviteRequestResponseMessageTable.data = [];
+ $app.inviteMessageTable.visible = false;
+ $app.inviteResponseMessageTable.visible = false;
+ $app.inviteRequestMessageTable.visible = false;
+ $app.inviteRequestResponseMessageTable.visible = false;
});
+ $app.methods.refreshInviteMessageTable = function (messageType) {
+ API.refreshInviteMessageTableData(messageType);
+ }
+
API.refreshInviteMessageTableData = function (messageType) {
return this.call(`message/${this.currentUser.id}/${messageType}`, {
method: 'GET'
}).then((json) => {
var args = {
- json
+ json,
+ messageType
};
this.$emit(`INVITE:${messageType.toUpperCase()}`, args);
return args;
@@ -8224,6 +8217,10 @@ speechSynthesis.getVoices();
$app.inviteRequestMessageTable.data = args.json;
});
+ API.$on('INVITE:REQUESTRESPONSE', function (args) {
+ $app.inviteRequestResponseMessageTable.data = args.json;
+ });
+
API.editInviteMessage = function (params, messageType, slot) {
return this.call(`message/${this.currentUser.id}/${messageType}/${slot}`, {
method: 'PUT',
@@ -8231,7 +8228,9 @@ speechSynthesis.getVoices();
}).then((json) => {
var args = {
json,
- params
+ params,
+ messageType,
+ slot
};
return args;
});
@@ -8240,19 +8239,22 @@ speechSynthesis.getVoices();
// App: Edit Invite Message Dialog
$app.data.editInviteMessageDialog = {
- visible: false
+ visible: false,
+ inviteMessage: '',
+ messageType: '',
+ newMessage: ''
};
$app.methods.showEditInviteMessageDialog = function (messageType, inviteMessage) {
this.$nextTick(() => adjustDialogZ(this.$refs.editInviteMessageDialog.$el));
var D = this.editInviteMessageDialog;
- //D.newMessage = inviteMessage.message;
+ D.newMessage = inviteMessage.message;
D.visible = true;
D.inviteMessage = inviteMessage;
D.messageType = messageType;
};
- $app.methods.saveInviteMessage = function () {
+ $app.methods.saveEditInviteMessage = function () {
var D = this.editInviteMessageDialog;
D.visible = false;
if (D.inviteMessage.message !== D.newMessage) {
@@ -8273,13 +8275,189 @@ speechSynthesis.getVoices();
API.refreshInviteMessageTableData(messageType);
});
}
- //D.newMessage = '';
};
- $app.methods.cancelInviteMessage = function () {
- var D = this.editInviteMessageDialog;
+ $app.methods.cancelEditInviteMessage = function () {
+ this.editInviteMessageDialog.visible = false;
+ };
+
+ // App: Edit and Send Invite Response Message Dialog
+
+ $app.data.editAndSendInviteResponseDialog = {
+ visible: false,
+ inviteMessage: '',
+ messageType: '',
+ newMessage: ''
+ };
+
+ $app.methods.showEditAndSendInviteResponseDialog = function (messageType, inviteMessage) {
+ this.$nextTick(() => adjustDialogZ(this.$refs.editAndSendInviteResponseDialog.$el));
+ this.editAndSendInviteResponseDialog = {
+ newMessage: inviteMessage.message,
+ visible: true,
+ messageType,
+ inviteMessage
+ };
+ };
+
+ $app.methods.saveEditAndSendInviteResponse = async function () {
+ var D = this.editAndSendInviteResponseDialog;
D.visible = false;
- //D.newMessage = '';
+ var messageType = D.messageType;
+ var slot = D.inviteMessage.slot;
+ if (D.inviteMessage.message !== D.newMessage) {
+ var params = {
+ message: D.newMessage
+ };
+ await API.editInviteMessage(params, messageType, slot).catch((err) => {
+ throw err;
+ }).then((args) => {
+ this.$message({
+ message: 'Invite message updated',
+ type: 'success'
+ });
+ return args;
+ });
+ await API.refreshInviteMessageTableData(messageType).catch((err) => {
+ throw err;
+ }).then((args) => {
+ if (args.json[slot].message !== D.newMessage) {
+ this.$message({
+ message: 'VRC API didn\'t update message, try again',
+ type: 'error'
+ });
+ throw 'VRC API didn\'t update message, try again';
+ }
+ });
+ }
+
+ var I = this.sendInviteResponseDialog;
+ var params = {
+ responseSlot: slot,
+ rsvp: true
+ };
+ API.sendInviteResponse(params, I.invite.id).catch((err) => {
+ D.visible = false;
+ throw err;
+ }).then((args) => {
+ API.hideNotification({
+ notificationId: I.invite.id
+ });
+ this.$message({
+ message: 'Invite response message sent',
+ type: 'success'
+ });
+ D.visible = false;
+ this.sendInviteResponseDialogVisible = false;
+ this.sendInviteRequestResponseDialogVisible = false;
+ return args;
+ });
+ };
+
+ $app.methods.cancelEditAndSendInviteResponse = function () {
+ this.editAndSendInviteResponseDialog.visible = false;
+ };
+
+ $app.data.sendInviteResponseDialog = {
+ message: '',
+ invite: ''
+ };
+
+ $app.data.sendInviteResponseDialogVisible = false;
+
+ $app.data.sendInviteResponseConfirmDialog = {
+ visible: false
+ };
+
+ API.$on('LOGIN', function () {
+ $app.sendInviteResponseDialogVisible = false;
+ $app.sendInviteResponseConfirmDialog.visible = false;
+ });
+
+ $app.methods.showSendInviteResponseDialog = function (invite) {
+ this.sendInviteResponseDialog = {
+ invite
+ };
+ API.refreshInviteMessageTableData('response');
+ this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteResponseDialog.$el));
+ this.sendInviteResponseDialogVisible = true;
+ };
+
+ $app.methods.showSendInviteResponseConfirmDialog = function (val) {
+ if (this.editAndSendInviteResponseDialog.visible === true || val === null) {
+ return;
+ }
+ this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteResponseConfirmDialog.$el));
+ this.sendInviteResponseConfirmDialog.visible = true;
+ this.sendInviteResponseDialog.messageSlot = val.slot;
+ };
+
+ $app.methods.cancelSendInviteResponse = function () {
+ this.sendInviteResponseDialogVisible = false;
+ };
+
+ $app.methods.cancelInviteResponseConfirm = function () {
+ this.sendInviteResponseConfirmDialog.visible = false;
+ };
+
+ $app.methods.sendInviteResponseConfirm = function () {
+ var D = this.sendInviteResponseDialog;
+ var params = {
+ responseSlot: D.messageSlot,
+ rsvp: true
+ };
+ API.sendInviteResponse(params, D.invite.id, D.messageType).catch((err) => {
+ throw err;
+ }).then((args) => {
+ API.hideNotification({
+ notificationId: D.invite.id
+ });
+ this.$message({
+ message: 'Invite response message sent',
+ type: 'success'
+ });
+ return args;
+ });
+ this.sendInviteResponseDialogVisible = false;
+ this.sendInviteRequestResponseDialogVisible = false;
+ this.sendInviteResponseConfirmDialog.visible = false;
+ };
+
+ API.sendInviteResponse = function (params, inviteID) {
+ return this.call(`invite/${inviteID}/response`, {
+ method: 'POST',
+ params
+ }).then((json) => {
+ var args = {
+ json,
+ params,
+ inviteID
+ };
+ this.$emit('INVITE:RESPONSE:SEND', args);
+ return args;
+ });
+ };
+
+ // App: Invite Request Response Message Dialog
+
+ $app.data.sendInviteRequestResponseDialogVisible = false;
+
+ $app.methods.cancelSendInviteRequestResponse = function () {
+ this.sendInviteRequestResponseDialogVisible = false;
+ };
+
+ API.$on('LOGIN', function () {
+ $app.sendInviteRequestResponseDialogVisible = false;
+ $app.showSendInviteResponseConfirmDialog.visible = false;
+ });
+
+ $app.methods.showSendInviteRequestResponseDialog = function (invite) {
+ this.sendInviteResponseDialog = {
+ invite
+ };
+ API.refreshInviteMessageTableData('requestResponse');
+ this.$nextTick(() => adjustDialogZ(this.$refs.sendInviteRequestResponseDialog.$el));
+ this.sendInviteRequestResponseDialogVisible = true;
};
$app = new Vue($app);
diff --git a/html/src/index.pug b/html/src/index.pug
index b62c2d34..9c02bfe5 100644
--- a/html/src/index.pug
+++ b/html/src/index.pug
@@ -406,6 +406,8 @@ html
el-table-column(label="Action" width="80" align="right")
template(v-once #default="scope")
el-button(v-if="scope.row.type === 'friendRequest'" type="text" icon="el-icon-check" size="mini" @click="acceptNotification(scope.row)")
+ el-button(v-else-if="scope.row.type === 'invite'" type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteResponseDialog(scope.row)")
+ el-button(v-else-if="scope.row.type === 'requestInvite'" type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteRequestResponseDialog(scope.row)")
el-button(type="text" icon="el-icon-close" size="mini" @click="hideNotification(scope.row)")
//- profile
@@ -446,17 +448,17 @@ html
el-button(size="small" @click="promptUserDialog()") User
el-button(size="small" @click="promptWorldDialog()") World
el-button(size="small" @click="promptAvatarDialog()") Avatar
- div.options-container(style="margin-top:30px")
+ div.options-container
span.header Invite Messages
el-tooltip(placement="top")
template(#content)
span Refresh
- el-button(type="default" @click="API.refreshInviteMessageTableData('message')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
+ el-button(type="default" @click="inviteMessageTable.visible = true; refreshInviteMessageTable('message')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top")
template(#content)
span Clear results
- el-button(type="default" @click="inviteMessageTable.data = []" size="mini" icon="el-icon-delete" circle style="margin-left:0")
- data-tables(v-if="inviteMessageTable.data.length > 0" v-bind="inviteMessageTable" style="margin-top:10px")
+ el-button(type="default" @click="inviteMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:0")
+ data-tables(v-if="inviteMessageTable.visible" v-bind="inviteMessageTable" style="margin-top:10px")
el-table-column(label="Slot" prop="slot" sortable="custom" width="70")
el-table-column(label="Message" prop="message")
el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right")
@@ -465,17 +467,17 @@ html
el-table-column(label="Action" width="60" align="right")
template(v-once #default="scope")
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('message', scope.row)")
- div.options-container(style="margin-top:30px")
+ div.options-container
span.header Invite Response Messages
el-tooltip(placement="top")
template(#content)
span Refresh
- el-button(type="default" @click="API.refreshInviteMessageTableData('response')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
+ el-button(type="default" @click="inviteResponseMessageTable.visible = true; refreshInviteMessageTable('response')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top")
template(#content)
span Clear results
- el-button(type="default" @click="inviteResponseMessageTable.data = []" size="mini" icon="el-icon-delete" circle style="margin-left:0")
- data-tables(v-if="inviteResponseMessageTable.data.length > 0" v-bind="inviteResponseMessageTable" style="margin-top:10px")
+ el-button(type="default" @click="inviteResponseMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:0")
+ data-tables(v-if="inviteResponseMessageTable.visible" v-bind="inviteResponseMessageTable" style="margin-top:10px")
el-table-column(label="Slot" prop="slot" sortable="custom" width="70")
el-table-column(label="Message" prop="message")
el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right")
@@ -484,17 +486,17 @@ html
el-table-column(label="Action" width="60" align="right")
template(v-once #default="scope")
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('response', scope.row)")
- div.options-container(style="margin-top:30px")
+ div.options-container
span.header Invite Request Messages
el-tooltip(placement="top")
template(#content)
span Refresh
- el-button(type="default" @click="API.refreshInviteMessageTableData('request')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
+ el-button(type="default" @click="inviteRequestMessageTable.visible = true; refreshInviteMessageTable('request')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
el-tooltip(placement="top")
template(#content)
span Clear results
- el-button(type="default" @click="inviteRequestMessageTable.data = []" size="mini" icon="el-icon-delete" circle style="margin-left:0")
- data-tables(v-if="inviteRequestMessageTable.data.length > 0" v-bind="inviteRequestMessageTable" style="margin-top:10px")
+ el-button(type="default" @click="inviteRequestMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:0")
+ data-tables(v-if="inviteRequestMessageTable.visible" v-bind="inviteRequestMessageTable" style="margin-top:10px")
el-table-column(label="Slot" prop="slot" sortable="custom" width="70")
el-table-column(label="Message" prop="message")
el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right")
@@ -503,7 +505,26 @@ html
el-table-column(label="Action" width="60" align="right")
template(v-once #default="scope")
el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('request', scope.row)")
- div.options-container(v-if="API.currentUser.$isVRCPlus" style="margin-top:30px")
+ div.options-container
+ span.header Invite Request Response Messages
+ el-tooltip(placement="top")
+ template(#content)
+ span Refresh
+ el-button(type="default" @click="inviteRequestResponseMessageTable.visible = true; refreshInviteMessageTable('requestResponse')" size="mini" icon="el-icon-refresh" circle style="margin-left:5px")
+ el-tooltip(placement="top")
+ template(#content)
+ span Clear results
+ el-button(type="default" @click="inviteRequestResponseMessageTable.visible = false" size="mini" icon="el-icon-delete" circle style="margin-left:0")
+ data-tables(v-if="inviteRequestResponseMessageTable.visible" v-bind="inviteRequestResponseMessageTable" style="margin-top:10px")
+ el-table-column(label="Slot" prop="slot" sortable="custom" width="70")
+ el-table-column(label="Message" prop="message")
+ el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right")
+ template(v-once #default="scope")
+ countdown-timer(:datetime="scope.row.updatedAt" :hours="1")
+ el-table-column(label="Action" width="60" align="right")
+ template(v-once #default="scope")
+ el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditInviteMessageDialog('requestResponse', scope.row)")
+ div.options-container
span.header VRCPlus Icons
el-tooltip(placement="top")
template(#content)
@@ -543,7 +564,7 @@ html
template(#content)
span Clear results
el-button(type="default" @click="configTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:0")
- el-tree(:data="configTreeData" style="margin-top:10px;font-size:12px")
+ el-tree(v-if="configTreeData.length > 0" :data="configTreeData" style="margin-top:10px;font-size:12px")
template(#default="scope")
span
span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px")
@@ -558,7 +579,7 @@ html
template(#content)
span Clear results
el-button(type="default" @click="currentUserTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:0")
- el-tree(:data="currentUserTreeData" style="margin-top:10px;font-size:12px")
+ el-tree(v-if="currentUserTreeData.length > 0" :data="currentUserTreeData" style="margin-top:10px;font-size:12px")
template(#default="scope")
span
span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px")
@@ -1452,11 +1473,58 @@ html
//- dialog: Edit Invite Message
el-dialog.x-dialog(ref="editInviteMessageDialog" :visible.sync="editInviteMessageDialog.visible" title="Edit Invite Message" width="400px")
div(style='font-size:12px')
- span Edit cool down time 1 hour.
+ span 1 hour edit cool down time.
el-input(type="textarea" v-model="editInviteMessageDialog.newMessage" size="mini" maxlength="64" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
template(#footer)
- el-button(type="small" @click="cancelInviteMessage") Cancel
- el-button(type="primary" size="small" @click="saveInviteMessage") Save
+ el-button(type="small" @click="cancelEditInviteMessage") Cancel
+ el-button(type="primary" size="small" @click="saveEditInviteMessage") Save
+
+ //- dialog: Edit And Send Invite Response Message
+ el-dialog.x-dialog(ref="editAndSendInviteResponseDialog" :visible.sync="editAndSendInviteResponseDialog.visible" title="Edit and Send Invite Message" width="400px")
+ div(style='font-size:12px')
+ span 1 hour edit cool down time.
+ el-input(type="textarea" v-model="editAndSendInviteResponseDialog.newMessage" size="mini" maxlength="64" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px")
+ template(#footer)
+ el-button(type="small" @click="cancelEditAndSendInviteResponse") Cancel
+ el-button(type="primary" size="small" @click="saveEditAndSendInviteResponse") Send
+
+ //- dialog Table: Send Invite Response Message
+ el-dialog.x-dialog(ref="sendInviteResponseDialog" :visible.sync="sendInviteResponseDialogVisible" title="Send Invite Response Message" width="800px")
+ data-tables(v-bind="inviteResponseMessageTable" @current-change="showSendInviteResponseConfirmDialog" style="margin-top:10px;cursor:pointer")
+ el-table-column(label="Slot" prop="slot" sortable="custom" width="70")
+ el-table-column(label="Message" prop="message")
+ el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right")
+ template(v-once #default="scope")
+ countdown-timer(:datetime="scope.row.updatedAt" :hours="1")
+ el-table-column(label="Action" width="60" align="right")
+ template(v-once #default="scope")
+ el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditAndSendInviteResponseDialog('response', scope.row)")
+ template(#footer)
+ el-button(type="small" @click="API.refreshInviteMessageTableData('response')") Refresh
+ el-button(type="small" @click="cancelSendInviteResponse") Cancel
+
+ //- dialog Table: Send Invite Response Message
+ el-dialog.x-dialog(ref="sendInviteRequestResponseDialog" :visible.sync="sendInviteRequestResponseDialogVisible" title="Send Invite Request Response Message" width="800px")
+ data-tables(v-bind="inviteRequestResponseMessageTable" @current-change="showSendInviteResponseConfirmDialog" style="margin-top:10px;cursor:pointer")
+ el-table-column(label="Slot" prop="slot" sortable="custom" width="70")
+ el-table-column(label="Message" prop="message")
+ el-table-column(label="Cool Down" prop="updatedAt" sortable="custom" width="110" align="right")
+ template(v-once #default="scope")
+ countdown-timer(:datetime="scope.row.updatedAt" :hours="1")
+ el-table-column(label="Action" width="60" align="right")
+ template(v-once #default="scope")
+ el-button(type="text" icon="el-icon-edit" size="mini" @click="showEditAndSendInviteResponseDialog('requestResponse', scope.row)")
+ template(#footer)
+ el-button(type="small" @click="API.refreshInviteMessageTableData('requestResponse')") Refresh
+ el-button(type="small" @click="cancelSendInviteRequestResponse") Cancel
+
+ //- dialog: Send Invite Message Confirm
+ el-dialog.x-dialog(ref="sendInviteResponseConfirmDialog" :visible.sync="sendInviteResponseConfirmDialog.visible" title="Send Invite Message" width="400px")
+ div(style='font-size:12px')
+ span Are you sure you want to send?
+ template(#footer)
+ el-button(type="small" @click="cancelInviteResponseConfirm") Cancel
+ el-button(type="primary" size="small" @click="sendInviteResponseConfirm") Confirm
//- dialog: open source software notice
el-dialog.x-dialog(:visible.sync="ossDialog" title="Open Source Software Notice" width="650px")
@@ -1740,6 +1808,30 @@ html
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
div(style="margin-top:15px")
+ p(style="font-weight:bold") vue-swatches
+ pre(style="font-size:12px;white-space:pre-line").
+ MIT License
+
+ Copyright (c) 2018 - Present Diego Jara
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ div(style="margin-top:15px")
p(style="font-weight:bold") vuejs-toggle-switch
pre(style="font-size:12px;white-space:pre-line").
MIT License