diff --git a/App.xaml b/App.xaml deleted file mode 100644 index 7ab24189..00000000 --- a/App.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/App.xaml.cs b/App.xaml.cs deleted file mode 100644 index 6e94f65a..00000000 --- a/App.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; - -namespace VRCX -{ - public partial class App : Application - { - } -} diff --git a/AppApi.cs b/AppApi.cs index 713c52a6..70b63a08 100644 --- a/AppApi.cs +++ b/AppApi.cs @@ -15,7 +15,6 @@ using System.IO; using System.Net; using Windows.UI.Notifications; using Windows.Data.Xml.Dom; -using System.Windows; namespace VRCX { @@ -28,59 +27,9 @@ namespace VRCX Instance = new AppApi(); } - public void CloseMainWindow() - { - try - { - MainWindow.Instance.Dispatcher.BeginInvoke(new MethodInvoker(() => - { - MainWindow.Instance.Close(); - })); - } - catch - { - } - } - - public void MinimizeMainWindow() - { - try - { - MainWindow.Instance.Dispatcher.BeginInvoke(new MethodInvoker(() => - { - MainWindow.Instance.WindowState = WindowState.Minimized; - })); - } - catch - { - } - } - - public void ToggleMaximizeMainWindow() - { - try - { - MainWindow.Instance.Dispatcher.BeginInvoke(new MethodInvoker(() => - { - var mainWindow = MainWindow.Instance; - if (mainWindow.WindowState == WindowState.Maximized) - { - mainWindow.WindowState = WindowState.Normal; - } - else - { - mainWindow.WindowState = WindowState.Maximized; - } - })); - } - catch - { - } - } - public void ShowDevTools() { - MainWindow.Instance.Browser.ShowDevTools(); + MainForm.Instance.Browser.ShowDevTools(); } public void DeleteAllCookies() @@ -196,13 +145,13 @@ namespace VRCX { try { - /*MainForm.Instance.BeginInvoke(new MethodInvoker(() => + MainForm.Instance.BeginInvoke(new MethodInvoker(() => { if (VRForm.Instance == null) { new VRForm().Show(); } - }));*/ + })); } catch { @@ -266,7 +215,7 @@ namespace VRCX { if (enabled == true) { - var path = System.Reflection.Assembly.GetExecutingAssembly().Location; + var path = Application.ExecutablePath; key.SetValue("VRCX", $"\"{path}\" --startup"); } else diff --git a/CefService.cs b/CefService.cs index 12c50895..e999767f 100644 --- a/CefService.cs +++ b/CefService.cs @@ -1,5 +1,5 @@ using CefSharp; -using CefSharp.Wpf; +using CefSharp.WinForms; using System; using System.IO; @@ -16,8 +16,6 @@ namespace VRCX internal void Init() { - AppContext.SetSwitch("Switch.System.Windows.Input.Stylus.EnablePointerSupport", true); - var cefSettings = new CefSettings { CachePath = Path.Combine(Program.BaseDirectory, "cache"), @@ -36,7 +34,6 @@ namespace VRCX SchemeHandlerFactory = new FolderSchemeHandlerFactory(Application.StartupPath + "/../../../html") });*/ - cefSettings.CefCommandLineArgs.Add("disable-threaded-scrolling"); cefSettings.CefCommandLineArgs.Add("ignore-certificate-errors"); cefSettings.CefCommandLineArgs.Add("disable-plugins"); cefSettings.CefCommandLineArgs.Add("disable-spell-checking"); diff --git a/DragHandler.cs b/DragHandler.cs deleted file mode 100644 index 1cff1afb..00000000 --- a/DragHandler.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright(c) 2019 pypy. All rights reserved. -// -// This work is licensed under the terms of the MIT license. -// For a copy, see . - -using System; -using System.Collections.Generic; -using System.Drawing; -using CefSharp; -using CefSharp.Enums; - -namespace VRCX -{ - public class DragHandler : IDragHandler - { - public event Action RegionsChanged; - - public void Dispose() - { - RegionsChanged = null; - } - - bool IDragHandler.OnDragEnter(IWebBrowser chromiumWebBrowser, IBrowser browser, IDragData dragData, DragOperationsMask mask) - { - return false; - } - - void IDragHandler.OnDraggableRegionsChanged(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IList regions) - { - //By default popup browers are native windows in WPF so we cannot handle their drag using this method - if (browser.IsPopup == false) - { - //NOTE: I haven't tested with dynamically adding removing regions so this may need some tweaking - Region draggableRegion = null; - - if (regions.Count > 0) - { - //Take the individual Region and construct a complex Region that represents them all. - foreach (var region in regions) - { - var rect = new Rectangle(region.X, region.Y, region.Width, region.Height); - - if (draggableRegion == null) - { - draggableRegion = new Region(rect); - } - else - { - if (region.Draggable) - { - draggableRegion.Union(rect); - } - else - { - //In the scenario where we have an outer region, that is draggable and it has - // an inner region that's not, we must exclude the non draggable. - // Not all scenarios are covered in this example. - draggableRegion.Exclude(rect); - } - } - } - } - - var handler = RegionsChanged; - - if (handler != null) - { - handler(draggableRegion); - } - } - } - } -} diff --git a/MainWindow.xaml b/MainWindow.xaml deleted file mode 100644 index ff415ab7..00000000 --- a/MainWindow.xaml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs deleted file mode 100644 index bdea8491..00000000 --- a/MainWindow.xaml.cs +++ /dev/null @@ -1,244 +0,0 @@ -using CefSharp; -using CefSharp.Wpf; -using System; -using System.ComponentModel; -using System.Drawing; -using System.IO; -using System.Reflection; -using System.Windows; -using System.Windows.Forms; -using System.Windows.Input; - -namespace VRCX -{ - public partial class MainWindow : Window - { - public static MainWindow Instance; - public ChromiumWebBrowser Browser; - - // Store draggable region if we have one - used for hit testing - private Region Region_; - - private NotifyIcon NotifyIcon_; - private bool Quit_; - - private int LastLocationX; - private int LastLocationY; - private int LastSizeWidth; - private int LastSizeHeight; - - public MainWindow() - { - Instance = this; - InitializeComponent(); - DoTrayIcon(); - - var dragHandler = new DragHandler(); - dragHandler.RegionsChanged += (region) => - { - if (region != null) - { - // Only wire up event handler once - if (Region_ == null) - { - Browser.PreviewMouseLeftButtonDown += (sender, e) => - { - var point = e.GetPosition(Browser); - - if (Region_.IsVisible((float)point.X, (float)point.Y)) - { - var window = GetWindow(this); - window.DragMove(); - - e.Handled = true; - } - }; - } - - Region_ = region; - } - }; - - Browser = new ChromiumWebBrowser( - Path.Combine(Program.BaseDirectory, "html/index.html") - ) - { - DragHandler = dragHandler, - MenuHandler = new NoopMenuHandler(), - BrowserSettings = - { - DefaultEncoding = "UTF-8", - } - }; - - Browser.IsBrowserInitializedChanged += (A, B) => - { - if (Browser.IsDisposed == false) - { - // Browser.ShowDevTools(); - } - }; - - Util.ApplyJavascriptBindings(Browser.JavascriptObjectRepository); - - Content = Browser; - } - - - private void Window_Loaded(object sender, RoutedEventArgs e) - { - // restore last window location and size - try - { - int.TryParse(VRCXStorage.Instance.Get("VRCX_LocationX"), out LastLocationX); - int.TryParse(VRCXStorage.Instance.Get("VRCX_LocationY"), out LastLocationY); - int.TryParse(VRCXStorage.Instance.Get("VRCX_SizeWidth"), out LastSizeWidth); - int.TryParse(VRCXStorage.Instance.Get("VRCX_SizeHeight"), out LastSizeHeight); - var location = new System.Drawing.Point(LastLocationX, LastLocationY); - var size = new System.Drawing.Size(LastSizeWidth, LastSizeHeight); - var screen = Screen.FromPoint(location); - if (screen.Bounds.Contains(location.X, location.Y) == true) - { - Left = location.X; - Top = location.Y; - } - if (size.Width > 0 && size.Height > 0) - { - Width = size.Width; - Height = size.Height; - } - } - catch - { - } - - // restore last window state - try - { - var state = WindowState; - if ("true".Equals(VRCXStorage.Instance.Get("VRCX_StartAsMinimizedState"))) - { - state = WindowState.Minimized; - } - else - { - if (int.TryParse(VRCXStorage.Instance.Get("VRCX_WindowState"), out int v)) - { - state = (WindowState)v; - } - if (state == WindowState.Minimized) - { - state = WindowState.Normal; - } - } - WindowState = state; - } - catch - { - } - } - - private void Window_Closed(object sender, EventArgs e) - { - try - { - VRCXStorage.Instance.Set("VRCX_LocationX", LastLocationX.ToString()); - VRCXStorage.Instance.Set("VRCX_LocationY", LastLocationY.ToString()); - VRCXStorage.Instance.Set("VRCX_SizeWidth", LastSizeWidth.ToString()); - VRCXStorage.Instance.Set("VRCX_SizeHeight", LastSizeHeight.ToString()); - VRCXStorage.Instance.Set("VRCX_WindowState", ((int)WindowState).ToString()); - } - catch - { - } - - if (NotifyIcon_ != null) - { - NotifyIcon_.Visible = false; - } - - Content = null; - } - - private void DoTrayIcon() - { - var contextMenu = new ContextMenu(); - - contextMenu.MenuItems.Add("Open", (sender, e) => - { - if (WindowState == WindowState.Minimized) - { - WindowState = WindowState.Normal; - } - Show(); - Focus(); - }); - - contextMenu.MenuItems.Add("-"); - - contextMenu.MenuItems.Add("Quit VRCX", (sender, e) => - { - Quit_ = true; - Close(); - }); - - NotifyIcon_ = new NotifyIcon(); - - try - { - var location = Assembly.GetExecutingAssembly().Location; - NotifyIcon_.Icon = System.Drawing.Icon.ExtractAssociatedIcon(location); - } - catch - { - } - - NotifyIcon_.ContextMenu = contextMenu; - NotifyIcon_.Text = "VRCX"; - NotifyIcon_.DoubleClick += (sender, e) => - { - if (WindowState == WindowState.Minimized) - { - WindowState = WindowState.Normal; - } - Show(); - Focus(); - }; - NotifyIcon_.Visible = true; - } - - private void Window_Closing(object sender, CancelEventArgs e) - { - try - { - if (Quit_ == false && - "true".Equals(SharedVariable.Instance.Get("config:vrcx_closetotray")) == true) - { - e.Cancel = true; - Hide(); - } - } - catch - { - } - } - - private void Window_LocationChanged(object sender, EventArgs e) - { - if (WindowState == WindowState.Normal) - { - LastLocationX = (int)Left; - LastLocationY = (int)Top; - } - } - - private void Window_SizeChanged(object sender, SizeChangedEventArgs e) - { - if (WindowState == WindowState.Normal) - { - LastSizeWidth = (int)Width; - LastSizeHeight = (int)Height; - } - } - } -} diff --git a/NoopMenuHandler.cs b/NoopMenuHandler.cs deleted file mode 100644 index 74da7af7..00000000 --- a/NoopMenuHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright(c) 2019 pypy. All rights reserved. -// -// This work is licensed under the terms of the MIT license. -// For a copy, see . - -using CefSharp; - -namespace VRCX -{ - public class NoopMenuHandler : IContextMenuHandler - { - public void OnBeforeContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) - { - model.Clear(); - } - - public bool OnContextMenuCommand(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags) - { - return false; - } - - public void OnContextMenuDismissed(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame) - { - } - - public bool RunContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback) - { - return false; - } - } -} diff --git a/Program.cs b/Program.cs index d5006f4d..2ed55c6c 100644 --- a/Program.cs +++ b/Program.cs @@ -45,11 +45,7 @@ namespace VRCX CefService.Instance.Init(); VRCXVR.Instance.Init(); - - var app = new App(); - app.InitializeComponent(); - app.Run(); - + Application.Run(new MainForm()); WebApi.Instance.SaveCookies(); VRCXVR.Instance.Exit(); CefService.Instance.Exit(); diff --git a/VRCX.csproj b/VRCX.csproj index acadaa4a..78816d01 100644 --- a/VRCX.csproj +++ b/VRCX.csproj @@ -10,7 +10,6 @@ VRCX v4.6.2 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true true @@ -78,12 +77,9 @@ - - - @@ -93,26 +89,29 @@ - - - App.xaml - - - MainWindow.xaml - - - + + Form + + + VRForm.cs + + + Form + + + MainForm.cs + @@ -123,6 +122,12 @@ + + VRForm.cs + + + MainForm.cs + ResXFileCodeGenerator Resources.Designer.cs @@ -167,7 +172,7 @@ 86.0.241 - + 86.0.241 @@ -198,15 +203,5 @@ 1.0.113.7 - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - \ No newline at end of file diff --git a/html/src/app.js b/html/src/app.js index cd3f4915..7137a656 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -3524,18 +3524,6 @@ speechSynthesis.getVoices(); } }; - $app.methods.minimizeWindow = function () { - AppApi.MinimizeMainWindow(); - }; - - $app.methods.toggleMaximizeWindow = function () { - AppApi.ToggleMaximizeMainWindow(); - }; - - $app.methods.closeWindow = function () { - AppApi.CloseMainWindow(); - }; - $app.methods.openExternalLink = function (link) { this.$confirm(`${link}`, 'Open External Link', { confirmButtonText: 'Confirm', diff --git a/html/src/app.scss b/html/src/app.scss index 16afdead..76e290d3 100644 --- a/html/src/app.scss +++ b/html/src/app.scss @@ -138,24 +138,10 @@ a { .x-app { position: absolute; display: flex; - flex-direction: column; width: 100%; height: 100%; - overflow: hidden; -} - -.x-title-bar { - display: flex; - width: 100%; - height: 30px; -} - -.x-wrap { - position: relative; - display: flex; - width: 100%; - height: calc(100% - 30px); overflow: hidden auto; + cursor: default; } .x-container { @@ -170,10 +156,10 @@ a { position: absolute; // modal 시작이 2000이라서 z-index: 1999; + display: flex; width: 100%; height: 100%; background: #fff; - overflow: hidden scroll; } .x-menu-container { diff --git a/html/src/index.pug b/html/src/index.pug index 94054066..5b38b6b5 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -13,152 +13,117 @@ html link(rel="stylesheet" href="app.css") body .x-app#x-app(style="display:none") - .x-title-bar - div(style="width: 100%; -webkit-user-select: none; -webkit-app-region: drag; background: #eee;") VRCX - div(style="margin-left: auto;") - span.x-link(@click="minimizeWindow") min - span.x-link(@click="toggleMaximizeWindow") max - span.x-link(@click="closeWindow") close + //- login + .x-login-container(v-show="!API.isLoggedIn") + div(style="width:300px;margin:auto" v-loading="loginForm.loading") + div(style="margin:15px" v-if="Object.keys(loginForm.savedCredentials).length !== 0") + h2(style="font-weight:bold;text-align:center;margin:0") Saved Accounts + .x-friend-list(style="margin-top:10px") + .x-friend-item(v-for="user in loginForm.savedCredentials" :key="user.user.id") + .x-friend-item(@click="relogin(user.loginParmas)" style="width:202px;padding:0") + .avatar + img(v-if="displayVRCPlusIconsAsAvatar && user.user.userIcon" v-lazy="user.user.userIcon") + img(v-else v-lazy="user.user.currentAvatarThumbnailImageUrl") + .detail + span.name(v-text="user.user.displayName") + span.extra(v-text="user.user.username") + el-button(type="default" @click="deleteSavedLogin(user.user.username)" size="mini" icon="el-icon-delete" circle) + div(style="margin:15px") + h2(style="font-weight:bold;text-align:center;margin:0") Login + el-form(ref="loginForm" :model="loginForm" :rules="loginForm.rules" @submit.native.prevent="login()") + el-form-item(label="Username or Email" prop="username" required) + el-input(v-model="loginForm.username" name="username" placeholder="Username or Email" clearable) + el-form-item(label="Password" prop="password" required) + el-input(type="password" v-model="loginForm.password" name="password" placeholder="Password" clearable show-password) + el-checkbox(v-model="loginForm.saveCredentials") Save Credentials + el-form-item(style="margin-top:35px") + el-button(native-type="submit" type="primary" :loading="loginForm.loading" style="width:100%") Login + el-form-item + el-button(:loading="loginForm.loading" style="width:100%" @click="loginWithSteam()") Login with Steam + div(style="text-align:center;font-size:12px") + p © 2019-2021 #[a(href="https://github.com/pypy-vrc" target="_blank") pypy] (mina#5656) + p VRCX is an assistant application for provide information about manage friendship. this application uses unofficial VRChat API (VRCSDK). + p VRCX isn't endorsed by VRChat and doesn't reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat. VRChat is trademark of VRChat Inc. VRChat © VRChat Inc. + p pypy is not responsible for any problems caused by VRCX. Use at your own risk! - .x-wrap + //- menu + .x-menu-container + el-menu(ref="menu" collapse @select="selectMenu") + mixin menuitem(index, name, icon) + el-menu-item(index=index) + i(class=icon) + template(#title) + span= name + +menuitem('feed', 'Feed', 'el-icon-news') + +menuitem('gameLog', 'Game Log', 'el-icon-s-data') + +menuitem('search', 'Search', 'el-icon-search') + +menuitem('favorite', 'Favorite', 'el-icon-star-off') + +menuitem('friendLog', 'Friend Log', 'el-icon-notebook-2') + +menuitem('moderation', 'Moderation', 'el-icon-finished') + +menuitem('notification', 'Notification', 'el-icon-bell') + +menuitem('profile', 'Profile', 'el-icon-user') + +menuitem('settings', 'Settings', 'el-icon-s-tools') - //- login - .x-login-container(v-show="!API.isLoggedIn") - div(style="width:300px;margin:auto" v-loading="loginForm.loading") - div(style="margin:15px" v-if="Object.keys(loginForm.savedCredentials).length !== 0") - h2(style="font-weight:bold;text-align:center;margin:0") Saved Accounts - .x-friend-list(style="margin-top:10px") - .x-friend-item(v-for="user in loginForm.savedCredentials" :key="user.user.id") - .x-friend-item(@click="relogin(user.loginParmas)" style="width:202px;padding:0") - .avatar - img(v-if="displayVRCPlusIconsAsAvatar && user.user.userIcon" v-lazy="user.user.userIcon") - img(v-else v-lazy="user.user.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="user.user.displayName") - span.extra(v-text="user.user.username") - el-button(type="default" @click="deleteSavedLogin(user.user.username)" size="mini" icon="el-icon-delete" circle) - div(style="margin:15px") - h2(style="font-weight:bold;text-align:center;margin:0") Login - el-form(ref="loginForm" :model="loginForm" :rules="loginForm.rules" @submit.native.prevent="login()") - el-form-item(label="Username or Email" prop="username" required) - el-input(v-model="loginForm.username" name="username" placeholder="Username or Email" clearable) - el-form-item(label="Password" prop="password" required) - el-input(type="password" v-model="loginForm.password" name="password" placeholder="Password" clearable show-password) - el-checkbox(v-model="loginForm.saveCredentials") Save Credentials - el-form-item(style="margin-top:35px") - el-button(native-type="submit" type="primary" :loading="loginForm.loading" style="width:100%") Login - el-form-item - el-button(:loading="loginForm.loading" style="width:100%" @click="loginWithSteam()") Login with Steam - div(style="text-align:center;font-size:12px") - p © 2019-2021 #[a(href="https://github.com/pypy-vrc" target="_blank") pypy] (mina#5656) - p VRCX is an assistant application for provide information about manage friendship. this application uses unofficial VRChat API (VRCSDK). - p VRCX isn't endorsed by VRChat and doesn't reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat. VRChat is trademark of VRChat Inc. VRChat © VRChat Inc. - p pypy is not responsible for any problems caused by VRCX. Use at your own risk! - - //- menu - .x-menu-container - el-menu(ref="menu" collapse @select="selectMenu") - mixin menuitem(index, name, icon) - el-menu-item(index=index) - i(class=icon) - template(#title) - span= name - +menuitem('feed', 'Feed', 'el-icon-news') - +menuitem('gameLog', 'Game Log', 'el-icon-s-data') - +menuitem('search', 'Search', 'el-icon-search') - +menuitem('favorite', 'Favorite', 'el-icon-star-off') - +menuitem('friendLog', 'Friend Log', 'el-icon-notebook-2') - +menuitem('moderation', 'Moderation', 'el-icon-finished') - +menuitem('notification', 'Notification', 'el-icon-bell') - +menuitem('profile', 'Profile', 'el-icon-user') - +menuitem('settings', 'Settings', 'el-icon-s-tools') - - //- feed - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'feed'") - data-tables(v-bind="feedTable") - template(#tool) - div(style="margin:0 0 10px;display:flex;align-items:center") - div(style="flex:none;margin-right:10px") - el-switch(v-model="feedTable.filters[2].value" active-color="#13ce66") - el-select(v-model="feedTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") - el-option(v-once v-for="type in ['GPS', 'Online', 'Offline', 'Status', 'Avatar']" :key="type" :label="type" :value="type") - el-input(v-model="feedTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") - el-button(type="default" @click="clearFeed()" icon="el-icon-delete" circle style="flex:none") - el-table-column(type="expand") - template(v-once #default="scope") - div(style="position:relative;font-size:14px") - template(v-if="scope.row.type === 'GPS'") - location(:location="scope.row.location[1]") - el-tag(type="info" effect="plain" size="mini" style="margin-left:5px") {{ scope.row.time | timeToText }} - br - span - i.el-icon-right - location(:location="scope.row.location[0]") - template(v-else-if="scope.row.type === 'Offline'") - location(:location="scope.row.location") - el-tag(type="info" effect="plain" size="mini" style="margin-left:5px") {{ scope.row.time | timeToText }} - template(v-else-if="scope.row.type === 'Online'") - location(:location="scope.row.location") - template(v-else-if="scope.row.type === 'Avatar'") - template(v-if="scope.row.avatar[0] === Object(scope.row.avatar[0])") - //- high resolution (v2) 2020.07.12~ - el-popover(placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="scope.row.avatar[1].currentAvatarThumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") - img.x-link(v-lazy="scope.row.avatar[1].currentAvatarImageUrl" style="width:500px;height:375px" @click="showAvatarAuthorDialog(scope.row.userId, scope.row.avatar[1].currentAvatarImageUrl)") - span(style="position:relative;top:-50px;margin:0 5px") - i.el-icon-right - el-popover(placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="scope.row.avatar[0].currentAvatarThumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") - img.x-link(v-lazy="scope.row.avatar[0].currentAvatarImageUrl" style="width:500px;height:375px" @click="showAvatarAuthorDialog(scope.row.userId, scope.row.avatar[0].currentAvatarImageUrl)") - template(v-else) - //- legacy - el-popover(placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="scope.row.avatar[1]" style="flex:none;width:160px;height:120px;border-radius:4px") - img.x-link(v-lazy="scope.row.avatar[1]" style="width:500px;height:375px" @click="openExternalLink(scope.row.avatar[1])") - span(style="position:relative;top:-50px;margin:0 5px") - i.el-icon-right - el-popover(placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="scope.row.avatar[0]" style="flex:none;width:160px;height:120px;border-radius:4px") - img.x-link(v-lazy="scope.row.avatar[0]" style="width:500px;height:375px" @click="openExternalLink(scope.row.avatar[0])") - template(v-else-if="scope.row.type === 'Status'") - el-tooltip(placement="top") - template(#content) - span(v-if="scope.row.status[1].status === 'active'") Online - span(v-else-if="scope.row.status[1].status === 'join me'") Join Me - span(v-else-if="scope.row.status[1].status === 'ask me'") Ask Me - span(v-else-if="scope.row.status[1].status === 'busy'") Do Not Disturb - span(v-else) Offline - i.x-user-status(:class="userStatusClass(scope.row.status[1])") - span(v-text="scope.row.status[1].statusDescription") - br - span - i.el-icon-right - el-tooltip(placement="top") - template(#content) - span(v-if="scope.row.status[0].status === 'active'") Online - span(v-else-if="scope.row.status[0].status === 'join me'") Join Me - span(v-else-if="scope.row.status[0].status === 'ask me'") Ask Me - span(v-else-if="scope.row.status[0].status === 'busy'") Do Not Disturb - span(v-else) Offline - i.x-user-status(:class="userStatusClass(scope.row.status[0])") - span(v-text="scope.row.status[0].statusDescription") - el-table-column(label="Date" prop="created_at" sortable="custom" width="100") - template(v-once #default="scope") - el-tooltip(placement="right") - template(#content) - span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} - span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} - el-table-column(label="Type" prop="type" width="80") - el-table-column(label="User" prop="displayName") - template(v-once #default="scope") - span.x-link(v-text="scope.row.displayName" @click="showUserDialog(scope.row.userId)") - el-table-column(label="Detail") - template(v-once #default="scope") + //- feed + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'feed'") + data-tables(v-bind="feedTable") + template(#tool) + div(style="margin:0 0 10px;display:flex;align-items:center") + div(style="flex:none;margin-right:10px") + el-switch(v-model="feedTable.filters[2].value" active-color="#13ce66") + el-select(v-model="feedTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") + el-option(v-once v-for="type in ['GPS', 'Online', 'Offline', 'Status', 'Avatar']" :key="type" :label="type" :value="type") + el-input(v-model="feedTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") + el-button(type="default" @click="clearFeed()" icon="el-icon-delete" circle style="flex:none") + el-table-column(type="expand") + template(v-once #default="scope") + div(style="position:relative;font-size:14px") template(v-if="scope.row.type === 'GPS'") + location(:location="scope.row.location[1]") + el-tag(type="info" effect="plain" size="mini" style="margin-left:5px") {{ scope.row.time | timeToText }} + br + span + i.el-icon-right location(:location="scope.row.location[0]") - template(v-else-if="scope.row.type === 'Offline' || scope.row.type === 'Online'") + template(v-else-if="scope.row.type === 'Offline'") location(:location="scope.row.location") + el-tag(type="info" effect="plain" size="mini" style="margin-left:5px") {{ scope.row.time | timeToText }} + template(v-else-if="scope.row.type === 'Online'") + location(:location="scope.row.location") + template(v-else-if="scope.row.type === 'Avatar'") + template(v-if="scope.row.avatar[0] === Object(scope.row.avatar[0])") + //- high resolution (v2) 2020.07.12~ + el-popover(placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="scope.row.avatar[1].currentAvatarThumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") + img.x-link(v-lazy="scope.row.avatar[1].currentAvatarImageUrl" style="width:500px;height:375px" @click="showAvatarAuthorDialog(scope.row.userId, scope.row.avatar[1].currentAvatarImageUrl)") + span(style="position:relative;top:-50px;margin:0 5px") + i.el-icon-right + el-popover(placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="scope.row.avatar[0].currentAvatarThumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") + img.x-link(v-lazy="scope.row.avatar[0].currentAvatarImageUrl" style="width:500px;height:375px" @click="showAvatarAuthorDialog(scope.row.userId, scope.row.avatar[0].currentAvatarImageUrl)") + template(v-else) + //- legacy + el-popover(placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="scope.row.avatar[1]" style="flex:none;width:160px;height:120px;border-radius:4px") + img.x-link(v-lazy="scope.row.avatar[1]" style="width:500px;height:375px" @click="openExternalLink(scope.row.avatar[1])") + span(style="position:relative;top:-50px;margin:0 5px") + i.el-icon-right + el-popover(placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="scope.row.avatar[0]" style="flex:none;width:160px;height:120px;border-radius:4px") + img.x-link(v-lazy="scope.row.avatar[0]" style="width:500px;height:375px" @click="openExternalLink(scope.row.avatar[0])") template(v-else-if="scope.row.type === 'Status'") + el-tooltip(placement="top") + template(#content) + span(v-if="scope.row.status[1].status === 'active'") Online + span(v-else-if="scope.row.status[1].status === 'join me'") Join Me + span(v-else-if="scope.row.status[1].status === 'ask me'") Ask Me + span(v-else-if="scope.row.status[1].status === 'busy'") Do Not Disturb + span(v-else) Offline + i.x-user-status(:class="userStatusClass(scope.row.status[1])") + span(v-text="scope.row.status[1].statusDescription") + br + span + i.el-icon-right el-tooltip(placement="top") template(#content) span(v-if="scope.row.status[0].status === 'active'") Online @@ -168,1768 +133,1794 @@ html span(v-else) Offline i.x-user-status(:class="userStatusClass(scope.row.status[0])") span(v-text="scope.row.status[0].statusDescription") - - //- gameLog - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'gameLog'") - data-tables(v-bind="gameLogTable") - template(#tool) - div(style="margin:0 0 10px;display:flex;align-items:center") - el-select(v-model="gameLogTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") - el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'Notification']" :key="type" :label="type" :value="type") - el-input(v-model="gameLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") - el-button(type="default" @click="resetGameLog()" icon="el-icon-refresh" circle style="flex:none") - el-table-column(type="expand") - template(v-once #default="scope") - template(v-if="scope.row.type === 'Notification'") - span(v-text="scope.row.data") - el-table-column(label="Date" prop="created_at" sortable="custom" width="100") - template(v-once #default="scope") - el-tooltip(placement="right") - template(#content) - span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} - span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} - el-table-column(label="Type" prop="type" width="120") - el-table-column(label="Detail" prop="data") - template(v-once #default="scope") - location(v-if="scope.row.type === 'Location'" :location="scope.row.data[0]" :hint="scope.row.data[1]") - span.x-link(v-else-if="scope.row.type !== 'Notification'" v-text="scope.row.data" @click="lookupUser(scope.row.data)") - - //- search - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'search'") - div(style="margin:0 0 10px;display:flex;align-items:center") - el-input(v-model="searchText" clearable placeholder="Search" @keyup.native.13="search()" style="flex:1") - el-button(type="default" @click="clearSearch()" icon="el-icon-delete" circle style="flex:none;margin-left:10px") - el-tabs(ref="searchTab" type="card" style="margin-top:15px") - el-tab-pane(label="User" v-loading="isSearchUserLoading" style="min-height:60px") - .x-friend-list - .x-friend-item(v-for="user in searchUserResults" :key="user.id" @click="showUserDialog(user.id)") - template(v-once) - .avatar - //img(v-if="user.userIcon" v-lazy="user.userIcon") No userIcon from search, API bug? - img(v-lazy="user.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="user.displayName" :class="user.trustClass") - span.extra(v-text="user.username" style="font-family:monospace") - el-button-group(style="margin-top:15px") - el-button(v-if="searchUserParams.offset" @click="moreSearchUser(-1)" icon="el-icon-back" size="small") Prev - el-button(v-if="searchUserResults.length" @click="moreSearchUser(1)" icon="el-icon-right" size="small") Next - el-tab-pane(label="World" v-loading="isSearchWorldLoading" style="min-height:60px") - el-dropdown(@command="(row) => searchWorld(row)" size="small" trigger="click" style="margin-bottom:15px") - el-button(size="small") Search by Category #[i.el-icon-arrow-down.el-icon--right] - el-dropdown-menu(#default="dropdown") - el-dropdown-item(v-for="row in API.cachedConfig.dynamicWorldRows" :key="row.index" v-text="row.name" :command="row") - .x-friend-list - .x-friend-item(v-for="world in searchWorldResults" :key="world.id" @click="showWorldDialog(world.id)") - template(v-once) - .avatar - img(v-lazy="world.thumbnailImageUrl") - .detail - span.name(v-text="world.name") - span.extra(v-if="world.occupants") {{ world.authorName }} ({{ world.occupants }}) - span.extra(v-else v-text="world.authorName") - el-button-group(style="margin-top:15px") - el-button(v-if="searchWorldParams.offset" @click="moreSearchWorld(-1)" icon="el-icon-back" size="small") Prev - el-button(v-if="searchWorldResults.length" @click="moreSearchWorld(1)" icon="el-icon-right" size="small") Next - el-tab-pane(label="Avatar" v-loading="isSearchAvatarLoading" style="min-height:60px") - el-dropdown(@command="(command) => searchAvatar(command)" size="small" trigger="click" style="margin-bottom:15px") - el-button(size="small") Search by Category #[i.el-icon-arrow-down.el-icon--right] - el-dropdown-menu(#default="dropdown") - el-dropdown-item(command="updated") Updated Recently - el-dropdown-item(command="created") New - el-dropdown-item(command="mine") Mine - span(style="margin-left:10px;font-size:12px;color:#909399") Avatar search is not possible. - .x-friend-list - .x-friend-item(v-for="avatar in searchAvatarResults" :key="avatar.id" @click="showAvatarDialog(avatar.id)") - template(v-once) - .avatar - img(v-lazy="avatar.thumbnailImageUrl") - .detail - span.name(v-text="avatar.name") - span.extra(v-text="avatar.authorName") - el-button-group(style="margin-top:15px") - el-button(v-if="searchAvatarParams.offset" @click="moreSearchAvatar(-1)" icon="el-icon-back" size="small") Prev - el-button(v-if="searchAvatarResults.length" @click="moreSearchAvatar(1)" icon="el-icon-right" size="small") Next - - //- favorite - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'favorite'") - el-button(type="default" :loading="API.isFavoriteLoading" @click="API.refreshFavorites()" size="small" icon="el-icon-refresh" circle style="position:relative;float:right;z-index:1") - el-tabs(type="card" v-loading="API.isFavoriteLoading") - el-tab-pane(label="Friend") - el-collapse(style="border:0") - el-collapse-item(v-for="group in API.favoriteFriendGroups" :key="group.name") - template(slot="title") - span(v-text="group.displayName" style="font-weight:bold;font-size:14px;margin-left:10px") - span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }} - el-button(@click.stop="changeFavoriteGroupName(group)" size="mini" icon="el-icon-edit" circle style="margin-left:10px") - el-button(@click.stop="clearFavoriteGroup(group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") - .x-friend-list(v-if="group.count" style="margin-top:10px") - .x-friend-item(v-for="favorite in favoriteFriends" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showUserDialog(favorite.id)") - template(v-if="favorite.ref") - .avatar(:class="userStatusClass(favorite.ref)") - img(v-if="displayVRCPlusIconsAsAvatar && favorite.ref.userIcon" v-lazy="favorite.ref.userIcon") - img(v-else v-lazy="favorite.ref.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="favorite.ref.displayName" :class="favorite.ref.$trustClass") - location.extra(v-if="favorite.ref.location !== 'offline'" :location="favorite.ref.location" :link="false") - span(v-else v-text="favorite.ref.statusDescription") - template(v-else) - span(v-text="favorite.name || favorite.id") - el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px") - el-tab-pane(label="World") - el-collapse(style="border:0") - el-collapse-item(v-for="group in API.favoriteWorldGroups" :key="group.name") - template(slot="title") - span(v-text="group.displayName" style="font-weight:bold;font-size:14px;margin-left:10px") - span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }} - el-button(@click.stop="changeFavoriteGroupName(group)" size="mini" icon="el-icon-edit" circle style="margin-left:10px") - el-button(@click.stop="clearFavoriteGroup(group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") - .x-friend-list(v-if="group.count" style="margin-top:10px") - .x-friend-item(v-for="favorite in favoriteWorlds" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showWorldDialog(favorite.id)") - template(v-if="favorite.ref") - .avatar - img(v-lazy="favorite.ref.thumbnailImageUrl") - .detail - span.name(v-text="favorite.ref.name") - span.extra(v-if="favorite.ref.occupants") {{ favorite.ref.authorName }} ({{ favorite.ref.occupants }}) - span.extra(v-else v-text="favorite.ref.authorName") - template(v-else) - span(v-text="favorite.name || favorite.id") - el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px") - el-tab-pane(label="Avatar") - el-collapse(style="border:0") - el-collapse-item(v-for="group in API.favoriteAvatarGroups" :key="group.name") - template(slot="title") - span(v-text="group.displayName" style="font-weight:bold;font-size:14px;margin-left:10px") - span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }} - //- el-button(@click.stop="changeFavoriteGroupName(group)" size="mini" icon="el-icon-edit" circle style="margin-left:10px") - el-button(@click.stop="clearFavoriteGroup(group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") - .x-friend-list(v-if="group.count" style="margin-top:10px") - .x-friend-item(v-for="favorite in favoriteAvatars" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showAvatarDialog(favorite.id)") - template(v-if="favorite.ref") - .avatar - img(v-lazy="favorite.ref.thumbnailImageUrl") - .detail - span.name(v-text="favorite.ref.name") - span.extra(v-text="favorite.ref.authorName") - template(v-else) - span(v-text="favorite.name || favorite.id") - el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px") - - //- friendLog - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'friendLog'") - data-tables(v-bind="friendLogTable") - template(#tool) - div(style="margin:0 0 10px;display:flex;align-items:center") - el-select(v-model="friendLogTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") - el-option(v-once v-for="type in ['Friend', 'Unfriend', 'FriendRequest', 'CancelFriendRequest', 'DisplayName', 'TrustLevel']" :key="type" :label="type" :value="type") - el-input(v-model="friendLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin-left:10px") - el-table-column(label="Date" prop="created_at" sortable="custom" width="100") - template(v-once #default="scope") - el-tooltip(placement="right") - template(#content) - span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} - span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} - el-table-column(label="Type" prop="type" width="150") - el-table-column(label="User" prop="displayName") - template(v-once #default="scope") - span(v-if="scope.row.type === 'DisplayName'") {{ scope.row.previousDisplayName }} #[i.el-icon-right] - |   - span.x-link(v-text="scope.row.displayName || scope.row.userId" @click="showUserDialog(scope.row.userId)") - template(v-if="scope.row.type === 'TrustLevel'") - br - span ({{ scope.row.previousTrustLevel }} #[i.el-icon-right] {{ scope.row.trustLevel }}) - el-table-column(label="Action" width="80" align="right") - template(v-once #default="scope") - el-button(type="text" icon="el-icon-close" size="mini" @click="deleteFriendLog(scope.row)") - - //- moderation - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'moderation'") - data-tables(v-bind="playerModerationTable" v-loading="API.isPlayerModerationsLoading") - template(#tool) - div(style="margin:0 0 10px;display:flex;align-items:center") - el-select(v-model="playerModerationTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") - el-option(v-once v-for="type in ['block', 'mute', 'unmute', 'hideAvatar', 'showAvatar']" :key="type" :label="type" :value="type") - el-input(v-model="playerModerationTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") - el-button(type="default" :loading="API.isPlayerModerationsLoading" @click="API.refreshPlayerModerations()" icon="el-icon-refresh" circle style="flex:none") - el-table-column(label="Date" prop="created" sortable="custom" width="100") - template(v-once #default="scope") - el-tooltip(placement="right") - template(#content) - span {{ scope.row.created | formatDate('YYYY-MM-DD HH24:MI:SS') }} - span {{ scope.row.created | formatDate('MM-DD HH24:MI') }} - el-table-column(label="Type" prop="type" width="100") - el-table-column(label="Source" prop="sourceDisplayName") - template(v-once #default="scope") - span.x-link(v-text="scope.row.sourceDisplayName" @click="showUserDialog(scope.row.sourceUserId)") - el-table-column(label="Target" prop="targetDisplayName") - template(v-once #default="scope") - span.x-link(v-text="scope.row.targetDisplayName" @click="showUserDialog(scope.row.targetUserId)") - el-table-column(label="Action" width="80" align="right") - template(v-once #default="scope") - el-button(v-if="scope.row.sourceUserId === API.currentUser.id" type="text" icon="el-icon-close" size="mini" @click="deletePlayerModeration(scope.row)") - - //- notification - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'notification'" v-loading="API.isNotificationsLoading") - data-tables(v-bind="notificationTable") - template(#tool) - div(style="margin:0 0 10px;display:flex;align-items:center") - el-select(v-model="notificationTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") - el-option(v-once v-for="type in ['requestInvite', 'invite', 'requestInviteResponse', 'inviteResponse', 'friendRequest', 'message']" :key="type" :label="type" :value="type") - el-input(v-model="notificationTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") - el-button(type="default" :loading="API.isNotificationsLoading" @click="API.refreshNotifications()" icon="el-icon-refresh" circle style="flex:none") - el-table-column(label="Date" prop="created_at" sortable="custom" width="100") - template(v-once #default="scope") - el-tooltip(placement="right") - template(#content) - span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} - span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} - el-table-column(label="Type" prop="type" width="150") - template(v-once #default="scope") - el-tooltip(placement="top" v-if="scope.row.type === 'invite'") - template(#content) - span(v-text="API.parseInviteLocation(scope.row)") - span.x-link(v-text="scope.row.type" @click="showWorldDialog(scope.row.details.worldId)") - span(v-else v-text="scope.row.type") - el-table-column(label="User" prop="senderUsername" width="150") - template(v-once #default="scope") - span.x-link(v-text="scope.row.senderUsername" @click="showUserDialog(scope.row.senderUserId)") - el-table-column(label="Photo" width="100" prop="photo") - template(v-once #default="scope") - el-popover(v-if="scope.row.details.imageUrl" placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="scope.row.details.imageUrl" style="flex:none;height:60px;border-radius:4px") - img.x-link(v-lazy="scope.row.details.imageUrl" style="width:500px" @click="openExternalLink(scope.row.details.imageUrl)") - el-table-column(label="Message" prop="message") - template(v-once #default="scope") - span(v-if="scope.row.message" v-text="scope.row.message") - span(v-else-if='scope.row.details.inviteMessage' v-text="scope.row.details.inviteMessage") - span(v-else-if='scope.row.details.requestMessage' v-text="scope.row.details.requestMessage") - span(v-else-if='scope.row.details.responseMessage' v-text="scope.row.details.responseMessage") - el-table-column(label="Action" width="80" align="right") - template(v-once #default="scope") - template(v-if="scope.row.senderUserId !== API.currentUser.id") - 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 - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'profile'") - div.options-container(style="margin-top:0") - span.header Profile - .x-friend-list(style="margin-top:10px") - .x-friend-item(@click="showUserDialog(API.currentUser.id)") - .avatar - img(v-if="displayVRCPlusIconsAsAvatar && API.currentUser.userIcon" v-lazy="API.currentUser.userIcon") - img(v-else v-lazy="API.currentUser.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="API.currentUser.displayName") - span.extra(v-text="API.currentUser.username") - .x-friend-item(style="cursor:default") - .detail - span.name Last Login - span.extra {{ API.currentUser.last_login | formatDate('YYYY-MM-DD HH24:MI:SS') }} - .x-friend-item(style="cursor:default") - .detail - span.name Two-Factor Auth (2FA) - span.extra {{ API.currentUser.twoFactorAuthEnabled ? 'Enabled' : 'Disabled' }} - div(style="margin-top:10px") - el-button(size="small" icon="el-icon-switch-button" @click="logout()") Logout - el-button(size="small" icon="el-icon-printer" @click="showExportFriendsListDialog()") Export Friends List - div.options-container - span.header Game Info - .x-friend-list(style="margin-top:10px") - .x-friend-item - .detail(@click="API.getVisits()") - span.name Online Users - span.extra(v-if="visits") {{visits}} users online. - span.extra(v-else) Click to refresh - div.options-container - span.header Direct Access - div(style="margin-top:10px") - el-button-group - el-button(size="small" @click="promptUserDialog()") User - el-button(size="small" @click="promptWorldDialog()") World - el-button(size="small" @click="promptAvatarDialog()") Avatar - div.options-container - span.header Invite Messages - el-tooltip(placement="top") - template(#content) - span Refresh - 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.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") - 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('message', scope.row)") - div.options-container - span.header Invite Response Messages - el-tooltip(placement="top") - template(#content) - span Refresh - 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.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") - 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('response', scope.row)") - div.options-container - span.header Invite Request Messages - el-tooltip(placement="top") - template(#content) - span Refresh - 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.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") - 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('request', scope.row)") - 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) - span Refresh - el-button(type="default" @click="displayVRCPlusIconsTable()" 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="VRCPlusIconsTable = {}" size="mini" icon="el-icon-delete" circle style="margin-left:0") - template(v-if="API.currentUser.$isVRCPlus") - el-tooltip(placement="top") + el-table-column(label="Date" prop="created_at" sortable="custom" width="100") + template(v-once #default="scope") + el-tooltip(placement="right") template(#content) - span Upload icon - div(style="display:inline-block") - el-button(type="default" @click="displayVRCPlusIconUpload" size="mini" icon="el-icon-upload2" circle style="margin-left:0") - input(type="file" multiple accept="image/*" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none") - el-tooltip(placement="top") - template(#content) - span Reset icon - el-button(type="default" @click="setVRCPlusIcon('')" size="mini" icon="el-icon-close" circle style="margin-left:0" :disabled="!API.currentUser.userIcon") - br - .x-friend-item(v-for="icon in VRCPlusIconsTable" :key="icon.id" style="display:inline-block;margin-top:10px;cursor:default") - .vrcplus-icon(style="" @click="setVRCPlusIcon(icon.id)" :class="{ 'current-vrcplus-icon': compareCurrentVRCPlusIcon(icon.id) }") - img.avatar(v-if="icon.versions[1].file.url" v-lazy="icon.versions[1].file.url") - el-tooltip(placement="top") + span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} + span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} + el-table-column(label="Type" prop="type" width="80") + el-table-column(label="User" prop="displayName") + template(v-once #default="scope") + span.x-link(v-text="scope.row.displayName" @click="showUserDialog(scope.row.userId)") + el-table-column(label="Detail") + template(v-once #default="scope") + template(v-if="scope.row.type === 'GPS'") + location(:location="scope.row.location[0]") + template(v-else-if="scope.row.type === 'Offline' || scope.row.type === 'Online'") + location(:location="scope.row.location") + template(v-else-if="scope.row.type === 'Status'") + el-tooltip(placement="top") + template(#content) + span(v-if="scope.row.status[0].status === 'active'") Online + span(v-else-if="scope.row.status[0].status === 'join me'") Join Me + span(v-else-if="scope.row.status[0].status === 'ask me'") Ask Me + span(v-else-if="scope.row.status[0].status === 'busy'") Do Not Disturb + span(v-else) Offline + i.x-user-status(:class="userStatusClass(scope.row.status[0])") + span(v-text="scope.row.status[0].statusDescription") + + //- gameLog + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'gameLog'") + data-tables(v-bind="gameLogTable") + template(#tool) + div(style="margin:0 0 10px;display:flex;align-items:center") + el-select(v-model="gameLogTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") + el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'Notification']" :key="type" :label="type" :value="type") + el-input(v-model="gameLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") + el-button(type="default" @click="resetGameLog()" icon="el-icon-refresh" circle style="flex:none") + el-table-column(type="expand") + template(v-once #default="scope") + template(v-if="scope.row.type === 'Notification'") + span(v-text="scope.row.data") + el-table-column(label="Date" prop="created_at" sortable="custom" width="100") + template(v-once #default="scope") + el-tooltip(placement="right") template(#content) - span Delete - el-button(type="default" @click="deleteVRCPlusIcon(icon.id)" size="mini" icon="el-icon-delete" circle style="float:right;") - div.options-container - span.header Past Display Names - data-tables(v-bind="pastDisplayNameTable" style="margin-top:10px") - el-table-column(label="Date" prop="updated_at" sortable="custom") - template(v-once #default="scope") - span {{ scope.row.updated_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} - el-table-column(label="Name" prop="displayName") - div.options-container - span.header Config JSON - el-tooltip(placement="top") - template(#content) - span Refresh - el-button(type="default" @click="refreshConfigTreeData()" 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="configTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") - 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") - span(v-if="!scope.data.children" v-text="scope.data.value") - div.options-container - span.header Current User JSON - el-tooltip(placement="top") - template(#content) - span Refresh - el-button(type="default" @click="refreshCurrentUserTreeData()" 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="currentUserTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") - 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") - span(v-if="!scope.data.children" v-text="scope.data.value") + span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} + span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} + el-table-column(label="Type" prop="type" width="120") + el-table-column(label="Detail" prop="data") + template(v-once #default="scope") + location(v-if="scope.row.type === 'Location'" :location="scope.row.data[0]" :hint="scope.row.data[1]") + span.x-link(v-else-if="scope.row.type !== 'Notification'" v-text="scope.row.data" @click="lookupUser(scope.row.data)") - //- settings - .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'settings'") - div.options-container(style="margin-top:0") - span.header Settings - .x-friend-list(style="margin-top:10px") - .x-friend-item(style="cursor:default") - .detail - span.name Version - span.extra(v-text="appVersion") - .x-friend-item(@click="checkAppVersion()") - .detail - span.name Latest Version - span.extra(v-if="latestAppVersion" v-text="latestAppVersion") - span.extra(v-else) Click to refresh - .x-friend-item(@click="openExternalLink('https://github.com/pypy-vrc/VRCX')") - .detail - span.name Repository URL - span.extra https://github.com/pypy-vrc/VRCX - div.options-container - span.header Appearance - div.options-container-item - span.name Dark Mode - el-switch(v-model="isDarkMode") - div.options-container-item - span.name VRCPlus Profile Icons - el-switch(v-model="displayVRCPlusIconsAsAvatar") - div.options-container - span.header Side Pannel Sorting Options - div.options-container-item - span.name VIP - el-switch(v-model="orderFriendsGroup0" inactive-text="by name" active-text="by state") - div.options-container-item - span.name Online - el-switch(v-model="orderFriendsGroup1" inactive-text="by name" active-text="by state") - div.options-container-item - span.name Active - el-switch(v-model="orderFriendsGroup2" inactive-text="by name" active-text="by state") - div.options-container-item - span.name Offline - el-switch(v-model="orderFriendsGroup3" inactive-text="by name" active-text="by state") - div.options-container - span.header Trust Rank Colors - div.options-container-item - div - v-swatches(v-model="trustColor.untrusted" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-untrusted") - span.color-picker(slot="trigger") #[i.el-icon-s-open] Visitor - div - v-swatches(v-model="trustColor.basic" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-basic") - span.color-picker(slot="trigger") #[i.el-icon-s-open] New User - div - v-swatches(v-model="trustColor.known" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-known") - span.color-picker(slot="trigger") #[i.el-icon-s-open] User - div - v-swatches(v-model="trustColor.trusted" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-trusted") - span.color-picker(slot="trigger") #[i.el-icon-s-open] Known User - div - v-swatches(v-model="trustColor.veteran" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-veteran") - span.color-picker(slot="trigger") #[i.el-icon-s-open] Trusted User - div - v-swatches(v-model="trustColor.legend" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-legend") - span.color-picker(slot="trigger") #[i.el-icon-s-open] Veteran User - div - v-swatches(v-model="trustColor.legendary" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-legendary") - span.color-picker(slot="trigger") #[i.el-icon-s-open] Legendary User - div.options-container - span.header Discord Presence - div.options-container-item - span * Only works when VRChat is running. - div.options-container-item - span.name Enable - el-switch(v-model="discordActive") - div.options-container-item - span.name Instance details - el-switch(v-model="discordInstance" :disabled="!discordActive") - div.options-container - span.header SteamVR Overlay - div.options-container-item - span * It runs automatically when VRChat is running. - br - br - span Grip: Vive or Other Controllers Grab, Oculus X/A Buttons - br - span Menu: Vive Menu, Index B, Oculus Y/B Buttons - br - div.options-container-item - span.name Enable - el-switch(v-model="openVR") - div.options-container-item - span.name Force Run (Opens SteamVR) - el-switch(v-model="openVRAlways" :disabled="!openVR") - div.options-container-item - span.name(style="min-width:137px") Overlay Button - el-switch(v-model="overlaybutton" inactive-text="Grip" active-text="Menu" :disabled="!openVR") - br - span.sub-header Display Options - div.options-container-item - span.name Minimal Feed Icons - el-switch(v-model="minimalFeed" :disabled="!openVR") - div.options-container-item - span.name Hide VR Devices - el-switch(v-model="hideDevicesFromFeed" :disabled="!openVR") - div.options-container-item - span.name Hide Private Worlds - el-switch(v-model="hidePrivateFromFeed") - div.options-container-item - span.name Hide Joins When Joining - el-switch(v-model="hideOnPlayerJoined") - div.options-container-item - el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog()" :disabled="!openVR") Wrist Feed Filters - el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog()") Notification Filters - br - span.sub-header VR Notifications - div.options-container-item - span.name Overlay Notifications - el-switch(v-model="overlayNotifications" :disabled="!openVR") - div.options-container-item - el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout()" :disabled="!overlayNotifications || !openVR") Notification Timeout - el-button(size="small" icon="el-icon-rank" @click="showNotificationPositionDialog()" :disabled="!overlayNotifications || !openVR") Notification Position - br - span.sub-header Desktop Notifications - div.options-container-item - span.name When to display notifications: - br - toggle-switch(:options="desktopToastToggleSwitchOption" group="desktopToastToggleSwitchOption" v-model="desktopToast" class="toggle-switch") - br - span.sub-header TTS Options - div.options-container-item - span.name Notification TTS - el-switch(v-model="notificationTTS") - div.options-container-item - span.name Text-to-Speech Voice - el-dropdown(@command="(voice) => changeTTSVoice(voice)" trigger="click" size="small") - el-button(v-text="TTSvoices[notificationTTSVoice].name" size="mini" :disabled="!notificationTTS") - el-dropdown-menu(#default="dropdown") - el-dropdown-item(v-if="voice" v-for="(voice, index) in TTSvoices" :key="index" v-text="voice.name" :command="index") - div.options-container - span.header Application - div.options-container-item - span.name Start at Windows startup - el-switch(v-model="isStartAtWindowsStartup") - div.options-container-item - span.name Start as minimized state - el-switch(v-model="isStartAsMinimizedState") - div.options-container-item - span.name Close to tray - el-switch(v-model="isCloseToTray") - div.options-container-item - span.name Auto login - el-switch(v-model="isAutoLogin") - div.options-container-item - el-button-group - el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") Launch Options - div.options-container(style="margin-top:45px;border-top:1px solid #eee;padding-top:30px") - span.header Legal Notice - div.options-container-item - p © 2019-2021 #[a(href="https://github.com/pypy-vrc" target="_blank") pypy] (mina#5656) - p VRCX is an assistant application for provide information about manage friendship. this application uses unofficial VRChat API (VRCSDK). - p VRCX isn't endorsed by VRChat and doesn't reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat. VRChat is trademark of VRChat Inc. VRChat © VRChat Inc. - p pypy is not responsible for any problems caused by VRCX. Use at your own risk! - div.options-container-item - el-button(@click="ossDialog = true" size="small") Open Source Software Notice - - //- friends - .x-aside-container - el-select(v-model="quickSearch" clearable placeholder="Search" filterable remote :remote-method="quickSearchRemoteMethod" popper-class="x-quick-search" @change="quickSearchChange" @visible-change="quickSearchVisibleChange" style="flex:none;padding:10px") - el-option(v-for="item in quickSearchItems" :key="item.value" :value="item.value" :label="item.label") - .x-friend-item - template(v-if="item.ref") + //- search + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'search'") + div(style="margin:0 0 10px;display:flex;align-items:center") + el-input(v-model="searchText" clearable placeholder="Search" @keyup.native.13="search()" style="flex:1") + el-button(type="default" @click="clearSearch()" icon="el-icon-delete" circle style="flex:none;margin-left:10px") + el-tabs(ref="searchTab" type="card" style="margin-top:15px") + el-tab-pane(label="User" v-loading="isSearchUserLoading" style="min-height:60px") + .x-friend-list + .x-friend-item(v-for="user in searchUserResults" :key="user.id" @click="showUserDialog(user.id)") + template(v-once) + .avatar + //img(v-if="user.userIcon" v-lazy="user.userIcon") No userIcon from search, API bug? + img(v-lazy="user.currentAvatarThumbnailImageUrl") .detail - span.name(v-text="item.ref.displayName" :class="item.ref.$trustClass") - location.extra(:location="item.ref.location" :link="false") - img.avatar(v-if="displayVRCPlusIconsAsAvatar && item.ref.userIcon" v-lazy="item.ref.userIcon") - img.avatar(v-else v-lazy="item.ref.currentAvatarThumbnailImageUrl") - span(v-else) Search More: #[span(v-text="item.label" style="font-weight:bold")] - .x-friend-list(style="padding-bottom:10px") - .x-friend-group - i.el-icon-arrow-right(:class="{ rotate: isFriendsGroupMe }") - span.x-link(@click="isFriendsGroupMe = !isFriendsGroupMe" style="margin-left:5px") ME - div(v-show="isFriendsGroupMe") - .x-friend-item(:key="API.currentUser.id" @click="showUserDialog(API.currentUser.id)") + span.name(v-text="user.displayName" :class="user.trustClass") + span.extra(v-text="user.username" style="font-family:monospace") + el-button-group(style="margin-top:15px") + el-button(v-if="searchUserParams.offset" @click="moreSearchUser(-1)" icon="el-icon-back" size="small") Prev + el-button(v-if="searchUserResults.length" @click="moreSearchUser(1)" icon="el-icon-right" size="small") Next + el-tab-pane(label="World" v-loading="isSearchWorldLoading" style="min-height:60px") + el-dropdown(@command="(row) => searchWorld(row)" size="small" trigger="click" style="margin-bottom:15px") + el-button(size="small") Search by Category #[i.el-icon-arrow-down.el-icon--right] + el-dropdown-menu(#default="dropdown") + el-dropdown-item(v-for="row in API.cachedConfig.dynamicWorldRows" :key="row.index" v-text="row.name" :command="row") + .x-friend-list + .x-friend-item(v-for="world in searchWorldResults" :key="world.id" @click="showWorldDialog(world.id)") + template(v-once) + .avatar + img(v-lazy="world.thumbnailImageUrl") + .detail + span.name(v-text="world.name") + span.extra(v-if="world.occupants") {{ world.authorName }} ({{ world.occupants }}) + span.extra(v-else v-text="world.authorName") + el-button-group(style="margin-top:15px") + el-button(v-if="searchWorldParams.offset" @click="moreSearchWorld(-1)" icon="el-icon-back" size="small") Prev + el-button(v-if="searchWorldResults.length" @click="moreSearchWorld(1)" icon="el-icon-right" size="small") Next + el-tab-pane(label="Avatar" v-loading="isSearchAvatarLoading" style="min-height:60px") + el-dropdown(@command="(command) => searchAvatar(command)" size="small" trigger="click" style="margin-bottom:15px") + el-button(size="small") Search by Category #[i.el-icon-arrow-down.el-icon--right] + el-dropdown-menu(#default="dropdown") + el-dropdown-item(command="updated") Updated Recently + el-dropdown-item(command="created") New + el-dropdown-item(command="mine") Mine + span(style="margin-left:10px;font-size:12px;color:#909399") Avatar search is not possible. + .x-friend-list + .x-friend-item(v-for="avatar in searchAvatarResults" :key="avatar.id" @click="showAvatarDialog(avatar.id)") + template(v-once) + .avatar + img(v-lazy="avatar.thumbnailImageUrl") + .detail + span.name(v-text="avatar.name") + span.extra(v-text="avatar.authorName") + el-button-group(style="margin-top:15px") + el-button(v-if="searchAvatarParams.offset" @click="moreSearchAvatar(-1)" icon="el-icon-back" size="small") Prev + el-button(v-if="searchAvatarResults.length" @click="moreSearchAvatar(1)" icon="el-icon-right" size="small") Next + + //- favorite + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'favorite'") + el-button(type="default" :loading="API.isFavoriteLoading" @click="API.refreshFavorites()" size="small" icon="el-icon-refresh" circle style="position:relative;float:right;z-index:1") + el-tabs(type="card" v-loading="API.isFavoriteLoading") + el-tab-pane(label="Friend") + el-collapse(style="border:0") + el-collapse-item(v-for="group in API.favoriteFriendGroups" :key="group.name") + template(slot="title") + span(v-text="group.displayName" style="font-weight:bold;font-size:14px;margin-left:10px") + span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }} + el-button(@click.stop="changeFavoriteGroupName(group)" size="mini" icon="el-icon-edit" circle style="margin-left:10px") + el-button(@click.stop="clearFavoriteGroup(group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + .x-friend-list(v-if="group.count" style="margin-top:10px") + .x-friend-item(v-for="favorite in favoriteFriends" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showUserDialog(favorite.id)") + template(v-if="favorite.ref") + .avatar(:class="userStatusClass(favorite.ref)") + img(v-if="displayVRCPlusIconsAsAvatar && favorite.ref.userIcon" v-lazy="favorite.ref.userIcon") + img(v-else v-lazy="favorite.ref.currentAvatarThumbnailImageUrl") + .detail + span.name(v-text="favorite.ref.displayName" :class="favorite.ref.$trustClass") + location.extra(v-if="favorite.ref.location !== 'offline'" :location="favorite.ref.location" :link="false") + span(v-else v-text="favorite.ref.statusDescription") + template(v-else) + span(v-text="favorite.name || favorite.id") + el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px") + el-tab-pane(label="World") + el-collapse(style="border:0") + el-collapse-item(v-for="group in API.favoriteWorldGroups" :key="group.name") + template(slot="title") + span(v-text="group.displayName" style="font-weight:bold;font-size:14px;margin-left:10px") + span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }} + el-button(@click.stop="changeFavoriteGroupName(group)" size="mini" icon="el-icon-edit" circle style="margin-left:10px") + el-button(@click.stop="clearFavoriteGroup(group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + .x-friend-list(v-if="group.count" style="margin-top:10px") + .x-friend-item(v-for="favorite in favoriteWorlds" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showWorldDialog(favorite.id)") + template(v-if="favorite.ref") + .avatar + img(v-lazy="favorite.ref.thumbnailImageUrl") + .detail + span.name(v-text="favorite.ref.name") + span.extra(v-if="favorite.ref.occupants") {{ favorite.ref.authorName }} ({{ favorite.ref.occupants }}) + span.extra(v-else v-text="favorite.ref.authorName") + template(v-else) + span(v-text="favorite.name || favorite.id") + el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px") + el-tab-pane(label="Avatar") + el-collapse(style="border:0") + el-collapse-item(v-for="group in API.favoriteAvatarGroups" :key="group.name") + template(slot="title") + span(v-text="group.displayName" style="font-weight:bold;font-size:14px;margin-left:10px") + span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }} + //- el-button(@click.stop="changeFavoriteGroupName(group)" size="mini" icon="el-icon-edit" circle style="margin-left:10px") + el-button(@click.stop="clearFavoriteGroup(group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + .x-friend-list(v-if="group.count" style="margin-top:10px") + .x-friend-item(v-for="favorite in favoriteAvatars" v-if="favorite.groupKey === group.key" :key="favorite.id" @click="showAvatarDialog(favorite.id)") + template(v-if="favorite.ref") + .avatar + img(v-lazy="favorite.ref.thumbnailImageUrl") + .detail + span.name(v-text="favorite.ref.name") + span.extra(v-text="favorite.ref.authorName") + template(v-else) + span(v-text="favorite.name || favorite.id") + el-button(type="text" icon="el-icon-close" size="mini" @click.stop="deleteFavorite(favorite.id)" style="margin-left:5px") + + //- friendLog + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'friendLog'") + data-tables(v-bind="friendLogTable") + template(#tool) + div(style="margin:0 0 10px;display:flex;align-items:center") + el-select(v-model="friendLogTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") + el-option(v-once v-for="type in ['Friend', 'Unfriend', 'FriendRequest', 'CancelFriendRequest', 'DisplayName', 'TrustLevel']" :key="type" :label="type" :value="type") + el-input(v-model="friendLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin-left:10px") + el-table-column(label="Date" prop="created_at" sortable="custom" width="100") + template(v-once #default="scope") + el-tooltip(placement="right") + template(#content) + span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} + span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} + el-table-column(label="Type" prop="type" width="150") + el-table-column(label="User" prop="displayName") + template(v-once #default="scope") + span(v-if="scope.row.type === 'DisplayName'") {{ scope.row.previousDisplayName }} #[i.el-icon-right] + |   + span.x-link(v-text="scope.row.displayName || scope.row.userId" @click="showUserDialog(scope.row.userId)") + template(v-if="scope.row.type === 'TrustLevel'") + br + span ({{ scope.row.previousTrustLevel }} #[i.el-icon-right] {{ scope.row.trustLevel }}) + el-table-column(label="Action" width="80" align="right") + template(v-once #default="scope") + el-button(type="text" icon="el-icon-close" size="mini" @click="deleteFriendLog(scope.row)") + + //- moderation + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'moderation'") + data-tables(v-bind="playerModerationTable" v-loading="API.isPlayerModerationsLoading") + template(#tool) + div(style="margin:0 0 10px;display:flex;align-items:center") + el-select(v-model="playerModerationTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") + el-option(v-once v-for="type in ['block', 'mute', 'unmute', 'hideAvatar', 'showAvatar']" :key="type" :label="type" :value="type") + el-input(v-model="playerModerationTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") + el-button(type="default" :loading="API.isPlayerModerationsLoading" @click="API.refreshPlayerModerations()" icon="el-icon-refresh" circle style="flex:none") + el-table-column(label="Date" prop="created" sortable="custom" width="100") + template(v-once #default="scope") + el-tooltip(placement="right") + template(#content) + span {{ scope.row.created | formatDate('YYYY-MM-DD HH24:MI:SS') }} + span {{ scope.row.created | formatDate('MM-DD HH24:MI') }} + el-table-column(label="Type" prop="type" width="100") + el-table-column(label="Source" prop="sourceDisplayName") + template(v-once #default="scope") + span.x-link(v-text="scope.row.sourceDisplayName" @click="showUserDialog(scope.row.sourceUserId)") + el-table-column(label="Target" prop="targetDisplayName") + template(v-once #default="scope") + span.x-link(v-text="scope.row.targetDisplayName" @click="showUserDialog(scope.row.targetUserId)") + el-table-column(label="Action" width="80" align="right") + template(v-once #default="scope") + el-button(v-if="scope.row.sourceUserId === API.currentUser.id" type="text" icon="el-icon-close" size="mini" @click="deletePlayerModeration(scope.row)") + + //- notification + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'notification'" v-loading="API.isNotificationsLoading") + data-tables(v-bind="notificationTable") + template(#tool) + div(style="margin:0 0 10px;display:flex;align-items:center") + el-select(v-model="notificationTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter") + el-option(v-once v-for="type in ['requestInvite', 'invite', 'requestInviteResponse', 'inviteResponse', 'friendRequest', 'message']" :key="type" :label="type" :value="type") + el-input(v-model="notificationTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") + el-button(type="default" :loading="API.isNotificationsLoading" @click="API.refreshNotifications()" icon="el-icon-refresh" circle style="flex:none") + el-table-column(label="Date" prop="created_at" sortable="custom" width="100") + template(v-once #default="scope") + el-tooltip(placement="right") + template(#content) + span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} + span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} + el-table-column(label="Type" prop="type" width="150") + template(v-once #default="scope") + el-tooltip(placement="top" v-if="scope.row.type === 'invite'") + template(#content) + span(v-text="API.parseInviteLocation(scope.row)") + span.x-link(v-text="scope.row.type" @click="showWorldDialog(scope.row.details.worldId)") + span(v-else v-text="scope.row.type") + el-table-column(label="User" prop="senderUsername" width="150") + template(v-once #default="scope") + span.x-link(v-text="scope.row.senderUsername" @click="showUserDialog(scope.row.senderUserId)") + el-table-column(label="Photo" width="100" prop="photo") + template(v-once #default="scope") + el-popover(v-if="scope.row.details.imageUrl" placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="scope.row.details.imageUrl" style="flex:none;height:60px;border-radius:4px") + img.x-link(v-lazy="scope.row.details.imageUrl" style="width:500px" @click="openExternalLink(scope.row.details.imageUrl)") + el-table-column(label="Message" prop="message") + template(v-once #default="scope") + span(v-if="scope.row.message" v-text="scope.row.message") + span(v-else-if='scope.row.details.inviteMessage' v-text="scope.row.details.inviteMessage") + span(v-else-if='scope.row.details.requestMessage' v-text="scope.row.details.requestMessage") + span(v-else-if='scope.row.details.responseMessage' v-text="scope.row.details.responseMessage") + el-table-column(label="Action" width="80" align="right") + template(v-once #default="scope") + template(v-if="scope.row.senderUserId !== API.currentUser.id") + 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 + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'profile'") + div.options-container(style="margin-top:0") + span.header Profile + .x-friend-list(style="margin-top:10px") + .x-friend-item(@click="showUserDialog(API.currentUser.id)") + .avatar + img(v-if="displayVRCPlusIconsAsAvatar && API.currentUser.userIcon" v-lazy="API.currentUser.userIcon") + img(v-else v-lazy="API.currentUser.currentAvatarThumbnailImageUrl") + .detail + span.name(v-text="API.currentUser.displayName") + span.extra(v-text="API.currentUser.username") + .x-friend-item(style="cursor:default") + .detail + span.name Last Login + span.extra {{ API.currentUser.last_login | formatDate('YYYY-MM-DD HH24:MI:SS') }} + .x-friend-item(style="cursor:default") + .detail + span.name Two-Factor Auth (2FA) + span.extra {{ API.currentUser.twoFactorAuthEnabled ? 'Enabled' : 'Disabled' }} + div(style="margin-top:10px") + el-button(size="small" icon="el-icon-switch-button" @click="logout()") Logout + el-button(size="small" icon="el-icon-printer" @click="showExportFriendsListDialog()") Export Friends List + div.options-container + span.header Game Info + .x-friend-list(style="margin-top:10px") + .x-friend-item + .detail(@click="API.getVisits()") + span.name Online Users + span.extra(v-if="visits") {{visits}} users online. + span.extra(v-else) Click to refresh + div.options-container + span.header Direct Access + div(style="margin-top:10px") + el-button-group + el-button(size="small" @click="promptUserDialog()") User + el-button(size="small" @click="promptWorldDialog()") World + el-button(size="small" @click="promptAvatarDialog()") Avatar + div.options-container + span.header Invite Messages + el-tooltip(placement="top") + template(#content) + span Refresh + 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.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") + 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('message', scope.row)") + div.options-container + span.header Invite Response Messages + el-tooltip(placement="top") + template(#content) + span Refresh + 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.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") + 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('response', scope.row)") + div.options-container + span.header Invite Request Messages + el-tooltip(placement="top") + template(#content) + span Refresh + 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.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") + 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('request', scope.row)") + 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) + span Refresh + el-button(type="default" @click="displayVRCPlusIconsTable()" 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="VRCPlusIconsTable = {}" size="mini" icon="el-icon-delete" circle style="margin-left:0") + template(v-if="API.currentUser.$isVRCPlus") + el-tooltip(placement="top") + template(#content) + span Upload icon + div(style="display:inline-block") + el-button(type="default" @click="displayVRCPlusIconUpload" size="mini" icon="el-icon-upload2" circle style="margin-left:0") + input(type="file" multiple accept="image/*" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none") + el-tooltip(placement="top") + template(#content) + span Reset icon + el-button(type="default" @click="setVRCPlusIcon('')" size="mini" icon="el-icon-close" circle style="margin-left:0" :disabled="!API.currentUser.userIcon") + br + .x-friend-item(v-for="icon in VRCPlusIconsTable" :key="icon.id" style="display:inline-block;margin-top:10px;cursor:default") + .vrcplus-icon(style="" @click="setVRCPlusIcon(icon.id)" :class="{ 'current-vrcplus-icon': compareCurrentVRCPlusIcon(icon.id) }") + img.avatar(v-if="icon.versions[1].file.url" v-lazy="icon.versions[1].file.url") + el-tooltip(placement="top") + template(#content) + span Delete + el-button(type="default" @click="deleteVRCPlusIcon(icon.id)" size="mini" icon="el-icon-delete" circle style="float:right;") + div.options-container + span.header Past Display Names + data-tables(v-bind="pastDisplayNameTable" style="margin-top:10px") + el-table-column(label="Date" prop="updated_at" sortable="custom") + template(v-once #default="scope") + span {{ scope.row.updated_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} + el-table-column(label="Name" prop="displayName") + div.options-container + span.header Config JSON + el-tooltip(placement="top") + template(#content) + span Refresh + el-button(type="default" @click="refreshConfigTreeData()" 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="configTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") + 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") + span(v-if="!scope.data.children" v-text="scope.data.value") + div.options-container + span.header Current User JSON + el-tooltip(placement="top") + template(#content) + span Refresh + el-button(type="default" @click="refreshCurrentUserTreeData()" 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="currentUserTreeData = []" size="mini" icon="el-icon-delete" circle style="margin-left:0") + 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") + span(v-if="!scope.data.children" v-text="scope.data.value") + + //- settings + .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'settings'") + div.options-container(style="margin-top:0") + span.header Settings + .x-friend-list(style="margin-top:10px") + .x-friend-item(style="cursor:default") + .detail + span.name Version + span.extra(v-text="appVersion") + .x-friend-item(@click="checkAppVersion()") + .detail + span.name Latest Version + span.extra(v-if="latestAppVersion" v-text="latestAppVersion") + span.extra(v-else) Click to refresh + .x-friend-item(@click="openExternalLink('https://github.com/pypy-vrc/VRCX')") + .detail + span.name Repository URL + span.extra https://github.com/pypy-vrc/VRCX + div.options-container + span.header Appearance + div.options-container-item + span.name Dark Mode + el-switch(v-model="isDarkMode") + div.options-container-item + span.name VRCPlus Profile Icons + el-switch(v-model="displayVRCPlusIconsAsAvatar") + div.options-container + span.header Side Pannel Sorting Options + div.options-container-item + span.name VIP + el-switch(v-model="orderFriendsGroup0" inactive-text="by name" active-text="by state") + div.options-container-item + span.name Online + el-switch(v-model="orderFriendsGroup1" inactive-text="by name" active-text="by state") + div.options-container-item + span.name Active + el-switch(v-model="orderFriendsGroup2" inactive-text="by name" active-text="by state") + div.options-container-item + span.name Offline + el-switch(v-model="orderFriendsGroup3" inactive-text="by name" active-text="by state") + div.options-container + span.header Trust Rank Colors + div.options-container-item + div + v-swatches(v-model="trustColor.untrusted" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-untrusted") + span.color-picker(slot="trigger") #[i.el-icon-s-open] Visitor + div + v-swatches(v-model="trustColor.basic" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-basic") + span.color-picker(slot="trigger") #[i.el-icon-s-open] New User + div + v-swatches(v-model="trustColor.known" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-known") + span.color-picker(slot="trigger") #[i.el-icon-s-open] User + div + v-swatches(v-model="trustColor.trusted" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-trusted") + span.color-picker(slot="trigger") #[i.el-icon-s-open] Known User + div + v-swatches(v-model="trustColor.veteran" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-veteran") + span.color-picker(slot="trigger") #[i.el-icon-s-open] Trusted User + div + v-swatches(v-model="trustColor.legend" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-legend") + span.color-picker(slot="trigger") #[i.el-icon-s-open] Veteran User + div + v-swatches(v-model="trustColor.legendary" show-fallback fallback-input-type="color" popover-x="right" :swatches="trustColorSwatches" class="x-tag-legendary") + span.color-picker(slot="trigger") #[i.el-icon-s-open] Legendary User + div.options-container + span.header Discord Presence + div.options-container-item + span * Only works when VRChat is running. + div.options-container-item + span.name Enable + el-switch(v-model="discordActive") + div.options-container-item + span.name Instance details + el-switch(v-model="discordInstance" :disabled="!discordActive") + div.options-container + span.header SteamVR Overlay + div.options-container-item + span * It runs automatically when VRChat is running. + br + br + span Grip: Vive or Other Controllers Grab, Oculus X/A Buttons + br + span Menu: Vive Menu, Index B, Oculus Y/B Buttons + br + div.options-container-item + span.name Enable + el-switch(v-model="openVR") + div.options-container-item + span.name Force Run (Opens SteamVR) + el-switch(v-model="openVRAlways" :disabled="!openVR") + div.options-container-item + span.name(style="min-width:137px") Overlay Button + el-switch(v-model="overlaybutton" inactive-text="Grip" active-text="Menu" :disabled="!openVR") + br + span.sub-header Display Options + div.options-container-item + span.name Minimal Feed Icons + el-switch(v-model="minimalFeed" :disabled="!openVR") + div.options-container-item + span.name Hide VR Devices + el-switch(v-model="hideDevicesFromFeed" :disabled="!openVR") + div.options-container-item + span.name Hide Private Worlds + el-switch(v-model="hidePrivateFromFeed") + div.options-container-item + span.name Hide Joins When Joining + el-switch(v-model="hideOnPlayerJoined") + div.options-container-item + el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog()" :disabled="!openVR") Wrist Feed Filters + el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog()") Notification Filters + br + span.sub-header VR Notifications + div.options-container-item + span.name Overlay Notifications + el-switch(v-model="overlayNotifications" :disabled="!openVR") + div.options-container-item + el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout()" :disabled="!overlayNotifications || !openVR") Notification Timeout + el-button(size="small" icon="el-icon-rank" @click="showNotificationPositionDialog()" :disabled="!overlayNotifications || !openVR") Notification Position + br + span.sub-header Desktop Notifications + div.options-container-item + span.name When to display notifications: + br + toggle-switch(:options="desktopToastToggleSwitchOption" group="desktopToastToggleSwitchOption" v-model="desktopToast" class="toggle-switch") + br + span.sub-header TTS Options + div.options-container-item + span.name Notification TTS + el-switch(v-model="notificationTTS") + div.options-container-item + span.name Text-to-Speech Voice + el-dropdown(@command="(voice) => changeTTSVoice(voice)" trigger="click" size="small") + el-button(v-text="TTSvoices[notificationTTSVoice].name" size="mini" :disabled="!notificationTTS") + el-dropdown-menu(#default="dropdown") + el-dropdown-item(v-if="voice" v-for="(voice, index) in TTSvoices" :key="index" v-text="voice.name" :command="index") + div.options-container + span.header Application + div.options-container-item + span.name Start at Windows startup + el-switch(v-model="isStartAtWindowsStartup") + div.options-container-item + span.name Start as minimized state + el-switch(v-model="isStartAsMinimizedState") + div.options-container-item + span.name Close to tray + el-switch(v-model="isCloseToTray") + div.options-container-item + span.name Auto login + el-switch(v-model="isAutoLogin") + div.options-container-item + el-button-group + el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") Launch Options + div.options-container(style="margin-top:45px;border-top:1px solid #eee;padding-top:30px") + span.header Legal Notice + div.options-container-item + p © 2019-2021 #[a(href="https://github.com/pypy-vrc" target="_blank") pypy] (mina#5656) + p VRCX is an assistant application for provide information about manage friendship. this application uses unofficial VRChat API (VRCSDK). + p VRCX isn't endorsed by VRChat and doesn't reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat. VRChat is trademark of VRChat Inc. VRChat © VRChat Inc. + p pypy is not responsible for any problems caused by VRCX. Use at your own risk! + div.options-container-item + el-button(@click="ossDialog = true" size="small") Open Source Software Notice + + //- friends + .x-aside-container + el-select(v-model="quickSearch" clearable placeholder="Search" filterable remote :remote-method="quickSearchRemoteMethod" popper-class="x-quick-search" @change="quickSearchChange" @visible-change="quickSearchVisibleChange" style="flex:none;padding:10px") + el-option(v-for="item in quickSearchItems" :key="item.value" :value="item.value" :label="item.label") + .x-friend-item + template(v-if="item.ref") + .detail + span.name(v-text="item.ref.displayName" :class="item.ref.$trustClass") + location.extra(:location="item.ref.location" :link="false") + img.avatar(v-if="displayVRCPlusIconsAsAvatar && item.ref.userIcon" v-lazy="item.ref.userIcon") + img.avatar(v-else v-lazy="item.ref.currentAvatarThumbnailImageUrl") + span(v-else) Search More: #[span(v-text="item.label" style="font-weight:bold")] + .x-friend-list(style="padding-bottom:10px") + .x-friend-group + i.el-icon-arrow-right(:class="{ rotate: isFriendsGroupMe }") + span.x-link(@click="isFriendsGroupMe = !isFriendsGroupMe" style="margin-left:5px") ME + div(v-show="isFriendsGroupMe") + .x-friend-item(:key="API.currentUser.id" @click="showUserDialog(API.currentUser.id)") + .avatar(:class="userStatusClass(API.currentUser)") + img(v-if="displayVRCPlusIconsAsAvatar && API.currentUser.userIcon" v-lazy="API.currentUser.userIcon") + img(v-else v-lazy="API.currentUser.currentAvatarThumbnailImageUrl") + .detail + span.name(v-text="API.currentUser.displayName" :class="API.currentUser.$trustClass") + location.extra(v-if="isGameRunning === true" :location="lastLocation.location" :link="false") + span.extra(v-else v-text="API.currentUser.statusDescription" :link="false") + .x-friend-group(v-show="friendsGroup0.length") + i.el-icon-arrow-right(:class="{ rotate: isFriendsGroup0 }") + span.x-link(@click="isFriendsGroup0 = !isFriendsGroup0" style="margin-left:5px") VIP―{{ friendsGroup0.length }} + div(v-show="isFriendsGroup0") + .x-friend-item(v-for="friend in friendsGroup0" :key="friend.id" @click="showUserDialog(friend.id)") + template(v-if="friend.ref") + .avatar(:class="userStatusClass(friend.ref)") + img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") + img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") + .detail + span.name(v-if="friend.memo" :class="friend.ref.$trustClass") {{ friend.ref.displayName }} ({{ friend.memo }}) + span.name(v-else v-text="friend.ref.displayName" :class="friend.ref.$trustClass") + location.extra(:location="friend.ref.location" :link="false") + template(v-else) + span(v-text="friend.name || friend.id") + el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px") + .x-friend-group(v-show="friendsGroup1.length") + i.el-icon-arrow-right(:class="{ rotate: isFriendsGroup1 }") + span.x-link(@click="isFriendsGroup1 = !isFriendsGroup1" style="margin-left:5px") ONLINE―{{ friendsGroup1.length }} + div(v-show="isFriendsGroup1") + .x-friend-item(v-for="friend in friendsGroup1" :key="friend.id" @click="showUserDialog(friend.id)") + template(v-if="friend.ref") + .avatar(:class="userStatusClass(friend.ref)") + img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") + img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") + .detail + span.name(v-if="friend.memo" :class="friend.ref.$trustClass") {{ friend.ref.displayName }} ({{ friend.memo }}) + span.name(v-else v-text="friend.ref.displayName" :class="friend.ref.$trustClass") + location.extra(:location="friend.ref.location" :link="false") + template(v-else) + span(v-text="friend.name || friend.id") + el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px") + .x-friend-group(v-show="friendsGroup2.length") + i.el-icon-arrow-right(:class="{ rotate: isFriendsGroup2 }") + span.x-link(@click="isFriendsGroup2 = !isFriendsGroup2" style="margin-left:5px") ACTIVE―{{ friendsGroup2.length }} + div(v-show="isFriendsGroup2") + .x-friend-item(v-for="friend in friendsGroup2" :key="friend.id" @click="showUserDialog(friend.id)") + template(v-if="friend.ref") + .avatar + img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") + img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") + .detail + span.name(v-if="friend.memo" :class="friend.ref.$trustClass") {{ friend.ref.displayName }} ({{ friend.memo }}) + span.name(v-else v-text="friend.ref.displayName" :class="friend.ref.$trustClass") + span.extra(v-text="friend.ref.statusDescription" :link="false") + template(v-else) + span(v-text="friend.name || friend.id") + el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px") + .x-friend-group(v-show="friendsGroup3.length") + i.el-icon-arrow-right(:class="{ rotate: isFriendsGroup3 }") + span.x-link(@click="isFriendsGroup3 = !isFriendsGroup3" style="margin-left:5px") OFFLINE―{{ friendsGroup3.length }} + div(v-show="isFriendsGroup3") + .x-friend-item(v-for="friend in friendsGroup3" :key="friend.id" @click="showUserDialog(friend.id)") + template(v-if="friend.ref") + .avatar + img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") + img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") + .detail + span.name(v-if="friend.memo" :class="friend.ref.$trustClass") {{ friend.ref.displayName }} ({{ friend.memo }}) + span.name(v-else v-text="friend.ref.displayName" :class="friend.ref.$trustClass") + span.extra(v-text="friend.ref.statusDescription") + template(v-else) + span(v-text="friend.name || friend.id") + el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px") + + //- dialog: user + el-dialog.x-dialog.x-user-dialog(ref="userDialog" :visible.sync="userDialog.visible" :show-close="false" width="770px") + div(v-loading="userDialog.loading") + div(style="display:flex") + el-popover(placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="userDialog.ref.currentAvatarThumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") + img.x-link(v-lazy="userDialog.ref.currentAvatarImageUrl" style="width:500px;height:375px" @click="openExternalLink(userDialog.ref.currentAvatarImageUrl)") + div(style="flex:1;display:flex;align-items:center;margin-left:15px") + div(style="flex:1") + div + el-tooltip(v-if="userDialog.ref.status" placement="top") + template(#content) + span(v-if="userDialog.ref.location === 'offline'") Offline + span(v-else-if="userDialog.ref.status === 'active'") Online + span(v-else-if="userDialog.ref.status === 'join me'") Join Me + span(v-else-if="userDialog.ref.status === 'ask me'") Ask Me + span(v-else-if="userDialog.ref.status === 'busy'") Do Not Disturb + span(v-else) Offline + i.x-user-status(:class="userStatusClass(userDialog.ref)") + span(v-text="userDialog.ref.displayName" style="margin-left:5px;font-weight:bold") + el-popover(placement="top" trigger="click") + span(slot="reference" v-text="userDialog.ref.username" style="margin-left:5px;color:#909399;font-family:monospace;font-size:12px;cursor:pointer") + span(style="display:block;text-align:center;font-family:monospace") {{ userDialog.ref.username | textToHex }} + el-tooltip(v-for="item in userDialog.ref.$languages" :key="item.key" placement="top") + template(#content) + span {{ item.value }} ({{ item.key }}) + span.famfamfam-flags(:class="languageClass(item.key)" style="display:inline-block;margin-left:5px") + div(style="margin-top:5px") + el-tag.name(type="info" effect="plain" size="mini" :class="userDialog.ref.$trustClass" v-text="userDialog.ref.$trustLevel") + el-tag.x-tag-friend(v-if="userDialog.isFriend && userDialog.friend" type="info" effect="plain" size="mini" style="margin-left:5px") Friend No.{{userDialog.friend.no}} + el-tag.x-tag-vrcplus(type="info" effect="plain" size="mini" v-if="userDialog.ref.$isVRCPlus" style="margin-left:5px") VRC+ + el-tag.x-tag-platform-pc(type="info" effect="plain" size="mini" v-if="userDialog.ref.last_platform === 'standalonewindows'" style="margin-left:5px") PC + el-tag.x-tag-platform-quest(type="info" effect="plain" size="mini" v-if="userDialog.ref.last_platform === 'android'" style="margin-left:5px") Quest + div(style="margin-top:5px") + span(v-text="userDialog.ref.statusDescription" style="font-size:12px") + div(v-if="userDialog.ref.userIcon" style="flex:none;margin-right:10px") + el-popover(placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="userDialog.ref.userIcon" style="flex:none;width:120px;height:120px;border-radius:4px") + img.x-link(v-lazy="userDialog.ref.userIcon" style="width:500px;height:500px;" @click="openExternalLink(userDialog.ref.userIcon)") + div(style="flex:none") + el-button(v-if="userDialog.isFavorite" @click="userDialogCommand('Delete Favorite')" type="warning" icon="el-icon-star-on" circle) + el-button(v-else type="default" @click="userDialogCommand('Add Favorite')" icon="el-icon-star-off" circle) + el-dropdown(trigger="click" @command="userDialogCommand" size="small") + el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest) ? 'success' : (userDialog.isBlock || userDialog.isMute || userDialog.isHideAvatar) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px") + el-dropdown-menu(#default="dropdown") + template(v-if="userDialog.ref.id === API.currentUser.id") + el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Details") Show Avatar Details + el-dropdown-item(icon="el-icon-edit" command="Edit Social Status" divided) Social Status + el-dropdown-item(icon="el-icon-edit" command="Edit Language") Language + el-dropdown-item(icon="el-icon-edit" command="Edit Bio") Bio + el-dropdown-item(icon="el-icon-switch-button" command="Logout" divided) Logout + template(v-else) + template(v-if="lastLocation.location && isGameRunning") + el-dropdown-item(icon="el-icon-message" command="Invite") Invite + el-dropdown-item(icon="el-icon-message" command="Invite Message") Invite With Message + template(v-if="userDialog.isFriend") + el-dropdown-item(icon="el-icon-postcard" command="Request Invite") Request Invite + el-dropdown-item(icon="el-icon-postcard" command="Request Invite Message") Request Invite With Message + template(v-else-if="userDialog.incomingRequest") + el-dropdown-item(icon="el-icon-check" command="Accept Friend Request") Accept Friend Request + el-dropdown-item(icon="el-icon-close" command="Decline Friend Request") Decline Friend Request + el-dropdown-item(v-else-if="userDialog.outgoingRequest" icon="el-icon-close" command="Cancel Friend Request") Cancel Friend Request + el-dropdown-item(v-else icon="el-icon-plus" command="Send Friend Request") Send Friend Request + el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Details" divided) Show Avatar Details + el-dropdown-item(v-if="userDialog.isBlock" icon="el-icon-circle-check" command="Unblock" divided style="color:#F56C6C") Unblock + el-dropdown-item(v-else icon="el-icon-circle-close" command="Block" divided) Block + el-dropdown-item(v-if="userDialog.isMute" icon="el-icon-microphone" command="Unmute" style="color:#F56C6C") Unmute + el-dropdown-item(v-else icon="el-icon-turn-off-microphone" command="Mute") Mute + el-dropdown-item(v-if="userDialog.isHideAvatar" icon="el-icon-user-solid" command="Show Avatar" style="color:#F56C6C") Show Avatar + el-dropdown-item(v-else icon="el-icon-user" command="Hide Avatar") Hide Avatar + template(v-if="userDialog.isFriend") + el-dropdown-item(icon="el-icon-delete" command="Unfriend" divided) Unfriend + el-tabs + el-tab-pane(label="Info") + div(v-if="userDialog.ref.location" style="display:flex;flex-direction:column;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #eee") + div(style="flex:none") + location(:location="userDialog.ref.location") + template(#default v-if="userDialog.instance.occupants") ({{ userDialog.instance.occupants }}) + launch(:location="userDialog.ref.location" style="margin-left:5px") + invite-yourself(:location="userDialog.ref.location" style="margin-left:5px") + .x-friend-list(style="flex:1;margin-top:10px") + .x-friend-item(v-if="userDialog.$location.userId" @click="showUserDialog(userDialog.$location.userId)") + template(v-if="userDialog.$location.user") + .avatar(:class="userStatusClass(userDialog.$location.user)") + img(v-if="displayVRCPlusIconsAsAvatar && userDialog.$location.user.userIcon" v-lazy="userDialog.$location.user.userIcon") + img(v-else v-lazy="userDialog.$location.user.currentAvatarThumbnailImageUrl") + .detail + span.name(v-text="userDialog.$location.user.displayName" :class="userDialog.$location.user.$trustClass") + span.extra Instance Creator + span(v-else v-text="userDialog.$location.userId") + .x-friend-item(v-for="user in userDialog.users" :key="user.id" @click="showUserDialog(user.id)") + .avatar(:class="userStatusClass(user)") + img(v-if="displayVRCPlusIconsAsAvatar && user.userIcon" v-lazy="user.userIcon") + img(v-else v-lazy="user.currentAvatarThumbnailImageUrl") + .detail + span.name(v-text="user.displayName" :class="user.$trustClass") + span.extra + timer(:epoch="user.$location_at") + .x-friend-list(style="max-height:none") + .x-friend-item(style="width:100%;cursor:default") + .detail + span.name Note + el-input.extra(v-model="userDialog.memo" type="textarea" :rows="2" placeholder="Click to add a note" size="mini" resize="none") + .x-friend-item(style="width:100%;cursor:default") + .detail + span.name Bio + pre.extra(style="font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0 0.5em 0 0") {{ userDialog.ref.bio || '-' }} + div(style="margin-top:5px") + el-tooltip(v-if="link" v-for="(link, index) in userDialog.ref.bioLinks" :key="index") + template(#content) + span(v-text="link") + img(:src="getFaviconUrl(link)" style="width:16px;height:16px;vertical-align:middle;margin-right:5px;cursor:pointer" @click.stop="openExternalLink(link)") + .x-friend-item(style="cursor:default") + .detail + span.name Avatar Copying + span.extra(v-if="userDialog.ref.allowAvatarCopying" style="color:#67C23A") Allow + span.extra(v-else style="color:#F56C6C") Deny + .x-friend-item(style="cursor:default") + .detail + span.name(v-if="userDialog.ref.state === 'online' && userDialog.ref.$online_for") Online For + span.name(v-else) Offline For + span.extra(v-text="userOnlineFor(userDialog)") + .x-friend-item(style="cursor:default") + .detail + span.name Last Login + span.extra {{ userDialog.ref.last_login | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} + .x-friend-item(style="cursor:default") + .detail + span.name Date Joined + span.extra(v-text="userDialog.ref.date_joined") + .x-friend-item(v-if="userDialog.ref.id === API.currentUser.id && API.currentUser.homeLocation" @click="showWorldDialog(API.currentUser.homeLocation)" style="width:100%") + .detail + span.name Home Location + span.extra + location(:location="API.currentUser.homeLocation" :link="false") + el-button(@click.stop="resetHome()" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + el-tab-pane(label="Worlds") + el-button(type="default" :loading="userDialog.isWorldsLoading" @click="refreshUserDialogWorlds()" size="mini" icon="el-icon-refresh" circle) + span(style="margin-left:5px") Total {{ userDialog.worlds.length }} + el-radio-group(v-model="userDialog.worldSorting" size="mini" style="margin-left:30px" @change="changeUserDialogWorldSorting") + el-radio(label="name") by name + el-radio(label="update") by update + .x-friend-list(v-loading="userDialog.isWorldsLoading" style="margin-top:10px;min-height:60px") + .x-friend-item(v-for="world in userDialog.worlds" :key="world.id" @click="showWorldDialog(world.id)") + .avatar + img(v-lazy="world.thumbnailImageUrl") + .detail + span.name(v-text="world.name") + span.extra(v-if="world.occupants") ({{ world.occupants }}) + el-tab-pane(label="Avatars") + el-button(type="default" :loading="userDialog.isAvatarsLoading" @click="refreshUserDialogAvatars()" size="mini" icon="el-icon-refresh" circle) + span(style="margin-left:5px") Total {{ userDialogAvatars.length }} + el-radio-group(v-model="userDialog.avatarSorting" size="mini" style="margin-left:30px" @change="changeUserDialogAvatarSorting") + el-radio(label="name") by name + el-radio(label="update") by update + el-radio-group(v-model="userDialog.avatarReleaseStatus" size="mini" style="margin-left:60px") + el-radio(label="all") all + el-radio(label="public") public + el-radio(label="private") private + .x-friend-list(v-loading="userDialog.isAvatarsLoading" style="margin-top:10px;min-height:60px") + .x-friend-item(v-for="avatar in userDialogAvatars" :key="avatar.id" @click="showAvatarDialog(avatar.id)") + .avatar + img(v-lazy="avatar.thumbnailImageUrl") + .detail + span.name(v-text="avatar.name") + span.extra(v-text="avatar.releaseStatus" v-if="avatar.releaseStatus === 'public'" style="color: #67c23a;") + span.extra(v-text="avatar.releaseStatus" v-else-if="avatar.releaseStatus === 'private'" style="color: #f56c6c;") + span.extra(v-text="avatar.releaseStatus" v-else) + el-tab-pane(label="JSON") + el-button(type="default" @click="refreshUserDialogTreeData()" size="mini" icon="el-icon-refresh" circle) + el-tree(:data="userDialog.treeData" style="margin-top:5px;font-size:12px") + template(#default="scope") + span + span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") + span(v-if="!scope.data.children" v-text="scope.data.value") + + //- dialog: world + el-dialog.x-dialog.x-world-dialog(ref="worldDialog" :visible.sync="worldDialog.visible" :show-close="false" width="600px") + div(v-loading="worldDialog.loading") + div(style="display:flex") + el-popover(placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="worldDialog.ref.thumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") + img.x-link(v-lazy="worldDialog.ref.imageUrl" style="width:500px;height:375px" @click="openExternalLink(worldDialog.ref.imageUrl)") + div(style="flex:1;display:flex;align-items:center;margin-left:15px") + div(style="flex:1") + div + i.el-icon-s-home(v-show="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id") + span(v-text="worldDialog.ref.name" style="font-weight:bold") + div(style="margin-top:5px") + span.x-link(v-text="worldDialog.ref.authorName" @click="showUserDialog(worldDialog.ref.authorId)" style="color:#909399;font-family:monospace") + div(style="margin-top:5px") + el-tag(v-if="worldDialog.ref.$isLabs" type="primary" effect="plain" size="mini") Labs + el-tag(v-else-if="worldDialog.ref.releaseStatus === 'public'" type="success" effect="plain" size="mini") Public + el-tag(v-else type="danger" effect="plain" size="mini") Private + el-tag(type="info" effect="plain" size="mini" v-text="worldDialog.fileSize" style="margin-left:5px") + div(style="margin-top:5px") + span(v-show="worldDialog.ref.name !== worldDialog.ref.description" v-text="worldDialog.ref.description" style="font-size:12px") + div(style="flex:none;margin-left:10px") + el-button(v-if="worldDialog.isFavorite" type="warning" icon="el-icon-star-on" circle @click="worldDialogCommand('Delete Favorite')") + el-button(v-else type="default" icon="el-icon-star-off" circle @click="worldDialogCommand('Add Favorite')") + el-dropdown(trigger="click" @command="worldDialogCommand" size="small" style="margin-left:5px") + el-button(type="default" icon="el-icon-more" circle) + el-dropdown-menu(#default="dropdown") + el-dropdown-item(icon="el-icon-refresh" command="Refresh") Refresh + el-dropdown-item(icon="el-icon-s-flag" command="New Instance" divided) New Instance + el-dropdown-item(v-if="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id" icon="el-icon-magic-stick" command="Reset Home" divided) Reset Home + el-dropdown-item(v-else icon="el-icon-s-home" command="Make Home" divided) Make Home + el-tabs + el-tab-pane(label="Instances") + div. + #[i.el-icon-user] Public {{ worldDialog.ref.publicOccupants | commaNumber }} + #[i.el-icon-user-solid(style="margin-left:10px")] Private {{ worldDialog.ref.privateOccupants | commaNumber }} + #[i.el-icon-check(style="margin-left:10px")] Capacity {{ worldDialog.ref.capacity | commaNumber }} + div(v-for="room in worldDialog.rooms" :key="room.id") + div(style="margin:5px 0") + span.x-link(@click="showLaunchDialog(room.$location.tag)"). + \#{{ room.$location.instanceName }} {{ room.$location.accessType }} #[template(v-if="room.occupants") ({{ room.occupants }})] + invite-yourself(:location="room.$location.tag" style="margin-left:5px") + .x-friend-list(style="margin:10px 0" v-if="room.$location.userId || room.users.length") + .x-friend-item(v-if="room.$location.userId" @click="showUserDialog(room.$location.userId)") + template(v-if="room.$location.user") + .avatar(:class="userStatusClass(room.$location.user)") + img(v-if="displayVRCPlusIconsAsAvatar && room.$location.user.userIcon" v-lazy="room.$location.user.userIcon") + img(v-else v-lazy="room.$location.user.currentAvatarThumbnailImageUrl") + .detail + span.name(v-text="room.$location.user.displayName" :class="room.$location.user.$trustClass") + span.extra Instance Creator + span(v-else v-text="room.$location.userId") + .x-friend-item(v-for="user in room.users" :key="user.id" @click="showUserDialog(user.id)") + .avatar(:class="userStatusClass(user)") + img(v-if="displayVRCPlusIconsAsAvatar && user.userIcon" v-lazy="user.userIcon") + img(v-else v-lazy="user.currentAvatarThumbnailImageUrl") + .detail + span.name(v-text="user.displayName" :class="user.$trustClass") + span.extra + timer(:epoch="user.$location_at") + el-tab-pane(label="Info") + .x-friend-list(style="max-height:none") + .x-friend-item(style="cursor:default") + .detail + span.name Players + span.extra {{ worldDialog.ref.occupants | commaNumber }} + .x-friend-item(style="cursor:default") + .detail + span.name Favorites + span.extra {{ worldDialog.ref.favorites | commaNumber }} + .x-friend-item(style="cursor:default") + .detail + span.name Visits + span.extra {{ worldDialog.ref.visits | commaNumber }} + .x-friend-item(style="cursor:default") + .detail + span.name Capacity + span.extra(v-text="worldDialog.ref.capacity") + .x-friend-item(style="cursor:default") + .detail + span.name Heat + span.extra {{ worldDialog.ref.heat | commaNumber }} {{ '🔥'.repeat(worldDialog.ref.heat) }} + .x-friend-item(style="cursor:default") + .detail + span.name Popularity + span.extra {{ worldDialog.ref.popularity | commaNumber }} {{ '💖'.repeat(worldDialog.ref.popularity) }} + .x-friend-item(style="cursor:default") + .detail + span.name Created + span.extra {{ worldDialog.ref.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} + .x-friend-item(style="cursor:default") + .detail + span.name Last Updated + span.extra {{ worldDialog.fileCreatedAt | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} + .x-friend-item(style="cursor:default") + .detail + span.name Version + span.extra(v-text="worldDialog.ref.version") + .x-friend-item(style="width:100%;cursor:default") + .detail + span.name Platform + span.extra(v-text="worldDialogPlatform") + el-tab-pane(label="JSON") + el-button(type="default" @click="refreshWorldDialogTreeData()" size="mini" icon="el-icon-refresh" circle) + el-tree(:data="worldDialog.treeData" style="margin-top:5px;font-size:12px") + template(#default="scope") + span + span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") + span(v-if="!scope.data.children" v-text="scope.data.value") + + //- dialog: avatar + el-dialog.x-dialog.x-avatar-dialog(ref="avatarDialog" :visible.sync="avatarDialog.visible" :show-close="false" width="600px") + div(v-loading="avatarDialog.loading") + div(style="display:flex") + el-popover(placement="right" width="500px" trigger="click") + img.x-link(slot="reference" v-lazy="avatarDialog.ref.thumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") + img.x-link(v-lazy="avatarDialog.ref.imageUrl" style="width:500px;height:375px" @click="openExternalLink(avatarDialog.ref.imageUrl)") + div(style="flex:1;display:flex;align-items:center;margin-left:15px") + div(style="flex:1") + div + span(v-text="avatarDialog.ref.name" style="font-weight:bold") + div(style="margin-top:5px") + span.x-link(v-text="avatarDialog.ref.authorName" @click="showUserDialog(avatarDialog.ref.authorId)" style="color:#909399;font-family:monospace") + div(style="margin-top:5px") + el-tag(v-if="avatarDialog.ref.releaseStatus === 'public'" type="success" effect="plain" size="mini") Public + el-tag(v-else type="danger" effect="plain" size="mini") Private + el-tag(type="info" effect="plain" size="mini" v-text="avatarDialog.fileSize" style="margin-left:5px") + div(style="margin-top:5px") + span(v-show="avatarDialog.ref.name !== avatarDialog.ref.description" v-text="avatarDialog.ref.description" style="font-size:12px") + div(style="flex:none;margin-left:10px") + el-button(v-if="avatarDialog.isFavorite" type="warning" icon="el-icon-star-on" circle @click="avatarDialogCommand('Delete Favorite')") + el-button(v-else type="default" icon="el-icon-star-off" circle @click="avatarDialogCommand('Add Favorite')") + el-dropdown(trigger="click" @command="avatarDialogCommand" size="small") + el-button(type="default" icon="el-icon-more" circle) + el-dropdown-menu(#default="dropdown") + el-dropdown-item(icon="el-icon-check" command="Select Avatar") Select Avatar + template(v-if="avatarDialog.ref.authorId === API.currentUser.id") + el-dropdown-item(v-if="avatarDialog.ref.releaseStatus === 'public'" icon="el-icon-user-solid" command="Make Private" divided) Make Private + el-dropdown-item(v-else icon="el-icon-user" command="Make Public" divided) Make Public + el-tabs + el-tab-pane(label="Info") + .x-friend-list + .x-friend-item(style="cursor:default") + .detail + span.name Created + span.extra {{ avatarDialog.ref.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} + .x-friend-item(style="cursor:default") + .detail + span.name Last Updated + span.extra {{ avatarDialog.fileCreatedAt | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} + .x-friend-item(style="cursor:default") + .detail + span.name Version + span.extra(v-text="avatarDialog.ref.version") + .x-friend-item(style="width:100%;cursor:default") + .detail + span.name Platform + span.extra(v-text="avatarDialogPlatform") + el-tab-pane(label="JSON") + el-button(type="default" @click="refreshAvatarDialogTreeData()" size="mini" icon="el-icon-refresh" circle) + el-tree(:data="avatarDialog.treeData" style="margin-top:5px;font-size:12px") + template(#default="scope") + span + span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") + span(v-if="!scope.data.children" v-text="scope.data.value") + + //- dialog: favorite + el-dialog.x-dialog(ref="favoriteDialog" :visible.sync="favoriteDialog.visible" title="Choose Group" width="250px") + div(v-loading="favoriteDialog.loading") + el-button(v-for="group in favoriteDialog.groups" :key="group.name" style="display:block;width:100%;margin:10px 0" @click="addFavorite(group)" :disabled="group.count >= group.capacity") {{ group.displayName }} ({{ group.count }} / {{ group.capacity }}) + + //- dialog: invite + el-dialog.x-dialog(ref="inviteDialog" :visible.sync="inviteDialog.visible" title="Invite" width="450px") + div(v-loading="inviteDialog.loading") + location(:location="inviteDialog.worldId" :link="false") + el-select(v-model="inviteDialog.userIds" multiple clearable placeholder="Choose Friends" filterable :disabled="inviteDialog.loading" style="width:100%;margin-top:15px") + el-option-group(v-if="API.currentUser" label="ME") + el-option.x-friend-item(:label="API.currentUser.displayName" :value="API.currentUser.id" style="height:auto") .avatar(:class="userStatusClass(API.currentUser)") img(v-if="displayVRCPlusIconsAsAvatar && API.currentUser.userIcon" v-lazy="API.currentUser.userIcon") img(v-else v-lazy="API.currentUser.currentAvatarThumbnailImageUrl") .detail - span.name(v-text="API.currentUser.displayName" :class="API.currentUser.$trustClass") - location.extra(v-if="isGameRunning === true" :location="lastLocation.location" :link="false") - span.extra(v-else v-text="API.currentUser.statusDescription" :link="false") - .x-friend-group(v-show="friendsGroup0.length") - i.el-icon-arrow-right(:class="{ rotate: isFriendsGroup0 }") - span.x-link(@click="isFriendsGroup0 = !isFriendsGroup0" style="margin-left:5px") VIP―{{ friendsGroup0.length }} - div(v-show="isFriendsGroup0") - .x-friend-item(v-for="friend in friendsGroup0" :key="friend.id" @click="showUserDialog(friend.id)") + span.name(v-text="API.currentUser.displayName") + el-option-group(v-if="friendsGroup0.length" label="VIP") + el-option.x-friend-item(v-for="friend in friendsGroup0" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto") template(v-if="friend.ref") .avatar(:class="userStatusClass(friend.ref)") img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") .detail - span.name(v-if="friend.memo" :class="friend.ref.$trustClass") {{ friend.ref.displayName }} ({{ friend.memo }}) - span.name(v-else v-text="friend.ref.displayName" :class="friend.ref.$trustClass") - location.extra(:location="friend.ref.location" :link="false") - template(v-else) - span(v-text="friend.name || friend.id") - el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px") - .x-friend-group(v-show="friendsGroup1.length") - i.el-icon-arrow-right(:class="{ rotate: isFriendsGroup1 }") - span.x-link(@click="isFriendsGroup1 = !isFriendsGroup1" style="margin-left:5px") ONLINE―{{ friendsGroup1.length }} - div(v-show="isFriendsGroup1") - .x-friend-item(v-for="friend in friendsGroup1" :key="friend.id" @click="showUserDialog(friend.id)") + span.name(v-text="friend.ref.displayName" :class="friend.ref.$trustClass") + span(v-else v-text="friend.id") + el-option-group(v-if="friendsGroup1.length" label="ONLINE") + el-option.x-friend-item(v-for="friend in friendsGroup1" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto") template(v-if="friend.ref") .avatar(:class="userStatusClass(friend.ref)") img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") .detail - span.name(v-if="friend.memo" :class="friend.ref.$trustClass") {{ friend.ref.displayName }} ({{ friend.memo }}) - span.name(v-else v-text="friend.ref.displayName" :class="friend.ref.$trustClass") - location.extra(:location="friend.ref.location" :link="false") - template(v-else) - span(v-text="friend.name || friend.id") - el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px") - .x-friend-group(v-show="friendsGroup2.length") - i.el-icon-arrow-right(:class="{ rotate: isFriendsGroup2 }") - span.x-link(@click="isFriendsGroup2 = !isFriendsGroup2" style="margin-left:5px") ACTIVE―{{ friendsGroup2.length }} - div(v-show="isFriendsGroup2") - .x-friend-item(v-for="friend in friendsGroup2" :key="friend.id" @click="showUserDialog(friend.id)") + span.name(v-text="friend.ref.displayName" :class="friend.ref.$trustClass") + span(v-else v-text="friend.id") + el-option-group(v-if="friendsGroup2.length" label="ACTIVE") + el-option.x-friend-item(v-for="friend in friendsGroup2" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto") template(v-if="friend.ref") .avatar img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") .detail - span.name(v-if="friend.memo" :class="friend.ref.$trustClass") {{ friend.ref.displayName }} ({{ friend.memo }}) - span.name(v-else v-text="friend.ref.displayName" :class="friend.ref.$trustClass") - span.extra(v-text="friend.ref.statusDescription" :link="false") - template(v-else) - span(v-text="friend.name || friend.id") - el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px") - .x-friend-group(v-show="friendsGroup3.length") - i.el-icon-arrow-right(:class="{ rotate: isFriendsGroup3 }") - span.x-link(@click="isFriendsGroup3 = !isFriendsGroup3" style="margin-left:5px") OFFLINE―{{ friendsGroup3.length }} - div(v-show="isFriendsGroup3") - .x-friend-item(v-for="friend in friendsGroup3" :key="friend.id" @click="showUserDialog(friend.id)") - template(v-if="friend.ref") - .avatar - img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") - img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") - .detail - span.name(v-if="friend.memo" :class="friend.ref.$trustClass") {{ friend.ref.displayName }} ({{ friend.memo }}) - span.name(v-else v-text="friend.ref.displayName" :class="friend.ref.$trustClass") - span.extra(v-text="friend.ref.statusDescription") - template(v-else) - span(v-text="friend.name || friend.id") - el-button(type="text" icon="el-icon-close" size="mini" @click.stop="confirmDeleteFriend(friend.id)" style="margin-left:5px") + span.name(v-text="friend.ref.displayName" :class="friend.ref.$trustClass") + span(v-else v-text="friend.id") + template(#footer) + el-button(type="primary" size="small" :disabled="inviteDialog.loading || !inviteDialog.userIds.length" @click="sendInvite()") Invite + el-button(type="primary" size="small" :disabled="inviteDialog.loading || !inviteDialog.userIds.length" @click="showSendInviteDialog()") Invite With Message - //- dialog: user - el-dialog.x-dialog.x-user-dialog(ref="userDialog" :visible.sync="userDialog.visible" :show-close="false" width="770px") - div(v-loading="userDialog.loading") - div(style="display:flex") - el-popover(placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="userDialog.ref.currentAvatarThumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") - img.x-link(v-lazy="userDialog.ref.currentAvatarImageUrl" style="width:500px;height:375px" @click="openExternalLink(userDialog.ref.currentAvatarImageUrl)") - div(style="flex:1;display:flex;align-items:center;margin-left:15px") - div(style="flex:1") - div - el-tooltip(v-if="userDialog.ref.status" placement="top") - template(#content) - span(v-if="userDialog.ref.location === 'offline'") Offline - span(v-else-if="userDialog.ref.status === 'active'") Online - span(v-else-if="userDialog.ref.status === 'join me'") Join Me - span(v-else-if="userDialog.ref.status === 'ask me'") Ask Me - span(v-else-if="userDialog.ref.status === 'busy'") Do Not Disturb - span(v-else) Offline - i.x-user-status(:class="userStatusClass(userDialog.ref)") - span(v-text="userDialog.ref.displayName" style="margin-left:5px;font-weight:bold") - el-popover(placement="top" trigger="click") - span(slot="reference" v-text="userDialog.ref.username" style="margin-left:5px;color:#909399;font-family:monospace;font-size:12px;cursor:pointer") - span(style="display:block;text-align:center;font-family:monospace") {{ userDialog.ref.username | textToHex }} - el-tooltip(v-for="item in userDialog.ref.$languages" :key="item.key" placement="top") - template(#content) - span {{ item.value }} ({{ item.key }}) - span.famfamfam-flags(:class="languageClass(item.key)" style="display:inline-block;margin-left:5px") - div(style="margin-top:5px") - el-tag.name(type="info" effect="plain" size="mini" :class="userDialog.ref.$trustClass" v-text="userDialog.ref.$trustLevel") - el-tag.x-tag-friend(v-if="userDialog.isFriend && userDialog.friend" type="info" effect="plain" size="mini" style="margin-left:5px") Friend No.{{userDialog.friend.no}} - el-tag.x-tag-vrcplus(type="info" effect="plain" size="mini" v-if="userDialog.ref.$isVRCPlus" style="margin-left:5px") VRC+ - el-tag.x-tag-platform-pc(type="info" effect="plain" size="mini" v-if="userDialog.ref.last_platform === 'standalonewindows'" style="margin-left:5px") PC - el-tag.x-tag-platform-quest(type="info" effect="plain" size="mini" v-if="userDialog.ref.last_platform === 'android'" style="margin-left:5px") Quest - div(style="margin-top:5px") - span(v-text="userDialog.ref.statusDescription" style="font-size:12px") - div(v-if="userDialog.ref.userIcon" style="flex:none;margin-right:10px") - el-popover(placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="userDialog.ref.userIcon" style="flex:none;width:120px;height:120px;border-radius:4px") - img.x-link(v-lazy="userDialog.ref.userIcon" style="width:500px;height:500px;" @click="openExternalLink(userDialog.ref.userIcon)") - div(style="flex:none") - el-button(v-if="userDialog.isFavorite" @click="userDialogCommand('Delete Favorite')" type="warning" icon="el-icon-star-on" circle) - el-button(v-else type="default" @click="userDialogCommand('Add Favorite')" icon="el-icon-star-off" circle) - el-dropdown(trigger="click" @command="userDialogCommand" size="small") - el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest) ? 'success' : (userDialog.isBlock || userDialog.isMute || userDialog.isHideAvatar) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px") - el-dropdown-menu(#default="dropdown") - template(v-if="userDialog.ref.id === API.currentUser.id") - el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Details") Show Avatar Details - el-dropdown-item(icon="el-icon-edit" command="Edit Social Status" divided) Social Status - el-dropdown-item(icon="el-icon-edit" command="Edit Language") Language - el-dropdown-item(icon="el-icon-edit" command="Edit Bio") Bio - el-dropdown-item(icon="el-icon-switch-button" command="Logout" divided) Logout - template(v-else) - template(v-if="lastLocation.location && isGameRunning") - el-dropdown-item(icon="el-icon-message" command="Invite") Invite - el-dropdown-item(icon="el-icon-message" command="Invite Message") Invite With Message - template(v-if="userDialog.isFriend") - el-dropdown-item(icon="el-icon-postcard" command="Request Invite") Request Invite - el-dropdown-item(icon="el-icon-postcard" command="Request Invite Message") Request Invite With Message - template(v-else-if="userDialog.incomingRequest") - el-dropdown-item(icon="el-icon-check" command="Accept Friend Request") Accept Friend Request - el-dropdown-item(icon="el-icon-close" command="Decline Friend Request") Decline Friend Request - el-dropdown-item(v-else-if="userDialog.outgoingRequest" icon="el-icon-close" command="Cancel Friend Request") Cancel Friend Request - el-dropdown-item(v-else icon="el-icon-plus" command="Send Friend Request") Send Friend Request - el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Details" divided) Show Avatar Details - el-dropdown-item(v-if="userDialog.isBlock" icon="el-icon-circle-check" command="Unblock" divided style="color:#F56C6C") Unblock - el-dropdown-item(v-else icon="el-icon-circle-close" command="Block" divided) Block - el-dropdown-item(v-if="userDialog.isMute" icon="el-icon-microphone" command="Unmute" style="color:#F56C6C") Unmute - el-dropdown-item(v-else icon="el-icon-turn-off-microphone" command="Mute") Mute - el-dropdown-item(v-if="userDialog.isHideAvatar" icon="el-icon-user-solid" command="Show Avatar" style="color:#F56C6C") Show Avatar - el-dropdown-item(v-else icon="el-icon-user" command="Hide Avatar") Hide Avatar - template(v-if="userDialog.isFriend") - el-dropdown-item(icon="el-icon-delete" command="Unfriend" divided) Unfriend - el-tabs - el-tab-pane(label="Info") - div(v-if="userDialog.ref.location" style="display:flex;flex-direction:column;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #eee") - div(style="flex:none") - location(:location="userDialog.ref.location") - template(#default v-if="userDialog.instance.occupants") ({{ userDialog.instance.occupants }}) - launch(:location="userDialog.ref.location" style="margin-left:5px") - invite-yourself(:location="userDialog.ref.location" style="margin-left:5px") - .x-friend-list(style="flex:1;margin-top:10px") - .x-friend-item(v-if="userDialog.$location.userId" @click="showUserDialog(userDialog.$location.userId)") - template(v-if="userDialog.$location.user") - .avatar(:class="userStatusClass(userDialog.$location.user)") - img(v-if="displayVRCPlusIconsAsAvatar && userDialog.$location.user.userIcon" v-lazy="userDialog.$location.user.userIcon") - img(v-else v-lazy="userDialog.$location.user.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="userDialog.$location.user.displayName" :class="userDialog.$location.user.$trustClass") - span.extra Instance Creator - span(v-else v-text="userDialog.$location.userId") - .x-friend-item(v-for="user in userDialog.users" :key="user.id" @click="showUserDialog(user.id)") - .avatar(:class="userStatusClass(user)") - img(v-if="displayVRCPlusIconsAsAvatar && user.userIcon" v-lazy="user.userIcon") - img(v-else v-lazy="user.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="user.displayName" :class="user.$trustClass") - span.extra - timer(:epoch="user.$location_at") - .x-friend-list(style="max-height:none") - .x-friend-item(style="width:100%;cursor:default") - .detail - span.name Note - el-input.extra(v-model="userDialog.memo" type="textarea" :rows="2" placeholder="Click to add a note" size="mini" resize="none") - .x-friend-item(style="width:100%;cursor:default") - .detail - span.name Bio - pre.extra(style="font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0 0.5em 0 0") {{ userDialog.ref.bio || '-' }} - div(style="margin-top:5px") - el-tooltip(v-if="link" v-for="(link, index) in userDialog.ref.bioLinks" :key="index") - template(#content) - span(v-text="link") - img(:src="getFaviconUrl(link)" style="width:16px;height:16px;vertical-align:middle;margin-right:5px;cursor:pointer" @click.stop="openExternalLink(link)") - .x-friend-item(style="cursor:default") - .detail - span.name Avatar Copying - span.extra(v-if="userDialog.ref.allowAvatarCopying" style="color:#67C23A") Allow - span.extra(v-else style="color:#F56C6C") Deny - .x-friend-item(style="cursor:default") - .detail - span.name(v-if="userDialog.ref.state === 'online' && userDialog.ref.$online_for") Online For - span.name(v-else) Offline For - span.extra(v-text="userOnlineFor(userDialog)") - .x-friend-item(style="cursor:default") - .detail - span.name Last Login - span.extra {{ userDialog.ref.last_login | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} - .x-friend-item(style="cursor:default") - .detail - span.name Date Joined - span.extra(v-text="userDialog.ref.date_joined") - .x-friend-item(v-if="userDialog.ref.id === API.currentUser.id && API.currentUser.homeLocation" @click="showWorldDialog(API.currentUser.homeLocation)" style="width:100%") - .detail - span.name Home Location - span.extra - location(:location="API.currentUser.homeLocation" :link="false") - el-button(@click.stop="resetHome()" size="mini" icon="el-icon-delete" circle style="margin-left:5px") - el-tab-pane(label="Worlds") - el-button(type="default" :loading="userDialog.isWorldsLoading" @click="refreshUserDialogWorlds()" size="mini" icon="el-icon-refresh" circle) - span(style="margin-left:5px") Total {{ userDialog.worlds.length }} - el-radio-group(v-model="userDialog.worldSorting" size="mini" style="margin-left:30px" @change="changeUserDialogWorldSorting") - el-radio(label="name") by name - el-radio(label="update") by update - .x-friend-list(v-loading="userDialog.isWorldsLoading" style="margin-top:10px;min-height:60px") - .x-friend-item(v-for="world in userDialog.worlds" :key="world.id" @click="showWorldDialog(world.id)") - .avatar - img(v-lazy="world.thumbnailImageUrl") - .detail - span.name(v-text="world.name") - span.extra(v-if="world.occupants") ({{ world.occupants }}) - el-tab-pane(label="Avatars") - el-button(type="default" :loading="userDialog.isAvatarsLoading" @click="refreshUserDialogAvatars()" size="mini" icon="el-icon-refresh" circle) - span(style="margin-left:5px") Total {{ userDialogAvatars.length }} - el-radio-group(v-model="userDialog.avatarSorting" size="mini" style="margin-left:30px" @change="changeUserDialogAvatarSorting") - el-radio(label="name") by name - el-radio(label="update") by update - el-radio-group(v-model="userDialog.avatarReleaseStatus" size="mini" style="margin-left:60px") - el-radio(label="all") all - el-radio(label="public") public - el-radio(label="private") private - .x-friend-list(v-loading="userDialog.isAvatarsLoading" style="margin-top:10px;min-height:60px") - .x-friend-item(v-for="avatar in userDialogAvatars" :key="avatar.id" @click="showAvatarDialog(avatar.id)") - .avatar - img(v-lazy="avatar.thumbnailImageUrl") - .detail - span.name(v-text="avatar.name") - span.extra(v-text="avatar.releaseStatus" v-if="avatar.releaseStatus === 'public'" style="color: #67c23a;") - span.extra(v-text="avatar.releaseStatus" v-else-if="avatar.releaseStatus === 'private'" style="color: #f56c6c;") - span.extra(v-text="avatar.releaseStatus" v-else) - el-tab-pane(label="JSON") - el-button(type="default" @click="refreshUserDialogTreeData()" size="mini" icon="el-icon-refresh" circle) - el-tree(:data="userDialog.treeData" style="margin-top:5px;font-size:12px") - template(#default="scope") - span - span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") - span(v-if="!scope.data.children" v-text="scope.data.value") + //- dialog: social status + el-dialog.x-dialog(ref="socialStatusDialog" :visible.sync="socialStatusDialog.visible" title="Social Status" width="400px") + div(v-loading="socialStatusDialog.loading") + el-select(v-model="socialStatusDialog.status" style="dispaly:block") + el-option(label="Online" value="active"). + #[i.x-user-status.active] Online + el-option(label="Join Me" value="join me"). + #[i.x-user-status.joinme] Join Me + el-option(label="Ask Me" value="ask me"). + #[i.x-user-status.askme] Ask Me + el-option(label="Do Not Disturb" value="busy"). + #[i.x-user-status.busy] Do Not Disturb + el-option(label="Offline" value="offline"). + #[i.x-user-status.offline] Offline + el-input(v-model="socialStatusDialog.statusDescription" placeholder="Status" style="dispaly:block;margin-top:10px") + template(#footer) + el-button(type="primary" size="small" :disabled="socialStatusDialog.loading" @click="saveSocialStatus") Update - //- dialog: world - el-dialog.x-dialog.x-world-dialog(ref="worldDialog" :visible.sync="worldDialog.visible" :show-close="false" width="600px") - div(v-loading="worldDialog.loading") - div(style="display:flex") - el-popover(placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="worldDialog.ref.thumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") - img.x-link(v-lazy="worldDialog.ref.imageUrl" style="width:500px;height:375px" @click="openExternalLink(worldDialog.ref.imageUrl)") - div(style="flex:1;display:flex;align-items:center;margin-left:15px") - div(style="flex:1") - div - i.el-icon-s-home(v-show="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id") - span(v-text="worldDialog.ref.name" style="font-weight:bold") - div(style="margin-top:5px") - span.x-link(v-text="worldDialog.ref.authorName" @click="showUserDialog(worldDialog.ref.authorId)" style="color:#909399;font-family:monospace") - div(style="margin-top:5px") - el-tag(v-if="worldDialog.ref.$isLabs" type="primary" effect="plain" size="mini") Labs - el-tag(v-else-if="worldDialog.ref.releaseStatus === 'public'" type="success" effect="plain" size="mini") Public - el-tag(v-else type="danger" effect="plain" size="mini") Private - el-tag(type="info" effect="plain" size="mini" v-text="worldDialog.fileSize" style="margin-left:5px") - div(style="margin-top:5px") - span(v-show="worldDialog.ref.name !== worldDialog.ref.description" v-text="worldDialog.ref.description" style="font-size:12px") - div(style="flex:none;margin-left:10px") - el-button(v-if="worldDialog.isFavorite" type="warning" icon="el-icon-star-on" circle @click="worldDialogCommand('Delete Favorite')") - el-button(v-else type="default" icon="el-icon-star-off" circle @click="worldDialogCommand('Add Favorite')") - el-dropdown(trigger="click" @command="worldDialogCommand" size="small" style="margin-left:5px") - el-button(type="default" icon="el-icon-more" circle) - el-dropdown-menu(#default="dropdown") - el-dropdown-item(icon="el-icon-refresh" command="Refresh") Refresh - el-dropdown-item(icon="el-icon-s-flag" command="New Instance" divided) New Instance - el-dropdown-item(v-if="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id" icon="el-icon-magic-stick" command="Reset Home" divided) Reset Home - el-dropdown-item(v-else icon="el-icon-s-home" command="Make Home" divided) Make Home - el-tabs - el-tab-pane(label="Instances") - div. - #[i.el-icon-user] Public {{ worldDialog.ref.publicOccupants | commaNumber }} - #[i.el-icon-user-solid(style="margin-left:10px")] Private {{ worldDialog.ref.privateOccupants | commaNumber }} - #[i.el-icon-check(style="margin-left:10px")] Capacity {{ worldDialog.ref.capacity | commaNumber }} - div(v-for="room in worldDialog.rooms" :key="room.id") - div(style="margin:5px 0") - span.x-link(@click="showLaunchDialog(room.$location.tag)"). - \#{{ room.$location.instanceName }} {{ room.$location.accessType }} #[template(v-if="room.occupants") ({{ room.occupants }})] - invite-yourself(:location="room.$location.tag" style="margin-left:5px") - .x-friend-list(style="margin:10px 0" v-if="room.$location.userId || room.users.length") - .x-friend-item(v-if="room.$location.userId" @click="showUserDialog(room.$location.userId)") - template(v-if="room.$location.user") - .avatar(:class="userStatusClass(room.$location.user)") - img(v-if="displayVRCPlusIconsAsAvatar && room.$location.user.userIcon" v-lazy="room.$location.user.userIcon") - img(v-else v-lazy="room.$location.user.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="room.$location.user.displayName" :class="room.$location.user.$trustClass") - span.extra Instance Creator - span(v-else v-text="room.$location.userId") - .x-friend-item(v-for="user in room.users" :key="user.id" @click="showUserDialog(user.id)") - .avatar(:class="userStatusClass(user)") - img(v-if="displayVRCPlusIconsAsAvatar && user.userIcon" v-lazy="user.userIcon") - img(v-else v-lazy="user.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="user.displayName" :class="user.$trustClass") - span.extra - timer(:epoch="user.$location_at") - el-tab-pane(label="Info") - .x-friend-list(style="max-height:none") - .x-friend-item(style="cursor:default") - .detail - span.name Players - span.extra {{ worldDialog.ref.occupants | commaNumber }} - .x-friend-item(style="cursor:default") - .detail - span.name Favorites - span.extra {{ worldDialog.ref.favorites | commaNumber }} - .x-friend-item(style="cursor:default") - .detail - span.name Visits - span.extra {{ worldDialog.ref.visits | commaNumber }} - .x-friend-item(style="cursor:default") - .detail - span.name Capacity - span.extra(v-text="worldDialog.ref.capacity") - .x-friend-item(style="cursor:default") - .detail - span.name Heat - span.extra {{ worldDialog.ref.heat | commaNumber }} {{ '🔥'.repeat(worldDialog.ref.heat) }} - .x-friend-item(style="cursor:default") - .detail - span.name Popularity - span.extra {{ worldDialog.ref.popularity | commaNumber }} {{ '💖'.repeat(worldDialog.ref.popularity) }} - .x-friend-item(style="cursor:default") - .detail - span.name Created - span.extra {{ worldDialog.ref.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} - .x-friend-item(style="cursor:default") - .detail - span.name Last Updated - span.extra {{ worldDialog.fileCreatedAt | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} - .x-friend-item(style="cursor:default") - .detail - span.name Version - span.extra(v-text="worldDialog.ref.version") - .x-friend-item(style="width:100%;cursor:default") - .detail - span.name Platform - span.extra(v-text="worldDialogPlatform") - el-tab-pane(label="JSON") - el-button(type="default" @click="refreshWorldDialogTreeData()" size="mini" icon="el-icon-refresh" circle) - el-tree(:data="worldDialog.treeData" style="margin-top:5px;font-size:12px") - template(#default="scope") - span - span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") - span(v-if="!scope.data.children" v-text="scope.data.value") - - //- dialog: avatar - el-dialog.x-dialog.x-avatar-dialog(ref="avatarDialog" :visible.sync="avatarDialog.visible" :show-close="false" width="600px") - div(v-loading="avatarDialog.loading") - div(style="display:flex") - el-popover(placement="right" width="500px" trigger="click") - img.x-link(slot="reference" v-lazy="avatarDialog.ref.thumbnailImageUrl" style="flex:none;width:160px;height:120px;border-radius:4px") - img.x-link(v-lazy="avatarDialog.ref.imageUrl" style="width:500px;height:375px" @click="openExternalLink(avatarDialog.ref.imageUrl)") - div(style="flex:1;display:flex;align-items:center;margin-left:15px") - div(style="flex:1") - div - span(v-text="avatarDialog.ref.name" style="font-weight:bold") - div(style="margin-top:5px") - span.x-link(v-text="avatarDialog.ref.authorName" @click="showUserDialog(avatarDialog.ref.authorId)" style="color:#909399;font-family:monospace") - div(style="margin-top:5px") - el-tag(v-if="avatarDialog.ref.releaseStatus === 'public'" type="success" effect="plain" size="mini") Public - el-tag(v-else type="danger" effect="plain" size="mini") Private - el-tag(type="info" effect="plain" size="mini" v-text="avatarDialog.fileSize" style="margin-left:5px") - div(style="margin-top:5px") - span(v-show="avatarDialog.ref.name !== avatarDialog.ref.description" v-text="avatarDialog.ref.description" style="font-size:12px") - div(style="flex:none;margin-left:10px") - el-button(v-if="avatarDialog.isFavorite" type="warning" icon="el-icon-star-on" circle @click="avatarDialogCommand('Delete Favorite')") - el-button(v-else type="default" icon="el-icon-star-off" circle @click="avatarDialogCommand('Add Favorite')") - el-dropdown(trigger="click" @command="avatarDialogCommand" size="small") - el-button(type="default" icon="el-icon-more" circle) - el-dropdown-menu(#default="dropdown") - el-dropdown-item(icon="el-icon-check" command="Select Avatar") Select Avatar - template(v-if="avatarDialog.ref.authorId === API.currentUser.id") - el-dropdown-item(v-if="avatarDialog.ref.releaseStatus === 'public'" icon="el-icon-user-solid" command="Make Private" divided) Make Private - el-dropdown-item(v-else icon="el-icon-user" command="Make Public" divided) Make Public - el-tabs - el-tab-pane(label="Info") - .x-friend-list - .x-friend-item(style="cursor:default") - .detail - span.name Created - span.extra {{ avatarDialog.ref.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} - .x-friend-item(style="cursor:default") - .detail - span.name Last Updated - span.extra {{ avatarDialog.fileCreatedAt | formatDate('YYYY-MM-DD HH24:MI:SS') || '-' }} - .x-friend-item(style="cursor:default") - .detail - span.name Version - span.extra(v-text="avatarDialog.ref.version") - .x-friend-item(style="width:100%;cursor:default") - .detail - span.name Platform - span.extra(v-text="avatarDialogPlatform") - el-tab-pane(label="JSON") - el-button(type="default" @click="refreshAvatarDialogTreeData()" size="mini" icon="el-icon-refresh" circle) - el-tree(:data="avatarDialog.treeData" style="margin-top:5px;font-size:12px") - template(#default="scope") - span - span(v-text="scope.data.key" style="font-weight:bold;margin-right:5px") - span(v-if="!scope.data.children" v-text="scope.data.value") - - //- dialog: favorite - el-dialog.x-dialog(ref="favoriteDialog" :visible.sync="favoriteDialog.visible" title="Choose Group" width="250px") - div(v-loading="favoriteDialog.loading") - el-button(v-for="group in favoriteDialog.groups" :key="group.name" style="display:block;width:100%;margin:10px 0" @click="addFavorite(group)" :disabled="group.count >= group.capacity") {{ group.displayName }} ({{ group.count }} / {{ group.capacity }}) - - //- dialog: invite - el-dialog.x-dialog(ref="inviteDialog" :visible.sync="inviteDialog.visible" title="Invite" width="450px") - div(v-loading="inviteDialog.loading") - location(:location="inviteDialog.worldId" :link="false") - el-select(v-model="inviteDialog.userIds" multiple clearable placeholder="Choose Friends" filterable :disabled="inviteDialog.loading" style="width:100%;margin-top:15px") - el-option-group(v-if="API.currentUser" label="ME") - el-option.x-friend-item(:label="API.currentUser.displayName" :value="API.currentUser.id" style="height:auto") - .avatar(:class="userStatusClass(API.currentUser)") - img(v-if="displayVRCPlusIconsAsAvatar && API.currentUser.userIcon" v-lazy="API.currentUser.userIcon") - img(v-else v-lazy="API.currentUser.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="API.currentUser.displayName") - el-option-group(v-if="friendsGroup0.length" label="VIP") - el-option.x-friend-item(v-for="friend in friendsGroup0" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto") - template(v-if="friend.ref") - .avatar(:class="userStatusClass(friend.ref)") - img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") - img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="friend.ref.displayName" :class="friend.ref.$trustClass") - span(v-else v-text="friend.id") - el-option-group(v-if="friendsGroup1.length" label="ONLINE") - el-option.x-friend-item(v-for="friend in friendsGroup1" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto") - template(v-if="friend.ref") - .avatar(:class="userStatusClass(friend.ref)") - img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") - img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="friend.ref.displayName" :class="friend.ref.$trustClass") - span(v-else v-text="friend.id") - el-option-group(v-if="friendsGroup2.length" label="ACTIVE") - el-option.x-friend-item(v-for="friend in friendsGroup2" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto") - template(v-if="friend.ref") - .avatar - img(v-if="displayVRCPlusIconsAsAvatar && friend.ref.userIcon" v-lazy="friend.ref.userIcon") - img(v-else v-lazy="friend.ref.currentAvatarThumbnailImageUrl") - .detail - span.name(v-text="friend.ref.displayName" :class="friend.ref.$trustClass") - span(v-else v-text="friend.id") - template(#footer) - el-button(type="primary" size="small" :disabled="inviteDialog.loading || !inviteDialog.userIds.length" @click="sendInvite()") Invite - el-button(type="primary" size="small" :disabled="inviteDialog.loading || !inviteDialog.userIds.length" @click="showSendInviteDialog()") Invite With Message - - //- dialog: social status - el-dialog.x-dialog(ref="socialStatusDialog" :visible.sync="socialStatusDialog.visible" title="Social Status" width="400px") - div(v-loading="socialStatusDialog.loading") - el-select(v-model="socialStatusDialog.status" style="dispaly:block") - el-option(label="Online" value="active"). - #[i.x-user-status.active] Online - el-option(label="Join Me" value="join me"). - #[i.x-user-status.joinme] Join Me - el-option(label="Ask Me" value="ask me"). - #[i.x-user-status.askme] Ask Me - el-option(label="Do Not Disturb" value="busy"). - #[i.x-user-status.busy] Do Not Disturb - el-option(label="Offline" value="offline"). - #[i.x-user-status.offline] Offline - el-input(v-model="socialStatusDialog.statusDescription" placeholder="Status" style="dispaly:block;margin-top:10px") - template(#footer) - el-button(type="primary" size="small" :disabled="socialStatusDialog.loading" @click="saveSocialStatus") Update - - //- dialog: language - el-dialog.x-dialog(ref="languageDialog" :visible.sync="languageDialog.visible" title="Language" width="400px") - div(v-loading="languageDialog.loading") - div(style="margin:5px 0") - el-tag(v-for="item in API.currentUser.$languages" :key="item.key" size="small" type="info" effect="plain" closable @close="removeUserLanguage(item.key)" style="margin-right:5px") + //- dialog: language + el-dialog.x-dialog(ref="languageDialog" :visible.sync="languageDialog.visible" title="Language" width="400px") + div(v-loading="languageDialog.loading") + div(style="margin:5px 0") + el-tag(v-for="item in API.currentUser.$languages" :key="item.key" size="small" type="info" effect="plain" closable @close="removeUserLanguage(item.key)" style="margin-right:5px") + span.famfamfam-flags(:class="languageClass(item.key)" style="display:inline-block;margin-right:5px") + | {{ item.value }} ({{ item.key }}) + div(v-if="languageDialog.languageChoice === true") + el-select(v-model="languageDialog.languageValue" size="mini") + el-option(v-for="item in languageDialog.languages" :key="item.key" :value="item.key" :label="item.value") span.famfamfam-flags(:class="languageClass(item.key)" style="display:inline-block;margin-right:5px") | {{ item.value }} ({{ item.key }}) - div(v-if="languageDialog.languageChoice === true") - el-select(v-model="languageDialog.languageValue" size="mini") - el-option(v-for="item in languageDialog.languages" :key="item.key" :value="item.key" :label="item.value") - span.famfamfam-flags(:class="languageClass(item.key)" style="display:inline-block;margin-right:5px") - | {{ item.value }} ({{ item.key }}) - el-button(@click="languageDialog.languageChoice=false; addUserLanguage(languageDialog.languageValue)" size="mini") Ok - el-button(@click="languageDialog.languageChoice=false" size="mini" style="margin-left:0") Cancel - div(v-else) - el-button(@click="languageDialog.languageValue='';languageDialog.languageChoice=true" size="mini") Add Language + el-button(@click="languageDialog.languageChoice=false; addUserLanguage(languageDialog.languageValue)" size="mini") Ok + el-button(@click="languageDialog.languageChoice=false" size="mini" style="margin-left:0") Cancel + div(v-else) + el-button(@click="languageDialog.languageValue='';languageDialog.languageChoice=true" size="mini") Add Language - //- dialog: bio - el-dialog.x-dialog(ref="bioDialog" :visible.sync="bioDialog.visible" title="Bio" width="400px") - div(v-loading="bioDialog.loading") - el-input(type="textarea" v-model="bioDialog.bio" size="mini" maxlength="512" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="Please input a bio") - el-input(v-for="(link, index) in bioDialog.bioLinks" :key="index" :value="link" v-model="bioDialog.bioLinks[index]" size="small" style="margin-top:5px") - img(slot="prepend" :src="getFaviconUrl(link)" style="width:16px;height:16px") - el-button(slot="append" icon="el-icon-delete" @click="bioDialog.bioLinks.splice(index, 1)") - el-button(@click="bioDialog.bioLinks.push('')" size="mini" style="margin-top:5px") Add Link - template(#footer) - el-button(type="primary" size="small" :disabled="bioDialog.loading" @click="saveBio") Update + //- dialog: bio + el-dialog.x-dialog(ref="bioDialog" :visible.sync="bioDialog.visible" title="Bio" width="400px") + div(v-loading="bioDialog.loading") + el-input(type="textarea" v-model="bioDialog.bio" size="mini" maxlength="512" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="Please input a bio") + el-input(v-for="(link, index) in bioDialog.bioLinks" :key="index" :value="link" v-model="bioDialog.bioLinks[index]" size="small" style="margin-top:5px") + img(slot="prepend" :src="getFaviconUrl(link)" style="width:16px;height:16px") + el-button(slot="append" icon="el-icon-delete" @click="bioDialog.bioLinks.splice(index, 1)") + el-button(@click="bioDialog.bioLinks.push('')" size="mini" style="margin-top:5px") Add Link + template(#footer) + el-button(type="primary" size="small" :disabled="bioDialog.loading" @click="saveBio") Update - //- dialog: new instance - el-dialog.x-dialog(ref="newInstanceDialog" :visible.sync="newInstanceDialog.visible" title="New Instance" width="600px") - el-form(:model="newInstanceDialog" label-width="100px") - el-form-item(label="Access Type") - el-radio-group(v-model="newInstanceDialog.accessType" size="mini" @change="buildInstance") - el-radio-button(label="public") - el-radio-button(label="friends+") - el-radio-button(label="friends") - el-radio-button(label="invite+") - el-radio-button(label="invite") - el-form-item(label="World ID") - el-input(v-model="newInstanceDialog.worldId" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") - el-form-item(label="Instance ID") - el-input(v-model="newInstanceDialog.instanceId" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") - el-form-item(label="Location") - el-input(v-model="newInstanceDialog.location" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") - el-form-item(label="URL") - el-input(ref="wtf" v-model="newInstanceDialog.url" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") - template(#footer) - el-button(size="small" @click="makeHome(newInstanceDialog.location)") Make Home - el-button(size="small" @click="showInviteDialog(newInstanceDialog.location)") Invite - el-button(type="primary" size="small" @click="showLaunchDialog(newInstanceDialog.location)") Launch + //- dialog: new instance + el-dialog.x-dialog(ref="newInstanceDialog" :visible.sync="newInstanceDialog.visible" title="New Instance" width="600px") + el-form(:model="newInstanceDialog" label-width="100px") + el-form-item(label="Access Type") + el-radio-group(v-model="newInstanceDialog.accessType" size="mini" @change="buildInstance") + el-radio-button(label="public") + el-radio-button(label="friends+") + el-radio-button(label="friends") + el-radio-button(label="invite+") + el-radio-button(label="invite") + el-form-item(label="World ID") + el-input(v-model="newInstanceDialog.worldId" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") + el-form-item(label="Instance ID") + el-input(v-model="newInstanceDialog.instanceId" size="mini" @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") + el-form-item(label="Location") + el-input(v-model="newInstanceDialog.location" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") + el-form-item(label="URL") + el-input(ref="wtf" v-model="newInstanceDialog.url" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") + template(#footer) + el-button(size="small" @click="makeHome(newInstanceDialog.location)") Make Home + el-button(size="small" @click="showInviteDialog(newInstanceDialog.location)") Invite + el-button(type="primary" size="small" @click="showLaunchDialog(newInstanceDialog.location)") Launch - //- dialog: launch options - el-dialog.x-dialog(ref="launchOptionsDialog" :visible.sync="launchOptionsDialog.visible" title="Launch Options" width="400px") - div(style='font-size:12px;') - | These options are for advanced users only. #[br] - | to change fps: --fps=<N> ex) #[el-tag(size="mini") --fps=144] - el-input(type="textarea" v-model="launchOptionsDialog.arguments" size="mini" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px") - template(#footer) - div(style="display:flex") - el-button(size="small" @click="openExternalLink('https://docs.vrchat.com/docs/launch-options')") VRChat Docs - el-button(size="small" @click="openExternalLink('https://docs.unity3d.com/Manual/CommandLineArguments.html')") Unity Manual - el-button(type="primary" size="small" :disabled="launchOptionsDialog.loading" @click="updateLaunchOptions" style="margin-left:auto") OK + //- dialog: launch options + el-dialog.x-dialog(ref="launchOptionsDialog" :visible.sync="launchOptionsDialog.visible" title="Launch Options" width="400px") + div(style='font-size:12px;') + | These options are for advanced users only. #[br] + | to change fps: --fps=<N> ex) #[el-tag(size="mini") --fps=144] + el-input(type="textarea" v-model="launchOptionsDialog.arguments" size="mini" show-word-limit :autosize="{ minRows:2, maxRows:5 }" placeholder="" style="margin-top:10px") + template(#footer) + div(style="display:flex") + el-button(size="small" @click="openExternalLink('https://docs.vrchat.com/docs/launch-options')") VRChat Docs + el-button(size="small" @click="openExternalLink('https://docs.unity3d.com/Manual/CommandLineArguments.html')") Unity Manual + el-button(type="primary" size="small" :disabled="launchOptionsDialog.loading" @click="updateLaunchOptions" style="margin-left:auto") OK - //- dialog: launch - el-dialog.x-dialog(ref="launchDialog" :visible.sync="launchDialog.visible" title="Launch" width="400px") - div #[span(v-text="launchDialog.url" style="word-break:break-all;font-size:12px")] - template(#footer) - el-checkbox(v-model="launchDialog.desktop" style="float:left;margin-top:5px") Start as Desktop (No VR) - el-button(size="small" @click="showInviteDialog(launchDialog.location)") Invite - el-button(type="primary" size="small" @click="launchGame(locationToLaunchArg(launchDialog.location))") Launch + //- dialog: launch + el-dialog.x-dialog(ref="launchDialog" :visible.sync="launchDialog.visible" title="Launch" width="400px") + div #[span(v-text="launchDialog.url" style="word-break:break-all;font-size:12px")] + template(#footer) + el-checkbox(v-model="launchDialog.desktop" style="float:left;margin-top:5px") Start as Desktop (No VR) + el-button(size="small" @click="showInviteDialog(launchDialog.location)") Invite + el-button(type="primary" size="small" @click="launchGame(locationToLaunchArg(launchDialog.location))") Launch - //- dialog: export friends list - el-dialog.x-dialog(:visible.sync="exportFriendsListDialog" title="Export Friends List" width="650px") - el-input(type="textarea" v-model="exportFriendsListContent" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()") + //- dialog: export friends list + el-dialog.x-dialog(:visible.sync="exportFriendsListDialog" title="Export Friends List" width="650px") + el-input(type="textarea" v-model="exportFriendsListContent" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()") - //- dialog: Notification position - el-dialog.x-dialog(ref="notificationPositionDialog" :visible.sync="notificationPositionDialog.visible" title="Notification Position" width="400px") - div(style='font-size:12px;') - | Choose a notification position. - svg(version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 300 200" style="margin-top:15px;" xml:space="preserve") - path(style="fill:black;" d="M291.89,5A3.11,3.11,0,0,1,295,8.11V160.64a3.11,3.11,0,0,1-3.11,3.11H8.11A3.11,3.11,0,0,1,5,160.64V8.11A3.11,3.11,0,0,1,8.11,5H291.89m0-5H8.11A8.11,8.11,0,0,0,0,8.11V160.64a8.11,8.11,0,0,0,8.11,8.11H291.89a8.11,8.11,0,0,0,8.11-8.11V8.11A8.11,8.11,0,0,0,291.89,0Z") - rect(style="fill:#c4c4c4;" x="5" y="5" width="290" height="158.75" rx="2.5") - el-radio-group(v-model="notificationPosition" size="mini" @change="changeNotificationPosition") - el-radio(label="topLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:120px;") ‎ - el-radio(label="top" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:120px;") ‎ - el-radio(label="topRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:120px;") ‎ - el-radio(label="centerLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:200px;") ‎ - el-radio(label="topCenter" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:200px;") ‎ - el-radio(label="centerRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:200px;") ‎ - el-radio(label="bottomLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:280px;") ‎ - el-radio(label="bottom" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:280px;") ‎ - el-radio(label="bottomRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:280px;") ‎ - template(#footer) - div(style="display:flex") - el-button(type="primary" size="small" style="margin-left:auto" @click="notificationPositionDialog.visible = false") OK + //- dialog: Notification position + el-dialog.x-dialog(ref="notificationPositionDialog" :visible.sync="notificationPositionDialog.visible" title="Notification Position" width="400px") + div(style='font-size:12px;') + | Choose a notification position. + svg(version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 300 200" style="margin-top:15px;" xml:space="preserve") + path(style="fill:black;" d="M291.89,5A3.11,3.11,0,0,1,295,8.11V160.64a3.11,3.11,0,0,1-3.11,3.11H8.11A3.11,3.11,0,0,1,5,160.64V8.11A3.11,3.11,0,0,1,8.11,5H291.89m0-5H8.11A8.11,8.11,0,0,0,0,8.11V160.64a8.11,8.11,0,0,0,8.11,8.11H291.89a8.11,8.11,0,0,0,8.11-8.11V8.11A8.11,8.11,0,0,0,291.89,0Z") + rect(style="fill:#c4c4c4;" x="5" y="5" width="290" height="158.75" rx="2.5") + el-radio-group(v-model="notificationPosition" size="mini" @change="changeNotificationPosition") + el-radio(label="topLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:120px;") ‎ + el-radio(label="top" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:120px;") ‎ + el-radio(label="topRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:120px;") ‎ + el-radio(label="centerLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:200px;") ‎ + el-radio(label="topCenter" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:200px;") ‎ + el-radio(label="centerRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:200px;") ‎ + el-radio(label="bottomLeft" v-model="notificationPosition" style="margin:0;position:absolute;left:35px;top:280px;") ‎ + el-radio(label="bottom" v-model="notificationPosition" style="margin:0;position:absolute;left:195px;top:280px;") ‎ + el-radio(label="bottomRight" v-model="notificationPosition" style="margin:0;position:absolute;right:25px;top:280px;") ‎ + template(#footer) + div(style="display:flex") + el-button(type="primary" size="small" style="margin-left:auto" @click="notificationPositionDialog.visible = false") OK - //- dialog: Noty feed filters - el-dialog.x-dialog(ref="notyFeedFiltersDialog" :visible.sync="notyFeedFiltersDialog.visible" title="Notification Filters" width="450px") - div.toggle-list - div - span.toggle-name OnPlayerJoining - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOnPlayerJoining" v-model="sharedFeedFilters.noty.OnPlayerJoining" class="toggle-switch") - div - span.toggle-name OnPlayerJoined - toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchNotyGroupOnPlayerJoined" v-model="sharedFeedFilters.noty.OnPlayerJoined" class="toggle-switch") - div - span.toggle-name OnPlayerLeft - toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchNotyGroupOnPlayerLeft" v-model="sharedFeedFilters.noty.OnPlayerLeft" class="toggle-switch") - div - span.toggle-name Online - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOnline" v-model="sharedFeedFilters.noty.Online" class="toggle-switch") - div - span.toggle-name Offline - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOffline" v-model="sharedFeedFilters.noty.Offline" class="toggle-switch") - div - span.toggle-name GPS - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupGPS" v-model="sharedFeedFilters.noty.GPS" class="toggle-switch") - div - span.toggle-name Status - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupStatus" v-model="sharedFeedFilters.noty.Status" class="toggle-switch") - div - span.toggle-name Invite - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupinvite" v-model="sharedFeedFilters.noty.invite" class="toggle-switch") - div - span.toggle-name Request Invite - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestInvite" v-model="sharedFeedFilters.noty.requestInvite" class="toggle-switch") - div - span.toggle-name Invite Response - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupinviteResponse" v-model="sharedFeedFilters.noty.inviteResponse" class="toggle-switch") - div - span.toggle-name Request Invite Response - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestInviteResponse" v-model="sharedFeedFilters.noty.requestInviteResponse" class="toggle-switch") - div - span.toggle-name Friend Request - toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestfriendRequest" v-model="sharedFeedFilters.noty.friendRequest" class="toggle-switch") - div - span.toggle-name New Friend - toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestFriend" v-model="sharedFeedFilters.noty.Friend" class="toggle-switch") - div - span.toggle-name Unfriend - toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestUnfriend" v-model="sharedFeedFilters.noty.Unfriend" class="toggle-switch") - div - span.toggle-name Display Name - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestDisplayName" v-model="sharedFeedFilters.noty.DisplayName" class="toggle-switch") - div - span.toggle-name Trust Level - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestTrustLevel" v-model="sharedFeedFilters.noty.TrustLevel" class="toggle-switch") - div - span.toggle-name Show Avatar - toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestShowAvatar" v-model="sharedFeedFilters.noty.showAvatar" class="toggle-switch") - div - span.toggle-name Hide Avatar - toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestHideAvatar" v-model="sharedFeedFilters.noty.hideAvatar" class="toggle-switch") - div - span.toggle-name Block - toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestblock" v-model="sharedFeedFilters.noty.block" class="toggle-switch") - div - span.toggle-name Mute - toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestmute" v-model="sharedFeedFilters.noty.mute" class="toggle-switch") - div - span.toggle-name Unmute - toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestunmute" v-model="sharedFeedFilters.noty.unmute" class="toggle-switch") - template(#footer) - el-button(type="small" @click="cancelSharedFeedFilters") Cancel - el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save - - //- dialog: wrist feed filters - el-dialog.x-dialog(ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" title="Wrist Feed Filters" width="450px") - div.toggle-list - div - span.toggle-name Self Location - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGroupLocation" v-model="sharedFeedFilters.wrist.Location" class="toggle-switch") - div - span.toggle-name OnPlayerJoining - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOnPlayerJoining" v-model="sharedFeedFilters.wrist.OnPlayerJoining" class="toggle-switch") - div - span.toggle-name OnPlayerJoined - toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchWristGroupOnPlayerJoined" v-model="sharedFeedFilters.wrist.OnPlayerJoined" class="toggle-switch") - div - span.toggle-name OnPlayerLeft - toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchWristGroupOnPlayerLeft" v-model="sharedFeedFilters.wrist.OnPlayerLeft" class="toggle-switch") - div - span.toggle-name Online - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOnline" v-model="sharedFeedFilters.wrist.Online" class="toggle-switch") - div - span.toggle-name Offline - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOffline" v-model="sharedFeedFilters.wrist.Offline" class="toggle-switch") - div - span.toggle-name GPS - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupGPS" v-model="sharedFeedFilters.wrist.GPS" class="toggle-switch") - div - span.toggle-name Status - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupStatus" v-model="sharedFeedFilters.wrist.Status" class="toggle-switch") - div - span.toggle-name Invite - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupinvite" v-model="sharedFeedFilters.wrist.invite" class="toggle-switch") - div - span.toggle-name Request Invite - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestInvite" v-model="sharedFeedFilters.wrist.requestInvite" class="toggle-switch") - div - span.toggle-name Invite Response - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupinviteResponse" v-model="sharedFeedFilters.wrist.inviteResponse" class="toggle-switch") - div - span.toggle-name Request Invite Response - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestInviteResponse" v-model="sharedFeedFilters.wrist.requestInviteResponse" class="toggle-switch") - div - span.toggle-name Friend Request - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestfriendRequest" v-model="sharedFeedFilters.wrist.friendRequest" class="toggle-switch") - div - span.toggle-name New Friend - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestFriend" v-model="sharedFeedFilters.wrist.Friend" class="toggle-switch") - div - span.toggle-name Unfriend - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestUnfriend" v-model="sharedFeedFilters.wrist.Unfriend" class="toggle-switch") - div - span.toggle-name Display Name - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestDisplayName" v-model="sharedFeedFilters.wrist.DisplayName" class="toggle-switch") - div - span.toggle-name Trust Level - toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestTrustLevel" v-model="sharedFeedFilters.wrist.TrustLevel" class="toggle-switch") - div - span.toggle-name Show Avatar - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestShowAvatar" v-model="sharedFeedFilters.wrist.showAvatar" class="toggle-switch") - div - span.toggle-name Hide Avatar - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestHideAvatar" v-model="sharedFeedFilters.wrist.hideAvatar" class="toggle-switch") - div - span.toggle-name Block - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestblock" v-model="sharedFeedFilters.wrist.block" class="toggle-switch") - div - span.toggle-name Mute - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestmute" v-model="sharedFeedFilters.wrist.mute" class="toggle-switch") - div - span.toggle-name Unmute - toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestunmute" v-model="sharedFeedFilters.wrist.unmute" class="toggle-switch") - template(#footer) - el-button(type="small" @click="cancelSharedFeedFilters") Cancel - el-button(type="primary" size="small" @click="saveSharedFeedFilters") Save - - //- 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 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="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") - template(v-if="API.currentUser.$isVRCPlus") - input.inviteImageUploadButton(type="file" multiple accept="image/png" @change="inviteImageUpload") - data-tables(v-bind="inviteResponseMessageTable" @row-click="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="cancelSendInviteResponse") Cancel - el-button(type="small" @click="API.refreshInviteMessageTableData('response')") Refresh - - //- dialog Table: Send Invite Request Response Message - el-dialog.x-dialog(ref="sendInviteRequestResponseDialog" :visible.sync="sendInviteRequestResponseDialogVisible" title="Send Invite Request Response Message" width="800px") - template(v-if="API.currentUser.$isVRCPlus") - input.inviteImageUploadButton(type="file" multiple accept="image/png" @change="inviteImageUpload") - data-tables(v-bind="inviteRequestResponseMessageTable" @row-click="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="cancelSendInviteRequestResponse") Cancel - el-button(type="small" @click="API.refreshInviteMessageTableData('requestResponse')") Refresh + //- dialog: Noty feed filters + el-dialog.x-dialog(ref="notyFeedFiltersDialog" :visible.sync="notyFeedFiltersDialog.visible" title="Notification Filters" width="450px") + div.toggle-list + div + span.toggle-name OnPlayerJoining + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOnPlayerJoining" v-model="sharedFeedFilters.noty.OnPlayerJoining" class="toggle-switch") + div + span.toggle-name OnPlayerJoined + toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchNotyGroupOnPlayerJoined" v-model="sharedFeedFilters.noty.OnPlayerJoined" class="toggle-switch") + div + span.toggle-name OnPlayerLeft + toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchNotyGroupOnPlayerLeft" v-model="sharedFeedFilters.noty.OnPlayerLeft" class="toggle-switch") + div + span.toggle-name Online + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOnline" v-model="sharedFeedFilters.noty.Online" class="toggle-switch") + div + span.toggle-name Offline + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOffline" v-model="sharedFeedFilters.noty.Offline" class="toggle-switch") + div + span.toggle-name GPS + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupGPS" v-model="sharedFeedFilters.noty.GPS" class="toggle-switch") + div + span.toggle-name Status + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupStatus" v-model="sharedFeedFilters.noty.Status" class="toggle-switch") + div + span.toggle-name Invite + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupinvite" v-model="sharedFeedFilters.noty.invite" class="toggle-switch") + div + span.toggle-name Request Invite + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestInvite" v-model="sharedFeedFilters.noty.requestInvite" class="toggle-switch") + div + span.toggle-name Invite Response + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupinviteResponse" v-model="sharedFeedFilters.noty.inviteResponse" class="toggle-switch") + div + span.toggle-name Request Invite Response + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestInviteResponse" v-model="sharedFeedFilters.noty.requestInviteResponse" class="toggle-switch") + div + span.toggle-name Friend Request + toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestfriendRequest" v-model="sharedFeedFilters.noty.friendRequest" class="toggle-switch") + div + span.toggle-name New Friend + toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestFriend" v-model="sharedFeedFilters.noty.Friend" class="toggle-switch") + div + span.toggle-name Unfriend + toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestUnfriend" v-model="sharedFeedFilters.noty.Unfriend" class="toggle-switch") + div + span.toggle-name Display Name + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestDisplayName" v-model="sharedFeedFilters.noty.DisplayName" class="toggle-switch") + div + span.toggle-name Trust Level + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestTrustLevel" v-model="sharedFeedFilters.noty.TrustLevel" class="toggle-switch") + div + span.toggle-name Show Avatar + toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestShowAvatar" v-model="sharedFeedFilters.noty.showAvatar" class="toggle-switch") + div + span.toggle-name Hide Avatar + toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestHideAvatar" v-model="sharedFeedFilters.noty.hideAvatar" class="toggle-switch") + div + span.toggle-name Block + toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestblock" v-model="sharedFeedFilters.noty.block" class="toggle-switch") + div + span.toggle-name Mute + toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestmute" v-model="sharedFeedFilters.noty.mute" class="toggle-switch") + div + span.toggle-name Unmute + toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestunmute" v-model="sharedFeedFilters.noty.unmute" class="toggle-switch") + template(#footer) + el-button(type="small" @click="cancelSharedFeedFilters") Cancel + el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save + + //- dialog: wrist feed filters + el-dialog.x-dialog(ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" title="Wrist Feed Filters" width="450px") + div.toggle-list + div + span.toggle-name Self Location + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGroupLocation" v-model="sharedFeedFilters.wrist.Location" class="toggle-switch") + div + span.toggle-name OnPlayerJoining + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOnPlayerJoining" v-model="sharedFeedFilters.wrist.OnPlayerJoining" class="toggle-switch") + div + span.toggle-name OnPlayerJoined + toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchWristGroupOnPlayerJoined" v-model="sharedFeedFilters.wrist.OnPlayerJoined" class="toggle-switch") + div + span.toggle-name OnPlayerLeft + toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchWristGroupOnPlayerLeft" v-model="sharedFeedFilters.wrist.OnPlayerLeft" class="toggle-switch") + div + span.toggle-name Online + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOnline" v-model="sharedFeedFilters.wrist.Online" class="toggle-switch") + div + span.toggle-name Offline + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOffline" v-model="sharedFeedFilters.wrist.Offline" class="toggle-switch") + div + span.toggle-name GPS + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupGPS" v-model="sharedFeedFilters.wrist.GPS" class="toggle-switch") + div + span.toggle-name Status + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupStatus" v-model="sharedFeedFilters.wrist.Status" class="toggle-switch") + div + span.toggle-name Invite + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupinvite" v-model="sharedFeedFilters.wrist.invite" class="toggle-switch") + div + span.toggle-name Request Invite + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestInvite" v-model="sharedFeedFilters.wrist.requestInvite" class="toggle-switch") + div + span.toggle-name Invite Response + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupinviteResponse" v-model="sharedFeedFilters.wrist.inviteResponse" class="toggle-switch") + div + span.toggle-name Request Invite Response + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestInviteResponse" v-model="sharedFeedFilters.wrist.requestInviteResponse" class="toggle-switch") + div + span.toggle-name Friend Request + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestfriendRequest" v-model="sharedFeedFilters.wrist.friendRequest" class="toggle-switch") + div + span.toggle-name New Friend + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestFriend" v-model="sharedFeedFilters.wrist.Friend" class="toggle-switch") + div + span.toggle-name Unfriend + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestUnfriend" v-model="sharedFeedFilters.wrist.Unfriend" class="toggle-switch") + div + span.toggle-name Display Name + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestDisplayName" v-model="sharedFeedFilters.wrist.DisplayName" class="toggle-switch") + div + span.toggle-name Trust Level + toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestTrustLevel" v-model="sharedFeedFilters.wrist.TrustLevel" class="toggle-switch") + div + span.toggle-name Show Avatar + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestShowAvatar" v-model="sharedFeedFilters.wrist.showAvatar" class="toggle-switch") + div + span.toggle-name Hide Avatar + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestHideAvatar" v-model="sharedFeedFilters.wrist.hideAvatar" class="toggle-switch") + div + span.toggle-name Block + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestblock" v-model="sharedFeedFilters.wrist.block" class="toggle-switch") + div + span.toggle-name Mute + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestmute" v-model="sharedFeedFilters.wrist.mute" class="toggle-switch") + div + span.toggle-name Unmute + toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestunmute" v-model="sharedFeedFilters.wrist.unmute" class="toggle-switch") + template(#footer) + el-button(type="small" @click="cancelSharedFeedFilters") Cancel + el-button(type="primary" size="small" @click="saveSharedFeedFilters") Save + + //- 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 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="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") + template(v-if="API.currentUser.$isVRCPlus") + input.inviteImageUploadButton(type="file" multiple accept="image/png" @change="inviteImageUpload") + data-tables(v-bind="inviteResponseMessageTable" @row-click="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="cancelSendInviteResponse") Cancel + el-button(type="small" @click="API.refreshInviteMessageTableData('response')") Refresh + + //- dialog Table: Send Invite Request Response Message + el-dialog.x-dialog(ref="sendInviteRequestResponseDialog" :visible.sync="sendInviteRequestResponseDialogVisible" title="Send Invite Request Response Message" width="800px") + template(v-if="API.currentUser.$isVRCPlus") + input.inviteImageUploadButton(type="file" multiple accept="image/png" @change="inviteImageUpload") + data-tables(v-bind="inviteRequestResponseMessageTable" @row-click="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="cancelSendInviteRequestResponse") Cancel + el-button(type="small" @click="API.refreshInviteMessageTableData('requestResponse')") Refresh - //- dialog: Send Invite Response Message Confirm - el-dialog.x-dialog(ref="sendInviteResponseConfirmDialog" :visible.sync="sendInviteResponseConfirmDialog.visible" title="Send Invite Response 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 Table: Send Invite Message - el-dialog.x-dialog(ref="sendInviteDialog" :visible.sync="sendInviteDialogVisible" title="Send Invite Message" width="800px") - template(v-if="API.currentUser.$isVRCPlus") - input.inviteImageUploadButton(type="file" multiple accept="image/png" @change="inviteImageUpload") - data-tables(v-bind="inviteMessageTable" @row-click="showSendInviteConfirmDialog" 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="showEditAndSendInviteDialog('message', scope.row)") - template(#footer) - el-button(type="small" @click="cancelSendInvite") Cancel - el-button(type="small" @click="API.refreshInviteMessageTableData('message')") Refresh - - //- dialog Table: Send Invite Request Message - el-dialog.x-dialog(ref="sendInviteRequestDialog" :visible.sync="sendInviteRequestDialogVisible" title="Send Invite Request Message" width="800px") - template(v-if="API.currentUser.$isVRCPlus") - input.inviteImageUploadButton(type="file" multiple accept="image/png" @change="inviteImageUpload") - data-tables(v-bind="inviteRequestMessageTable" @row-click="showSendInviteConfirmDialog" 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="showEditAndSendInviteDialog('request', scope.row)") - template(#footer) - el-button(type="small" @click="cancelSendInviteRequest") Cancel - el-button(type="small" @click="API.refreshInviteMessageTableData('request')") Refresh - - //- dialog: Send Invite Message Confirm - el-dialog.x-dialog(ref="sendInviteConfirmDialog" :visible.sync="sendInviteConfirmDialog.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="cancelInviteConfirm") Cancel - el-button(type="primary" size="small" @click="sendInviteConfirm") Confirm - - //- dialog: Edit And Send Invite Message - el-dialog.x-dialog(ref="editAndSendInviteDialog" :visible.sync="editAndSendInviteDialog.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="editAndSendInviteDialog.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="cancelEditAndSendInvite") Cancel - el-button(type="primary" size="small" @click="saveEditAndSendInvite") Send + //- dialog: Send Invite Response Message Confirm + el-dialog.x-dialog(ref="sendInviteResponseConfirmDialog" :visible.sync="sendInviteResponseConfirmDialog.visible" title="Send Invite Response 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 Table: Send Invite Message + el-dialog.x-dialog(ref="sendInviteDialog" :visible.sync="sendInviteDialogVisible" title="Send Invite Message" width="800px") + template(v-if="API.currentUser.$isVRCPlus") + input.inviteImageUploadButton(type="file" multiple accept="image/png" @change="inviteImageUpload") + data-tables(v-bind="inviteMessageTable" @row-click="showSendInviteConfirmDialog" 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="showEditAndSendInviteDialog('message', scope.row)") + template(#footer) + el-button(type="small" @click="cancelSendInvite") Cancel + el-button(type="small" @click="API.refreshInviteMessageTableData('message')") Refresh + + //- dialog Table: Send Invite Request Message + el-dialog.x-dialog(ref="sendInviteRequestDialog" :visible.sync="sendInviteRequestDialogVisible" title="Send Invite Request Message" width="800px") + template(v-if="API.currentUser.$isVRCPlus") + input.inviteImageUploadButton(type="file" multiple accept="image/png" @change="inviteImageUpload") + data-tables(v-bind="inviteRequestMessageTable" @row-click="showSendInviteConfirmDialog" 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="showEditAndSendInviteDialog('request', scope.row)") + template(#footer) + el-button(type="small" @click="cancelSendInviteRequest") Cancel + el-button(type="small" @click="API.refreshInviteMessageTableData('request')") Refresh + + //- dialog: Send Invite Message Confirm + el-dialog.x-dialog(ref="sendInviteConfirmDialog" :visible.sync="sendInviteConfirmDialog.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="cancelInviteConfirm") Cancel + el-button(type="primary" size="small" @click="sendInviteConfirm") Confirm + + //- dialog: Edit And Send Invite Message + el-dialog.x-dialog(ref="editAndSendInviteDialog" :visible.sync="editAndSendInviteDialog.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="editAndSendInviteDialog.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="cancelEditAndSendInvite") Cancel + el-button(type="primary" size="small" @click="saveEditAndSendInvite") Send - //- dialog: open source software notice - el-dialog.x-dialog(:visible.sync="ossDialog" title="Open Source Software Notice" width="650px") - div(style="height:350px;overflow:hidden scroll;word-break:break-all") - div - span VRCX is based on open source software. It was possible because of their contribution. - div(style="margin-top:15px") - p(style="font-weight:bold") animate.css - pre(style="font-size:12px;white-space:pre-line"). - The MIT License (MIT) - - Copyright (c) 2019 Daniel Eden - - 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") CefSharp - pre(style="font-size:12px;white-space:pre-line"). - // Copyright © The CefSharp Authors. All rights reserved. - // - // Redistribution and use in source and binary forms, with or without - // modification, are permitted provided that the following conditions are - // met: - // - // * Redistributions of source code must retain the above copyright - // notice, this list of conditions and the following disclaimer. - // - // * Redistributions in binary form must reproduce the above - // copyright notice, this list of conditions and the following disclaimer - // in the documentation and/or other materials provided with the - // distribution. - // - // * Neither the name of Google Inc. nor the name Chromium Embedded - // Framework nor the name CefSharp nor the names of its contributors - // may be used to endorse or promote products derived from this software - // without specific prior written permission. - // - // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - div(style="margin-top:15px") - p(style="font-weight:bold") DiscordRichPresence - pre(style="font-size:12px;white-space:pre-line"). - MIT License - - Copyright (c) 2018 Lachee - - 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") element - pre(style="font-size:12px;white-space:pre-line"). - The MIT License (MIT) - - Copyright (c) 2016-present ElemeFE - - 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") Newtonsoft.Json - pre(style="font-size:12px;white-space:pre-line"). - The MIT License (MIT) - - Copyright (c) 2007 James Newton-King - - 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") normalize - pre(style="font-size:12px;white-space:pre-line"). - The MIT License (MIT) - - Copyright © Nicolas Gallagher and Jonathan Neal - - 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") noty - pre(style="font-size:12px;white-space:pre-line"). - Copyright (c) 2012 Nedim Arabacı - - 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") OpenVR SDK - pre(style="font-size:12px;white-space:pre-line"). - Copyright (c) 2015, Valve Corporation - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - div(style="margin-top:15px") - p(style="font-weight:bold") SharpDX - pre(style="font-size:12px;white-space:pre-line"). - Copyright (c) 2010-2014 SharpDX - Alexandre Mutel - - 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") vue - pre(style="font-size:12px;white-space:pre-line"). - The MIT License (MIT) - - Copyright (c) 2013-present, Yuxi (Evan) You - - 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") vue-data-tables - pre(style="font-size:12px;white-space:pre-line"). - The MIT License (MIT) - - Copyright (c) 2018 Leon Zhang - - 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") vue-lazyload - pre(style="font-size:12px;white-space:pre-line"). - The MIT License (MIT) - - Copyright (c) 2016 Awe - - 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") 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 - - Copyright (c) 2018 Lars-Martin Hejll - - 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. + //- dialog: open source software notice + el-dialog.x-dialog(:visible.sync="ossDialog" title="Open Source Software Notice" width="650px") + div(style="height:350px;overflow:hidden scroll;word-break:break-all") + div + span VRCX is based on open source software. It was possible because of their contribution. + div(style="margin-top:15px") + p(style="font-weight:bold") animate.css + pre(style="font-size:12px;white-space:pre-line"). + The MIT License (MIT) + + Copyright (c) 2019 Daniel Eden + + 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") CefSharp + pre(style="font-size:12px;white-space:pre-line"). + // Copyright © The CefSharp Authors. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions are + // met: + // + // * Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // + // * Redistributions in binary form must reproduce the above + // copyright notice, this list of conditions and the following disclaimer + // in the documentation and/or other materials provided with the + // distribution. + // + // * Neither the name of Google Inc. nor the name Chromium Embedded + // Framework nor the name CefSharp nor the names of its contributors + // may be used to endorse or promote products derived from this software + // without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + div(style="margin-top:15px") + p(style="font-weight:bold") DiscordRichPresence + pre(style="font-size:12px;white-space:pre-line"). + MIT License + + Copyright (c) 2018 Lachee + + 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") element + pre(style="font-size:12px;white-space:pre-line"). + The MIT License (MIT) + + Copyright (c) 2016-present ElemeFE + + 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") Newtonsoft.Json + pre(style="font-size:12px;white-space:pre-line"). + The MIT License (MIT) + + Copyright (c) 2007 James Newton-King + + 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") normalize + pre(style="font-size:12px;white-space:pre-line"). + The MIT License (MIT) + + Copyright © Nicolas Gallagher and Jonathan Neal + + 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") noty + pre(style="font-size:12px;white-space:pre-line"). + Copyright (c) 2012 Nedim Arabacı + + 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") OpenVR SDK + pre(style="font-size:12px;white-space:pre-line"). + Copyright (c) 2015, Valve Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + div(style="margin-top:15px") + p(style="font-weight:bold") SharpDX + pre(style="font-size:12px;white-space:pre-line"). + Copyright (c) 2010-2014 SharpDX - Alexandre Mutel + + 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") vue + pre(style="font-size:12px;white-space:pre-line"). + The MIT License (MIT) + + Copyright (c) 2013-present, Yuxi (Evan) You + + 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") vue-data-tables + pre(style="font-size:12px;white-space:pre-line"). + The MIT License (MIT) + + Copyright (c) 2018 Leon Zhang + + 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") vue-lazyload + pre(style="font-size:12px;white-space:pre-line"). + The MIT License (MIT) + + Copyright (c) 2016 Awe + + 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") 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 + + Copyright (c) 2018 Lars-Martin Hejll + + 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. script(src="app.js")