aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cache/wal/chromium/Cached Theme.pakbin0 -> 1372 bytes
-rw-r--r--.cache/wal/chromium/bg.pngbin0 -> 495 bytes
-rw-r--r--.cache/wal/chromium/manifest.json90
-rw-r--r--.cache/wal/colors16
-rw-r--r--.cache/wal/colors-kitty.conf20
-rw-r--r--.cache/wal/colors-konsole.colorscheme63
-rw-r--r--.cache/wal/colors-oomox17
-rw-r--r--.cache/wal/colors-putty.reg26
-rw-r--r--.cache/wal/colors-rofi-dark.rasi161
-rw-r--r--.cache/wal/colors-rofi-light.rasi161
-rw-r--r--.cache/wal/colors-speedcrunch.json15
-rw-r--r--.cache/wal/colors-sway21
-rw-r--r--.cache/wal/colors-tty.sh19
-rw-r--r--.cache/wal/colors-wal-dmenu.h6
-rw-r--r--.cache/wal/colors-wal-dwm.h18
-rw-r--r--.cache/wal/colors-wal-st.h34
-rw-r--r--.cache/wal/colors-wal-tabbed.h6
-rw-r--r--.cache/wal/colors-wal.vim23
-rw-r--r--.cache/wal/colors-waybar.css20
-rw-r--r--.cache/wal/colors.Xresources68
-rw-r--r--.cache/wal/colors.css28
-rw-r--r--.cache/wal/colors.hs37
-rw-r--r--.cache/wal/colors.json28
-rw-r--r--.cache/wal/colors.scss26
-rw-r--r--.cache/wal/colors.sh36
-rw-r--r--.cache/wal/colors.yml24
-rw-r--r--.cache/wal/sequences1
-rw-r--r--.cache/wal/wal1
-rw-r--r--.config/BetterDiscord/plugins/BDFDB.config.json5
-rw-r--r--.config/BetterDiscord/plugins/BadgesEverywhere.config.json29
-rw-r--r--.config/BetterDiscord/plugins/BetterSearchPage.config.json7
-rw-r--r--.config/BetterDiscord/plugins/BetterSearchPage.plugin.js194
-rw-r--r--.config/BetterDiscord/plugins/EditChannels.config.json17
-rw-r--r--.config/BetterDiscord/plugins/EditChannels.plugin.js773
-rw-r--r--.config/BetterDiscord/plugins/EditServers.config.json28
-rw-r--r--.config/BetterDiscord/plugins/EditServers.plugin.js1079
-rw-r--r--.config/BetterDiscord/plugins/NotificationSounds.config.json356
-rw-r--r--.config/BetterDiscord/plugins/NotificationSounds.plugin.js546
-rw-r--r--.config/BetterDiscord/plugins/PinDMs.config.json40
-rw-r--r--.config/BetterDiscord/plugins/PinDMs.plugin.js1149
-rw-r--r--.config/BetterDiscord/plugins/QuickMention.plugin.js78
-rw-r--r--.config/BetterDiscord/plugins/RemoveNicknames.config.json17
-rw-r--r--.config/BetterDiscord/plugins/RemoveNicknames.plugin.js201
-rw-r--r--.config/BetterDiscord/plugins/SendLargeMessages.plugin.js259
-rw-r--r--.config/BetterDiscord/themes/Material-Discord.theme.css3
-rw-r--r--.config/BetterDiscord/themes/MinimalCord.theme.css138
-rw-r--r--.config/BetterDiscord/themes/glass.theme.css40
-rw-r--r--.config/BetterDiscord/themes/pywal.theme.css75
-rw-r--r--.config/brave-flags.conf1
-rw-r--r--.config/chromium-flags.conf1
-rw-r--r--.config/coc/commands3
-rw-r--r--.config/coc/extensions/package.json14
-rw-r--r--.config/coc/snippets-mru9
-rw-r--r--.config/coc/ultisnips/css.snippets5
-rw-r--r--.config/coc/ultisnips/html.snippets16
-rw-r--r--.config/coc/ultisnips/tex.snippets34
-rw-r--r--.config/i3/config165
-rw-r--r--.config/i3/config.backup140
-rw-r--r--.config/konsolerc25
-rw-r--r--.config/nvim/autoload/plug.vim2665
-rw-r--r--.config/nvim/autoload/plug.vim.old2597
-rw-r--r--.config/nvim/coc-settings.json3
-rw-r--r--.config/nvim/ftplugin/tex.vim4
-rw-r--r--.config/nvim/init.vim246
-rw-r--r--.config/nvim/links.sh9
-rw-r--r--.config/nvim/pywal/base.vim19
-rw-r--r--.config/nvim/pywal/colors.vim46
-rw-r--r--.config/nvim/pywal/pywal.vim65
-rw-r--r--.config/picom.conf70
-rw-r--r--.config/polybar/config.ini358
-rw-r--r--.config/polybar/onstart.sh3
-rwxr-xr-x.config/polybar/player-mpris-simple.sh9
-rw-r--r--.config/rofi/config17
-rw-r--r--.config/rofi/pywal.rasi40
-rw-r--r--.config/spicetify/Themes/Loekaars/README.md11
-rw-r--r--.config/spicetify/Themes/Loekaars/color.ini18
-rw-r--r--.config/spicetify/Themes/Loekaars/user.css630
-rw-r--r--.config/spicetify/config.ini36
-rw-r--r--.config/startpage/index.html45
-rw-r--r--.config/startpage/jquery.min.js2
-rw-r--r--.config/startpage/pywal.css63
-rw-r--r--.config/startpage/script.js7
-rw-r--r--.config/startpage/style.css91
-rw-r--r--.config/user-dirs.dirs15
-rw-r--r--.config/user-dirs.locale1
-rw-r--r--.config/zathura/zathurarc13
-rwxr-xr-x.local/share/bin/brave9
-rwxr-xr-x.local/share/bin/loadwall5
-rwxr-xr-x.local/share/bin/m3uprefix9
-rw-r--r--.local/share/konsole/Loekaars.profile35
l---------.local/share/konsole/colors-konsole.colorscheme1
-rwxr-xr-x.xprofile9
-rw-r--r--.zshrc117
-rw-r--r--readme.md24
94 files changed, 13704 insertions, 0 deletions
diff --git a/.cache/wal/chromium/Cached Theme.pak b/.cache/wal/chromium/Cached Theme.pak
new file mode 100644
index 0000000..f075cfe
--- /dev/null
+++ b/.cache/wal/chromium/Cached Theme.pak
Binary files differ
diff --git a/.cache/wal/chromium/bg.png b/.cache/wal/chromium/bg.png
new file mode 100644
index 0000000..a3058f4
--- /dev/null
+++ b/.cache/wal/chromium/bg.png
Binary files differ
diff --git a/.cache/wal/chromium/manifest.json b/.cache/wal/chromium/manifest.json
new file mode 100644
index 0000000..6ae3e9a
--- /dev/null
+++ b/.cache/wal/chromium/manifest.json
@@ -0,0 +1,90 @@
+{
+ "description": "Colorscheme generated by jswal",
+ "manifest_version": 2,
+ "name": "pywal",
+ "theme": {
+ "images": {
+ "theme_frame": "bg.png"
+ },
+ "colors": {
+ "bookmark_text": [
+ 196,
+ 196,
+ 196
+ ],
+ "frame": [
+ 255,
+ 255,
+ 255
+ ],
+ "ntp_background": [
+ 19,
+ 19,
+ 21
+ ],
+ "ntp_text": [
+ 19,
+ 19,
+ 21
+ ],
+ "tab_background_text": [
+ 137,
+ 137,
+ 137
+ ],
+ "tab_background_text_inactive": [
+ 137,
+ 137,
+ 137
+ ],
+ "tab_background_text_incognito": [
+ 137,
+ 137,
+ 137
+ ],
+ "tab_background_text_incognito_inactive": [
+ 137,
+ 137,
+ 137
+ ],
+ "tab_text": [
+ 196,
+ 196,
+ 196
+ ],
+ "toolbar": [
+ 26,
+ 26,
+ 27
+ ],
+ "button_background": [
+ 255,
+ 255,
+ 255
+ ]
+ },
+ "tints": {
+ "buttons": [
+ 0.0972222222222219,
+ 11.76470588235294,
+ 40
+ ],
+ "frame_inactive": [
+ -1,
+ -1,
+ -1
+ ],
+ "frame_incognito": [
+ -1,
+ -1,
+ -1
+ ],
+ "frame_incognito_inactive": [
+ -1,
+ -1,
+ -1
+ ]
+ }
+ },
+ "version": "2"
+} \ No newline at end of file
diff --git a/.cache/wal/colors b/.cache/wal/colors
new file mode 100644
index 0000000..e28d163
--- /dev/null
+++ b/.cache/wal/colors
@@ -0,0 +1,16 @@
+#141415
+#292b37
+#3c2a27
+#3b3033
+#3f3330
+#4b4240
+#66615a
+#c4c4c4
+#4e4e4f
+#292b37
+#3c2a27
+#3b3033
+#3f3330
+#4b4240
+#66615a
+#c4c4c4
diff --git a/.cache/wal/colors-kitty.conf b/.cache/wal/colors-kitty.conf
new file mode 100644
index 0000000..a56601f
--- /dev/null
+++ b/.cache/wal/colors-kitty.conf
@@ -0,0 +1,20 @@
+foreground #c4c4c4
+background #141415
+cursor #c4c4c4
+
+color0 #141415
+color8 #4e4e4f
+color1 #292b37
+color9 #292b37
+color2 #3c2a27
+color10 #3c2a27
+color3 #3b3033
+color11 #3b3033
+color4 #3f3330
+color12 #3f3330
+color5 #4b4240
+color13 #4b4240
+color6 #66615a
+color14 #66615a
+color7 #c4c4c4
+color15 #c4c4c4
diff --git a/.cache/wal/colors-konsole.colorscheme b/.cache/wal/colors-konsole.colorscheme
new file mode 100644
index 0000000..c09a9de
--- /dev/null
+++ b/.cache/wal/colors-konsole.colorscheme
@@ -0,0 +1,63 @@
+[Background]
+Color=20,20,21
+
+[BackgroundIntense]
+Color=20,20,21
+
+[Color0]
+Color=20,20,21
+
+[Color0Intense]
+Color=78,78,79
+
+[Color1]
+Color=88,91,113
+
+[Color1Intense]
+Color=88,91,113
+
+[Color2]
+Color=116,85,80
+
+[Color2Intense]
+Color=116,85,80
+
+[Color3]
+Color=103,86,90
+
+[Color3Intense]
+Color=103,86,90
+
+[Color4]
+Color=102,84,80
+
+[Color4Intense]
+Color=102,84,80
+
+[Color5]
+Color=75,66,64
+
+[Color5Intense]
+Color=75,66,64
+
+[Color6]
+Color=102,97,90
+
+[Color6Intense]
+Color=102,97,90
+
+[Color7]
+Color=196,196,196
+
+[Color7Intense]
+Color=196,196,196
+
+[Foreground]
+Color=196,196,196
+
+[ForegroundIntense]
+Color=196,196,196
+
+[General]
+Description=Colorscheme generated by wal
+Opacity=0.92 \ No newline at end of file
diff --git a/.cache/wal/colors-oomox b/.cache/wal/colors-oomox
new file mode 100644
index 0000000..806f6b6
--- /dev/null
+++ b/.cache/wal/colors-oomox
@@ -0,0 +1,17 @@
+NAME=wal
+BG=141415
+FG=c4c4c4
+MENU_BG=141415
+MENU_FG=c4c4c4
+SEL_BG=292b37
+SEL_FG=141415
+TXT_BG=141415
+TXT_FG=c4c4c4
+BTN_BG=3c2a27
+BTN_FG=c4c4c4
+HDR_BTN_BG=3b3033
+HDR_BTN_FG=c4c4c4
+GTK3_GENERATE_DARK=True
+ROUNDNESS=0
+SPACING=3
+GRADIENT=0.0
diff --git a/.cache/wal/colors-putty.reg b/.cache/wal/colors-putty.reg
new file mode 100644
index 0000000..2708554
--- /dev/null
+++ b/.cache/wal/colors-putty.reg
@@ -0,0 +1,26 @@
+Windows Registry Editor Version 5.00
+
+[HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\wal]
+"Colour0"="196,196,196" ; Default Foreground
+"Colour1"="196,196,196" ; Default Bold Foreground
+"Colour2"="20,20,21" ; Default Background
+"Colour3"="20,20,21" ; Default Bold Background
+"Colour4"="20,20,21" ; Cursor Text
+"Colour5"="196,196,196" ; Cursor Color
+"Colour6"="20,20,21" ; ANSI Black
+"Colour7"="78,78,79" ; ANSI Black Bold
+"Colour8"="41,43,55" ; ANSI Red
+"Colour9"="41,43,55" ; ANSI Red Bold
+"Colour10"="60,42,39" ; ANSI Green
+"Colour11"="60,42,39" ; ANSI Green Bold
+"Colour12"="59,48,51" ; ANSI Yellow
+"Colour13"="59,48,51" ; ANSI Yellow Bold
+"Colour14"="63,51,48" ; ANSI Blue
+"Colour15"="63,51,48" ; ANSI Blue Bold
+"Colour16"="75,66,64" ; ANSI Magenta
+"Colour17"="75,66,64" ; ANSI Magenta Bold
+"Colour18"="102,97,90" ; ANSI Cyan
+"Colour19"="102,97,90" ; ANSI Cyan Bold
+"Colour20"="196,196,196" ; ANSI White
+"Colour21"="196,196,196" ; ANSI White Bold
+
diff --git a/.cache/wal/colors-rofi-dark.rasi b/.cache/wal/colors-rofi-dark.rasi
new file mode 100644
index 0000000..77b916d
--- /dev/null
+++ b/.cache/wal/colors-rofi-dark.rasi
@@ -0,0 +1,161 @@
+* {
+ active-background: #3c2a27;
+ active-foreground: @foreground;
+ normal-background: @background;
+ normal-foreground: @foreground;
+ urgent-background: #292b37;
+ urgent-foreground: @foreground;
+
+ alternate-active-background: @background;
+ alternate-active-foreground: @foreground;
+ alternate-normal-background: @background;
+ alternate-normal-foreground: @foreground;
+ alternate-urgent-background: @background;
+ alternate-urgent-foreground: @foreground;
+
+ selected-active-background: #292b37;
+ selected-active-foreground: @foreground;
+ selected-normal-background: #3c2a27;
+ selected-normal-foreground: @foreground;
+ selected-urgent-background: #3b3033;
+ selected-urgent-foreground: @foreground;
+
+ background-color: @background;
+ background: #141415;
+ foreground: #c4c4c4;
+ border-color: @background;
+ spacing: 2;
+}
+
+#window {
+ background-color: @background;
+ border: 0;
+ padding: 2.5ch;
+}
+
+#mainbox {
+ border: 0;
+ padding: 0;
+}
+
+#message {
+ border: 2px 0px 0px;
+ border-color: @border-color;
+ padding: 1px;
+}
+
+#textbox {
+ text-color: @foreground;
+}
+
+#inputbar {
+ children: [ prompt,textbox-prompt-colon,entry,case-indicator ];
+}
+
+#textbox-prompt-colon {
+ expand: false;
+ str: ":";
+ margin: 0px 0.3em 0em 0em;
+ text-color: @normal-foreground;
+}
+
+#listview {
+ fixed-height: 0;
+ border: 2px 0px 0px;
+ border-color: @border-color;
+ spacing: 2px;
+ scrollbar: true;
+ padding: 2px 0px 0px;
+}
+
+#element {
+ border: 0;
+ padding: 1px;
+}
+
+#element.normal.normal {
+ background-color: @normal-background;
+ text-color: @normal-foreground;
+}
+
+#element.normal.urgent {
+ background-color: @urgent-background;
+ text-color: @urgent-foreground;
+}
+
+#element.normal.active {
+ background-color: @active-background;
+ text-color: @active-foreground;
+}
+
+#element.selected.normal {
+ background-color: @selected-normal-background;
+ text-color: @selected-normal-foreground;
+}
+
+#element.selected.urgent {
+ background-color: @selected-urgent-background;
+ text-color: @selected-urgent-foreground;
+}
+
+#element.selected.active {
+ background-color: @selected-active-background;
+ text-color: @selected-active-foreground;
+}
+
+#element.alternate.normal {
+ background-color: @alternate-normal-background;
+ text-color: @alternate-normal-foreground;
+}
+
+#element.alternate.urgent {
+ background-color: @alternate-urgent-background;
+ text-color: @alternate-urgent-foreground;
+}
+
+#element.alternate.active {
+ background-color: @alternate-active-background;
+ text-color: @alternate-active-foreground;
+}
+
+#scrollbar {
+ width: 4px;
+ border: 0;
+ handle-width: 8px;
+ padding: 0;
+}
+
+#sidebar {
+ border: 2px 0px 0px;
+ border-color: @border-color;
+}
+
+#button {
+ text-color: @normal-foreground;
+}
+
+#button.selected {
+ background-color: @selected-normal-background;
+ text-color: @selected-normal-foreground;
+}
+
+#inputbar {
+ spacing: 0;
+ text-color: @normal-foreground;
+ padding: 1px;
+}
+
+#case-indicator {
+ spacing: 0;
+ text-color: @normal-foreground;
+}
+
+#entry {
+ spacing: 0;
+ text-color: @normal-foreground;
+}
+
+#prompt {
+ spacing: 0;
+ text-color: @normal-foreground;
+}
diff --git a/.cache/wal/colors-rofi-light.rasi b/.cache/wal/colors-rofi-light.rasi
new file mode 100644
index 0000000..8e67236
--- /dev/null
+++ b/.cache/wal/colors-rofi-light.rasi
@@ -0,0 +1,161 @@
+* {
+ active-background: #3c2a27;
+ active-foreground: @foreground;
+ normal-background: @background;
+ normal-foreground: @foreground;
+ urgent-background: #292b37;
+ urgent-foreground: @foreground;
+
+ alternate-active-background: @background;
+ alternate-active-foreground: @foreground;
+ alternate-normal-background: @background;
+ alternate-normal-foreground: @foreground;
+ alternate-urgent-background: @background;
+ alternate-urgent-foreground: @foreground;
+
+ selected-active-background: #292b37;
+ selected-active-foreground: @foreground;
+ selected-normal-background: #3c2a27;
+ selected-normal-foreground: @foreground;
+ selected-urgent-background: #3b3033;
+ selected-urgent-foreground: @foreground;
+
+ background-color: @background;
+ background: #c4c4c4;
+ foreground: #141415;
+ border-color: @background;
+ spacing: 2;
+}
+
+#window {
+ background-color: @background;
+ border: 0;
+ padding: 2.5ch;
+}
+
+#mainbox {
+ border: 0;
+ padding: 0;
+}
+
+#message {
+ border: 2px 0px 0px;
+ border-color: @border-color;
+ padding: 1px;
+}
+
+#textbox {
+ text-color: @foreground;
+}
+
+#inputbar {
+ children: [ prompt,textbox-prompt-colon,entry,case-indicator ];
+}
+
+#textbox-prompt-colon {
+ expand: false;
+ str: ":";
+ margin: 0px 0.3em 0em 0em;
+ text-color: @normal-foreground;
+}
+
+#listview {
+ fixed-height: 0;
+ border: 2px 0px 0px;
+ border-color: @border-color;
+ spacing: 2px;
+ scrollbar: true;
+ padding: 2px 0px 0px;
+}
+
+#element {
+ border: 0;
+ padding: 1px;
+}
+
+#element.normal.normal {
+ background-color: @normal-background;
+ text-color: @normal-foreground;
+}
+
+#element.normal.urgent {
+ background-color: @urgent-background;
+ text-color: @urgent-foreground;
+}
+
+#element.normal.active {
+ background-color: @active-background;
+ text-color: @active-foreground;
+}
+
+#element.selected.normal {
+ background-color: @selected-normal-background;
+ text-color: @selected-normal-foreground;
+}
+
+#element.selected.urgent {
+ background-color: @selected-urgent-background;
+ text-color: @selected-urgent-foreground;
+}
+
+#element.selected.active {
+ background-color: @selected-active-background;
+ text-color: @selected-active-foreground;
+}
+
+#element.alternate.normal {
+ background-color: @alternate-normal-background;
+ text-color: @alternate-normal-foreground;
+}
+
+#element.alternate.urgent {
+ background-color: @alternate-urgent-background;
+ text-color: @alternate-urgent-foreground;
+}
+
+#element.alternate.active {
+ background-color: @alternate-active-background;
+ text-color: @alternate-active-foreground;
+}
+
+#scrollbar {
+ width: 4px;
+ border: 0;
+ handle-width: 8px;
+ padding: 0;
+}
+
+#sidebar {
+ border: 2px 0px 0px;
+ border-color: @border-color;
+}
+
+#button {
+ text-color: @normal-foreground;
+}
+
+#button.selected {
+ background-color: @selected-normal-background;
+ text-color: @selected-normal-foreground;
+}
+
+#inputbar {
+ spacing: 0;
+ text-color: @normal-foreground;
+ padding: 1px;
+}
+
+#case-indicator {
+ spacing: 0;
+ text-color: @normal-foreground;
+}
+
+#entry {
+ spacing: 0;
+ text-color: @normal-foreground;
+}
+
+#prompt {
+ spacing: 0;
+ text-color: @normal-foreground;
+}
diff --git a/.cache/wal/colors-speedcrunch.json b/.cache/wal/colors-speedcrunch.json
new file mode 100644
index 0000000..ead54c0
--- /dev/null
+++ b/.cache/wal/colors-speedcrunch.json
@@ -0,0 +1,15 @@
+{
+ "cursor": "#c4c4c4",
+ "number": "#c4c4c4",
+ "parens": "#4b4240",
+ "result": "#3f3330",
+ "comment": "#4e4e4f",
+ "matched": "#3f3330",
+ "function": "#292b37",
+ "operator": "#3b3033",
+ "variable": "#3c2a27",
+ "scrollbar": "#3b3033",
+ "separator": "#3b3033",
+ "background": "#141415",
+ "editorbackground": "#141415"
+}
diff --git a/.cache/wal/colors-sway b/.cache/wal/colors-sway
new file mode 100644
index 0000000..09f95c7
--- /dev/null
+++ b/.cache/wal/colors-sway
@@ -0,0 +1,21 @@
+set $wallpaper /home/loek/.config/wpg/wallpapers/pywal.png
+
+set $background #141415
+set $foreground #c4c4c4
+
+set $color0 #141415
+set $color1 #292b37
+set $color2 #3c2a27
+set $color3 #3b3033
+set $color4 #3f3330
+set $color5 #4b4240
+set $color6 #66615a
+set $color7 #c4c4c4
+set $color8 #4e4e4f
+set $color9 #292b37
+set $color10 #3c2a27
+set $color11 #3b3033
+set $color12 #3f3330
+set $color13 #4b4240
+set $color14 #66615a
+set $color15 #c4c4c4
diff --git a/.cache/wal/colors-tty.sh b/.cache/wal/colors-tty.sh
new file mode 100644
index 0000000..3e7acdb
--- /dev/null
+++ b/.cache/wal/colors-tty.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+[ "${TERM:-none}" = "linux" ] && \
+ printf '%b' '\e]P0141415
+ \e]P1292b37
+ \e]P23c2a27
+ \e]P33b3033
+ \e]P43f3330
+ \e]P54b4240
+ \e]P666615a
+ \e]P7c4c4c4
+ \e]P84e4e4f
+ \e]P9292b37
+ \e]PA3c2a27
+ \e]PB3b3033
+ \e]PC3f3330
+ \e]PD4b4240
+ \e]PE66615a
+ \e]PFc4c4c4
+ \ec'
diff --git a/.cache/wal/colors-wal-dmenu.h b/.cache/wal/colors-wal-dmenu.h
new file mode 100644
index 0000000..43ddfac
--- /dev/null
+++ b/.cache/wal/colors-wal-dmenu.h
@@ -0,0 +1,6 @@
+static const char *colors[SchemeLast][2] = {
+ /* fg bg */
+ [SchemeNorm] = { "#c4c4c4", "#141415" },
+ [SchemeSel] = { "#c4c4c4", "#292b37" },
+ [SchemeOut] = { "#c4c4c4", "#66615a" },
+};
diff --git a/.cache/wal/colors-wal-dwm.h b/.cache/wal/colors-wal-dwm.h
new file mode 100644
index 0000000..2380319
--- /dev/null
+++ b/.cache/wal/colors-wal-dwm.h
@@ -0,0 +1,18 @@
+static const char norm_fg[] = "#c4c4c4";
+static const char norm_bg[] = "#141415";
+static const char norm_border[] = "#4e4e4f";
+
+static const char sel_fg[] = "#c4c4c4";
+static const char sel_bg[] = "#3c2a27";
+static const char sel_border[] = "#c4c4c4";
+
+static const char urg_fg[] = "#c4c4c4";
+static const char urg_bg[] = "#292b37";
+static const char urg_border[] = "#292b37";
+
+static const char *colors[][3] = {
+ /* fg bg border */
+ [SchemeNorm] = { norm_fg, norm_bg, norm_border }, // unfocused wins
+ [SchemeSel] = { sel_fg, sel_bg, sel_border }, // the focused win
+ [SchemeUrg] = { urg_fg, urg_bg, urg_border },
+};
diff --git a/.cache/wal/colors-wal-st.h b/.cache/wal/colors-wal-st.h
new file mode 100644
index 0000000..55463ed
--- /dev/null
+++ b/.cache/wal/colors-wal-st.h
@@ -0,0 +1,34 @@
+const char *colorname[] = {
+
+ /* 8 normal colors */
+ [0] = "#141415", /* black */
+ [1] = "#292b37", /* red */
+ [2] = "#3c2a27", /* green */
+ [3] = "#3b3033", /* yellow */
+ [4] = "#3f3330", /* blue */
+ [5] = "#4b4240", /* magenta */
+ [6] = "#66615a", /* cyan */
+ [7] = "#c4c4c4", /* white */
+
+ /* 8 bright colors */
+ [8] = "#4e4e4f", /* black */
+ [9] = "#292b37", /* red */
+ [10] = "#3c2a27", /* green */
+ [11] = "#3b3033", /* yellow */
+ [12] = "#3f3330", /* blue */
+ [13] = "#4b4240", /* magenta */
+ [14] = "#66615a", /* cyan */
+ [15] = "#c4c4c4", /* white */
+
+ /* special colors */
+ [256] = "#141415", /* background */
+ [257] = "#c4c4c4", /* foreground */
+ [258] = "#c4c4c4", /* cursor */
+};
+
+/* Default colors (colorname index)
+ * foreground, background, cursor */
+ unsigned int defaultbg = 0;
+ unsigned int defaultfg = 257;
+ unsigned int defaultcs = 258;
+ unsigned int defaultrcs= 258;
diff --git a/.cache/wal/colors-wal-tabbed.h b/.cache/wal/colors-wal-tabbed.h
new file mode 100644
index 0000000..01d5d13
--- /dev/null
+++ b/.cache/wal/colors-wal-tabbed.h
@@ -0,0 +1,6 @@
+static const char* selbgcolor = "#141415";
+static const char* selfgcolor = "#c4c4c4";
+static const char* normbgcolor = "#3c2a27";
+static const char* normfgcolor = "#c4c4c4";
+static const char* urgbgcolor = "#292b37";
+static const char* urgfgcolor = "#c4c4c4";
diff --git a/.cache/wal/colors-wal.vim b/.cache/wal/colors-wal.vim
new file mode 100644
index 0000000..a23e0f7
--- /dev/null
+++ b/.cache/wal/colors-wal.vim
@@ -0,0 +1,23 @@
+" Special
+let wallpaper = "/home/loek/.config/wpg/wallpapers/pywal.png"
+let background = "#141415"
+let foreground = "#c4c4c4"
+let cursor = "#c4c4c4"
+
+" Colors
+let color0 = "#141415"
+let color1 = "#292b37"
+let color2 = "#3c2a27"
+let color3 = "#3b3033"
+let color4 = "#3f3330"
+let color5 = "#4b4240"
+let color6 = "#66615a"
+let color7 = "#c4c4c4"
+let color8 = "#4e4e4f"
+let color9 = "#292b37"
+let color10 = "#3c2a27"
+let color11 = "#3b3033"
+let color12 = "#3f3330"
+let color13 = "#4b4240"
+let color14 = "#66615a"
+let color15 = "#c4c4c4"
diff --git a/.cache/wal/colors-waybar.css b/.cache/wal/colors-waybar.css
new file mode 100644
index 0000000..ede28aa
--- /dev/null
+++ b/.cache/wal/colors-waybar.css
@@ -0,0 +1,20 @@
+@define-color foreground #c4c4c4;
+@define-color background #141415;
+@define-color cursor #c4c4c4;
+
+@define-color color0 #141415;
+@define-color color1 #292b37;
+@define-color color2 #3c2a27;
+@define-color color3 #3b3033;
+@define-color color4 #3f3330;
+@define-color color5 #4b4240;
+@define-color color6 #66615a;
+@define-color color7 #c4c4c4;
+@define-color color8 #4e4e4f;
+@define-color color9 #292b37;
+@define-color color10 #3c2a27;
+@define-color color11 #3b3033;
+@define-color color12 #3f3330;
+@define-color color13 #4b4240;
+@define-color color14 #66615a;
+@define-color color15 #c4c4c4;
diff --git a/.cache/wal/colors.Xresources b/.cache/wal/colors.Xresources
new file mode 100644
index 0000000..cebd8a2
--- /dev/null
+++ b/.cache/wal/colors.Xresources
@@ -0,0 +1,68 @@
+! X colors.
+! Generated by 'wal'
+*foreground: #c4c4c4
+*background: #141415
+*.foreground: #c4c4c4
+*.background: #141415
+emacs*foreground: #c4c4c4
+emacs*background: #141415
+URxvt*foreground: #c4c4c4
+XTerm*foreground: #c4c4c4
+UXTerm*foreground: #c4c4c4
+URxvt*background: [100]#141415
+XTerm*background: #141415
+UXTerm*background: #141415
+URxvt*cursorColor: #c4c4c4
+XTerm*cursorColor: #c4c4c4
+UXTerm*cursorColor: #c4c4c4
+URxvt*borderColor: [100]#141415
+
+! Colors 0-15.
+*.color0: #141415
+*color0: #141415
+*.color1: #292b37
+*color1: #292b37
+*.color2: #3c2a27
+*color2: #3c2a27
+*.color3: #3b3033
+*color3: #3b3033
+*.color4: #3f3330
+*color4: #3f3330
+*.color5: #4b4240
+*color5: #4b4240
+*.color6: #66615a
+*color6: #66615a
+*.color7: #c4c4c4
+*color7: #c4c4c4
+*.color8: #4e4e4f
+*color8: #4e4e4f
+*.color9: #292b37
+*color9: #292b37
+*.color10: #3c2a27
+*color10: #3c2a27
+*.color11: #3b3033
+*color11: #3b3033
+*.color12: #3f3330
+*color12: #3f3330
+*.color13: #4b4240
+*color13: #4b4240
+*.color14: #66615a
+*color14: #66615a
+*.color15: #c4c4c4
+*color15: #c4c4c4
+
+! Black color that will not be affected by bold highlighting.
+*.color66: #141415
+*color66: #141415
+
+! Xclock colors.
+XClock*foreground: #c4c4c4
+XClock*background: #141415
+XClock*majorColor: rgba:c4/c4/c4/ff
+XClock*minorColor: rgba:c4/c4/c4/ff
+XClock*hourColor: rgba:c4/c4/c4/ff
+XClock*minuteColor: rgba:c4/c4/c4/ff
+XClock*secondColor: rgba:c4/c4/c4/ff
+
+! Set depth to make transparency work.
+URxvt*depth: 32
diff --git a/.cache/wal/colors.css b/.cache/wal/colors.css
new file mode 100644
index 0000000..f4d80a0
--- /dev/null
+++ b/.cache/wal/colors.css
@@ -0,0 +1,28 @@
+/* CSS variables
+ Generated by 'wal' */
+:root {
+ --wallpaper: url("/home/loek/.config/wpg/wallpapers/pywal.png");
+
+ /* Special */
+ --background: #141415;
+ --foreground: #c4c4c4;
+ --cursor: #c4c4c4;
+
+ /* Colors */
+ --color0: #141415;
+ --color1: #292b37;
+ --color2: #3c2a27;
+ --color3: #3b3033;
+ --color4: #3f3330;
+ --color5: #4b4240;
+ --color6: #66615a;
+ --color7: #c4c4c4;
+ --color8: #4e4e4f;
+ --color9: #292b37;
+ --color10: #3c2a27;
+ --color11: #3b3033;
+ --color12: #3f3330;
+ --color13: #4b4240;
+ --color14: #66615a;
+ --color15: #c4c4c4;
+}
diff --git a/.cache/wal/colors.hs b/.cache/wal/colors.hs
new file mode 100644
index 0000000..3265973
--- /dev/null
+++ b/.cache/wal/colors.hs
@@ -0,0 +1,37 @@
+--Place this file in your .xmonad/lib directory and import module Colors into .xmonad/xmonad.hs config
+--The easy way is to create a soft link from this file to the file in .xmonad/lib using ln -s
+--Then recompile and restart xmonad.
+
+module Colors
+ ( wallpaper
+ , background, foreground, cursor
+ , color0, color1, color2, color3, color4, color5, color6, color7
+ , color8, color9, color10, color11, color12, color13, color14, color15
+ ) where
+
+-- Shell variables
+-- Generated by 'wal'
+wallpaper="/home/loek/.config/wpg/wallpapers/pywal.png"
+
+-- Special
+background="#141415"
+foreground="#c4c4c4"
+cursor="#c4c4c4"
+
+-- Colors
+color0="#141415"
+color1="#292b37"
+color2="#3c2a27"
+color3="#3b3033"
+color4="#3f3330"
+color5="#4b4240"
+color6="#66615a"
+color7="#c4c4c4"
+color8="#4e4e4f"
+color9="#292b37"
+color10="#3c2a27"
+color11="#3b3033"
+color12="#3f3330"
+color13="#4b4240"
+color14="#66615a"
+color15="#c4c4c4"
diff --git a/.cache/wal/colors.json b/.cache/wal/colors.json
new file mode 100644
index 0000000..9148f6c
--- /dev/null
+++ b/.cache/wal/colors.json
@@ -0,0 +1,28 @@
+{
+ "wallpaper": "/home/loek/.config/wpg/wallpapers/pywal.png",
+ "alpha": "100",
+
+ "special": {
+ "background": "#141415",
+ "foreground": "#c4c4c4",
+ "cursor": "#c4c4c4"
+ },
+ "colors": {
+ "color0": "#141415",
+ "color1": "#292b37",
+ "color2": "#3c2a27",
+ "color3": "#3b3033",
+ "color4": "#3f3330",
+ "color5": "#4b4240",
+ "color6": "#66615a",
+ "color7": "#c4c4c4",
+ "color8": "#4e4e4f",
+ "color9": "#292b37",
+ "color10": "#3c2a27",
+ "color11": "#3b3033",
+ "color12": "#3f3330",
+ "color13": "#4b4240",
+ "color14": "#66615a",
+ "color15": "#c4c4c4"
+ }
+}
diff --git a/.cache/wal/colors.scss b/.cache/wal/colors.scss
new file mode 100644
index 0000000..04d3d4d
--- /dev/null
+++ b/.cache/wal/colors.scss
@@ -0,0 +1,26 @@
+// SCSS Variables
+// Generated by 'wal'
+$wallpaper: "/home/loek/.config/wpg/wallpapers/pywal.png";
+
+// Special
+$background: #141415;
+$foreground: #c4c4c4;
+$cursor: #c4c4c4;
+
+// Colors
+$color0: #141415;
+$color1: #292b37;
+$color2: #3c2a27;
+$color3: #3b3033;
+$color4: #3f3330;
+$color5: #4b4240;
+$color6: #66615a;
+$color7: #c4c4c4;
+$color8: #4e4e4f;
+$color9: #292b37;
+$color10: #3c2a27;
+$color11: #3b3033;
+$color12: #3f3330;
+$color13: #4b4240;
+$color14: #66615a;
+$color15: #c4c4c4;
diff --git a/.cache/wal/colors.sh b/.cache/wal/colors.sh
new file mode 100644
index 0000000..30ebe31
--- /dev/null
+++ b/.cache/wal/colors.sh
@@ -0,0 +1,36 @@
+# Shell variables
+# Generated by 'wal'
+wallpaper='/home/loek/.config/wpg/wallpapers/pywal.png'
+
+# Special
+background='#141415'
+foreground='#c4c4c4'
+cursor='#c4c4c4'
+
+# Colors
+color0='#141415'
+color1='#292b37'
+color2='#3c2a27'
+color3='#3b3033'
+color4='#3f3330'
+color5='#4b4240'
+color6='#66615a'
+color7='#c4c4c4'
+color8='#4e4e4f'
+color9='#292b37'
+color10='#3c2a27'
+color11='#3b3033'
+color12='#3f3330'
+color13='#4b4240'
+color14='#66615a'
+color15='#c4c4c4'
+
+# FZF colors
+export FZF_DEFAULT_OPTS="
+ $FZF_DEFAULT_OPTS
+ --color fg:7,bg:0,hl:1,fg+:232,bg+:1,hl+:255
+ --color info:7,prompt:2,spinner:1,pointer:232,marker:1
+"
+
+# Fix LS_COLORS being unreadable.
+export LS_COLORS="${LS_COLORS}:su=30;41:ow=30;42:st=30;44:"
diff --git a/.cache/wal/colors.yml b/.cache/wal/colors.yml
new file mode 100644
index 0000000..4552903
--- /dev/null
+++ b/.cache/wal/colors.yml
@@ -0,0 +1,24 @@
+wallpaper: "/home/loek/.config/wpg/wallpapers/pywal.png"
+
+special:
+ background: "#141415"
+ foreground: "#c4c4c4"
+ cursor: "#c4c4c4"
+
+colors:
+ color0: "#141415"
+ color1: "#292b37"
+ color2: "#3c2a27"
+ color3: "#3b3033"
+ color4: "#3f3330"
+ color5: "#4b4240"
+ color6: "#66615a"
+ color7: "#c4c4c4"
+ color8: "#4e4e4f"
+ color9: "#292b37"
+ color10: "#3c2a27"
+ color11: "#3b3033"
+ color12: "#3f3330"
+ color13: "#4b4240"
+ color14: "#66615a"
+ color15: "#c4c4c4"
diff --git a/.cache/wal/sequences b/.cache/wal/sequences
new file mode 100644
index 0000000..5094427
--- /dev/null
+++ b/.cache/wal/sequences
@@ -0,0 +1 @@
+]4;0;#141415\]4;1;#292b37\]4;2;#3c2a27\]4;3;#3b3033\]4;4;#3f3330\]4;5;#4b4240\]4;6;#66615a\]4;7;#c4c4c4\]4;8;#4e4e4f\]4;9;#292b37\]4;10;#3c2a27\]4;11;#3b3033\]4;12;#3f3330\]4;13;#4b4240\]4;14;#66615a\]4;15;#c4c4c4\]10;#c4c4c4\]11;#141415\]12;#c4c4c4\]13;#c4c4c4\]17;#c4c4c4\]19;#141415\]4;232;#141415\]4;256;#c4c4c4\]708;#141415\ \ No newline at end of file
diff --git a/.cache/wal/wal b/.cache/wal/wal
new file mode 100644
index 0000000..30e642d
--- /dev/null
+++ b/.cache/wal/wal
@@ -0,0 +1 @@
+/home/loek/.config/wpg/wallpapers/pywal.png \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/BDFDB.config.json b/.config/BetterDiscord/plugins/BDFDB.config.json
new file mode 100644
index 0000000..7b5a5ac
--- /dev/null
+++ b/.config/BetterDiscord/plugins/BDFDB.config.json
@@ -0,0 +1,5 @@
+{
+ "welcomeScreen": {
+ "seen": false
+ }
+} \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/BadgesEverywhere.config.json b/.config/BetterDiscord/plugins/BadgesEverywhere.config.json
new file mode 100644
index 0000000..404c91c
--- /dev/null
+++ b/.config/BetterDiscord/plugins/BadgesEverywhere.config.json
@@ -0,0 +1,29 @@
+{
+ "badges": {
+ "1": true,
+ "2": true,
+ "4": true,
+ "8": true,
+ "64": true,
+ "128": true,
+ "256": true,
+ "512": true,
+ "16384": true,
+ "131072": true,
+ "262144": true,
+ "524288": true
+ },
+ "changelog": {
+ "currentversion": "1.5.5"
+ },
+ "indicators": {
+ "CURRENT_GUILD_BOOST": true
+ },
+ "settings": {
+ "showInChat": true,
+ "showInMemberList": true,
+ "showInPopout": true,
+ "showNitroDate": true,
+ "useColoredVersion": true
+ }
+} \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/BetterSearchPage.config.json b/.config/BetterDiscord/plugins/BetterSearchPage.config.json
new file mode 100644
index 0000000..856dabf
--- /dev/null
+++ b/.config/BetterDiscord/plugins/BetterSearchPage.config.json
@@ -0,0 +1,7 @@
+{
+ "settings": {
+ "addFirstLast": true,
+ "addJumpTo": true,
+ "cloneToTheTop": true
+ }
+} \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/BetterSearchPage.plugin.js b/.config/BetterDiscord/plugins/BetterSearchPage.plugin.js
new file mode 100644
index 0000000..64b71e4
--- /dev/null
+++ b/.config/BetterDiscord/plugins/BetterSearchPage.plugin.js
@@ -0,0 +1,194 @@
+//META{"name":"BetterSearchPage","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/BetterSearchPage","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/BetterSearchPage/BetterSearchPage.plugin.js"}*//
+
+var BetterSearchPage = (_ => {
+ return class BetterSearchPage {
+ getName () {return "BetterSearchPage";}
+
+ getVersion () {return "1.1.5";}
+
+ getAuthor () {return "DevilBro";}
+
+ getDescription () {return "Adds some extra controls to the search results page.";}
+
+ constructor () {
+ this.patchedModules = {
+ after: {
+ SearchResultsInner: "default"
+ }
+ };
+ }
+
+ initConstructor () {
+ this.defaults = {
+ settings: {
+ addFirstLast: {value:true, description:"Adds a first and last page button."},
+ addJumpTo: {value:true, description:"Adds a jump to input field (press enter to jump)."},
+ cloneToTheTop: {value:true, description:"Clones the controls to the top of the results page."}
+ }
+ };
+ }
+
+ getSettingsPanel () {
+ if (!window.BDFDB || typeof BDFDB != "object" || !BDFDB.loaded || !this.started) return;
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ let settingsPanel, settingsItems = [];
+
+ for (let key in settings) settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
+ className: BDFDB.disCN.marginbottom8,
+ type: "Switch",
+ plugin: this,
+ keys: ["settings", key],
+ label: this.defaults.settings[key].description,
+ value: settings[key]
+ }));
+
+ return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, settingsItems);
+ }
+
+ // Legacy
+ load () {}
+
+ start () {
+ if (!window.BDFDB) window.BDFDB = {myPlugins:{}};
+ if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this;
+ let libraryScript = document.querySelector("head script#BDFDBLibraryScript");
+ if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) {
+ if (libraryScript) libraryScript.remove();
+ libraryScript = document.createElement("script");
+ libraryScript.setAttribute("id", "BDFDBLibraryScript");
+ libraryScript.setAttribute("type", "text/javascript");
+ libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js");
+ libraryScript.setAttribute("date", performance.now());
+ libraryScript.addEventListener("load", _ => {this.initialize();});
+ document.head.appendChild(libraryScript);
+ }
+ else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize();
+ this.startTimeout = setTimeout(_ => {
+ try {return this.initialize();}
+ catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);}
+ }, 30000);
+ }
+
+ initialize () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ if (this.started) return;
+ BDFDB.PluginUtils.init(this);
+
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ }
+ else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!");
+ }
+
+ stop () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ this.stopping = true;
+
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+
+ BDFDB.PluginUtils.clear(this);
+ }
+ }
+
+
+ // Begin of own functions
+
+ onSettingsClosed (e) {
+ if (this.SettingsUpdated) {
+ delete this.SettingsUpdated;
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ }
+ }
+
+ processSearchResultsInner (e) {
+ if (e.instance.props.search) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name:"SearchPagination"});
+ if (index > -1) {
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ let currentpage = parseInt(Math.floor(e.instance.props.search.offset / BDFDB.DiscordConstants.SEARCH_PAGE_SIZE)) + 1;
+ let maxpage = e.instance.props.search.totalResults > 5000 ? parseInt(Math.ceil(5000 / BDFDB.DiscordConstants.SEARCH_PAGE_SIZE)) : parseInt(Math.ceil(e.instance.props.search.totalResults / BDFDB.DiscordConstants.SEARCH_PAGE_SIZE));
+ let doJump = page => {
+ page = page < 1 ? 1 : (page > maxpage ? maxpage : page);
+ if (page < currentpage) BDFDB.LibraryModules.SearchPageUtils.searchPreviousPage(e.instance.props.searchId, (currentpage - page) * BDFDB.DiscordConstants.SEARCH_PAGE_SIZE);
+ else if (page > currentpage) BDFDB.LibraryModules.SearchPageUtils.searchNextPage(e.instance.props.searchId, (page - currentpage) * BDFDB.DiscordConstants.SEARCH_PAGE_SIZE);
+ };
+ let pagination = children[index].type(children[index].props);
+ if (!pagination) return;
+
+ if (currentpage >= maxpage) {
+ pagination.props.children[2].props.className = BDFDB.DOMUtils.formatClassName(pagination.props.children[2].props.className, BDFDB.disCN.searchresultspaginationdisabled);
+ pagination.props.children[2].props.onClick = _ => {};
+ }
+ pagination.props.children[0] = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: "Previous",
+ children: pagination.props.children[0]
+ });
+ pagination.props.children[2] = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: currentpage >= maxpage ? "Max Page is 200" : "Next",
+ tooltipConfig: {color: currentpage >= maxpage && BDFDB.LibraryComponents.TooltipContainer.Colors.RED},
+ children: pagination.props.children[2]
+ });
+ if (settings.addFirstLast) {
+ pagination.props.children.unshift(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: "First",
+ "aria-label": "First",
+ onClick: _ => {if (currentpage != 1) doJump(1);},
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, {
+ className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.searchresultspaginationbutton, currentpage == 1 && BDFDB.disCN.searchresultspaginationdisabled),
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCN.searchresultspaginationicon,
+ nativeClass: true,
+ iconSVG: `<svg width="24" height="24" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><polygon fill="currentColor" fill-rule="nonzero" points="12.35 4.35 10 2 0 12 10 22 12.35 19.65 4.717 12"></polygon><polygon fill="currentColor" fill-rule="nonzero" points="24.35 4.35 22 2 12 12 22 22 24.35 19.65 16.717 12"></polygon><polygon points="0 0 24 0 24 24 0 24"></polygon></g></svg>`
+ })
+ })
+ }));
+ pagination.props.children.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: currentpage >= maxpage ? "Max Page is 200" : "Last",
+ tooltipConfig: {color: currentpage >= maxpage && BDFDB.LibraryComponents.TooltipContainer.Colors.RED},
+ "aria-label": "Last",
+ onClick: _ => {if (currentpage != maxpage) doJump(maxpage);},
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, {
+ className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.searchresultspaginationbutton, currentpage >= maxpage && BDFDB.disCN.searchresultspaginationdisabled),
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCN.searchresultspaginationicon,
+ nativeClass: true,
+ iconSVG: `<svg width="24" height="24" viewBox="0 0 24 24"><g fill="none" fill-rule="evenodd"><polygon fill="currentColor" fill-rule="nonzero" points="2.47 2 0.12 4.35 7.753 12 0.12 19.65 2.47 22 12.47 12"></polygon><polygon fill="currentColor" fill-rule="nonzero" points="14.47 2 12.12 4.35 19.753 12 12.12 19.65 14.47 22 24.47 12"></polygon><polygon points="0 0 24 0 24 24 0 24"></polygon></g></svg>`
+ })
+ })
+ }));
+ }
+ if (settings.addJumpTo) {
+ pagination.props.children.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ key: "BSP-pagination-jumpinput",
+ type: "number",
+ size: BDFDB.LibraryComponents.TextInput.Sizes.MINI,
+ suppress: true,
+ value: currentpage,
+ min: 1,
+ max: maxpage,
+ onKeyDown: (e, inputinstance) => {if (e.which == 13) doJump(inputinstance.props.value);}
+ }));
+ pagination.props.children.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: BDFDB.LanguageUtils.LanguageStrings.JUMP,
+ "aria-label": BDFDB.LanguageUtils.LanguageStrings.JUMP,
+ onClick: (e, buttoninstance) => {
+ let jumpinput = BDFDB.ReactUtils.findOwner(buttoninstance._reactInternalFiber.return, {key:"BSP-pagination-jumpinput"});
+ if (jumpinput) doJump(jumpinput.props.value);
+ },
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, {
+ className: BDFDB.disCN.searchresultspaginationbutton,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCN.searchresultspaginationicon,
+ nativeClass: true,
+ style: {transform: "rotate(90deg"},
+ name: BDFDB.LibraryComponents.SvgIcon.Names.RIGHT_CARET
+ })
+ })
+ }));
+ }
+ children[index] = pagination;
+ if (settings.cloneToTheTop) children.unshift(pagination);
+ }
+ }
+ }
+ }
+})(); \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/EditChannels.config.json b/.config/BetterDiscord/plugins/EditChannels.config.json
new file mode 100644
index 0000000..ac031cf
--- /dev/null
+++ b/.config/BetterDiscord/plugins/EditChannels.config.json
@@ -0,0 +1,17 @@
+{
+ "changelog": {
+ "currentversion": "4.1.3"
+ },
+ "settings": {
+ "changeChannelIcon": true,
+ "changeInAuditLog": true,
+ "changeInAutoComplete": true,
+ "changeInChannelHeader": true,
+ "changeInChannelList": true,
+ "changeInChatTextarea": true,
+ "changeInInviteLog": true,
+ "changeInMentions": true,
+ "changeInQuickSwitcher": true,
+ "changeInRecentMentions": true
+ }
+} \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/EditChannels.plugin.js b/.config/BetterDiscord/plugins/EditChannels.plugin.js
new file mode 100644
index 0000000..955776e
--- /dev/null
+++ b/.config/BetterDiscord/plugins/EditChannels.plugin.js
@@ -0,0 +1,773 @@
+//META{"name":"EditChannels","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/EditChannels","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/EditChannels/EditChannels.plugin.js"}*//
+
+var EditChannels = (_ => {
+ return class EditChannels {
+ getName () {return "EditChannels";}
+
+ getVersion () {return "4.1.3";}
+
+ getAuthor () {return "DevilBro";}
+
+ getDescription () {return "Allows you to rename and recolor channelnames.";}
+
+ constructor () {
+ this.changelog = {
+ "fixed":[["Context Menu Update","Fixes for the context menu update, yaaaaaay"]]
+ };
+
+ this.patchedModules = {
+ before: {
+ ChannelEditorContainer: "render",
+ ChannelAutoComplete: "render",
+ AutocompleteChannelResult: "render",
+ AuditLog: "render",
+ SettingsInvites: "render",
+ HeaderBarContainer: "render",
+ ChannelCategoryItem: "render",
+ ChannelItem: "render",
+ QuickSwitchChannelResult: "render",
+ MessageContent: "type"
+ },
+ after: {
+ AutocompleteChannelResult: "render",
+ AuditLog: "render",
+ HeaderBarContainer: "render",
+ ChannelCategoryItem: "render",
+ ChannelItem: "render",
+ QuickSwitchChannelResult: "render",
+ MessagesPopout: "render"
+ }
+ };
+ }
+
+ initConstructor () {
+ this.css = `
+ ${BDFDB.dotCN.messagespopoutchannelname}:hover > span[style*="color"] {
+ text-decoration: underline;
+ }
+ ${BDFDB.dotCN.categorywrapper}:hover ${BDFDB.dotCN.categoryname} span[style*="color"],
+ ${BDFDB.dotCN.categorywrapper}:hover ${BDFDB.dotCN.categoryicon}.EC-changed,
+ ${BDFDB.dotCN.channelwrapper + BDFDB.notCN.channelmodeselected + BDFDB.notCN.channelmodeconnected}:hover ${BDFDB.dotCN.channelname} span[style*="color"],
+ ${BDFDB.dotCN.channelwrapper + BDFDB.notCN.channelmodeselected + BDFDB.notCN.channelmodeconnected}:hover ${BDFDB.dotCN.channelicon}.EC-changed {
+ filter: brightness(150%);
+ }
+ `;
+
+ this.defaults = {
+ settings: {
+ changeChannelIcon: {value:true, inner:false, description:"Change color of Channel Icon"},
+ changeInChatTextarea: {value:true, inner:true, description:"Chat Textarea"},
+ changeInMentions: {value:true, inner:true, description:"Mentions"},
+ changeInChannelList: {value:true, inner:true, description:"Channel List"},
+ changeInChannelHeader: {value:true, inner:true, description:"Channel Header"},
+ changeInRecentMentions: {value:true, inner:true, description:"Recent Mentions Popout"},
+ changeInAutoComplete: {value:true, inner:true, description:"Autocomplete Menu"},
+ changeInAuditLog: {value:true, inner:true, description:"Audit Log"},
+ changeInInviteLog: {value:true, inner:true, description:"Invite Log"},
+ changeInQuickSwitcher: {value:true, inner:true, description:"Quick Switcher"}
+ }
+ };
+ }
+
+ getSettingsPanel () {
+ if (!window.BDFDB || typeof BDFDB != "object" || !BDFDB.loaded || !this.started) return;
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ let settingsPanel, settingsItems = [], innerItems = [];
+
+ for (let key in settings) (!this.defaults.settings[key].inner ? settingsItems : innerItems).push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
+ className: BDFDB.disCN.marginbottom8,
+ type: "Switch",
+ plugin: this,
+ keys: ["settings", key],
+ label: this.defaults.settings[key].description,
+ value: settings[key]
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelInner, {
+ title: "Change Channels in:",
+ first: settingsItems.length == 0,
+ children: innerItems
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Button",
+ className: BDFDB.disCN.marginbottom8,
+ color: BDFDB.LibraryComponents.Button.Colors.RED,
+ label: "Reset all Channels",
+ onClick: _ => {
+ BDFDB.ModalUtils.confirm(this, "Are you sure you want to reset all channels?", _ => {
+ BDFDB.DataUtils.remove(this, "channels");
+ this.forceUpdateAll();
+ });
+ },
+ children: BDFDB.LanguageUtils.LanguageStrings.RESET
+ }));
+
+ return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, settingsItems);
+ }
+
+ // Legacy
+ load () {}
+
+ start () {
+ if (!window.BDFDB) window.BDFDB = {myPlugins:{}};
+ if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this;
+ let libraryScript = document.querySelector("head script#BDFDBLibraryScript");
+ if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) {
+ if (libraryScript) libraryScript.remove();
+ libraryScript = document.createElement("script");
+ libraryScript.setAttribute("id", "BDFDBLibraryScript");
+ libraryScript.setAttribute("type", "text/javascript");
+ libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js");
+ libraryScript.setAttribute("date", performance.now());
+ libraryScript.addEventListener("load", _ => {this.initialize();});
+ document.head.appendChild(libraryScript);
+ }
+ else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize();
+ this.startTimeout = setTimeout(_ => {
+ try {return this.initialize();}
+ catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);}
+ }, 30000);
+ }
+
+ initialize () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ if (this.started) return;
+ BDFDB.PluginUtils.init(this);
+
+ let observer = new MutationObserver(_ => {this.changeAppTitle();});
+ BDFDB.ObserverUtils.connect(this, document.head.querySelector("title"), {name:"appTitleObserver",instance:observer}, {childList:true});
+
+ this.forceUpdateAll();
+ }
+ else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!");
+ }
+
+ stop () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ this.stopping = true;
+
+ let data = BDFDB.DataUtils.load(this, "channels");
+ BDFDB.DataUtils.remove(this, "channels");
+ try {this.forceUpdateAll();} catch (err) {}
+ BDFDB.DataUtils.save(data, this, "channels");
+
+ BDFDB.PluginUtils.clear(this);
+ }
+ }
+
+
+ // Begin of own functions
+
+ onChannelContextMenu (e) {
+ if (e.instance.props.channel) {
+ let [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: "devmode-copy-id", group: true});
+ children.splice(index > -1 ? index : children.length, 0, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
+ children: BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.context_localchannelsettings_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "settings-submenu"),
+ children: BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
+ children: [
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.submenu_channelsettings_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "settings-change"),
+ action: _ => {
+ BDFDB.ContextMenuUtils.close(e.instance);
+ this.openChannelSettingsModal(e.instance.props.channel);
+ }
+ }),
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.submenu_resetsettings_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "settings-reset"),
+ disabled: !BDFDB.DataUtils.load(this, "channels", e.instance.props.channel.id),
+ action: _ => {
+ BDFDB.ContextMenuUtils.close(e.instance);
+ BDFDB.DataUtils.remove(this, "channels", e.instance.props.channel.id);
+ this.forceUpdateAll();
+ }
+ })
+ ]
+ })
+ })
+ }));
+ }
+ }
+
+ onSettingsClosed () {
+ if (this.SettingsUpdated) {
+ delete this.SettingsUpdated;
+ this.forceUpdateAll();
+ }
+ }
+
+ processChannelEditorContainer (e) {
+ if (!e.instance.props.disabled && e.instance.props.channel && BDFDB.ChannelUtils.isTextChannel(e.instance.props.channel) && e.instance.props.type == BDFDB.DiscordConstants.TextareaTypes.NORMAL && BDFDB.DataUtils.get(this, "settings", "changeInChatTextarea")) {
+ let data = BDFDB.DataUtils.load(this, "channels", e.instance.props.channel.id);
+ e.instance.props.placeholder = BDFDB.LanguageUtils.LanguageStringsFormat("TEXTAREA_PLACEHOLDER", `#${data && data.name || e.instance.props.channel.name}`);
+ }
+ }
+
+ processChannelAutoComplete (e) {
+ if (e.instance.state.autocompleteType == "CHANNELS" && BDFDB.ArrayUtils.is(e.instance.state.autocompletes.channels) && e.instance.props.channel && e.instance.props.channel.guild_id) {
+ let lastword = (e.instance.props.textValue || "").slice(1).toLowerCase();
+ let channels = BDFDB.DataUtils.load(this, "channels");
+ if (!channels || !lastword) return;
+ let channelarray = [];
+ for (let id in channels) if (channels[id] && channels[id].name) {
+ let channel = BDFDB.LibraryModules.ChannelStore.getChannel(id);
+ let category = channel && channel.parent_id && BDFDB.LibraryModules.ChannelStore.getChannel(channel.parent_id);
+ let catdata = category && channels[category.id] || {};
+ if (BDFDB.ChannelUtils.isTextChannel(channel) && channel.guild_id == e.instance.props.channel.guild_id) channelarray.push(Object.assign({
+ lowercasename: channels[id].name.toLowerCase(),
+ lowercasecatname: catdata && catdata.name && catdata.name.toLowerCase(),
+ channel,
+ category,
+ catdata
+ }, channels[id]));
+ }
+ channelarray = BDFDB.ArrayUtils.keySort(channelarray.filter(n => e.instance.state.autocompletes.channels.every(channel => channel.id != n.channel.id) && (n.lowercasename.indexOf(lastword) != -1 || (n.lowercasecatname && n.lowercasecatname.indexOf(lastword) != -1))), "lowercasename");
+ e.instance.state.autocompletes.channels = [].concat(e.instance.state.autocompletes.channels, channelarray.map(n => n.channel)).slice(0, BDFDB.DiscordConstants.MAX_AUTOCOMPLETE_RESULTS);
+ }
+ }
+
+ processAutocompleteChannelResult (e) {
+ if (e.instance.props.channel && BDFDB.DataUtils.get(this, "settings", "changeInAutoComplete")) {
+ if (!e.returnvalue) {
+ e.instance.props.channel = this.getChannelData(e.instance.props.channel.id);
+ if (e.instance.props.category) e.instance.props.category = this.getChannelData(e.instance.props.category.id);
+ }
+ else {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.marginleft4]]});
+ if (index > -1) this.changeChannelColor(children[index], e.instance.props.channel.id);
+ [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.autocompleteicon]]});
+ if (index > -1) this.changeChannelIconColor(children[index], e.instance.props.channel.id, {alpha: 0.6});
+ if (e.instance.props.category) {
+ [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.autocompletedescription]]});
+ if (index > -1) this.changeChannelColor(children[index], e.instance.props.category.id);
+ }
+ }
+ }
+ }
+
+ processAuditLog (e) {
+ let channel = BDFDB.ReactUtils.getValue(e.instance, "props.log.options.channel");
+ if (channel && BDFDB.DataUtils.get(this, "settings", "changeInAuditLog")) {
+ if (!e.returnvalue) e.instance.props.log.options.channel = this.getChannelData(channel.id);
+ else {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["children", [["#" + channel.name]]]]});
+ if (index > -1) this.changeChannelColor(children[index], channel.id);
+ }
+ }
+ }
+
+ processSettingsInvites (e) {
+ if (BDFDB.ObjectUtils.is(e.instance.props.invites) && BDFDB.DataUtils.get(this, "settings", "changeInInviteLog")) {
+ e.instance.props.invites = Object.assign({}, e.instance.props.invites);
+ for (let id in e.instance.props.invites) e.instance.props.invites[id] = new BDFDB.DiscordObjects.Invite(Object.assign({}, e.instance.props.invites[id], {channel: this.getChannelData(e.instance.props.invites[id].channel.id)}));
+ }
+ }
+
+ processHeaderBarContainer (e) {
+ let channel = BDFDB.LibraryModules.ChannelStore.getChannel(e.instance.props.channelId);
+ if (channel && BDFDB.ChannelUtils.isTextChannel(channel) && BDFDB.DataUtils.get(this, "settings", "changeInChannelHeader")) {
+ if (!e.returnvalue) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.instance, {name: "Title"});
+ if (index > -1) {
+ children[index].props.children = this.getChannelData(channel.id).name;
+ this.changeChannelColor(children[index], channel.id);
+ }
+ }
+ else {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.instance, {name: "Icon"});
+ if (index > -1) {
+ let icon = BDFDB.ReactUtils.createElement(children[index].props.icon, {
+ className: BDFDB.disCN.channelheadericon
+ });
+ this.changeChannelIconColor(icon, channel.id, {alpha: 0.6});
+ children[index] = BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.channelheadericonwrapper,
+ children: icon
+ })
+ }
+ }
+ }
+ }
+
+ processChannelCategoryItem (e) {
+ if (e.instance.props.channel && BDFDB.DataUtils.get(this, "settings", "changeInChannelList")) {
+ if (!e.returnvalue) e.instance.props.channel = this.getChannelData(e.instance.props.channel.id);
+ else {
+ let modify = BDFDB.ObjectUtils.extract(e.instance.props, "muted", "locked", "selected", "unread", "connected");
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.categoryname]]});
+ if (index > -1) this.changeChannelColor(children[index], e.instance.props.channel.id, modify);
+ [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.categoryicon]]});
+ if (index > -1) this.changeChannelIconColor(children[index], e.instance.props.channel.id, Object.assign({alpha: 0.6}, modify));
+ }
+ }
+ }
+
+ processChannelItem (e) {
+ if (e.instance.props.channel && BDFDB.DataUtils.get(this, "settings", "changeInChannelList")) {
+ if (!e.returnvalue) e.instance.props.channel = this.getChannelData(e.instance.props.channel.id);
+ else {
+ let modify = BDFDB.ObjectUtils.extract(e.instance.props, "muted", "locked", "selected", "unread", "connected");
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.channelname]]});
+ if (index > -1) this.changeChannelColor(children[index], e.instance.props.channel.id, modify);
+ [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.channelicon]]});
+ if (index > -1) this.changeChannelIconColor(children[index], e.instance.props.channel.id, Object.assign({alpha: 0.6}, modify));
+ }
+ }
+ }
+
+ processQuickSwitchChannelResult (e) {
+ if (e.instance.props.channel && BDFDB.DataUtils.get(this, "settings", "changeInQuickSwitcher")) {
+ if (!e.returnvalue) {
+ e.instance.props.channel = this.getChannelData(e.instance.props.channel.id);
+ if (e.instance.props.category) e.instance.props.category = this.getChannelData(e.instance.props.category.id);
+ }
+ else {
+ let modify = BDFDB.ObjectUtils.extract(e.instance.props, "focused", "unread", "mentions");
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.quickswitchresultmatch]]});
+ if (index > -1) this.changeChannelColor(children[index], e.instance.props.channel.id, modify);
+ [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.quickswitchresulticon]]});
+ if (index > -1) this.changeChannelIconColor(children[index], e.instance.props.channel.id, Object.assign({alpha: 0.6}, modify));
+ if (e.instance.props.category) {
+ [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.quickswitchresultnote]]});
+ if (index > -1) this.changeChannelColor(children[index], e.instance.props.category.id);
+ }
+ }
+ }
+ }
+
+ processMessagesPopout (e) {
+ if (BDFDB.DataUtils.get(this, "settings", "changeInRecentMentions")) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name: "VerticalScroller"});
+ if (index > -1 && children[index].props.children && BDFDB.ArrayUtils.is(children[index].props.children[0])) for (let i in children[index].props.children[0]) {
+ let divider = children[index].props.children[0][i];
+ if (divider && divider.props && divider.props.className == BDFDB.disCN.messagespopoutchannelseparator) {
+ let channel = BDFDB.ReactUtils.findValue(children[index].props.children[0][parseInt(i)+1], "channel");
+ if (BDFDB.ChannelUtils.isTextChannel(channel)) {
+ let [children2, index2] = BDFDB.ReactUtils.findChildren(divider, {props:[["className", BDFDB.disCN.messagespopoutchannelname]]});
+ if (index2 > -1) {
+ children2[index2].props.children = "#" + this.getChannelData(channel.id).name;
+ this.changeChannelColor(children2[index2], channel.id);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ processMessageContent (e) {
+ if (BDFDB.ArrayUtils.is(e.instance.props.content) && BDFDB.DataUtils.get(this, "settings", "changeInMentions")) for (let ele of e.instance.props.content) {
+ if (BDFDB.ReactUtils.isValidElement(ele) && ele.type && ele.type.displayName == "Tooltip" && typeof ele.props.children == "function") {
+ let children = ele.props.children({});
+ if (children && children.type.displayName == "Mention" && children.props.children && typeof children.props.children[0] == "string" && children.props.children[0][0] == "#") {
+ let channelName = children.props.children[0].slice(1);
+ let guildId = BDFDB.LibraryModules.LastGuildStore.getGuildId();
+ let channels = guildId && (BDFDB.LibraryModules.GuildChannelStore.getChannels(guildId)[0] || BDFDB.LibraryModules.GuildChannelStore.getChannels(guildId).SELECTABLE);
+ if (Array.isArray(channels)) for (let channelObj of channels) {
+ if (channelName == channelObj.channel.name) {
+ let category = BDFDB.LibraryModules.ChannelStore.getChannel(channelObj.channel.parent_id);
+ if (!category || category && ele.props.text == category.name) {
+ if (category) {
+ let categoryData = BDFDB.DataUtils.load(this, "channels", category.id);
+ if (categoryData && categoryData.name) ele.props.text = categoryData.name;
+ }
+ let name = (BDFDB.DataUtils.load(this, "channels", channelObj.channel.id) || {}).name;
+ let color = this.getChannelDataColor(channelObj.channel.id);
+ if (name || color) {
+ let renderChildren = ele.props.children;
+ ele.props.children = (...args) => {
+ let renderedChildren = renderChildren(...args);
+ if (name) renderedChildren.props.children[0] = "#" + name;
+ if (color) {
+ let color1_0 = BDFDB.ColorUtils.convert(BDFDB.ObjectUtils.is(color) ? color[0] : color, "RGBA");
+ let color0_1 = BDFDB.ColorUtils.setAlpha(color1_0, 0.1, "RGBA");
+ let color0_7 = BDFDB.ColorUtils.setAlpha(color1_0, 0.7, "RGBA");
+ renderedChildren.props.style = Object.assign({}, renderedChildren.props.style, {
+ background: color0_1,
+ color: color1_0
+ });
+ let onMouseEnter = renderedChildren.props.onMouseEnter || ( _ => {});
+ renderedChildren.props.onMouseEnter = event => {
+ onMouseEnter(event);
+ event.target.style.setProperty("background", color0_7, "important");
+ event.target.style.setProperty("color", "#FFFFFF", "important");
+ };
+ let onMouseLeave = renderedChildren.props.onMouseLeave || ( _ => {});
+ renderedChildren.props.onMouseLeave = event => {
+ onMouseLeave(event);
+ event.target.style.setProperty("background", color0_1, "important");
+ event.target.style.setProperty("color", color1_0, "important");
+ };
+ }
+ return renderedChildren;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ changeAppTitle () {
+ let channel = BDFDB.LibraryModules.ChannelStore.getChannel(BDFDB.LibraryModules.LastChannelStore.getChannelId());
+ let title = document.head.querySelector("title");
+ if (title && BDFDB.ChannelUtils.isTextChannel(channel)) BDFDB.DOMUtils.setText(title, "@" + this.getChannelData(channel.id, BDFDB.DataUtils.get(this, "settings", "changeAppTitle")).name);
+ }
+
+ changeChannelColor (child, channelId, modify) {
+ let color = this.getChannelDataColor(channelId);
+ if (color) {
+ color = modify ? this.chooseColor(color, modify) : BDFDB.ColorUtils.convert(color, "RGBA");
+ let fontGradient = BDFDB.ObjectUtils.is(color);
+ if (fontGradient) child.props.children = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextGradientElement, {
+ gradient: BDFDB.ColorUtils.createGradient(color),
+ children: child.props.children
+ });
+ else child.props.children = BDFDB.ReactUtils.createElement("span", {
+ style: {color: color},
+ children: child.props.children
+ });
+ }
+ }
+
+ changeChannelIconColor (child, channelId, modify) {
+ let color = this.getChannelDataColor(channelId);
+ if (color && BDFDB.DataUtils.get(this, "settings", "changeChannelIcon")) {
+ color = modify ? this.chooseColor(BDFDB.ObjectUtils.is(color) ? color[0] : color, modify) : BDFDB.ColorUtils.convert(BDFDB.ObjectUtils.is(color) ? color[0] : color, "RGBA");
+ child.props.color = color || "currentColor";
+ if (color) {
+ child.props.foreground = null;
+ child.props.className = BDFDB.DOMUtils.formatClassName(child.props.className, "EC-changed");
+ }
+ }
+ }
+
+ chooseColor (color, config) {
+ if (color) {
+ if (BDFDB.ObjectUtils.is(config)) {
+ if (config.mentions || config.focused || config.hovered || config.selected || config.unread || config.connected) color = BDFDB.ColorUtils.change(color, 0.5);
+ else if (config.muted || config.locked) color = BDFDB.ColorUtils.change(color, -0.5);
+ }
+ return BDFDB.ColorUtils.convert(color, "RGBA");
+ }
+ return null;
+ }
+
+ getChannelDataColor (channelId) {
+ let channel = BDFDB.LibraryModules.ChannelStore.getChannel(channelId);
+ if (!channel) return null;
+ let channelData = BDFDB.DataUtils.load(this, "channels", channel.id);
+ if (channelData && channelData.color) return channelData.color;
+ let category = channel.parent_id && BDFDB.LibraryModules.ChannelStore.getChannel(channel.parent_id);
+ if (category) {
+ let categoryData = BDFDB.DataUtils.load(this, "channels", category.id);
+ if (categoryData && categoryData.inheritColor && categoryData.color) return categoryData.color;
+ }
+ return null;
+ }
+
+ getChannelData (channelId, change = true) {
+ let channel = BDFDB.LibraryModules.ChannelStore.getChannel(channelId);
+ if (!channel) return new BDFDB.DiscordObjects.Channel({});
+ let data = change && BDFDB.DataUtils.load(this, "channels", channel.id);
+ if (data) {
+ let nativeObject = new BDFDB.DiscordObjects.Channel(channel);
+ nativeObject.name = data.name || nativeObject.name;
+ return nativeObject;
+ }
+ return new BDFDB.DiscordObjects.Channel(channel);
+ }
+
+ forceUpdateAll () {
+ this.changeAppTitle();
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ BDFDB.ReactUtils.forceUpdate(BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.app), {name:"Channel", unlimited:true}));
+ }
+
+ openChannelSettingsModal (channel) {
+ let data = BDFDB.DataUtils.load(this, "channels", channel.id) || {};
+
+ BDFDB.ModalUtils.open(this, {
+ size: "MEDIUM",
+ header: this.labels.modal_header_text,
+ subheader: channel.name,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_channelname_text,
+ className: BDFDB.disCN.marginbottom20 + " input-channelname",
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ value: data.name,
+ placeholder: channel.name,
+ autoFocus: true
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, {
+ className: BDFDB.disCN.dividerdefault
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_colorpicker1_text,
+ className: BDFDB.disCN.marginbottom20,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ColorSwatches, {
+ color: data.color,
+ number: 1
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Switch",
+ className: BDFDB.disCN.marginbottom20 + " input-inheritcolor",
+ label: this.labels.modal_inheritcolor_text,
+ tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5,
+ value: channel.type == 4 && data.inheritColor,
+ disabled: channel.type != 4
+ })
+ ],
+ buttons: [{
+ contents: BDFDB.LanguageUtils.LanguageStrings.SAVE,
+ color: "BRAND",
+ close: true,
+ click: modal => {
+ let olddata = Object.assign({}, data);
+
+ data.name = modal.querySelector(".input-channelname " + BDFDB.dotCN.input).value.trim() || null;
+
+ data.color = BDFDB.ColorUtils.getSwatchColor(modal, 1);
+ if (data.color != null && !BDFDB.ObjectUtils.is(data.color)) {
+ if (data.color[0] < 30 && data.color[1] < 30 && data.color[2] < 30) data.color = BDFDB.ColorUtils.change(data.color, 30);
+ else if (data.color[0] > 225 && data.color[1] > 225 && data.color[2] > 225) data.color = BDFDB.ColorUtils.change(data.color, -30);
+ }
+
+ data.inheritColor = modal.querySelector(".input-inheritcolor " + BDFDB.dotCN.switchinner).checked;
+
+ let changed = false;
+ if (Object.keys(data).every(key => data[key] == null || data[key] == false) && (changed = true)) BDFDB.DataUtils.remove(this, "channels", channel.id);
+ else if (!BDFDB.equals(olddata, data) && (changed = true)) BDFDB.DataUtils.save(data, this, "channels", channel.id);
+ if (changed) this.forceUpdateAll();
+ }
+ }]
+ });
+ }
+
+ setLabelsByLanguage () {
+ switch (BDFDB.LanguageUtils.getLanguage().id) {
+ case "hr": //croatian
+ return {
+ context_localchannelsettings_text: "Postavke lokalnih kanala",
+ submenu_channelsettings_text: "Promijeni postavke",
+ submenu_resetsettings_text: "Vraćanje kanala",
+ modal_header_text: "Postavke lokalnih kanala",
+ modal_channelname_text: "Naziv lokalnog kanala",
+ modal_colorpicker1_text: "Boja lokalnog kanala",
+ modal_inheritcolor_text: "Naslijedi boju u potkanale"
+ };
+ case "da": //danish
+ return {
+ context_localchannelsettings_text: "Lokal kanalindstillinger",
+ submenu_channelsettings_text: "Skift indstillinger",
+ submenu_resetsettings_text: "Nulstil kanal",
+ modal_header_text: "Lokal kanalindstillinger",
+ modal_channelname_text: "Lokalt kanalnavn",
+ modal_colorpicker1_text: "Lokal kanalfarve",
+ modal_inheritcolor_text: "Arve farve til subkanaler"
+ };
+ case "de": //german
+ return {
+ context_localchannelsettings_text: "Lokale Kanaleinstellungen",
+ submenu_channelsettings_text: "Einstellungen ändern",
+ submenu_resetsettings_text: "Kanal zurücksetzen",
+ modal_header_text: "Lokale Kanaleinstellungen",
+ modal_channelname_text: "Lokaler Kanalname",
+ modal_colorpicker1_text: "Lokale Kanalfarbe",
+ modal_inheritcolor_text: "Farbe an Unterkanäle vererben"
+ };
+ case "es": //spanish
+ return {
+ context_localchannelsettings_text: "Ajustes local de canal",
+ submenu_channelsettings_text: "Cambiar ajustes",
+ submenu_resetsettings_text: "Restablecer canal",
+ modal_header_text: "Ajustes local de canal",
+ modal_channelname_text: "Nombre local del canal",
+ modal_colorpicker1_text: "Color local del canal",
+ modal_inheritcolor_text: "Heredar color a sub-canales"
+ };
+ case "fr": //french
+ return {
+ context_localchannelsettings_text: "Paramètres locale du salon",
+ submenu_channelsettings_text: "Modifier les paramètres",
+ submenu_resetsettings_text: "Réinitialiser le salon",
+ modal_header_text: "Paramètres locale du salon",
+ modal_channelname_text: "Nom local du salon",
+ modal_colorpicker1_text: "Couleur locale du salon",
+ modal_inheritcolor_text: "Hériter de la couleur sur les sous-salons"
+ };
+ case "it": //italian
+ return {
+ context_localchannelsettings_text: "Impostazioni locale canale",
+ submenu_channelsettings_text: "Cambia impostazioni",
+ submenu_resetsettings_text: "Ripristina canale",
+ modal_header_text: "Impostazioni locale canale",
+ modal_channelname_text: "Nome locale canale",
+ modal_colorpicker1_text: "Colore locale canale",
+ modal_inheritcolor_text: "Eredita colore per sub-canali"
+ };
+ case "nl": //dutch
+ return {
+ context_localchannelsettings_text: "Lokale kanaalinstellingen",
+ submenu_channelsettings_text: "Verandere instellingen",
+ submenu_resetsettings_text: "Reset kanaal",
+ modal_header_text: "Lokale kanaalinstellingen",
+ modal_channelname_text: "Lokale kanaalnaam",
+ modal_colorpicker1_text: "Lokale kanaalkleur",
+ modal_inheritcolor_text: "Overerving van kleuren naar subkanalen"
+ };
+ case "no": //norwegian
+ return {
+ context_localchannelsettings_text: "Lokal kanalinnstillinger",
+ submenu_channelsettings_text: "Endre innstillinger",
+ submenu_resetsettings_text: "Tilbakestill kanal",
+ modal_header_text: "Lokal kanalinnstillinger",
+ modal_channelname_text: "Lokalt kanalnavn",
+ modal_colorpicker1_text: "Lokal kanalfarge",
+ modal_inheritcolor_text: "Arve farge til underkanaler"
+ };
+ case "pl": //polish
+ return {
+ context_localchannelsettings_text: "Lokalne ustawienia kanału",
+ submenu_channelsettings_text: "Zmień ustawienia",
+ submenu_resetsettings_text: "Resetuj ustawienia",
+ modal_header_text: "Lokalne ustawienia kanału",
+ modal_channelname_text: "Lokalna nazwa kanału",
+ modal_colorpicker1_text: "Lokalny kolor kanału",
+ modal_inheritcolor_text: "Dziedzicz kolor do podkanałów"
+ };
+ case "pt-BR": //portuguese (brazil)
+ return {
+ context_localchannelsettings_text: "Configurações local do canal",
+ submenu_channelsettings_text: "Mudar configurações",
+ submenu_resetsettings_text: "Redefinir canal",
+ modal_header_text: "Configurações local do canal",
+ modal_channelname_text: "Nome local do canal",
+ modal_colorpicker1_text: "Cor local do canal",
+ modal_inheritcolor_text: "Herdar cor aos sub-canais"
+ };
+ case "fi": //finnish
+ return {
+ context_localchannelsettings_text: "Paikallinen kanavan asetukset",
+ submenu_channelsettings_text: "Vaihda asetuksia",
+ submenu_resetsettings_text: "Nollaa kanava",
+ modal_header_text: "Paikallinen kanavan asetukset",
+ modal_channelname_text: "Paikallinen kanavanimi",
+ modal_colorpicker1_text: "Paikallinen kanavanväri",
+ modal_inheritcolor_text: "Hävitä väri alikanaville"
+ };
+ case "sv": //swedish
+ return {
+ context_localchannelsettings_text: "Lokal kanalinställningar",
+ submenu_channelsettings_text: "Ändra inställningar",
+ submenu_resetsettings_text: "Återställ kanal",
+ modal_header_text: "Lokal kanalinställningar",
+ modal_channelname_text: "Lokalt kanalnamn",
+ modal_colorpicker1_text: "Lokal kanalfärg",
+ modal_inheritcolor_text: "Inherit färg till subkanaler"
+ };
+ case "tr": //turkish
+ return {
+ context_localchannelsettings_text: "Yerel Kanal Ayarları",
+ submenu_channelsettings_text: "Ayarları Değiştir",
+ submenu_resetsettings_text: "Kanal Sıfırla",
+ modal_header_text: "Yerel Kanal Ayarları",
+ modal_channelname_text: "Yerel Kanal Adı",
+ modal_colorpicker1_text: "Yerel Kanal Rengi",
+ modal_inheritcolor_text: "Renkleri alt kanallara miras alma"
+ };
+ case "cs": //czech
+ return {
+ context_localchannelsettings_text: "Místní nastavení kanálu",
+ submenu_channelsettings_text: "Změnit nastavení",
+ submenu_resetsettings_text: "Obnovit kanál",
+ modal_header_text: "Místní nastavení kanálu",
+ modal_channelname_text: "Místní název kanálu",
+ modal_colorpicker1_text: "Místní barvy kanálu",
+ modal_inheritcolor_text: "Zdědit barvu na subkanály"
+ };
+ case "bg": //bulgarian
+ return {
+ context_localchannelsettings_text: "Настройки за локални канали",
+ submenu_channelsettings_text: "Промяна на настройките",
+ submenu_resetsettings_text: "Възстановяване на канал",
+ modal_header_text: "Настройки за локални канали",
+ modal_channelname_text: "Локално име на канал",
+ modal_colorpicker1_text: "Локален цветен канал",
+ modal_inheritcolor_text: "Наследи цвета до подканали"
+ };
+ case "ru": //russian
+ return {
+ context_localchannelsettings_text: "Настройки локального канала",
+ submenu_channelsettings_text: "Изменить настройки",
+ submenu_resetsettings_text: "Сбросить канал",
+ modal_header_text: "Настройки локального канала",
+ modal_channelname_text: "Имя локального канала",
+ modal_colorpicker1_text: "Цвет локального канала",
+ modal_inheritcolor_text: "Наследовать цвет на подканалы"
+ };
+ case "uk": //ukrainian
+ return {
+ context_localchannelsettings_text: "Налаштування локального каналу",
+ submenu_channelsettings_text: "Змінити налаштування",
+ submenu_resetsettings_text: "Скидання каналу",
+ modal_header_text: "Налаштування локального каналу",
+ modal_channelname_text: "Локальне ім'я каналу",
+ modal_colorpicker1_text: "Колір місцевого каналу",
+ modal_inheritcolor_text: "Успадковують колір до підканалів"
+ };
+ case "ja": //japanese
+ return {
+ context_localchannelsettings_text: "ローカルチャネル設定",
+ submenu_channelsettings_text: "設定を変更する",
+ submenu_resetsettings_text: "チャネルをリセットする",
+ modal_header_text: "ローカルチャネル設定",
+ modal_channelname_text: "ローカルチャネル名",
+ modal_colorpicker1_text: "ローカルチャネルの色",
+ modal_inheritcolor_text: "サブチャンネルに色を継承"
+ };
+ case "zh-TW": //chinese (traditional)
+ return {
+ context_localchannelsettings_text: "本地頻道設置",
+ submenu_channelsettings_text: "更改設置",
+ submenu_resetsettings_text: "重置通道",
+ modal_header_text: "本地頻道設置",
+ modal_channelname_text: "本地頻道名稱",
+ modal_colorpicker1_text: "本地頻道顏色",
+ modal_inheritcolor_text: "繼承子通道的顏色"
+ };
+ case "ko": //korean
+ return {
+ context_localchannelsettings_text: "로컬 채널 설정",
+ submenu_channelsettings_text: "설정 변경",
+ submenu_resetsettings_text: "채널 재설정",
+ modal_header_text: "로컬 채널 설정",
+ modal_channelname_text: "로컬 채널 이름",
+ modal_colorpicker1_text: "지역 채널 색깔",
+ modal_inheritcolor_text: "하위 채널에 색상 상속"
+ };
+ default: //default: english
+ return {
+ context_localchannelsettings_text: "Local Channelsettings",
+ submenu_channelsettings_text: "Change Settings",
+ submenu_resetsettings_text: "Reset Channel",
+ modal_header_text: "Local Channelsettings",
+ modal_channelname_text: "Local Channelname",
+ modal_colorpicker1_text: "Local Channelcolor",
+ modal_inheritcolor_text: "Inherit color to Sub-Channels"
+ };
+ }
+ }
+ }
+})(); \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/EditServers.config.json b/.config/BetterDiscord/plugins/EditServers.config.json
new file mode 100644
index 0000000..8544d34
--- /dev/null
+++ b/.config/BetterDiscord/plugins/EditServers.config.json
@@ -0,0 +1,28 @@
+{
+ "changelog": {
+ "currentversion": "2.2.1"
+ },
+ "servers": {
+ "624859996949315584": {
+ "banner": "https://media.discordapp.net/attachments/624865812888420352/715196061400039474/8PiLwbX.png",
+ "color1": null,
+ "color2": null,
+ "color3": null,
+ "color4": null,
+ "ignoreCustomName": false,
+ "name": "Gerrit",
+ "removeBanner": false,
+ "removeIcon": false,
+ "shortName": null,
+ "url": null
+ }
+ },
+ "settings": {
+ "addOriginalTooltip": true,
+ "changeInGuildHeader": true,
+ "changeInGuildList": true,
+ "changeInMutualGuilds": true,
+ "changeInQuickSwitcher": true,
+ "changeInRecentMentions": true
+ }
+} \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/EditServers.plugin.js b/.config/BetterDiscord/plugins/EditServers.plugin.js
new file mode 100644
index 0000000..3291113
--- /dev/null
+++ b/.config/BetterDiscord/plugins/EditServers.plugin.js
@@ -0,0 +1,1079 @@
+//META{"name":"EditServers","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/EditServers","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/EditServers/EditServers.plugin.js"}*//
+
+var EditServers = (_ => {
+ return class EditServers {
+ getName () {return "EditServers";}
+
+ getVersion () {return "2.2.1";}
+
+ getAuthor () {return "DevilBro";}
+
+ getDescription () {return "Allows you to change the icon, name and color of servers.";}
+
+ constructor () {
+ this.changelog = {
+ "fixed":[["Context Menu Update","Fixes for the context menu update, yaaaaaay"]]
+ };
+
+ this.patchedModules = {
+ before: {
+ Guild: "render",
+ GuildIconWrapper: "render",
+ MutualGuilds: "render",
+ FriendRow: "render",
+ QuickSwitcher: "render",
+ QuickSwitchChannelResult: "render",
+ GuildSidebar: "render",
+ GuildHeader: "render"
+ },
+ after: {
+ MessagesPopout: "render",
+ Guild: "render",
+ BlobMask: "render",
+ GuildIconWrapper: "render",
+ GuildIcon: "render",
+ GuildHeader: "render"
+ }
+ };
+
+ this.patchPriority = 7;
+ }
+
+ initConstructor () {
+ this.defaults = {
+ settings: {
+ addOriginalTooltip: {value:true, inner:false, description:"Hovering over a changed Server Header shows the original Name as Tooltip"},
+ changeInGuildList: {value:true, inner:true, description:"Server List"},
+ changeInGuildHeader: {value:true, inner:true, description:"Server Header"},
+ changeInMutualGuilds: {value:true, inner:true, description:"Mutual Servers"},
+ changeInRecentMentions: {value:true, inner:true, description:"Recent Mentions Popout"},
+ changeInQuickSwitcher: {value:true, inner:true, description:"Quick Switcher"}
+ }
+ };
+ }
+
+ getSettingsPanel () {
+ if (!window.BDFDB || typeof BDFDB != "object" || !BDFDB.loaded || !this.started) return;
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ let settingsPanel, settingsItems = [], innerItems = [];
+
+ for (let key in settings) (!this.defaults.settings[key].inner ? settingsItems : innerItems).push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
+ className: BDFDB.disCN.marginbottom8,
+ type: "Switch",
+ plugin: this,
+ keys: ["settings", key],
+ label: this.defaults.settings[key].description,
+ value: settings[key]
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelInner, {
+ title: "Change Servers in:",
+ first: settingsItems.length == 0,
+ children: innerItems
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Button",
+ className: BDFDB.disCN.marginbottom8,
+ color: BDFDB.LibraryComponents.Button.Colors.RED,
+ label: "Reset all Servers",
+ onClick: _ => {
+ BDFDB.ModalUtils.confirm(this, "Are you sure you want to reset all Servers?", _ => {
+ BDFDB.DataUtils.remove(this, "servers");
+ BDFDB.ModuleUtils.forceAllUpdates(this);;
+ });
+ },
+ children: BDFDB.LanguageUtils.LanguageStrings.RESET
+ }));
+
+ return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, settingsItems);
+ }
+
+ // Legacy
+ load () {}
+
+ start () {
+ if (!window.BDFDB) window.BDFDB = {myPlugins:{}};
+ if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this;
+ let libraryScript = document.querySelector("head script#BDFDBLibraryScript");
+ if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) {
+ if (libraryScript) libraryScript.remove();
+ libraryScript = document.createElement("script");
+ libraryScript.setAttribute("id", "BDFDBLibraryScript");
+ libraryScript.setAttribute("type", "text/javascript");
+ libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js");
+ libraryScript.setAttribute("date", performance.now());
+ libraryScript.addEventListener("load", _ => {this.initialize();});
+ document.head.appendChild(libraryScript);
+ }
+ else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize();
+ this.startTimeout = setTimeout(_ => {
+ try {return this.initialize();}
+ catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);}
+ }, 30000);
+ }
+
+ initialize () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ if (this.started) return;
+ BDFDB.PluginUtils.init(this);
+
+ BDFDB.ModuleUtils.patch(this, BDFDB.LibraryModules.IconUtils, "getGuildBannerURL", {instead:e => {
+ let guild = BDFDB.LibraryModules.GuildStore.getGuild(e.methodArguments[0].id);
+ if (guild) {
+ if (e.methodArguments[0].id == "410787888507256842") return guild.banner;
+ let data = BDFDB.DataUtils.load(this, "servers", guild.id);
+ if (data && data.banner && !data.removeBanner) return data.banner;
+ }
+ return e.callOriginalMethod();
+ }});
+
+ BDFDB.ModuleUtils.patch(this, BDFDB.LibraryComponents.GuildComponents.Guild.prototype, "render", {
+ before: e => {this.processGuild({instance:e.thisObject, returnvalue:e.returnValue, methodname:"render"});},
+ after: e => {this.processGuild({instance:e.thisObject, returnvalue:e.returnValue, methodname:"render"});}
+ });
+
+ BDFDB.ModuleUtils.patch(this, BDFDB.LibraryComponents.Connectors.Link.prototype, "render", {
+ after: e => {
+ if (e.thisObject.props.className && e.thisObject.props.className.indexOf(BDFDB.disCN.guildiconwrapper) > -1) this.processGuildAcronym({instance:e.thisObject, returnvalue:e.returnValue, methodname:"render"});
+ }
+ });
+
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ }
+ else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!");
+ }
+
+ stop () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ this.stopping = true;
+
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+
+ for (let guildobj of BDFDB.GuildUtils.getAll()) if (guildobj.instance) delete guildobj.instance.props.guild.EditServersCachedBanner;
+
+ BDFDB.PluginUtils.clear(this);
+ }
+ }
+
+ // Begin of own functions
+
+ onGuildContextMenu (e) {
+ if (e.instance.props.guild) {
+ let [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: "devmode-copy-id", group: true});
+ children.splice(index > -1 ? index : children.length, 0, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
+ children: BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.context_localserversettings_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "settings-submenu"),
+ children: BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
+ children: [
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.submenu_serversettings_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "settings-change"),
+ action: _ => {
+ BDFDB.ContextMenuUtils.close(e.instance);
+ this.openGuildSettingsModal(e.instance.props.guild.id);
+ }
+ }),
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.submenu_resetsettings_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "settings-reset"),
+ disabled: !BDFDB.DataUtils.load(this, "servers", e.instance.props.guild.id),
+ action: _ => {
+ BDFDB.ContextMenuUtils.close(e.instance);
+ BDFDB.DataUtils.remove(this, "servers", e.instance.props.guild.id);
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ }
+ })
+ ]
+ })
+ })
+ }));
+ }
+ }
+
+ processGuild (e) {
+ if (BDFDB.GuildUtils.is(e.instance.props.guild) && BDFDB.DataUtils.get(this, "settings", "changeInGuildList")) {
+ e.instance.props.guild = this.getGuildData(e.instance.props.guild.id);
+ if (e.returnvalue) {
+ let data = BDFDB.DataUtils.load(this, "servers", e.instance.props.guild.id);
+ if (data && (data.color3 || data.color4)) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name: ["GuildTooltip", "BDFDB_TooltipContainer"]});
+ if (index > -1) children[index] = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ tooltipConfig: {
+ type: "right",
+ guild: e.instance.props.guild,
+ list: true,
+ offset: 12,
+ backgroundColor: data.color3,
+ fontColor: data.color4
+ },
+ children: children[index].props.children
+ });
+ }
+ }
+ }
+ }
+
+ processBlobMask (e) {
+ if (BDFDB.DataUtils.get(this, "settings", "changeInGuildList")) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name: "NavItem"});
+ if (index > -1 && children[index].props.to && children[index].props.to.pathname) {
+ let guild = BDFDB.LibraryModules.GuildStore.getGuild((children[index].props.to.pathname.split("/channels/")[1] || "").split("/")[0]);
+ if (guild) {
+ let data = BDFDB.DataUtils.load(this, "servers", guild.id);
+ if (data) {
+ if (data.shortName) children[index].props.name = data.shortName.split("").join(" ");
+ else if (data.name && data.ignoreCustomName) children[index].props.name = guild.name;
+ }
+ }
+ }
+ }
+ }
+
+ processGuildAcronym (e) {
+ if (typeof e.returnvalue.props.children == "function" && BDFDB.DataUtils.get(this, "settings", "changeInGuildList")) {
+ let pathname = BDFDB.ReactUtils.getValue(e.instance, "props.to.pathname");
+ let data = pathname && BDFDB.DataUtils.load(this, "servers", (pathname.split("/channels/")[1] || "").split("/")[0]);
+ if (data) {
+ let renderChildren = e.returnvalue.props.children;
+ e.returnvalue.props.children = (...args) => {
+ let renderedChildren = renderChildren(...args);
+ let [children, index] = BDFDB.ReactUtils.findChildren(renderedChildren, {props:[["className", BDFDB.disCN.guildiconacronym]]});
+ if (index > -1) {
+ let fontGradient = BDFDB.ObjectUtils.is(data.color2);
+ children[index].props.style = Object.assign({}, children[index].props.style, {
+ background: BDFDB.ObjectUtils.is(data.color1) ? BDFDB.ColorUtils.createGradient(data.color1) : BDFDB.ColorUtils.convert(data.color1, "RGBA"),
+ color: !fontGradient && BDFDB.ColorUtils.convert(data.color2, "RGBA")
+ });
+ if (fontGradient) children[index].props.children = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextGradientElement, {
+ gradient: BDFDB.ColorUtils.createGradient(data.color2),
+ children: children[index].props.children
+ });
+ }
+ return renderedChildren;
+ };
+ }
+ }
+ }
+
+ processGuildIconWrapper (e) {
+ if (BDFDB.GuildUtils.is(e.instance.props.guild)) {
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ if (e.instance.props.className && e.instance.props.className.indexOf(BDFDB.disCN.guildfolderguildicon) > -1) e.instance.props.guild = this.getGuildData(e.instance.props.guild.id, settings.changeInGuildList);
+ else if (e.instance.props.className && e.instance.props.className.indexOf(BDFDB.disCN.listavatar) > -1) e.instance.props.guild = this.getGuildData(e.instance.props.guild.id, settings.changeInMutualGuilds);
+ else e.instance.props.guild = this.getGuildData(e.instance.props.guild.id);
+ }
+ }
+
+ processGuildIcon (e) {
+ if (BDFDB.GuildUtils.is(e.instance.props.guild) && e.instance.props.style && (!e.instance.props.style.backgroundImage || e.instance.props.style.backgroundImage == "none")) {
+ let data = BDFDB.DataUtils.load(this, "servers", e.instance.props.guild.id);
+ if (data) {
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ if (e.instance.props.className && e.instance.props.className.indexOf(BDFDB.disCN.guildfolderguildicon) > -1) this.changeGuildIcon(e, data, settings.changeInGuildList);
+ else if (e.instance.props.className && e.instance.props.className.indexOf(BDFDB.disCN.listavatar) > -1 || BDFDB.ReactUtils.findConstructor(e.instance, "MutualGuild", {up: true})) this.changeGuildIcon(e, data, settings.changeInMutualGuilds);
+ else this.changeGuildIcon(e, data);
+ }
+ }
+ }
+
+ processMutualGuilds (e) {
+ if (BDFDB.DataUtils.get(this, "settings", "changeInMutualGuilds")) for (let i in e.instance.props.mutualGuilds) e.instance.props.mutualGuilds[i].guild = this.getGuildData(e.instance.props.mutualGuilds[i].guild.id);
+ }
+
+ processFriendRow (e) {
+ if (BDFDB.DataUtils.get(this, "settings", "changeInMutualGuilds")) for (let i in e.instance.props.mutualGuilds) e.instance.props.mutualGuilds[i] = this.getGuildData(e.instance.props.mutualGuilds[i].id);
+ }
+
+ processQuickSwitcher (e) {
+ if (BDFDB.DataUtils.get(this, "settings", "changeInQuickSwitcher")) for (let i in e.instance.props.results) if (e.instance.props.results[i].type == "GUILD") e.instance.props.results[i].record = this.getGuildData(e.instance.props.results[i].record.id);
+ }
+
+ processQuickSwitchChannelResult (e) {
+ if (e.instance.props.channel && e.instance.props.channel.guild_id && BDFDB.DataUtils.get(this, "settings", "changeInQuickSwitcher")) {
+ e.instance.props.children.props.children = this.getGuildData(e.instance.props.channel.guild_id).name;
+ }
+ }
+
+ processMessagesPopout (e) {
+ if (BDFDB.DataUtils.get(this, "settings", "changeInRecentMentions")) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name: "VerticalScroller"});
+ if (index > -1 && children[index].props.children && BDFDB.ArrayUtils.is(children[index].props.children[0])) for (let i in children[index].props.children[0]) {
+ let divider = children[index].props.children[0][i];
+ if (divider && divider.props && divider.props.className == BDFDB.disCN.messagespopoutchannelseparator) {
+ let channel = BDFDB.ReactUtils.findValue(children[index].props.children[0][parseInt(i)+1], "channel");
+ if (BDFDB.ChannelUtils.isTextChannel(channel)) {
+ let [children2, index2] = BDFDB.ReactUtils.findChildren(divider, {props:[["className", BDFDB.disCN.messagespopoutguildname]]});
+ if (index2 > -1) children2[index2].props.children = this.getGuildData(channel.guild_id).name;
+ }
+ }
+ }
+ }
+ }
+
+ processGuildSidebar (e) {
+ if (e.instance.props.guild) {
+ let data = BDFDB.DataUtils.load(this, "servers", e.instance.props.guild.id);
+ if (data) {
+ if (data.removeBanner) e.instance.props.guild = new BDFDB.DiscordObjects.Guild(Object.assign({}, e.instance.props.guild, {banner: null}));
+ else if (data.banner) e.instance.props.guild = new BDFDB.DiscordObjects.Guild(Object.assign({}, e.instance.props.guild, {banner: data.banner}));
+ }
+ }
+ }
+
+ processGuildHeader (e) {
+ if (e.instance.props.guild) {
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ if (settings.changeInGuildHeader) {
+ e.instance.props.guild = this.getGuildData(e.instance.props.guild.id);
+ let oldName = (BDFDB.LibraryModules.GuildStore.getGuild(e.instance.props.guild.id) || {}).name;
+ if (e.returnvalue && settings.addOriginalTooltip && oldName != e.instance.props.guild.name) {
+ e.returnvalue.props.children[0] = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: oldName,
+ children: e.returnvalue.props.children[0],
+ tooltipConfig: {type: "right"}
+ });
+ }
+ }
+ }
+ }
+
+ getGuildData (guildId, change = true) {
+ let guild = BDFDB.LibraryModules.GuildStore.getGuild(guildId);
+ if (!guild) return new BDFDB.DiscordObjects.Guild({});
+ let data = change && BDFDB.DataUtils.load(this, "servers", guild.id);
+ if (data) {
+ let newGuildObject = {}, nativeObject = new BDFDB.DiscordObjects.Guild(guild);
+ for (let key in nativeObject) newGuildObject[key] = nativeObject[key];
+ newGuildObject.name = data.name || nativeObject.name;
+ newGuildObject.acronym = data.shortName && data.shortName.replace(/\s/g, "") || BDFDB.LibraryModules.StringUtils.getAcronym(!data.ignoreCustomName && data.name || nativeObject.name);
+ if (data.removeIcon) {
+ newGuildObject.icon = null;
+ newGuildObject.getIconURL = _ => {return null;};
+ }
+ else if (data.url) {
+ newGuildObject.icon = data.url;
+ newGuildObject.getIconURL = _ => {return data.url;};
+ }
+ if (data.removeBanner) newGuildObject.banner = null;
+ else if (data.banner) newGuildObject.banner = data.banner;
+ return newGuildObject;
+ }
+ return new BDFDB.DiscordObjects.Guild(guild);
+ }
+
+ changeGuildIcon (e, data, change = true) {
+ if (change) {
+ let fontGradient = BDFDB.ObjectUtils.is(data.color2);
+ e.returnvalue.props.style = Object.assign({}, e.returnvalue.props.style, {
+ background: BDFDB.ObjectUtils.is(data.color1) ? BDFDB.ColorUtils.createGradient(data.color1) : BDFDB.ColorUtils.convert(data.color1, "RGBA"),
+ color: !fontGradient && BDFDB.ColorUtils.convert(data.color2, "RGBA")
+ });
+ if (fontGradient) e.returnvalue.props.children[0] = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextGradientElement, {
+ gradient: BDFDB.ColorUtils.createGradient(data.color2),
+ children: e.returnvalue.props.children[0]
+ });
+ }
+ }
+
+ openGuildSettingsModal (guildId) {
+ let guild = BDFDB.LibraryModules.GuildStore.getGuild(guildId);
+ if (!guild) return;
+ let data = BDFDB.DataUtils.load(this, "servers", guild.id) || {};
+
+ let currentIgnoreCustomNameState = data.ignoreCustomName;
+
+ BDFDB.ModalUtils.open(this, {
+ size: "MEDIUM",
+ header: this.labels.modal_header_text,
+ subheader: guild.name,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, {
+ tab: this.labels.modal_tabheader1_text,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_guildname_text,
+ className: BDFDB.disCN.marginbottom20,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ className: "input-guildname",
+ key: "GUILDNAME",
+ value: data.name,
+ placeholder: guild.name,
+ autoFocus: true,
+ onChange: (value, instance) => {
+ if (!currentIgnoreCustomNameState) {
+ let acronymInputIns = BDFDB.ReactUtils.findOwner(instance._reactInternalFiber.return.return.return, {key: "GUILDACRONYM"});
+ if (acronymInputIns) {
+ acronymInputIns.props.placeholder = value && BDFDB.LibraryModules.StringUtils.getAcronym(value) || guild.acronym;
+ BDFDB.ReactUtils.forceUpdate(acronymInputIns);
+ }
+ }
+ }
+ })
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_guildacronym_text,
+ className: BDFDB.disCN.marginbottom8,
+ children:
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ className: "input-guildacronym",
+ key: "GUILDACRONYM",
+ value: data.shortName,
+ placeholder: !data.ignoreCustomName && data.name && BDFDB.LibraryModules.StringUtils.getAcronym(data.name) || guild.acronym
+ })
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Switch",
+ className: BDFDB.disCN.marginbottom20 + " input-ignorecustomname",
+ label: this.labels.modal_ignorecustomname_text,
+ tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5,
+ value: data.ignoreCustomName,
+ onChange: (value, instance) => {
+ currentIgnoreCustomNameState = value;
+ let nameInputIns = BDFDB.ReactUtils.findOwner(instance._reactInternalFiber.return, {key: "GUILDNAME"});
+ let acronymInputIns = BDFDB.ReactUtils.findOwner(instance._reactInternalFiber.return, {key: "GUILDACRONYM"});
+ if (nameInputIns && acronymInputIns) {
+ acronymInputIns.props.placeholder = !value && nameInputIns.props.value && BDFDB.LibraryModules.StringUtils.getAcronym(nameInputIns.props.value) || guild.acronym;
+ BDFDB.ReactUtils.forceUpdate(acronymInputIns);
+ }
+ }
+ }),
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.marginbottom20,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
+ className: BDFDB.disCN.marginbottom8,
+ align: BDFDB.LibraryComponents.Flex.Align.CENTER,
+ direction: BDFDB.LibraryComponents.Flex.Direction.HORIZONTAL,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormTitle, {
+ className: BDFDB.disCN.marginreset,
+ tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5,
+ children: this.labels.modal_guildicon_text
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ className: "input-removeicon",
+ type: "Switch",
+ grow: 0,
+ label: BDFDB.LanguageUtils.LanguageStrings.REMOVE,
+ tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5,
+ value: data.removeIcon,
+ onChange: (value, instance) => {
+ let iconInputIins = BDFDB.ReactUtils.findOwner(instance._reactInternalFiber.return.return, {key: "GUILDICON"});
+ if (iconInputIins) {
+ delete iconInputIins.props.success;
+ delete iconInputIins.props.errorMessage;
+ iconInputIins.props.disabled = value;
+ BDFDB.ReactUtils.forceUpdate(iconInputIins);
+ }
+ }
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ className: "input-guildicon",
+ key: "GUILDICON",
+ success: !data.removeIcon && data.url,
+ maxLength: 100000000000000000000,
+ value: data.url,
+ placeholder: BDFDB.GuildUtils.getIcon(guild.id),
+ disabled: data.removeIcon,
+ onChange: (value, instance) => {
+ this.checkUrl(value, instance);
+ }
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.marginbottom20,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
+ className: BDFDB.disCN.marginbottom8,
+ align: BDFDB.LibraryComponents.Flex.Align.CENTER,
+ direction: BDFDB.LibraryComponents.Flex.Direction.HORIZONTAL,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormTitle, {
+ className: BDFDB.disCN.marginreset,
+ tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5,
+ children: this.labels.modal_guildbanner_text
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ className: "input-removebanner",
+ type: "Switch",
+ grow: 0,
+ label: BDFDB.LanguageUtils.LanguageStrings.REMOVE,
+ tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5,
+ value: data.removeBanner && guild.id != "410787888507256842",
+ disabled: guild.id == "410787888507256842",
+ onChange: (value, instance) => {
+ let bannerInputIns = BDFDB.ReactUtils.findOwner(instance._reactInternalFiber.return.return, {key: "GUILDBANNER"});
+ if (bannerInputIns) {
+ delete bannerInputIns.props.success;
+ delete bannerInputIns.props.errorMessage;
+ bannerInputIns.props.disabled = value;
+ BDFDB.ReactUtils.forceUpdate(bannerInputIns);
+ }
+ }
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ className: "input-guildbanner",
+ key: "GUILDBANNER",
+ success: !data.removeBanner && data.banner,
+ maxLength: 100000000000000000000,
+ value: data.banner,
+ placeholder: BDFDB.GuildUtils.getBanner(guild.id),
+ disabled: data.removeBanner || guild.id == "410787888507256842",
+ onChange: (value, instance) => {
+ this.checkUrl(value, instance);
+ }
+ })
+ ]
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, {
+ tab: this.labels.modal_tabheader2_text,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_colorpicker1_text,
+ className: BDFDB.disCN.marginbottom20,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ColorSwatches, {
+ color: data.color1,
+ number: 1
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_colorpicker2_text,
+ className: BDFDB.disCN.marginbottom20,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ColorSwatches, {
+ color: data.color2,
+ number: 2
+ })
+ ]
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, {
+ tab: this.labels.modal_tabheader3_text,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_colorpicker3_text,
+ className: BDFDB.disCN.marginbottom20,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ColorSwatches, {
+ color: data.color3,
+ number: 3
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_colorpicker4_text,
+ className: BDFDB.disCN.marginbottom20,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ColorSwatches, {
+ color: data.color4,
+ number: 4
+ })
+ ]
+ })
+ ]
+ })
+ ],
+ buttons: [{
+ contents: BDFDB.LanguageUtils.LanguageStrings.SAVE,
+ color: "BRAND",
+ close: true,
+ click: modal => {
+ let olddata = Object.assign({}, data);
+
+ let guildnameinput = modal.querySelector(".input-guildname " + BDFDB.dotCN.input);
+ let guildacronyminput = modal.querySelector(".input-guildacronym " + BDFDB.dotCN.input);
+ let ignorecustomnameinput = modal.querySelector(".input-ignorecustomname " + BDFDB.dotCN.switchinner);
+ let guildiconinput = modal.querySelector(".input-guildicon " + BDFDB.dotCN.input);
+ let removeiconinput = modal.querySelector(".input-removeicon " + BDFDB.dotCN.switchinner);
+ let guildbannerinput = modal.querySelector(".input-guildbanner " + BDFDB.dotCN.input);
+ let removebannerinput = modal.querySelector(".input-removebanner " + BDFDB.dotCN.switchinner);
+
+ data.name = guildnameinput.value.trim() || null;
+ data.shortName = guildacronyminput.value.trim() || null;
+ data.ignoreCustomName = ignorecustomnameinput.checked;
+ data.url = (!data.removeIcon && BDFDB.DOMUtils.containsClass(guildiconinput, BDFDB.disCN.inputsuccess) ? guildiconinput.value.trim() : null) || null;
+ data.removeIcon = removeiconinput.checked;
+ data.banner = (!data.removeBanner && BDFDB.DOMUtils.containsClass(guildbannerinput, BDFDB.disCN.inputsuccess) ? guildbannerinput.value.trim() : null) || null;
+ data.removeBanner = removebannerinput.checked && guild.id != "410787888507256842";
+
+ data.color1 = BDFDB.ColorUtils.getSwatchColor(modal, 1);
+ data.color2 = BDFDB.ColorUtils.getSwatchColor(modal, 2);
+ data.color3 = BDFDB.ColorUtils.getSwatchColor(modal, 3);
+ data.color4 = BDFDB.ColorUtils.getSwatchColor(modal, 4);
+
+ let changed = false;
+ if (Object.keys(data).every(key => !data[key]) && (changed = true)) BDFDB.DataUtils.remove(this, "servers", guild.id);
+ else if (!BDFDB.equals(olddata, data) && (changed = true)) BDFDB.DataUtils.save(data, this, "servers", guild.id);
+ if (changed) BDFDB.ModuleUtils.forceAllUpdates(this);;
+ }
+ }]
+ });
+ }
+
+ checkUrl (url, instance) {
+ BDFDB.TimeUtils.clear(instance.checkTimeout);
+ if (url == null || !url.trim()) {
+ delete instance.props.success;
+ delete instance.props.errorMessage;
+ instance.forceUpdate();
+ }
+ else instance.checkTimeout = BDFDB.TimeUtils.timeout(_ => {
+ BDFDB.LibraryRequires.request(url.trim(), (error, response, result) => {
+ if (response && response.headers["content-type"] && response.headers["content-type"].indexOf("image") != -1) {
+ instance.props.success = true;
+ delete instance.props.errorMessage;
+ }
+ else {
+ delete instance.props.success;
+ instance.props.errorMessage = this.labels.modal_invalidurl_text;
+ }
+ delete instance.checkTimeout;
+ instance.forceUpdate();
+ });
+ }, 1000);
+ }
+
+ setBanner (id, data) {
+ data = data || {};
+ let guild = BDFDB.LibraryModules.GuildStore.getGuild(id);
+ if (!guild) return;
+ if (guild.EditServersCachedBanner === undefined) guild.EditServersCachedBanner = guild.banner;
+ guild.banner = data.removeBanner ? null : (data.banner || guild.EditServersCachedBanner);
+ }
+
+ setLabelsByLanguage () {
+ switch (BDFDB.LanguageUtils.getLanguage().id) {
+ case "hr": //croatian
+ return {
+ context_localserversettings_text: "Lokalne postavke poslužitelja",
+ submenu_serversettings_text: "Promijeni postavke",
+ submenu_resetsettings_text: "Ponovno postavite poslužitelj",
+ modal_header_text: "Lokalne postavke poslužitelja",
+ modal_guildname_text: "Naziv lokalnog poslužitelja",
+ modal_guildacronym_text: "Akronim lokalnog poslužitelja",
+ modal_ignorecustomname_text: "Koristite izvorno ime poslužitelja za akronim poslužitelja",
+ modal_guildicon_text: "Ikona",
+ modal_guildbanner_text: "Baner",
+ modal_tabheader1_text: "Poslužitelja",
+ modal_tabheader2_text: "Boja ikona",
+ modal_tabheader3_text: "Boja tooltip",
+ modal_colorpicker1_text: "Boja ikona",
+ modal_colorpicker2_text: "Boja fonta",
+ modal_colorpicker3_text: "Boja tooltip",
+ modal_colorpicker4_text: "Boja fonta",
+ modal_invalidurl_text: "Nevažeći URL"
+ };
+ case "da": //danish
+ return {
+ context_localserversettings_text: "Lokal serverindstillinger",
+ submenu_serversettings_text: "Skift indstillinger",
+ submenu_resetsettings_text: "Nulstil server",
+ modal_header_text: "Lokal serverindstillinger",
+ modal_guildname_text: "Lokalt servernavn",
+ modal_guildacronym_text: "Lokalt serverakronym",
+ modal_ignorecustomname_text: "Brug det originale servernavn til serverens akronym",
+ modal_guildicon_text: "Ikon",
+ modal_guildbanner_text: "Banner",
+ modal_tabheader1_text: "Server",
+ modal_tabheader2_text: "Ikonfarve",
+ modal_tabheader3_text: "Tooltipfarve",
+ modal_colorpicker1_text: "Ikonfarve",
+ modal_colorpicker2_text: "Skriftfarve",
+ modal_colorpicker3_text: "Tooltipfarve",
+ modal_colorpicker4_text: "Skriftfarve",
+ modal_invalidurl_text: "Ugyldig URL"
+ };
+ case "de": //german
+ return {
+ context_localserversettings_text: "Lokale Servereinstellungen",
+ submenu_serversettings_text: "Einstellungen ändern",
+ submenu_resetsettings_text: "Server zurücksetzen",
+ modal_header_text: "Lokale Servereinstellungen",
+ modal_guildname_text: "Lokaler Servername",
+ modal_guildacronym_text: "Lokales Serverkürzel",
+ modal_ignorecustomname_text: "Benutze den ursprünglichen Servernamen für das Serverkürzel",
+ modal_guildicon_text: "Icon",
+ modal_guildbanner_text: "Banner",
+ modal_tabheader1_text: "Server",
+ modal_tabheader2_text: "Iconfarbe",
+ modal_tabheader3_text: "Tooltipfarbe",
+ modal_colorpicker1_text: "Iconfarbe",
+ modal_colorpicker2_text: "Schriftfarbe",
+ modal_colorpicker3_text: "Tooltipfarbe",
+ modal_colorpicker4_text: "Schriftfarbe",
+ modal_invalidurl_text: "Ungültige URL"
+ };
+ case "es": //spanish
+ return {
+ context_localserversettings_text: "Ajustes local de servidor",
+ submenu_serversettings_text: "Cambiar ajustes",
+ submenu_resetsettings_text: "Restablecer servidor",
+ modal_header_text: "Ajustes local de servidor",
+ modal_guildname_text: "Nombre local del servidor",
+ modal_guildacronym_text: "Acrónimo local del servidor",
+ modal_ignorecustomname_text: "Use el nombre del servidor original para el acrónimo del servidor",
+ modal_guildicon_text: "Icono",
+ modal_guildbanner_text: "Bandera",
+ modal_tabheader1_text: "Servidor",
+ modal_tabheader2_text: "Color del icono",
+ modal_tabheader3_text: "Color de tooltip",
+ modal_colorpicker1_text: "Color del icono",
+ modal_colorpicker2_text: "Color de fuente",
+ modal_colorpicker3_text: "Color de tooltip",
+ modal_colorpicker4_text: "Color de fuente",
+ modal_invalidurl_text: "URL inválida"
+ };
+ case "fr": //french
+ return {
+ context_localserversettings_text: "Paramètres locale du serveur",
+ submenu_serversettings_text: "Modifier les paramètres",
+ submenu_resetsettings_text: "Réinitialiser le serveur",
+ modal_header_text: "Paramètres locale du serveur",
+ modal_guildname_text: "Nom local du serveur",
+ modal_guildacronym_text: "Acronyme local de serveur",
+ modal_ignorecustomname_text: "Utilisez le nom de serveur d'origine pour l'acronyme de serveur",
+ modal_guildicon_text: "Icône",
+ modal_guildbanner_text: "Bannière",
+ modal_tabheader1_text: "Serveur",
+ modal_tabheader2_text: "Couleur de l'icône",
+ modal_tabheader3_text: "Couleur de tooltip",
+ modal_colorpicker1_text: "Couleur de l'icône",
+ modal_colorpicker2_text: "Couleur de la police",
+ modal_colorpicker3_text: "Couleur de tooltip",
+ modal_colorpicker4_text: "Couleur de la police",
+ modal_invalidurl_text: "URL invalide"
+ };
+ case "it": //italian
+ return {
+ context_localserversettings_text: "Impostazioni locale server",
+ submenu_serversettings_text: "Cambia impostazioni",
+ submenu_resetsettings_text: "Ripristina server",
+ modal_header_text: "Impostazioni locale server",
+ modal_guildname_text: "Nome locale server",
+ modal_guildacronym_text: "Acronimo locale server",
+ modal_ignorecustomname_text: "Utilizzare il nome del server originale per l'acronimo del server",
+ modal_guildicon_text: "Icona",
+ modal_guildbanner_text: "Bandiera",
+ modal_tabheader1_text: "Server",
+ modal_tabheader2_text: "Colore dell'icona",
+ modal_tabheader3_text: "Colore della tooltip",
+ modal_colorpicker1_text: "Colore dell'icona",
+ modal_colorpicker2_text: "Colore del carattere",
+ modal_colorpicker3_text: "Colore della tooltip",
+ modal_colorpicker4_text: "Colore del carattere",
+ modal_invalidurl_text: "URL non valido"
+ };
+ case "nl": //dutch
+ return {
+ context_localserversettings_text: "Lokale serverinstellingen",
+ submenu_serversettings_text: "Verandere instellingen",
+ submenu_resetsettings_text: "Reset server",
+ modal_header_text: "Lokale serverinstellingen",
+ modal_guildname_text: "Lokale servernaam",
+ modal_guildacronym_text: "Lokale server acroniem",
+ modal_ignorecustomname_text: "Gebruik de oorspronkelijke servernaam voor het serveracrononiem",
+ modal_guildicon_text: "Icoon",
+ modal_guildbanner_text: "Banier",
+ modal_tabheader1_text: "Server",
+ modal_tabheader2_text: "Icoonkleur",
+ modal_tabheader3_text: "Tooltipkleur",
+ modal_colorpicker1_text: "Icoonkleur",
+ modal_colorpicker2_text: "Doopvontkleur",
+ modal_colorpicker3_text: "Tooltipkleur",
+ modal_colorpicker4_text: "Doopvontkleur",
+ modal_invalidurl_text: "Ongeldige URL"
+ };
+ case "no": //norwegian
+ return {
+ context_localserversettings_text: "Lokal serverinnstillinger",
+ submenu_serversettings_text: "Endre innstillinger",
+ submenu_resetsettings_text: "Tilbakestill server",
+ modal_header_text: "Lokal serverinnstillinger",
+ modal_guildname_text: "Lokalt servernavn",
+ modal_guildacronym_text: "Lokalt serverforkortelse",
+ modal_ignorecustomname_text: "Bruk det originale servernavnet til serverforkortelsen",
+ modal_guildicon_text: "Ikon",
+ modal_guildbanner_text: "Banner",
+ modal_tabheader1_text: "Server",
+ modal_tabheader2_text: "Ikonfarge",
+ modal_tabheader3_text: "Tooltipfarge",
+ modal_colorpicker1_text: "Ikonfarge",
+ modal_colorpicker2_text: "Skriftfarge",
+ modal_colorpicker3_text: "Tooltipfarge",
+ modal_colorpicker4_text: "Skriftfarge",
+ modal_invalidurl_text: "Ugyldig URL"
+ };
+ case "pl": //polish
+ return {
+ context_localserversettings_text: "Lokalne ustawienia serwera",
+ submenu_serversettings_text: "Zmień ustawienia",
+ submenu_resetsettings_text: "Resetuj ustawienia",
+ modal_header_text: "Lokalne ustawienia serwera",
+ modal_guildname_text: "Lokalna nazwa serwera",
+ modal_guildacronym_text: "Akronim lokalnego serwera",
+ modal_ignorecustomname_text: "Użyj oryginalnej nazwy serwera dla akronimu serwera",
+ modal_guildicon_text: "Ikona",
+ modal_guildbanner_text: "Baner",
+ modal_tabheader1_text: "Serwer",
+ modal_tabheader2_text: "Kolor ikony",
+ modal_tabheader3_text: "Kolor podpowiedzi",
+ modal_colorpicker1_text: "Kolor ikony",
+ modal_colorpicker2_text: "Kolor czcionki",
+ modal_colorpicker3_text: "Kolor podpowiedzi",
+ modal_colorpicker4_text: "Kolor czcionki",
+ modal_invalidurl_text: "Nieprawidłowe URL"
+ };
+ case "pt-BR": //portuguese (brazil)
+ return {
+ context_localserversettings_text: "Configurações local do servidor",
+ submenu_serversettings_text: "Mudar configurações",
+ submenu_resetsettings_text: "Redefinir servidor",
+ modal_header_text: "Configurações local do servidor",
+ modal_guildname_text: "Nome local do servidor",
+ modal_guildacronym_text: "Acrônimo local de servidor",
+ modal_ignorecustomname_text: "Use o nome do servidor original para a sigla do servidor",
+ modal_guildicon_text: "Icone",
+ modal_guildbanner_text: "Bandeira",
+ modal_tabheader1_text: "Servidor",
+ modal_tabheader2_text: "Cor do ícone",
+ modal_tabheader3_text: "Cor da tooltip",
+ modal_colorpicker1_text: "Cor do ícone",
+ modal_colorpicker2_text: "Cor da fonte",
+ modal_colorpicker3_text: "Cor da tooltip",
+ modal_colorpicker4_text: "Cor da fonte",
+ modal_invalidurl_text: "URL inválida"
+ };
+ case "fi": //finnish
+ return {
+ context_localserversettings_text: "Paikallinen palvelimen asetukset",
+ submenu_serversettings_text: "Vaihda asetuksia",
+ submenu_resetsettings_text: "Nollaa palvelimen",
+ modal_header_text: "Paikallinen palvelimen asetukset",
+ modal_guildname_text: "Paikallinen palvelimenimi",
+ modal_guildacronym_text: "Paikallisen palvelimen lyhenne",
+ modal_ignorecustomname_text: "Käytä alkuperäistä palvelimen nimeä palvelimen lyhenteessä",
+ modal_guildicon_text: "Ikonin",
+ modal_guildbanner_text: "Banneri",
+ modal_tabheader1_text: "Palvelimen",
+ modal_tabheader2_text: "Ikoninväri",
+ modal_tabheader3_text: "Tooltipväri",
+ modal_colorpicker1_text: "Ikoninväri",
+ modal_colorpicker2_text: "Fontinväri",
+ modal_colorpicker3_text: "Tooltipväri",
+ modal_colorpicker4_text: "Fontinväri",
+ modal_invalidurl_text: "Virheellinen URL"
+ };
+ case "sv": //swedish
+ return {
+ context_localserversettings_text: "Lokal serverinställningar",
+ submenu_serversettings_text: "Ändra inställningar",
+ submenu_resetsettings_text: "Återställ server",
+ modal_header_text: "Lokal serverinställningar",
+ modal_guildname_text: "Lokalt servernamn",
+ modal_guildacronym_text: "Lokal server förkortning",
+ modal_ignorecustomname_text: "Använd det ursprungliga servernamnet för serverförkortningen",
+ modal_guildicon_text: "Ikon",
+ modal_guildbanner_text: "Banderoll",
+ modal_tabheader1_text: "Server",
+ modal_tabheader2_text: "Ikonfärg",
+ modal_tabheader3_text: "Tooltipfärg",
+ modal_colorpicker1_text: "Ikonfärg",
+ modal_colorpicker2_text: "Fontfärg",
+ modal_colorpicker3_text: "Tooltipfärg",
+ modal_colorpicker4_text: "Fontfärg",
+ modal_invalidurl_text: "Ogiltig URL"
+ };
+ case "tr": //turkish
+ return {
+ context_localserversettings_text: "Yerel Sunucu Ayarları",
+ submenu_serversettings_text: "Ayarları Değiştir",
+ submenu_resetsettings_text: "Sunucu Sıfırla",
+ modal_header_text: "Yerel sunucu ayarları",
+ modal_guildname_text: "Yerel sunucu adı",
+ modal_guildacronym_text: "Yerel sunucu kısaltması",
+ modal_ignorecustomname_text: "Sunucu kısaltması için orijinal sunucu adını kullanın",
+ modal_guildicon_text: "Simge",
+ modal_guildbanner_text: "Afişi",
+ modal_tabheader1_text: "Sunucu",
+ modal_tabheader2_text: "Simge rengi",
+ modal_tabheader3_text: "Tooltip rengi",
+ modal_colorpicker1_text: "Simge rengi",
+ modal_colorpicker2_text: "Yazı rengi",
+ modal_colorpicker3_text: "Tooltip rengi",
+ modal_colorpicker4_text: "Yazı rengi",
+ modal_invalidurl_text: "Geçersiz URL"
+ };
+ case "cs": //czech
+ return {
+ context_localserversettings_text: "Místní nastavení serveru",
+ submenu_serversettings_text: "Změnit nastavení",
+ submenu_resetsettings_text: "Obnovit server",
+ modal_header_text: "Místní nastavení serveru",
+ modal_guildname_text: "Místní název serveru",
+ modal_guildacronym_text: "Zkratka místního serveru",
+ modal_ignorecustomname_text: "Pro zkratku serveru použijte původní název serveru",
+ modal_guildicon_text: "Ikony",
+ modal_guildbanner_text: "Prapor",
+ modal_tabheader1_text: "Server",
+ modal_tabheader2_text: "Barva ikony",
+ modal_tabheader3_text: "Barva tooltip",
+ modal_colorpicker1_text: "Barva ikony",
+ modal_colorpicker2_text: "Barva fontu",
+ modal_colorpicker3_text: "Barva tooltip",
+ modal_colorpicker4_text: "Barva fontu",
+ modal_invalidurl_text: "Neplatná URL"
+ };
+ case "bg": //bulgarian
+ return {
+ context_localserversettings_text: "Настройки за локални cървър",
+ submenu_serversettings_text: "Промяна на настройките",
+ submenu_resetsettings_text: "Възстановяване на cървър",
+ modal_header_text: "Настройки за локални cървър",
+ modal_guildname_text: "Локално име на cървър",
+ modal_guildacronym_text: "Акроним на локалния сървър",
+ modal_ignorecustomname_text: "Използвайте оригиналното име на сървъра за съкращението на сървъра",
+ modal_guildicon_text: "Икона",
+ modal_guildbanner_text: "Знаме",
+ modal_tabheader1_text: "Cървър",
+ modal_tabheader2_text: "Цвят на иконата",
+ modal_tabheader3_text: "Цвят на подсказка",
+ modal_colorpicker1_text: "Цвят на иконата",
+ modal_colorpicker2_text: "Цвят на шрифта",
+ modal_colorpicker3_text: "Цвят на подсказка",
+ modal_colorpicker4_text: "Цвят на шрифта",
+ modal_invalidurl_text: "Невалиден URL"
+ };
+ case "ru": //russian
+ return {
+ context_localserversettings_text: "Настройки локального cервер",
+ submenu_serversettings_text: "Изменить настройки",
+ submenu_resetsettings_text: "Сбросить cервер",
+ modal_header_text: "Настройки локального cервер",
+ modal_guildname_text: "Имя локального cервер",
+ modal_guildacronym_text: "Акроним локального сервера",
+ modal_ignorecustomname_text: "Используйте оригинальное имя сервера для сокращения сервера",
+ modal_guildicon_text: "Значок",
+ modal_guildbanner_text: "Баннер",
+ modal_tabheader1_text: "Cервер",
+ modal_tabheader2_text: "Цвет значков",
+ modal_tabheader3_text: "Цвет подсказка",
+ modal_colorpicker1_text: "Цвет значков",
+ modal_colorpicker2_text: "Цвет шрифта",
+ modal_colorpicker3_text: "Цвет подсказка",
+ modal_colorpicker4_text: "Цвет шрифта",
+ modal_invalidurl_text: "Неверная URL"
+ };
+ case "uk": //ukrainian
+ return {
+ context_localserversettings_text: "Налаштування локального cервер",
+ submenu_serversettings_text: "Змінити налаштування",
+ submenu_resetsettings_text: "Скидання cервер",
+ modal_header_text: "Налаштування локального cервер",
+ modal_guildname_text: "Локальне ім'я cервер",
+ modal_guildacronym_text: "Акронім локального сервера",
+ modal_ignorecustomname_text: "Використовуйте оригінальне ім'я сервера для абревіатури сервера",
+ modal_guildicon_text: "Іконка",
+ modal_guildbanner_text: "Банер",
+ modal_tabheader1_text: "Cервер",
+ modal_tabheader2_text: "Колір ікони",
+ modal_tabheader3_text: "Колір підказка",
+ modal_colorpicker1_text: "Колір ікони",
+ modal_colorpicker2_text: "Колір шрифту",
+ modal_colorpicker3_text: "Колір підказка",
+ modal_colorpicker4_text: "Колір шрифту",
+ modal_invalidurl_text: "Недійсна URL"
+ };
+ case "ja": //japanese
+ return {
+ context_localserversettings_text: "ローカルサーバー設定",
+ submenu_serversettings_text: "設定を変更する",
+ submenu_resetsettings_text: "サーバーをリセットする",
+ modal_header_text: "ローカルサーバー設定",
+ modal_guildname_text: "ローカルサーバー名",
+ modal_guildacronym_text: "ローカルサーバーの頭字語",
+ modal_ignorecustomname_text: "サーバーの頭字語に元のサーバー名を使用する",
+ modal_guildicon_text: "アイコン",
+ modal_guildbanner_text: "バナー",
+ modal_tabheader1_text: "サーバー",
+ modal_tabheader2_text: "アイコンの色",
+ modal_tabheader3_text: "ツールチップの色",
+ modal_colorpicker1_text: "アイコンの色",
+ modal_colorpicker2_text: "フォントの色",
+ modal_colorpicker3_text: "ツールチップの色",
+ modal_colorpicker4_text: "フォントの色",
+ modal_invalidurl_text: "無効な URL"
+ };
+ case "zh-TW": //chinese (traditional)
+ return {
+ context_localserversettings_text: "本地服務器設置",
+ submenu_serversettings_text: "更改設置",
+ submenu_resetsettings_text: "重置服務器",
+ modal_header_text: "本地服務器設置",
+ modal_guildname_text: "服務器名稱",
+ modal_guildacronym_text: "本地服務器縮寫",
+ modal_ignorecustomname_text: "使用原始服務器名稱作為服務器首字母縮寫",
+ modal_guildicon_text: "圖標",
+ modal_guildbanner_text: "旗幟",
+ modal_tabheader1_text: "服務器",
+ modal_tabheader2_text: "圖標顏色",
+ modal_tabheader3_text: "工具提示顏色",
+ modal_colorpicker1_text: "圖標顏色",
+ modal_colorpicker2_text: "字體顏色",
+ modal_colorpicker3_text: "工具提示顏色",
+ modal_colorpicker4_text: "字體顏色",
+ modal_invalidurl_text: "無效的 URL"
+ };
+ case "ko": //korean
+ return {
+ context_localserversettings_text: "로컬 서버 설정",
+ submenu_serversettings_text: "설정 변경",
+ submenu_resetsettings_text: "서버 재설정",
+ modal_header_text: "로컬 서버 설정",
+ modal_guildname_text: "로컬 서버 이름",
+ modal_guildacronym_text: "로컬 서버 약어",
+ modal_ignorecustomname_text: "서버 약어에 원래 서버 이름을 사용하십시오",
+ modal_guildicon_text: "상",
+ modal_guildbanner_text: "기치",
+ modal_tabheader1_text: "서버",
+ modal_tabheader2_text: "상 색깔",
+ modal_tabheader3_text: "툴팁 색깔",
+ modal_colorpicker1_text: "상 색깔",
+ modal_colorpicker2_text: "글꼴 색깔",
+ modal_colorpicker3_text: "툴팁 색깔",
+ modal_colorpicker4_text: "글꼴 색깔",
+ modal_invalidurl_text: "잘못된 URL"
+ };
+ default: //default: english
+ return {
+ context_localserversettings_text: "Local Serversettings",
+ submenu_serversettings_text: "Change Settings",
+ submenu_resetsettings_text: "Reset Server",
+ modal_header_text: "Local Serversettings",
+ modal_guildname_text: "Local Servername",
+ modal_guildacronym_text: "Local Serveracronym",
+ modal_ignorecustomname_text: "Use the original Servername for the Serveracronym",
+ modal_guildicon_text: "Icon",
+ modal_guildbanner_text: "Banner",
+ modal_tabheader1_text: "Server",
+ modal_tabheader2_text: "Iconcolor",
+ modal_tabheader3_text: "Tooltipcolor",
+ modal_colorpicker1_text: "Iconcolor",
+ modal_colorpicker2_text: "Fontcolor",
+ modal_colorpicker3_text: "Tooltipcolor",
+ modal_colorpicker4_text: "Fontcolor",
+ modal_invalidurl_text: "Invalid URL"
+ };
+ }
+ }
+ }
+})(); \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/NotificationSounds.config.json b/.config/BetterDiscord/plugins/NotificationSounds.config.json
new file mode 100644
index 0000000..5101ee4
--- /dev/null
+++ b/.config/BetterDiscord/plugins/NotificationSounds.config.json
@@ -0,0 +1,356 @@
+{
+ "audios": {
+ "---": {
+ "---": null
+ },
+ "Default": {
+ "Communication Channel": "https://notificationsounds.com/soundfiles/63538fe6ef330c13a05a3ed7e599d5f7/file-sounds-917-communication-channel.wav",
+ "Isn't it": "https://notificationsounds.com/soundfiles/ba2fd310dcaa8781a9a652a31baf3c68/file-sounds-969-isnt-it.wav",
+ "Job Done": "https://notificationsounds.com/soundfiles/5b69b9cb83065d403869739ae7f0995e/file-sounds-937-job-done.wav",
+ "Served": "https://notificationsounds.com/soundfiles/b337e84de8752b27eda3a12363109e80/file-sounds-913-served.wav",
+ "Solemn": "https://notificationsounds.com/soundfiles/53fde96fcc4b4ce72d7739202324cd49/file-sounds-882-solemn.wav",
+ "System Fault": "https://notificationsounds.com/soundfiles/ebd9629fc3ae5e9f6611e2ee05a31cef/file-sounds-990-system-fault.wav",
+ "You wouldn't believe": "https://notificationsounds.com/soundfiles/087408522c31eeb1f982bc0eaf81d35f/file-sounds-949-you-wouldnt-believe.wav"
+ },
+ "Discord": {
+ "HotKeys Window Down": "/assets/71f048f8aa7d4b24bf4268a87cbbb192.mp3",
+ "HotKeys Window Left": "/assets/1de04408e62b5d52ae3ebbb91e9e1978.mp3",
+ "HotKeys Window Right": "/assets/2c0433f93db8449e4a82b76dc520cb29.mp3",
+ "HotKeys Window Up": "/assets/68472713f7a62c7c37e0a6a5d5a1faeb.mp3",
+ "Human Man Voice": "/assets/a37dcd6272ae41cf49295d58c9806fe3.mp3",
+ "Incoming Call": "/assets/84a1b4e11d634dbfa1e5dd97a96de3ad.mp3",
+ "Incoming Call Beat": "/assets/b9411af07f154a6fef543e7e442e4da9.mp3",
+ "Mention Ping": "/assets/fa4d62c3cbc80733bf1f01b9c6f181de.mp3",
+ "Mention Ping 2": "/assets/a5f42064e8120e381528b14fd3188b72.mp3",
+ "Mention Ping 3": "/assets/84c9fa3d07da865278bd77c97d952db4.mp3",
+ "New Chatmessage": "/assets/dd920c06a01e5bb8b09678581e29d56f.mp3",
+ "New Chatmessage 2": "/assets/15fe810f6cfab609c7fcda61652b9b34.mp3",
+ "New Chatmessage 3": "/assets/53ce6a92d3c233e8b4ac529d34d374e4.mp3",
+ "Outgoing Call": "/assets/c6e92752668dde4eee5923d70441579f.mp3",
+ "Overlay Unlocked": "/assets/ad322ffe0a88436296158a80d5d11baa.mp3",
+ "Push2Talk Start": "/assets/8b63833c8d252fedba6b9c4f2517c705.mp3",
+ "Push2Talk Stop": "/assets/74ab980d6890a0fa6aa0336182f9f620.mp3",
+ "Robot Man Voice": "/assets/66598bea6e59eb8acdf32cf2d9d75ba9.mp3",
+ "Stream Ended": "/assets/4e30f98aa537854f79f49a76af822bbc.mp3",
+ "Stream Started": "/assets/9ca817f41727edc1b2f1bc4f1911107c.mp3",
+ "Stream User Joined": "/assets/5827bbf9a67c61cbb0e02ffbf434b654.mp3",
+ "Stream User Left": "/assets/7cdcdcbc426cc43583365a671c24b740.mp3",
+ "Unknown": "/assets/ae7d16bb2eea76b9b9977db0fad66658.mp3",
+ "Voicechat Deafen": "/assets/e4d539271704b87764dc465b1a061abd.mp3",
+ "Voicechat Disconnect": "/assets/7e125dc075ec6e5ae796e4c3ab83abb3.mp3",
+ "Voicechat Mute": "/assets/429d09ee3b86e81a75b5e06d3fb482be.mp3",
+ "Voicechat Reconnect": "/assets/471cfd0005b112ff857705e894bf41a6.mp3",
+ "Voicechat Undeafen": "/assets/5a000a0d4dff083d12a1d4fc2c7cbf66.mp3",
+ "Voicechat Unmute": "/assets/43805b9dd757ac4f6b9b58c1a8ee5f0d.mp3",
+ "Voicechat User Joined": "/assets/5dd43c946894005258d85770f0d10cff.mp3",
+ "Voicechat User Left": "/assets/4fcfeb2cba26459c4750e60f626cebdc.mp3",
+ "Voicechat User Moved": "/assets/e81d11590762728c1b811eadfa5be766.mp3"
+ },
+ "Google": {
+ "Cyclist": "data:audio/mpeg;base64,T2dnUwACAAAAAAAAAAAdwrJsAAAAAMbngawBHgF2b3JiaXMAAAAAAYC7AAAAAAAAgDgBAAAAAAC4AU9nZ1MAAAAAAAAAAAAAHcKybAEAAACh23RhDqX///////////////+BA3ZvcmJpczUAAABYaXBoLk9yZyBsaWJWb3JiaXMgSSAyMDE4MDMxNiAoTm93IDEwMCUgZmV3ZXIgc2hlbGxzKQIAAAASAAAAQU5EUk9JRF9MT09QPWZhbHNlRgAAAFRJVExFPWFuZHJvaWQucmVzb3VyY2U6Ly9jb20uZ29vZ2xlLmFuZHJvaWQuc291bmRwaWNrZXIvc3RyaW5nL2N5Y2xpc3QBBXZvcmJpcyJCQ1YBAEAAACRzGCpGpXMWhBAaQlAZ4xxCzmvsGUJMEYIcMkxbyyVzkCGkoEKIWyiB0JBVAABAAACHQXgUhIpBCCGEJT1YkoMnPQghhIg5eBSEaUEIIYQQQgghhBBCCCGERTlokoMnQQgdhOMwOAyD5Tj4HIRFOVgQgydB6CCED0K4moOsOQghhCQ1SFCDBjnoHITCLCiKgsQwuBaEBDUojILkMMjUgwtCiJqDSTX4GoRnQXgWhGlBCCGEJEFIkIMGQcgYhEZBWJKDBjm4FITLQagahCo5CB+EIDRkFQCQAACgoiiKoigKEBqyCgDIAAAQQFEUx3EcyZEcybEcCwgNWQUAAAEACAAAoEiKpEiO5EiSJFmSJVmSJVmS5omqLMuyLMuyLMsyEBqyCgBIAABQUQxFcRQHCA1ZBQBkAAAIoDiKpViKpWiK54iOCISGrAIAgAAABAAAEDRDUzxHlETPVFXXtm3btm3btm3btm3btm1blmUZCA1ZBQBAAAAQ0mlmqQaIMAMZBkJDVgEACAAAgBGKMMSA0JBVAABAAACAGEoOogmtOd+c46BZDppKsTkdnEi1eZKbirk555xzzsnmnDHOOeecopxZDJoJrTnnnMSgWQqaCa0555wnsXnQmiqtOeeccc7pYJwRxjnnnCateZCajbU555wFrWmOmkuxOeecSLl5UptLtTnnnHPOOeecc84555zqxekcnBPOOeecqL25lpvQxTnnnE/G6d6cEM4555xzzjnnnHPOOeecIDRkFQAABABAEIaNYdwpCNLnaCBGEWIaMulB9+gwCRqDnELq0ehopJQ6CCWVcVJKJwgNWQUAAAIAQAghhRRSSCGFFFJIIYUUYoghhhhyyimnoIJKKqmooowyyyyzzDLLLLPMOuyssw47DDHEEEMrrcRSU2011lhr7jnnmoO0VlprrbVSSimllFIKQkNWAQAgAAAEQgYZZJBRSCGFFGKIKaeccgoqqIDQkFUAACAAgAAAAABP8hzRER3RER3RER3RER3R8RzPESVREiVREi3TMjXTU0VVdWXXlnVZt31b2IVd933d933d+HVhWJZlWZZlWZZlWZZlWZZlWZYgNGQVAAACAAAghBBCSCGFFFJIKcYYc8w56CSUEAgNWQUAAAIACAAAAHAUR3EcyZEcSbIkS9IkzdIsT/M0TxM9URRF0zRV0RVdUTdtUTZl0zVdUzZdVVZtV5ZtW7Z125dl2/d93/d93/d93/d93/d9XQdCQ1YBABIAADqSIymSIimS4ziOJElAaMgqAEAGAEAAAIriKI7jOJIkSZIlaZJneZaomZrpmZ4qqkBoyCoAABAAQAAAAAAAAIqmeIqpeIqoeI7oiJJomZaoqZoryqbsuq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq4LhIasAgAkAAB0JEdyJEdSJEVSJEdygNCQVQCADACAAAAcwzEkRXIsy9I0T/M0TxM90RM901NFV3SB0JBVAAAgAIAAAAAAAAAMybAUy9EcTRIl1VItVVMt1VJF1VNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVN0zRNEwgNWQkAAAEA0FpzzK2XjkHorJfIKKSg10455qTXzCiCnOcQMWOYx1IxQwzGlkGElAVCQ1YEAFEAAIAxyDHEHHLOSeokRc45Kh2lxjlHqaPUUUqxplo7SqW2VGvjnKPUUcoopVpLqx2lVGuqsQAAgAAHAIAAC6HQkBUBQBQAAIEMUgophZRizinnkFLKOeYcYoo5p5xjzjkonZTKOSedkxIppZxjzinnnJTOSeack9JJKAAAIMABACDAQig0ZEUAECcA4HAcTZM0TRQlTRNFTxRd1xNF1ZU0zTQ1UVRVTRRN1VRVWRZNVZYlTTNNTRRVUxNFVRVVU5ZNVbVlzzRt2VRV3RZV1bZlW/Z9V5Z13TNN2RZV1bZNVbV1V5Z1XbZt3Zc0zTQ1UVRVTRRV11RV2zZV1bY1UXRdUVVlWVRVWXZdWddVV9Z9TRRV1VNN2RVVVZZV2dVlVZZ1X3RV3VZd2ddVWdZ929aFX9Z9wqiqum7Krq6rsqz7si77uu3rlEnTTFMTRVXVRFFVTVe1bVN1bVsTRdcVVdWWRVN1ZVWWfV91ZdnXRNF1RVWVZVFVZVmVZV13ZVe3RVXVbVV2fd90XV2XdV1YZlv3hdN1dV2VZd9XZVn3ZV3H1nXf90zTtk3X1XXTVXXf1nXlmW3b+EVV1XVVloVflWXf14XheW7dF55RVXXdlF1fV2VZF25fN9q+bjyvbWPbPrKvIwxHvrAsXds2ur5NmHXd6BtD4TeGNNO0bdNVdd10XV+Xdd1o67pQVFVdV2XZ91VX9n1b94Xh9n3fGFXX91VZFobVlp1h932l7guVVbaF39Z155htXVh+4+j8vjJ0dVto67qxzL6uPLtxdIY+AgAABhwAAAJMKAOFhqwIAOIEABiEnENMQYgUgxBCSCmEkFLEGITMOSkZc1JCKamFUlKLGIOQOSYlc05KKKGlUEpLoYTWQimxhVJabK3VmlqLNYTSWiiltVBKi6mlGltrNUaMQcick5I5J6WU0loopbXMOSqdg5Q6CCmllFosKcVYOSclg45KByGlkkpMJaUYQyqxlZRiLCnF2FpsucWYcyilxZJKbCWlWFtMObYYc44Yg5A5JyVzTkoopbVSUmuVc1I6CCllDkoqKcVYSkoxc05KByGlDkJKJaUYU0qxhVJiKynVWEpqscWYc0sx1lBSiyWlGEtKMbYYc26x5dZBaC2kEmMoJcYWY66ttRpDKbGVlGIsKdUWY629xZhzKCXGkkqNJaVYW425xhhzTrHlmlqsucXYa2259Zpz0Km1WlNMubYYc465BVlz7r2D0FoopcVQSoyttVpbjDmHUmIrKdVYSoq1xZhza7H2UEqMJaVYS0o1thhrjjX2mlqrtcWYa2qx5ppz7zHm2FNrNbcYa06x5Vpz7r3m1mMBAAADDgAAASaUgUJDVgIAUQAABCFKMQahQYgx56Q0CDHmnJSKMecgpFIx5hyEUjLnIJSSUuYchFJSCqWkklJroZRSUmqtAACAAgcAgAAbNCUWByg0ZCUAkAoAYHAcy/I8UTRV2XYsyfNE0TRV1bYdy/I8UTRNVbVty/NE0TRV1XV13fI8UTRVVXVdXfdEUTVV1XVlWfc9UTRVVXVdWfZ901RV1XVlWbaFXzRVV3VdWZZl31hd1XVlWbZ1WxhW1XVdWZZtWzeGW9d13feFYTk6t27ruu/7wvE7xwAA8AQHAKACG1ZHOCkaCyw0ZCUAkAEAQBiDkEFIIYMQUkghpRBSSgkAABhwAAAIMKEMFBqyEgCIAgAACJFSSimNlFJKKaWRUkoppZQSQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQggFAPhPOAD4P9igKbE4QKEhKwGAcAAAwBilmHIMOgkpNYw5BqGUlFJqrWGMMQilpNRaS5VzEEpJqbXYYqycg1BSSq3FGmMHIaXWWqyx1po7CCmlFmusOdgcSmktxlhzzr33kFJrMdZac++9l9ZirDXn3IMQwrQUY6659uB77ym2WmvNPfgghFCx1Vpz8EEIIYSLMffcg/A9CCFcjDnnHoTwwQdhAAB3gwMARIKNM6wknRWOBhcashIACAkAIBBiijHnnIMQQgiRUow55xyEEEIoJVKKMeecgw5CCCVkjDnnHIQQQiillIwx55yDEEIJpZSSOecchBBCKKWUUjLnoIMQQgmllFJK5xyEEEIIpZRSSumggxBCCaWUUkopIYQQQgmllFJKKSWEEEIJpZRSSimlhBBKKKWUUkoppZQQQimllFJKKaWUEkIopZRSSimllJJCKaWUUkoppZRSUiillFJKKaWUUkoJpZRSSimllJRSSQUAABw4AAAEGEEnGVUWYaMJFx6AQkNWAgBAAAAUxFZTiZ1BzDFnqSEIMaipQkophjFDyiCmKVMKIYUhc4ohAqHFVkvFAAAAEAQACAgJADBAUDADAAwOED4HQSdAcLQBAAhCZIZINCwEhweVABExFQAkJijkAkCFxUXaxQV0GeCCLu46EEIQghDE4gAKSMDBCTc88YYn3OAEnaJSBwEAAAAAYAAADwAAxwUQEdEcRobGBkeHxwdISAAAAAAAuADABwDAIQJERDSHkaGxwdHh8QESEgAAAAAAAAAAAAQEBAAAAAAAAgAAAAQET2dnUwAAQHQAAAAAAAAdwrJsAgAAACT4cvEpJSYeUUZjIB4oKFNLcx0eHCAoK1lQZX+Po6OboJ2moqCnpqOgoZydp6MU09sFG6SrH9mg8WvjM78njo0nfE99N1mW3+h/e/zPSLXRf4xnJO/VZKGy6y3OQfgtAeT834j6QiyJJbFog/Xml6U4uW9t4WUpNgUE79UYJhbOdPqmOoBlAABA0+Uz0DtK4AUAcq+PEgB6iV6Wh+D9/9Hd8bxuv0rZXFM8sPOrV69ezQF0nwAANRZAGrwnABAAAAAAAMTIcg8aJiUex3H8+7XWWmvtfYDP0wAAsMVVCMUAnToAACNQBQBeSd7vT8Fv+OZQxtp7G0H+1hSnAUDXHQAAgAVGGjoBAAEAAAAAAICz1Dlu/0G7CQBAnQYAgDoEdr5rdAEAqAMAMAHotxoIthhelofg939mt5cv/VuYvKspHqAdp6+P40h4LgBMmgUAWBUCYAAAAHiyrhf9lpPw/Wk/rXoIIYQQAo4AAABECQme+CAbgMGVLwEAcp14XwYAwFFjHYQk8jBB88JpwJEkFzw03OLV4KkgiuIOEF4AAMA+oEcPInTZHlwbIB4FtH6sMAHUYihfy8JAzVwV+gEAKN83LIgmHhEQaiyLfYNQGgsk4bGtFWi6J9oByxrMv/5/evTo0T/830s/3qNHIfe5njttFeH/718AJG8oNUv3uvMFnT2XAHK05Dj+dYQcY9yHy5bV1ZfyUqqy8/7x8cHSMpqZXtJd8Ptf7J7L33n7FX3O75rigTTPr169mud5hp0AAMoqQBpcCQAEAAAAAIDv40hO+6zuQTy+0Z7xHWOMMSoAfEeOUQHg+bBoB3gDAAAAbtAAfmne70/B9/9R7tzvDe8lxjB/a4oHgCNZuL4PAIwUYKTBbQBAEgEwAAAAQJFuo/n97QwkqwAAAPBFAAAAEyYCAAAAOMMhRABokAAF1ihe2kPwG5487njzeg/U3ppxGuA4jtPXx3EcAABWFadg5wDAiAgAAOChTvn+t566VzA1AID5GgAASXGMPbKUGhPHcRzHj6G8YsVnCI91HwEAAIAC4DxDHQBwPvVYKIuRWSBVvxtKymCGsF0znQFoQA2AAsxkKDfPwtwPQAAAogSFUmpJZEQAVRITzsyBX/sF3GQoT5kGHgAPIBcQJ3IXWDAZ9+HcJY8US01ij3EF3GQo3+CiQaZvCtAAAP9NBPoLkDIv22FS3pF4ANzk1RhqcQKz304gAVT9tgDNCa2c/KeVOVvVjwZpcyQmHGcoC+oJ1kdd16Bx6j1vNOnI0OOEvxrx297DMv3n63jl7Uh/1ep4BfxwKNeowkynbyqQD4Az/ptDJeFzAktp5fz5n9uFFE4wTzn2JeDl1q2Jsgp6iV6Wh+D9/9TdcV+3b0WBXVM8sPOrV2+8OpmBvt4AEKwCpEFMJgAIMAAAAACIynKPSr87h8QYj/9cWmuttfYeVHtea44xApxtDRuiFMYCgI8AAABgwigACl5ZXm9Pwfv/gcP8bngv0fNzXTOCOgAAJAVAGtwkAJCAIMAAAABTAgComAAAAACCOIf4KjGCCvam1SQAALAeAO8AAAB8b5Bt1gdQhJImaKAB/iheLw/B5/8sD/eTx3sUXbY2a8YDEMZI4PcnAJAUOAHzDMBGRAAAAICwl7PFbskG4dgRAADgtgkAQJNmex6AvyEAAEAzJY8C6vk7ixNxB26bOgenaBeSUr9HbkoghHCDxcMCLAC++N0vL8H/v28O80l8LtEvvpriNIDaMh8JAECrPAU6E8AIEQAAACD8xYh5GLAVAECx46CkOopVQPiieV8AEMI7W3gSyiS9j0DHGQUAAEiA3ZKBQLqIV3v+flGIg90JFdE5zlldaQqnzzXmKawm7PmpbfWP8G4fYilWmeW0VgJIHridjrvg97/2nfFjef3Mrb/BmvEA3EcD/O0NANnD2okRDAAAQKAf5/1MfhZCfkYBIK5wSD0h2/3zTN6KQpkGwy8GyFGn5s44VrF7iBf0TovKMFBzhaq2Px5tVgAycopwGCeZDSnZS5a8VLdNeCOdPTXhp03SLeVN9EG5ANJIZa2JZOqyUyX0eGUJ179AaQBeR+3TRfAfpj9O+eHzZwpTateK04CN1NGbAABCLzgFSgCMjBgMAHCf8RvxfhIjuEoAADpadKEA1RIC+59GGixIEexq1qtd3VzZv1BwRvug1yIah6mZjUhVq5+aJPMGk2bifDMAqJbSGjyYvsJy9lZa+iT/iAFeMNV3bVRC597YvdsKWCOSqTzlgAqPiHPUh4/FrUWOXSIENopBvLPZjVi62RoAXtZ05SIg4KD52+XWHkd0kXNtOA1wiLAbAADSixOwABCc5BAA7l1yWBL3zgHQBQDwhkYe4KjLDCFf95fqmKgWwm2Jf5n0XIWbZDazSmD4pu+AHJHiNtnE7AbG0c2HmTQ0hTvn/TiezYaiphS58o8ee31pbF/CUGrxG3gkmb14CkmTpt20pXt+jSppXHJOWW481PeXZejYI1SEQbWFsRsxvz88AB629GkXCdhCyq29buGyXRseECPrEPU2fN0CENKwUUTHDABXw3az3rd7Ps7UG1Oa441dGpmPo+jTjGgKkzux/5IQby4968S40sKLXsQjcaRqi0ezO7KsUQD4esseXRjEAHm/EZpFPRkOwm2eIoRYdmnD2BkXO+zbhtjk7A0VTsLefMdt4Ozbtkq3fdwtphkdN9O3ZRvnWbocbOgA/rV0/fQ/aKFiC5+Totya8bB0dD8ZJHiLAIgZG9kEMYIBtX2clnEkrqvB9wjRlpSLByX6MPtW0PvOR/NHVf7pD8q51PdTSx3WT7QZ73eet7Vgvz3JjpZPAAA+p2NUJUkBiCm/KlS39KW6MipsruSLHjL36Z3W/hasu+L1TSiZDvsQCw2vip/yQ3YIZSvluAQ2otlIbol+otreluwZYeqgAx62jPEiICCY9pu2ebVJgGuHB30ooDMDzicApBcnYAbAEzNkAEr8W/X3xCt/5kB/roRyV4gNINEpAIA5fxqW5AudcoCzdt7ebhpvKhVb9NQJ89l7UDW+BcF6M+EbJXsl38kM+aZDZSrTbX4XXMTkTNKRvLxWmvslUrrlYDmbwM0J61HAZGCpqgttRwzbtzE5aUov1kLaImKGhihnsB3+tXR1K4IDB5rlqX1snQFncM14wHm2wTrg/R5AesEZHAvAliUhAMTx3X/9QkuC/IvTmaO3qOp6AarWFojSHWhSJS7zvpWqEkfFMzNm70u/via15gGkAstKxpQrDuA7D+phf9kQpfaBnuH8Itmt6ftgv87tnKY93HHnm7Ud9G0L9A025cS+N/dmJ6+aUVsTdm+xvxYxletEsge51kRFmoItuk6mlZgNHrZMdSsgwIH12+3YH1OY7NWGB/oYz5wHEXBVBZAK6+ggC8EAJa0lLEcMth1nokVu6Vm4dMHUR+gKjaTPvfliMifPbda468JZ1MqI2XzPopxi+vlKhsOuBnsYAABXc3XNykzAaqhGrr0gDRPkTfWWDhUWs3Dhvq64SbwzVF5x89NrLpqS24S1Egv1+hDbZg44Mip5Qw2aDf2rFnEiKYWRL4AC/rVM4+kPbIe2p7NOsrpWPDT0tgXspwBkGJwCEkAUSwgG1GkSreskaZeo+zLaAtDUAdoidAAFPmHw/Mq1AFEBpeOSWdCN/Xm1Q/HonTwbYHLN1FlHsxLU8EeC0F/jSgs0Qbl7hi76yUsap5RE8LN4mu0RLbGmUduOrBe7OquvYXdhSNmGfSUF6/Y2VH1kQ9EEJNagEBEXbbcTiD7QIc1FAB627PUQQTATOu3WPqdwWVczHsjteu4jWg3PBIBuwoaKxQgGEKxZ0z/N3HuencapXyIgQYKykCtj2F3M8DBih52QajfSCTPk5aRmlFvp/uP9Y9/3qtdkBJbUiqQAgO3xiQBIZ4JnnKdVYzqlrx2tl6uFdtQcG/nK0zefyK9l39yXmseKqfvF9pw6y5TlD6Fwha7g39SQYIfstpVS0p8o2Xp3L3vTJOABHrYcp13wH2i/E57a5xamqL9WPIDaxkwGu2cDkBmcAglAFDMzAOCLrhyf+r7/10v3slK9VmABmDnxVqBK/FcOp6b8YdnlTT31jMwCts/0bmUjvM3X8YeX5Ye2ug3KtqAUtUvOfRFw1Wi9vWHOGmNdj+R2u8+3I/NzOcCrrvshIyIU91NSKDspon/b0UR9AZkdem9aM6SOG2ll1U2rF3IcCvpEyzC5B/617H33B6pBd9vSYwpX6qsVD0jzRiTz4joAhBmcAhEAomMJwQC0McMfgl3emWfs7DO0koL7ADfNQomUQ9vebvuSmlS3q7pZrr4jpGwnS0dSyOzY8tQkCmV1bxmShObRXnGh4F1FTmBtCzxIQ1ZEBlP+BP+V2Vs92evyqrq3zK01buzVIA8DfzMhCY0WJeCB9uI9ZN8XWCH83NoNUotizEgo4AH+tZzmQ/AF3X+3qX00sq5mPEAeGz0ClqoAYlp1dJaQAYCy7V/Nyr1N+5T4nUvcCkTpznkap8/e6t6lRURGp8neoojFX8GIwwxgPirSMz5E95P5Kn7spU0AqP9smMokFlo72rf9xfG2oL4l0uj6rZTx12u593WyvYvXXb8RinqbWnC7QE53x1rwkRFotp7ykngHWdeJshTt7sMKaulDy5QA/rWcl0XwBd1+RzFeQ+qsrg0PWGFesyDUA0CGcQabACJLkMEAmKprvMxU6x7zX+qeWBZAAZdrBIQCvB3SU9mtiv0Cgshg0f0ZMtjkL24KtXGjdRSSJKHuFOmb5ab/sdbCFu/Z1e07cMsnVdU1Drdu/9ndBSuMpOipCM3gdc0qJ0TWZ/ZCgN3iqkur0Od96Cu5e6rz3o5gdoXkVKtZ2AcTLAAetlyPVfDL/hOmsk6oKR5geyVL83EbIE2w6iSJAUDAmRX8til53G2/+WupI6JNi42Wmx/SGUzOQYnRDLq23+b6yO8VJ87CASb6JpJeHqazbiuqdR4pkhjNggEnaA1I8D4wIhM15erXGun8dUkrbkTSP7YndqPbK4KceEQ2+61e/82t/ucFx5WfqFrdz0JD70cNJhQ7Y/dIaqUpfQIetly2u+AHqj4cRUNQOzyg0fU2TSgTIExXSwRBBgMArjStamnq9V89sjG0ENUXVO5sZ4JDSJa/M6poQ3d2waY96ZEkdO/crYPdzn0uOkP02Hwosa+eUc3Xvw8Fgaay99Q2Am6ny/hae/O0cbZ726pXP4hQZFMdcfjRmz0CTVKr7U5SUFkcZRCbBc/FvDGzDvsrVVJ42h5DNNmVBpAAHrbcbifBL+unHGUZo2RqwwM4RmdK8DIAZGoGEoAiFWQwoKiqZsI/k//JnvV8ldtC71qNAh9VULg7/I2O2/jAbJiObiobOciw05HDuOZUrmKU1QRlxZ426Zth/bZgGnhvmxRBipA9qWk0CTPZe2AnCrKzXeQxYSVKmsGC7bYObe9Wbe3xYsY2A8ukcjXb/UysrZCjO8mAJnP5UfkniCEU7IeokcDW0AH+tdy3k1/QAQlPzRx1Soea8QDabo8MeAJAxtiICgrBAIdu+9fjk/7+BndNpJq/Fqpn3DFGJuah6GmCzYDoSVUrzQmfEjKLSG4Tk9moU4lhOVRdnTEWIyvyViXZ/vMtuScWQ5B+iSzwNZGdijEKW8T/bs30gFi9Y2pHfcmOUNrTI+IbtxysgmWA7IOzOfKVSFOg5n8u37TY/UhlX7unZRvXkUgAT2dnUwAEorEAAAAAAAAdwrJsAwAAAE85894QoqKcmqSgl5yenZWbl5iTLP613K6T4AdSf9rNOoXLdU3xAGyEjlhENQDCjHWUJAYDUEVK07ImvfG4BP1FNWqJk0vqQBa1DVQMezbdEsjLaN1BthcZUKILI2qGanF/WpIPhfnFwIc5I2NOA0ACj8pNHYPoIyoiliO11tu721zNrWa9XBGLo3pqTxNlBPtzKGSCVlLN3Y+wiC6M7BYa73VVma0PNy+V+UXuzNLIcHC8YnmABR62vJ+j4A/aP9pkHVBTPIAW2UC/3wPI2BtqnCACANIR1B6Jq7lf4HlIS0EKYyLvWWdu7sosOX75Osrjk1kryWxQDSUo3hcWG2dV9+asnJlO6c6+tswghQQxABCTwQMAscWZRpHgDiXQakpebNO0/FTbsInOhZ5uZ2PZHh2V9ToPfK40TrEY7HUza6xyyOx7db8DDowyX7a3X258O2BUo5UXAN61PM9J8HH9CbekSBbXigew4gG4aQCZ2UgSRACAUDCTdOo0JqQjhx6d1psWvx3me89E0BIm48b+1hkcxIL6Iofpe1rUghg4y6smw0QP0Ma/mszAYrGKslI+WhzW8oDG9dLq5W1NSpVmYxf3jvTWqe3ObuQN9a3NX5WRWYtLGrFKbdkvwJY3fKBzFH7XhRV9bQ+RDSdvUHPAtolLKB62vNwnwVfix62sjVszHkAPebROQgDwFGwELoYIBoAT2ifhG918A2xfP/FCW6mEb7lJKHoFakcQcufWa39OGFrqjMZSWa9AwkjNNebsksaiXwyVgIH1ZwAgJ2SzXLbfxqtp75FYercm1rSoNefXzBRi872tn0GRrnQquSFu2VV0TMLCqff13QyUQ00/H9YTtSFVDqxK0UpCCAD+tbxcR8Fb/Cc9NXNBzXgAqHdBni+ANNklSQwGQPXCYzi9rVGEJDUWiFZd2fIC9qREGUaWh/sn8Z6WY7b/GIWftaIwVNZYglcv3qRq3SlRRIyHnmfIO/bwObfEgLt6Fw/3xp3g6PtQI1y8sju12dTOM9O2MKlUgub/HI08prZ2aCgWIfkW1tiDllKk6r38UsnS2vgss5Cvb8d1UosuiMtbgdgEJv61fFyq4A2MnzbRpugXasUDaKgzEjcJIKPpEksMADQp5sXcfCWVKGaMKE71pEqn5HEzzTeyaH8wV6/rkXJAIMqKFenT2n7JhjbtNPkvUyzcrHfI11ER8kEto7vN+ASUROt1wqEZAno96s7UEn1LClq9NRd+dYsFOlhvcRPt35cBCo/SANm7DdFqF9TUEiN9IyhXUdHC1V72gp8Q8hj1nQn+tbzfmuANRHzYaFAbtiAOABn2GCWGCKapEE5jRDyingAmYerorevnH4qG0fehmM4Hue0E/fRYgBvkIVgo7zYcb9BqEDwx08tkUrPND31kC36+QZONevsjdisdf09pG79WD2AMfm3E+l1/MYrUfKZuSLIX67B7PW5eigX4LgvGmsakDZu6X6Zd+MqidPW6v9BPo+c2dM8CHra835vgzfrjxoBa8QDYQcOqCiCmCW1JYjAA1wiSeGh8QyI7rh5pUAwOEm38ZnLk2H7vLOrMVNTMaupQQsM82iRdZqb/Gb0I3aGzN6cj6pDZwaS4lSwqZnZfOFXqC91E/0lNHZEl13XYo9yuHWnWGhMc5lq7laaqsKYwrfRJwhlR844VhvBe2s0tRlpVRhHNppAftvRZZi8EOi8BHra8X7vgA0x/zo0pekyNsQVbAPAyKQ0lRrAz0Xo8RZpTNQBLzHTKy1m1QX1P65o12kPbmf6kp/vq8/lDmHy7y9p3LTUj18BtZGJIrP0ARAcImbWvxU7v13sum2LCHPDH8MY26qZvIxGekOl5YZhBcN1IbKPgLOyx2iu89Ky0W/vj8XcCyghxRmQRupN/a54svPk/xwZWYT73wgINHQAetnxtq+ALRP7cR9B0phZqxgB4AkCmCeUgi8FkG0iWTbVHqwaZU93/rOm1iwnFfS3kuqh75wJGkskQxF8bg/MI8RbGtaXzAZjMaNNv1W826vsWE1Q/w3VD69JQasanFt+uNP1J5psg7OtcjnxZyc5rfquvWEioxNsb/aIw+GnrZGvJtiLVt3YzW2LJcj8IOau9zurGODvICCaXWJ0OHrZ8XBvPdT7GwZBErThCKZRjRgQDYE3D4ojmwsj/B4cSn2aaB3VNnfntlRoH6JrG+0M1l57XzY6mjBZ018jo4CT7PBtM5Nr3DMTeU6Jw/Nqgj1wb2bcfWjFnn1jhf/9kvS3XX4edu9FvVy5eEcNRfXQWaf/tfW73UagkKLR2cg6UbrQPOZx9XxuaPUZQ1h24Zv4CCAD+tbxfGvcFP/PGMFErVkQjAISZknNODIz09hck2oB4lQDAO8cn3soXb3GLfyxCIN/b71oV1ZFh9qFdj8DzzsTZtzMO9U6Io6OjJX6wLbCtxtmTEsYj97/q3vKEsGRYNnxT8RW/PvhadaYY9bDNTpw7K/SNysplORXsYYRuWkWUzD6GLZz38/atvmnOgEP7fhFN1EdS9g4ehDUsWP61fFwK95X8mQ1TamqKLegEEBZHqSQxGGWgZxyhaADgtDrHjedXaSKeXeMcoXkSCPrkcpiwDEPCPQsMrAk6uROb8HedmbJz9jUn8S22N72MfmkT3znDX3Y3815PQaC4rcPRm8gGMrjNS6iU8mTkCh1a/Xuj9bJ63mxRb/jcxPd2CGr6tXLgEE9dEmxJb7g1x0ivAcU1PADetXyelRv5ARLUhsVkTMmCkMEAsJ7wBYET3+Gwv+73BGfsSNR1/O+E97Lmi+ipueuJRDMgPiVFpOPmPsIUMhav4MpWANk9OMRPxcp2trHgrcomfVND6LlC8/gg/dxaPEyD/wa/fiV/ZW3peAxEoVh7ZiFqMSajkwdWBizjomvdkEPceOjEHoZYh3Voo9fmyVdKbyGaBSxYAB62fF4aT/EBBtSGI8YoY8RCMIAEKlcvtWnv0wga/fQniF+ud3eHUcGVM+dfAWRnTqeb1y6pI5sPKCwhW4m979q0POj1V2YY2tLONg5DfeTV3u8nP2nzmlxMn9qWcPVtRlZyNSQ//QWysoTF4HkSzBOLKD4m7iNw1w1Es6TOlZs+hOox3PwTevAmGO07OLMQwQNAAR62/O8svMQO+EJNEQATAwAYAIABAPxSMxNMAZTPgiYs+Wc+nWQckxqgNAAB",
+ "Crosswalk": "data:audio/mpeg;base64,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",
+ "Bubble": "data:audio/mpeg;base64,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"
+ }
+ },
+ "changelog": {
+ "currentversion": "3.4.5"
+ },
+ "choices": {
+ "message1": {
+ "category": "Google",
+ "focus": null,
+ "mute": true,
+ "song": "Cyclist",
+ "src": "data:audio/mpeg;base64,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",
+ "volume": 100
+ },
+ "dm": {
+ "category": "Discord",
+ "focus": true,
+ "mute": true,
+ "song": "New Chatmessage 3",
+ "src": "/assets/53ce6a92d3c233e8b4ac529d34d374e4.mp3",
+ "volume": 100
+ },
+ "mentioned": {
+ "category": "Google",
+ "focus": true,
+ "mute": true,
+ "song": "Crosswalk",
+ "src": "data:audio/mpeg;base64,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",
+ "volume": 100
+ },
+ "role": {
+ "category": "Default",
+ "focus": true,
+ "mute": true,
+ "song": "You wouldn't believe",
+ "src": "https://notificationsounds.com/soundfiles/087408522c31eeb1f982bc0eaf81d35f/file-sounds-949-you-wouldnt-believe.wav",
+ "volume": 100
+ },
+ "everyone": {
+ "category": "Google",
+ "focus": true,
+ "mute": true,
+ "song": "Crosswalk",
+ "src": "data:audio/mpeg;base64,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",
+ "volume": 100
+ },
+ "here": {
+ "category": "Google",
+ "focus": true,
+ "mute": true,
+ "song": "Crosswalk",
+ "src": "data:audio/mpeg;base64,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",
+ "volume": 100
+ },
+ "deafen": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/e4d539271704b87764dc465b1a061abd.mp3",
+ "volume": 100
+ },
+ "mute": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/429d09ee3b86e81a75b5e06d3fb482be.mp3",
+ "volume": 100
+ },
+ "disconnect": {
+ "category": "Discord",
+ "focus": null,
+ "mute": false,
+ "song": "Push2Talk Stop",
+ "src": "/assets/74ab980d6890a0fa6aa0336182f9f620.mp3",
+ "volume": 100
+ },
+ "undeafen": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/5a000a0d4dff083d12a1d4fc2c7cbf66.mp3",
+ "volume": 100
+ },
+ "unmute": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/43805b9dd757ac4f6b9b58c1a8ee5f0d.mp3",
+ "volume": 100
+ },
+ "user_join": {
+ "category": "Discord",
+ "focus": null,
+ "mute": false,
+ "song": "Push2Talk Start",
+ "src": "/assets/8b63833c8d252fedba6b9c4f2517c705.mp3",
+ "volume": 100
+ },
+ "user_leave": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/4fcfeb2cba26459c4750e60f626cebdc.mp3",
+ "volume": 100
+ },
+ "user_moved": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/e81d11590762728c1b811eadfa5be766.mp3",
+ "volume": 100
+ },
+ "reconnect": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/471cfd0005b112ff857705e894bf41a6.mp3",
+ "volume": 100
+ },
+ "ptt_start": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/8b63833c8d252fedba6b9c4f2517c705.mp3",
+ "volume": 100
+ },
+ "ptt_stop": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/74ab980d6890a0fa6aa0336182f9f620.mp3",
+ "volume": 100
+ },
+ "call_calling": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/c6e92752668dde4eee5923d70441579f.mp3",
+ "volume": 100
+ },
+ "call_ringing": {
+ "category": "Discord",
+ "focus": null,
+ "mute": true,
+ "song": "Incoming Call Beat",
+ "src": "/assets/b9411af07f154a6fef543e7e442e4da9.mp3",
+ "volume": 54.5
+ },
+ "call_ringing_beat": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/b9411af07f154a6fef543e7e442e4da9.mp3",
+ "volume": 100
+ },
+ "stream_started": {
+ "category": "Discord",
+ "focus": null,
+ "mute": false,
+ "song": "Unknown",
+ "src": "/assets/ae7d16bb2eea76b9b9977db0fad66658.mp3",
+ "volume": 100
+ },
+ "stream_ended": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/4e30f98aa537854f79f49a76af822bbc.mp3",
+ "volume": 100
+ },
+ "stream_user_joined": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/5827bbf9a67c61cbb0e02ffbf434b654.mp3",
+ "volume": 100
+ },
+ "stream_user_left": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/7cdcdcbc426cc43583365a671c24b740.mp3",
+ "volume": 100
+ },
+ "ddr-down": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/71f048f8aa7d4b24bf4268a87cbbb192.mp3",
+ "volume": 100
+ },
+ "ddr-left": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/1de04408e62b5d52ae3ebbb91e9e1978.mp3",
+ "volume": 100
+ },
+ "ddr-right": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/2c0433f93db8449e4a82b76dc520cb29.mp3",
+ "volume": 100
+ },
+ "ddr-up": {
+ "category": "---",
+ "focus": null,
+ "mute": false,
+ "song": "---",
+ "src": "/assets/68472713f7a62c7c37e0a6a5d5a1faeb.mp3",
+ "volume": 100
+ },
+ "mention1": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/fa4d62c3cbc80733bf1f01b9c6f181de.mp3",
+ "volume": 100
+ },
+ "mention2": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/a5f42064e8120e381528b14fd3188b72.mp3",
+ "volume": 100
+ },
+ "mention3": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/84c9fa3d07da865278bd77c97d952db4.mp3",
+ "volume": 100
+ },
+ "message2": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/15fe810f6cfab609c7fcda61652b9b34.mp3",
+ "volume": 100
+ },
+ "message3": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/53ce6a92d3c233e8b4ac529d34d374e4.mp3",
+ "volume": 100
+ },
+ "human_man": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/a37dcd6272ae41cf49295d58c9806fe3.mp3",
+ "volume": 100
+ },
+ "robot_man": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/66598bea6e59eb8acdf32cf2d9d75ba9.mp3",
+ "volume": 100
+ },
+ "discodo": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/ae7d16bb2eea76b9b9977db0fad66658.mp3",
+ "volume": 100
+ },
+ "overlayunlock": {
+ "category": "---",
+ "focus": null,
+ "mute": true,
+ "song": "---",
+ "src": "/assets/ad322ffe0a88436296158a80d5d11baa.mp3",
+ "volume": 100
+ }
+ }
+} \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/NotificationSounds.plugin.js b/.config/BetterDiscord/plugins/NotificationSounds.plugin.js
new file mode 100644
index 0000000..8e48187
--- /dev/null
+++ b/.config/BetterDiscord/plugins/NotificationSounds.plugin.js
@@ -0,0 +1,546 @@
+//META{"name":"NotificationSounds","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/NotificationSounds","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/NotificationSounds/NotificationSounds.plugin.js"}*//
+
+var NotificationSounds = (_ => {
+ var audios, choices, firedEvents, repatchIncoming, callAudio;
+
+ const settingsAudio = new Audio();
+
+ /* NEVER CHANGE THE SRC LINKS IN THE PLUGIN FILE, TO ADD NEW SONGS ADD THEM IN THE SETTINGS GUI IN THE PLUGINS PAGE */
+ const types = {
+ "message1": {implemented:true, name:"New Chatmessage", src:"/assets/dd920c06a01e5bb8b09678581e29d56f.mp3", mute:true, focus:null, include:true},
+ "dm": {implemented:true, name:"Direct Message", src:"/assets/84c9fa3d07da865278bd77c97d952db4.mp3", mute:true, focus:true, include:false},
+ "mentioned": {implemented:true, name:"Mentioned", src:"/assets/a5f42064e8120e381528b14fd3188b72.mp3", mute:true, focus:true, include:false},
+ "role": {implemented:true, name:"Mentioned (role)", src:"/assets/a5f42064e8120e381528b14fd3188b72.mp3", mute:true, focus:true, include:false},
+ "everyone": {implemented:true, name:"Mentioned (@everyone)", src:"/assets/a5f42064e8120e381528b14fd3188b72.mp3", mute:true, focus:true, include:false},
+ "here": {implemented:true, name:"Mentioned (@here)", src:"/assets/a5f42064e8120e381528b14fd3188b72.mp3", mute:true, focus:true, include:false},
+ "deafen": {implemented:true, name:"Voicechat Deafen", src:"/assets/e4d539271704b87764dc465b1a061abd.mp3", mute:false, focus:null, include:true},
+ "mute": {implemented:true, name:"Voicechat Mute", src:"/assets/429d09ee3b86e81a75b5e06d3fb482be.mp3", mute:false, focus:null, include:true},
+ "disconnect": {implemented:true, name:"Voicechat Disconnect", src:"/assets/7e125dc075ec6e5ae796e4c3ab83abb3.mp3", mute:false, focus:null, include:true},
+ "undeafen": {implemented:true, name:"Voicechat Undeafen", src:"/assets/5a000a0d4dff083d12a1d4fc2c7cbf66.mp3", mute:false, focus:null, include:true},
+ "unmute": {implemented:true, name:"Voicechat Unmute", src:"/assets/43805b9dd757ac4f6b9b58c1a8ee5f0d.mp3", mute:false, focus:null, include:true},
+ "user_join": {implemented:true, name:"Voicechat User Joined", src:"/assets/5dd43c946894005258d85770f0d10cff.mp3", mute:false, focus:null, include:true},
+ "user_leave": {implemented:true, name:"Voicechat User Left", src:"/assets/4fcfeb2cba26459c4750e60f626cebdc.mp3", mute:false, focus:null, include:true},
+ "user_moved": {implemented:true, name:"Voicechat User Moved", src:"/assets/e81d11590762728c1b811eadfa5be766.mp3", mute:false, focus:null, include:true},
+ "reconnect": {implemented:false, name:"Voicechat Reconnect", src:"/assets/471cfd0005b112ff857705e894bf41a6.mp3", mute:true, focus:null, include:true},
+ "ptt_start": {implemented:true, name:"Push2Talk Start", src:"/assets/8b63833c8d252fedba6b9c4f2517c705.mp3", mute:false, focus:null, include:true},
+ "ptt_stop": {implemented:true, name:"Push2Talk Stop", src:"/assets/74ab980d6890a0fa6aa0336182f9f620.mp3", mute:false, focus:null, include:true},
+ "call_calling": {implemented:true, name:"Outgoing Call", src:"/assets/c6e92752668dde4eee5923d70441579f.mp3", mute:false, focus:null, include:true},
+ "call_ringing": {implemented:true, name:"Incoming Call", src:"/assets/84a1b4e11d634dbfa1e5dd97a96de3ad.mp3", mute:true, focus:null, include:true},
+ "call_ringing_beat": {implemented:false, name:"Incoming Call Beat", src:"/assets/b9411af07f154a6fef543e7e442e4da9.mp3", mute:true, focus:null, include:true},
+ "stream_started": {implemented:true, name:"Stream Started", src:"/assets/9ca817f41727edc1b2f1bc4f1911107c.mp3", mute:false, focus:null, include:true},
+ "stream_ended": {implemented:true, name:"Stream Ended", src:"/assets/4e30f98aa537854f79f49a76af822bbc.mp3", mute:false, focus:null, include:true},
+ "stream_user_joined": {implemented:true, name:"Stream User Joined", src:"/assets/5827bbf9a67c61cbb0e02ffbf434b654.mp3", mute:false, focus:null, include:true},
+ "stream_user_left": {implemented:true, name:"Stream User Left", src:"/assets/7cdcdcbc426cc43583365a671c24b740.mp3", mute:false, focus:null, include:true},
+ "ddr-down": {implemented:true, name:"HotKeys Window Down", src:"/assets/71f048f8aa7d4b24bf4268a87cbbb192.mp3", mute:false, focus:null, include:true},
+ "ddr-left": {implemented:true, name:"HotKeys Window Left", src:"/assets/1de04408e62b5d52ae3ebbb91e9e1978.mp3", mute:false, focus:null, include:true},
+ "ddr-right": {implemented:true, name:"HotKeys Window Right", src:"/assets/2c0433f93db8449e4a82b76dc520cb29.mp3", mute:false, focus:null, include:true},
+ "ddr-up": {implemented:true, name:"HotKeys Window Up", src:"/assets/68472713f7a62c7c37e0a6a5d5a1faeb.mp3", mute:false, focus:null, include:true},
+ "mention1": {implemented:false, name:"Mention Ping", src:"/assets/fa4d62c3cbc80733bf1f01b9c6f181de.mp3", mute:true, focus:null, include:true},
+ "mention2": {implemented:false, name:"Mention Ping 2", src:"/assets/a5f42064e8120e381528b14fd3188b72.mp3", mute:true, focus:null, include:true},
+ "mention3": {implemented:false, name:"Mention Ping 3", src:"/assets/84c9fa3d07da865278bd77c97d952db4.mp3", mute:true, focus:null, include:true},
+ "message2": {implemented:false, name:"New Chatmessage 2", src:"/assets/15fe810f6cfab609c7fcda61652b9b34.mp3", mute:true, focus:null, include:true},
+ "message3": {implemented:false, name:"New Chatmessage 3", src:"/assets/53ce6a92d3c233e8b4ac529d34d374e4.mp3", mute:true, focus:null, include:true},
+ "human_man": {implemented:false, name:"Human Man Voice", src:"/assets/a37dcd6272ae41cf49295d58c9806fe3.mp3", mute:true, focus:null, include:true},
+ "robot_man": {implemented:false, name:"Robot Man Voice", src:"/assets/66598bea6e59eb8acdf32cf2d9d75ba9.mp3", mute:true, focus:null, include:true},
+ "discodo": {implemented:false, name:"Unknown", src:"/assets/ae7d16bb2eea76b9b9977db0fad66658.mp3", mute:true, focus:null, include:true},
+ "overlayunlock": {implemented:false, name:"Overlay Unlocked", src:"/assets/ad322ffe0a88436296158a80d5d11baa.mp3", mute:true, focus:null, include:true}
+ };
+
+ /* NEVER CHANGE THE SRC LINKS IN THE PLUGIN FILE, TO ADD NEW SONGS ADD THEM IN THE SETTINGS GUI IN THE PLUGINS PAGE */
+ const defaultAudios = {
+ "---": {
+ "---": null
+ },
+ "Default": {
+ "Communication Channel": "https://notificationsounds.com/soundfiles/63538fe6ef330c13a05a3ed7e599d5f7/file-sounds-917-communication-channel.wav",
+ "Isn't it": "https://notificationsounds.com/soundfiles/ba2fd310dcaa8781a9a652a31baf3c68/file-sounds-969-isnt-it.wav",
+ "Job Done": "https://notificationsounds.com/soundfiles/5b69b9cb83065d403869739ae7f0995e/file-sounds-937-job-done.wav",
+ "Served": "https://notificationsounds.com/soundfiles/b337e84de8752b27eda3a12363109e80/file-sounds-913-served.wav",
+ "Solemn": "https://notificationsounds.com/soundfiles/53fde96fcc4b4ce72d7739202324cd49/file-sounds-882-solemn.wav",
+ "System Fault": "https://notificationsounds.com/soundfiles/ebd9629fc3ae5e9f6611e2ee05a31cef/file-sounds-990-system-fault.wav",
+ "You wouldn't believe": "https://notificationsounds.com/soundfiles/087408522c31eeb1f982bc0eaf81d35f/file-sounds-949-you-wouldnt-believe.wav"
+ },
+ "Discord": {}
+ };
+
+ for (let id in types) if (types[id].include) defaultAudios.Discord[types[id].name] = types[id].src;
+
+ return class NotificationSounds {
+ getName () {return "NotificationSounds";}
+
+ getVersion () {return "3.4.5";}
+
+ getAuthor () {return "DevilBro";}
+
+ getDescription () {return "Allows you to replace the native sounds of Discord with your own";}
+
+ constructor () {
+ this.changelog = {
+ "fixed":[["Mention Sound","No longer plays when the server/channel has message notifications completely disabled"]]
+ };
+
+ this.patchedModules = {
+ after: {
+ Shakeable: "render"
+ }
+ };
+ }
+
+ initConstructor () {
+ audios = {};
+ choices = {};
+ firedEvents = {};
+ }
+
+ getSettingsPanel (collapseStates = {}) {
+ if (!window.BDFDB || typeof BDFDB != "object" || !BDFDB.loaded || !this.started) return;
+ let settingsPanel = {node: null}, settingsItems = [];
+
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
+ title: "Add new Song",
+ collapseStates: collapseStates,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
+ className: BDFDB.disCN.margintop4,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: "Categoryname",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ className: "input-newsong input-category",
+ value: "",
+ placeholder: "Categoryname"
+ })
+ })
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: "Songname",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ className: "input-newsong input-song",
+ value: "",
+ placeholder: "Songname"
+ })
+ })
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
+ className: BDFDB.disCN.margintop4,
+ align: BDFDB.LibraryComponents.Flex.Align.END,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: "Source",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ className: "input-newsong input-source",
+ type: "file",
+ filter: ["audio", "video"],
+ useFilePath: true,
+ value: "",
+ placeholder: "Source"
+ })
+ })
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Button, {
+ style: {marginBottom: 1},
+ onClick: _ => {
+ for (let input of settingsPanel.node.querySelectorAll(".input-newsong " + BDFDB.dotCN.input)) if (!input.value || input.value.length == 0 || input.value.trim().length == 0) return BDFDB.NotificationUtils.toast("Fill out all fields to add a new song.", {type:"danger"});
+ let category = settingsPanel.node.querySelector(".input-category " + BDFDB.dotCN.input).value.trim();
+ let song = settingsPanel.node.querySelector(".input-song " + BDFDB.dotCN.input).value.trim();
+ let source = settingsPanel.node.querySelector(".input-source " + BDFDB.dotCN.input).value.trim();
+ if (source.indexOf("http") == 0) BDFDB.LibraryRequires.request(source, (error, response, result) => {
+ if (response) {
+ let type = response.headers["content-type"];
+ if (type && (type.indexOf("octet-stream") > -1 || type.indexOf("audio") > -1 || type.indexOf("video") > -1)) return this.successSavedAudio(settingsPanel.node, collapseStates, {category, song, source});
+ }
+ BDFDB.NotificationUtils.toast("Use a valid direct link to a video or audio source. They usually end on something like .mp3, .mp4 or .wav.", {type:"danger"});
+ });
+ else BDFDB.LibraryRequires.fs.readFile(source, (error, response) => {
+ if (error) BDFDB.NotificationUtils.toast("Could not fetch file. Please make sure the file exists.", {type:"danger"});
+ else return this.successSavedAudio(settingsPanel.node, collapseStates, {category, song, source:`data:audio/mpeg;base64,${response.toString("base64")}`});
+ });
+ },
+ children: BDFDB.LanguageUtils.LanguageStrings.SAVE
+ })
+ ]
+ })
+ ]
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
+ title: "Implemented Sounds",
+ collapseStates: collapseStates,
+ dividertop: true,
+ children: Object.keys(BDFDB.ObjectUtils.filter(types, typedata => typedata.implemented)).map(type => this.createSoundCard(type, settingsPanel, collapseStates)).flat(10).filter(n => n)
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
+ title: "Unimplemented Sounds",
+ collapseStates: collapseStates,
+ dividertop: true,
+ children: Object.keys(BDFDB.ObjectUtils.filter(types, typedata => !typedata.implemented)).map(type => this.createSoundCard(type, settingsPanel, collapseStates)).flat(10).filter(n => n)
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
+ title: "Remove Songs",
+ collapseStates: collapseStates,
+ dividertop: true,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Button",
+ className: BDFDB.disCN.marginbottom8,
+ color: BDFDB.LibraryComponents.Button.Colors.RED,
+ label: "Delete all added songs",
+ onClick: _ => {
+ BDFDB.ModalUtils.confirm(this, "Are you sure you want to delete all added songs?", _ => {
+ BDFDB.DataUtils.remove(this, "choices");
+ BDFDB.DataUtils.remove(this, "audios");
+ this.loadAudios();
+ this.loadChoices();
+ BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel.node, collapseStates);
+ });
+ },
+ children: BDFDB.LanguageUtils.LanguageStrings.DELETE
+ })
+ }));
+
+ return settingsPanel.node = BDFDB.PluginUtils.createSettingsPanel(this, settingsItems);
+ }
+
+ createSoundCard (type, settingsPanel, collapseStates) {
+ return [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
+ className: BDFDB.disCN.marginbottom8,
+ align: BDFDB.LibraryComponents.Flex.Align.CENTER,
+ direction: BDFDB.LibraryComponents.Flex.Direction.HORIZONTAL,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsLabel, {
+ label: types[type].name
+ }),
+ types[type].focus != null ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Switch",
+ mini: true,
+ grow: 0,
+ label: "Mute when Channel focused:",
+ value: choices[type].focus,
+ onChange: value => {
+ choices[type].focus = value;
+ this.saveChoice(type, false);
+ }
+ }) : null,
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Switch",
+ mini: true,
+ grow: 0,
+ label: "Mute in DnD:",
+ value: choices[type].mute,
+ onChange: value => {
+ choices[type].mute = value;
+ this.saveChoice(type, false);
+ }
+ })
+ ].filter(n => n)
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
+ className: BDFDB.disCN.marginbottom8,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
+ grow: 0,
+ shrink: 0,
+ basis: "31%",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: "Category",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Select, {
+ value: choices[type].category,
+ options: Object.keys(audios).map(name => {return {value:name, label:name}}),
+ searchable: true,
+ onChange: category => {
+ choices[type].category = category.value;
+ choices[type].song = Object.keys(audios[category.value] || {})[0];
+ choices[type].src = audios[choices[type].category][choices[type].song] || types[type].src;
+ this.saveChoice(type, true);
+ BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel.node, collapseStates);
+ }
+ })
+ })
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
+ grow: 0,
+ shrink: 0,
+ basis: "31%",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: "Song",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Select, {
+ value: choices[type].song,
+ options: Object.keys(audios[choices[type].category] || {}).map(name => {return {value:name, label:name}}),
+ searchable: true,
+ onChange: song => {
+ choices[type].song = song.value;
+ choices[type].src = audios[choices[type].category][choices[type].song] || types[type].src;
+ this.saveChoice(type, true);
+ BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel.node, collapseStates);
+ }
+ })
+ })
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
+ grow: 0,
+ shrink: 0,
+ basis: "31%",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: "Volume",
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Slider, {
+ defaultValue: choices[type].volume,
+ digits: 1,
+ onValueRender: value => {
+ return value + "%";
+ },
+ onValueChange: value => {
+ choices[type].volume = value;
+ this.saveChoice(type, true);
+ }
+ })
+ })
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, {
+ className: BDFDB.disCN.marginbottom8
+ })
+ ];
+ }
+
+ // Legacy
+ load () {}
+
+ start () {
+ if (!window.BDFDB) window.BDFDB = {myPlugins:{}};
+ if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this;
+ let libraryScript = document.querySelector("head script#BDFDBLibraryScript");
+ if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) {
+ if (libraryScript) libraryScript.remove();
+ libraryScript = document.createElement("script");
+ libraryScript.setAttribute("id", "BDFDBLibraryScript");
+ libraryScript.setAttribute("type", "text/javascript");
+ libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js");
+ libraryScript.setAttribute("date", performance.now());
+ libraryScript.addEventListener("load", _ => {this.initialize();});
+ document.head.appendChild(libraryScript);
+ }
+ else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize();
+ this.startTimeout = setTimeout(_ => {
+ try {return this.initialize();}
+ catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);}
+ }, 30000);
+ }
+
+ initialize () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ if (this.started) return;
+ BDFDB.PluginUtils.init(this);
+
+ BDFDB.ModuleUtils.patch(this, BDFDB.LibraryModules.DispatchApiUtils, "dirtyDispatch", {before: e => {
+ if (BDFDB.ObjectUtils.is(e.methodArguments[0]) && e.methodArguments[0].type == BDFDB.DiscordConstants.ActionTypes.MESSAGE_CREATE && e.methodArguments[0].message) {
+ let message = e.methodArguments[0].message;
+ let guildId = message.guild_id || null;
+ if (!BDFDB.LibraryModules.MutedUtils.isGuildOrCategoryOrChannelMuted(guildId, message.channel_id) && message.author.id != BDFDB.UserUtils.me.id && !BDFDB.LibraryModules.FriendUtils.isBlocked(message.author.id)) {
+ if (!guildId && !(choices.dm.focus && document.hasFocus() && BDFDB.LibraryModules.LastChannelStore.getChannelId() == message.channel_id)) {
+ this.fireEvent("dm");
+ this.playAudio("dm");
+ return;
+ }
+ else if (BDFDB.LibraryModules.MentionUtils.isRawMessageMentioned(message, BDFDB.UserUtils.me.id)) {
+ if (message.mentions.length && !this.isSuppressMentionEnabled(guildId, message.channel_id) && !(choices.mentioned.focus && document.hasFocus() && BDFDB.LibraryModules.LastChannelStore.getChannelId() == message.channel_id)) for (let mention of message.mentions) if (mention.id == BDFDB.UserUtils.me.id) {
+ this.fireEvent("mentioned");
+ this.playAudio("mentioned");
+ return;
+ }
+ if (guildId && message.mention_roles.length && !BDFDB.LibraryModules.MutedUtils.isSuppressRolesEnabled(guildId, message.channel_id) && !(choices.role.focus && document.hasFocus() && BDFDB.LibraryModules.LastChannelStore.getChannelId() == message.channel_id)) {
+ let member = BDFDB.LibraryModules.MemberStore.getMember(guildId, BDFDB.UserUtils.me.id);
+ if (member && member.roles.length) for (let roleId of message.mention_roles) if (member.roles.includes(roleId)) {
+ this.fireEvent("role");
+ this.playAudio("role");
+ return;
+ }
+ }
+ if (message.mention_everyone && !BDFDB.LibraryModules.MutedUtils.isSuppressEveryoneEnabled(guildId, message.channel_id)) {
+ if (message.content.indexOf("@everyone") > -1 && !(choices.everyone.focus && document.hasFocus() && BDFDB.LibraryModules.LastChannelStore.getChannelId() == message.channel_id)) {
+ this.fireEvent("everyone");
+ this.playAudio("everyone");
+ return;
+ }
+ if (message.content.indexOf("@here") > -1 && !(choices.here.focus && document.hasFocus() && BDFDB.LibraryModules.LastChannelStore.getChannelId() == message.channel_id)) {
+ this.fireEvent("here");
+ this.playAudio("here");
+ return;
+ }
+ }
+ }
+ }
+ }
+ }});
+
+ BDFDB.ModuleUtils.patch(this, BDFDB.LibraryModules.SoundUtils, "playSound", {instead: e => {
+ let type = e.methodArguments[0];
+ if (choices[type]) BDFDB.TimeUtils.timeout(_ => {
+ if (type == "message1") {
+ if (firedEvents["dm"]) firedEvents["dm"] = false;
+ else if (firedEvents["mentioned"]) firedEvents["mentioned"] = false;
+ else if (firedEvents["role"]) firedEvents["role"] = false;
+ else if (firedEvents["everyone"]) firedEvents["everyone"] = false;
+ else if (firedEvents["here"]) firedEvents["here"] = false;
+ else this.playAudio(type);
+ }
+ else this.playAudio(type);
+ });
+ else e.callOriginalMethod();
+ }});
+ BDFDB.ModuleUtils.patch(this, BDFDB.LibraryModules.SoundUtils, "createSound", {after: e => {
+ let type = e.methodArguments[0];
+ let audio = new Audio();
+ audio.src = choices[type].src;
+ audio.volume = choices[type].volume/100;
+ e.returnValue.play = _ => {
+ if (!audio.paused || this.dontPlayAudio(type)) return;
+ audio.loop = false;
+ audio.play();
+ };
+ e.returnValue.loop = _ => {
+ if (!audio.paused || this.dontPlayAudio(type)) return;
+ audio.loop = true;
+ audio.play();
+ };
+ e.returnValue.stop = _ => {audio.pause();}
+ }});
+
+
+ for (let key in defaultAudios) defaultAudios[key] = BDFDB.ObjectUtils.sort(defaultAudios[key]);
+
+ this.loadAudios();
+ this.loadChoices();
+
+ let callListenerModule = BDFDB.ModuleUtils.findByProperties("handleRingUpdate");
+ if (callListenerModule) {
+ callListenerModule.terminate();
+ BDFDB.ModuleUtils.patch(this, callListenerModule, "handleRingUpdate", {instead: e => {
+ BDFDB.LibraryModules.CallUtils.getCalls().filter(call => call.ringing.length > 0 && BDFDB.LibraryModules.VoiceUtils.getCurrentClientVoiceChannelId() === call.channelId).length > 0 && !BDFDB.LibraryModules.SoundStateUtils.isSoundDisabled("call_calling") && !BDFDB.LibraryModules.StreamerModeStore.disableSounds ? callAudio.loop() : callAudio.stop();
+ }});
+ callListenerModule.initialize();
+ }
+
+ this.forceUpdateAll();
+ }
+ else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!");
+ }
+
+
+ stop () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ this.stopping = true;
+
+ BDFDB.PluginUtils.clear(this);
+ settingsAudio.pause();
+ }
+ }
+
+
+ // Begin of own functions
+
+ onSettingsClosed () {
+ if (this.SettingsUpdated) {
+ delete this.SettingsUpdated;
+ settingsAudio.pause();
+
+ this.forceUpdateAll();
+ }
+ }
+
+ processShakeable (e) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name: "IncomingCalls"});
+ if (index > -1) {
+ if (repatchIncoming) {
+ children[index] = null;
+ BDFDB.TimeUtils.timeout(_ => {
+ repatchIncoming = false;
+ BDFDB.ReactUtils.forceUpdate(BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.app), {name:"App", up:true}))
+ });
+ }
+ else children[index] = BDFDB.ReactUtils.createElement(children[index].type, {});
+ }
+ }
+
+ successSavedAudio (settingsPanel, collapseStates, data) {
+ BDFDB.NotificationUtils.toast(`Song ${data.song} was added to category ${data.category}.`, {type:"success"});
+ if (!audios[data.category]) audios[data.category] = {};
+ audios[data.category][data.song] = data.source;
+ BDFDB.DataUtils.save(audios, this, "audios");
+ BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel, collapseStates);
+
+ }
+
+ forceUpdateAll () {
+ repatchIncoming = true;
+ callAudio = BDFDB.LibraryModules.SoundUtils.createSound("call_calling");
+
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ }
+
+ loadAudios () {
+ audios = Object.assign({}, defaultAudios, BDFDB.DataUtils.load(this, "audios"));
+ BDFDB.DataUtils.save(audios, this, "audios");
+ }
+
+ loadChoices () {
+ let loadedChoices = BDFDB.DataUtils.load(this, "choices");
+ for (let type in types) {
+ let choice = loadedChoices[type] || {}, songFound = false;
+ for (let category in audios) if (choice.category == category) for (let song in audios[category]) if (choice.song == song) {
+ songFound = true;
+ break;
+ }
+ if (!songFound) choice = {
+ category: "---",
+ song: "---",
+ volume: 100,
+ src: types[type].src,
+ mute: types[type].mute,
+ focus: types[type].focus
+ };
+ choices[type] = choice;
+ this.saveChoice(type, false);
+ }
+ }
+
+ saveChoice (type, play) {
+ if (!choices[type]) return;
+ BDFDB.DataUtils.save(choices[type], this, "choices", type);
+ if (play) {
+ this.SettingsUpdated = true;
+ this.playAudio(type, settingsAudio);
+ }
+ }
+
+ playAudio (type, audio) {
+ if (!audio) {
+ if (this.dontPlayAudio(type)) return;
+ audio = new Audio();
+ }
+ else audio.pause();
+ audio.src = choices[type].src;
+ audio.volume = choices[type].volume/100;
+ audio.play();
+ }
+
+ isSuppressMentionEnabled (guildId, channelId) {
+ let channelSettings = BDFDB.LibraryModules.MutedUtils.getChannelMessageNotifications(guildId, channelId);
+ return channelSettings && (channelSettings == BDFDB.DiscordConstants.UserNotificationSettings.NO_MESSAGES || channelSettings == BDFDB.DiscordConstants.UserNotificationSettings.NULL && BDFDB.LibraryModules.MutedUtils.getMessageNotifications(guildId) == BDFDB.DiscordConstants.UserNotificationSettings.NO_MESSAGES);
+ }
+
+ dontPlayAudio (type) {
+ let status = BDFDB.UserUtils.getStatus();
+ return choices[type].mute && (status == "dnd" || status == "streaming");
+ }
+
+ fireEvent (type) {
+ firedEvents[type] = true;
+ BDFDB.TimeUtils.timeout(_ => {firedEvents[type] = false;},3000);
+ }
+ }
+})(); \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/PinDMs.config.json b/.config/BetterDiscord/plugins/PinDMs.config.json
new file mode 100644
index 0000000..63542a6
--- /dev/null
+++ b/.config/BetterDiscord/plugins/PinDMs.config.json
@@ -0,0 +1,40 @@
+{
+ "changelog": {
+ "currentversion": "1.7.2"
+ },
+ "dmCategories": {
+ "3516090637537661": {
+ "collapsed": false,
+ "color": null,
+ "dms": [
+ "456428001463369729",
+ "390601823523962892",
+ "450987439041806346",
+ "390786176652673034"
+ ],
+ "id": "3516090637537661",
+ "name": "Gouden mannen",
+ "pos": 1
+ },
+ "3586821958853646": {
+ "collapsed": false,
+ "color": null,
+ "dms": [
+ "606832163404644352",
+ "590888637877452840",
+ "458302680998215690",
+ "514803935761006592"
+ ],
+ "id": "3586821958853646",
+ "name": "Jongens",
+ "pos": 0
+ }
+ },
+ "settings": {
+ "showCategoryAmount": false,
+ "showCategoryUnread": true,
+ "showPinIcon": true,
+ "sortInRecentOrder": false,
+ "sortInRecentOrderGuild": false
+ }
+} \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/PinDMs.plugin.js b/.config/BetterDiscord/plugins/PinDMs.plugin.js
new file mode 100644
index 0000000..4c5b002
--- /dev/null
+++ b/.config/BetterDiscord/plugins/PinDMs.plugin.js
@@ -0,0 +1,1149 @@
+//META{"name":"PinDMs","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/PinDMs","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/PinDMs/PinDMs.plugin.js"}*//
+
+var PinDMs = (_ => {
+ let hoveredCategory, draggedCategory, releasedCategory;
+ let hoveredChannel, draggedChannel, releasedChannel;
+
+ return class PinDMs {
+ getName () {return "PinDMs";}
+
+ getVersion () {return "1.7.2";}
+
+ getAuthor () {return "DevilBro";}
+
+ getDescription () {return "Allows you to pin DMs, making them appear at the top of your DMs/Guild-list.";}
+
+ constructor () {
+ this.changelog = {
+ "fixed":[["Context Menu Update","Fixes for the context menu update, yaaaaaay"]]
+ };
+
+ this.patchedModules = {
+ before: {
+ PrivateChannelsList: "render",
+ UnreadDMs: "render"
+ },
+ after: {
+ PrivateChannelsList: "render",
+ UnreadDMs: "render",
+ PrivateChannel: ["render", "componentDidMount"],
+ DirectMessage: ["render", "componentDidMount", "componentWillUnmount"]
+ }
+ };
+ }
+
+ initConstructor () {
+ this.css = `
+ ${BDFDB.dotCNS.dmchannel + BDFDB.dotCN.namecontainerchildren} {
+ display: flex;
+ }
+ ${BDFDB.dotCN.dmchannel}:hover ${BDFDB.dotCN._pindmsunpinbutton} {
+ display: block;
+ }
+ ${BDFDB.dotCN._pindmspinnedchannelsheadercontainer} {
+ display: flex;
+ cursor: pointer;
+ }
+ ${BDFDB.dotCNS._pindmspinnedchannelsheadercontainer + BDFDB.dotCN.dmchannelheadertext} {
+ margin-right: 6px;
+ }
+ ${BDFDB.dotCN._pindmspinnedchannelsheadercontainer + BDFDB.dotCN._pindmspinnedchannelsheadercolored}:hover ${BDFDB.dotCN.dmchannelheadertext} {
+ filter: brightness(150%);
+ }
+ ${BDFDB.dotCNS._pindmspinnedchannelsheadercontainer + BDFDB.dotCN._pindmspinnedchannelsheaderamount} {
+ position: relative;
+ top: -1px;
+ margin-right: 6px;
+ }
+ ${BDFDB.dotCN._pindmspinnedchannelsheaderarrow} {
+ flex: 0;
+ width: 16px;
+ height: 16px;
+ margin-left: 0;
+ margin-right: 2px;
+ }
+ ${BDFDB.dotCNS._pindmspinnedchannelsheadercollapsed + BDFDB.dotCN._pindmspinnedchannelsheaderarrow + BDFDB.dotCN.channelheadericonwrapper} {
+ transform: rotate(-90deg);
+ }
+ ${BDFDB.dotCN._pindmsunpinbutton} {
+ display: none;
+ width: 16px;
+ height: 16px;
+ opacity: .7;
+ margin: 2px;
+ }
+ ${BDFDB.dotCN._pindmsunpinbutton}:hover {
+ opacity: 1;
+ }
+ ${BDFDB.dotCN._pindmsunpinicon} {
+ display: block;
+ width: 16px;
+ height: 16px;
+ }
+ ${BDFDB.dotCNS._pindmsdmchannelplaceholder + BDFDB.dotCN.namecontainerlayout} {
+ box-sizing: border-box;
+ border: 1px dashed currentColor;
+ }
+ ${BDFDB.dotCN._pindmspinnedchannelsheadercontainer + BDFDB.dotCN._pindmsdmchannelplaceholder} {
+ margin-left: 8px;
+ height: 12px;
+ box-sizing: border-box;
+ border: 1px dashed currentColor;
+ }
+ ${BDFDB.dotCN._pindmsdragpreview} {
+ pointer-events: none !important;
+ position: absolute !important;
+ opacity: 0.5 !important;
+ z-index: 10000 !important;
+ }`;
+
+ this.defaults = {
+ settings: {
+ sortInRecentOrder: {value:false, inner:true, description:"Channel List"},
+ sortInRecentOrderGuild: {value:false, inner:true, description:"Guild List"},
+ showPinIcon: {value:true, inner:false, description:"Shows a little 'Pin' icon for pinned DMs in the server list:"},
+ showCategoryUnread: {value:true, inner:false, description:"Shows the amount of unread Messages in a category in the channel list:"},
+ showCategoryAmount: {value:true, inner:false, description:"Shows the amount of pinned DMs in a category in the channel list:"}
+ }
+ };
+ }
+
+ getSettingsPanel () {
+ if (!window.BDFDB || typeof BDFDB != "object" || !BDFDB.loaded || !this.started) return;
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ let settingsPanel, settingsItems = [], innerItems = [];
+
+ for (let key in settings) (!this.defaults.settings[key].inner ? settingsItems : innerItems).push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
+ className: BDFDB.disCN.marginbottom8,
+ type: "Switch",
+ plugin: this,
+ keys: ["settings", key],
+ label: this.defaults.settings[key].description,
+ value: settings[key]
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelInner, {
+ title: "Sort pinned DMs in the recent message order instead of the pinned at order in:",
+ first: settingsItems.length == 0,
+ children: innerItems
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, {
+ type: "Button",
+ className: BDFDB.disCN.marginbottom8,
+ color: BDFDB.LibraryComponents.Button.Colors.RED,
+ label: "Unpin all pinned DMs",
+ onClick: _ => {
+ BDFDB.ModalUtils.confirm(this, "Are you sure you want to unpin all pinned DMs?", _ => {
+ BDFDB.DataUtils.remove(this, "dmCategories");
+ BDFDB.DataUtils.remove(this, "pinnedRecents");
+ });
+ },
+ children: BDFDB.LanguageUtils.LanguageStrings.UNPIN
+ }));
+
+ return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, settingsItems);
+ }
+
+ // Legacy
+ load () {}
+
+ start () {
+ if (!window.BDFDB) window.BDFDB = {myPlugins:{}};
+ if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this;
+ let libraryScript = document.querySelector("head script#BDFDBLibraryScript");
+ if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) {
+ if (libraryScript) libraryScript.remove();
+ libraryScript = document.createElement("script");
+ libraryScript.setAttribute("id", "BDFDBLibraryScript");
+ libraryScript.setAttribute("type", "text/javascript");
+ libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js");
+ libraryScript.setAttribute("date", performance.now());
+ libraryScript.addEventListener("load", _ => {this.initialize();});
+ document.head.appendChild(libraryScript);
+ }
+ else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize();
+ this.startTimeout = setTimeout(_ => {
+ try {return this.initialize();}
+ catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);}
+ }, 30000);
+ }
+
+ initialize () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ if (this.started) return;
+ BDFDB.PluginUtils.init(this);
+
+ this.forceUpdateAll();
+ }
+ else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!");
+ }
+
+ stop () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ this.stopping = true;
+
+ this.forceUpdateAll(true);
+
+ let unreadDMsInstance = BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.app), {name:"UnreadDMs", unlimited:true});
+ if (unreadDMsInstance) {
+ delete unreadDMsInstance.props.pinnedPrivateChannelIds;
+ unreadDMsInstance.props.unreadPrivateChannelIds = BDFDB.LibraryModules.DirectMessageUnreadStore.getUnreadPrivateChannelIds();
+ BDFDB.ReactUtils.forceUpdate(unreadDMsInstance);
+ }
+
+ BDFDB.PluginUtils.clear(this);
+ }
+ }
+
+
+ // Begin of own functions
+
+ onSettingsClosed (instance, wrapper, returnvalue) {
+ if (this.SettingsUpdated) {
+ delete this.SettingsUpdated;
+ this.forceUpdateAll();
+ }
+ }
+
+ onUserContextMenu (e) {
+ if (e.instance.props.user) {
+ let [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: "close-dm"});
+ if (index > -1) {
+ let id = BDFDB.LibraryModules.ChannelStore.getDMFromUserId(e.instance.props.user.id);
+ if (id) this.injectItem(e.instance, id, children, index);
+ }
+ }
+ }
+
+ onGroupDMContextMenu (e) {
+ if (e.instance.props.channel) {
+ let [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: "change-icon"});
+ if (index > -1) this.injectItem(e.instance, e.instance.props.channel.id, children, index + 1);
+ }
+ }
+
+ injectItem (instance, id, children, index) {
+ let pinnedInGuild = this.isPinned(id, "pinnedRecents");
+
+ let categories = this.sortAndUpdateCategories("dmCategories", true);
+ let currentCategory = this.getCategory(id, "dmCategories");
+
+ children.splice(index, 0, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.context_pindm_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "submenu-pin"),
+ children: [
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.context_pinchannel_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "submenu-channelist"),
+ children: [
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
+ children: currentCategory ? BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.context_unpinchannel_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "unpin-channellist"),
+ color: BDFDB.LibraryComponents.MenuItems.Colors.DANGER,
+ action: _ => {
+ BDFDB.ContextMenuUtils.close(instance);
+ this.removeFromCategory(id, currentCategory, "dmCategories");
+ }
+ }) : BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels.context_addtonewcategory_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "new-channellist"),
+ color: BDFDB.LibraryComponents.MenuItems.Colors.BRAND,
+ action: _ => {
+ BDFDB.ContextMenuUtils.close(instance);
+ this.openCategorySettingsModal({
+ id: this.generateID("dmCategories").toString(),
+ name: `${this.labels.header_pinneddms_text} #${categories.length + 1}`,
+ dms: [id],
+ pos: categories.length,
+ collapsed: false,
+ color: null
+ }, "dmCategories", true);
+ }
+ })
+ }),
+ categories.length ? BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
+ children: categories.map(category => currentCategory && currentCategory.id == category.id ? null : BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: category.name || this.labels.header_pinneddms_text,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "pin-channellist", category.id),
+ action: _ => {
+ BDFDB.ContextMenuUtils.close(instance);
+ if (currentCategory) this.removeFromCategory(id, currentCategory, "dmCategories");
+ this.addToCategory(id, category, "dmCategories");
+ }
+ })).filter(n => n)
+ }) : null
+ ].filter(n => n)
+ }),
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: this.labels[pinnedInGuild ? "context_unpinguild_text" : "context_pinguild_text"],
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, pinnedInGuild ? "unpin-serverlist" : "pin-serverlist"),
+ danger: pinnedInGuild,
+ action: _ => {
+ BDFDB.ContextMenuUtils.close(instance);
+ if (!pinnedInGuild) this.addPin(id, "pinnedRecents");
+ else this.removePin(id, "pinnedRecents");
+ }
+ })
+ ]
+ }));
+ }
+
+ processPrivateChannelsList (e) {
+ let categories = this.sortAndUpdateCategories("dmCategories", true);
+ if (categories.length) {
+ e.instance.props.channels = Object.assign({}, e.instance.props.channels);
+ e.instance.props.privateChannelIds = [].concat(e.instance.props.privateChannelIds || []);
+ e.instance.props.pinnedChannelIds = Object.assign({}, e.instance.props.pinnedChannelIds);
+ if (!e.returnvalue) {
+ if (draggedChannel && releasedChannel) {
+ let categoryId = releasedChannel.split("header_")[1];
+ let category = categories.find(n => categoryId != undefined ? n.id == categoryId : n.dms.includes(releasedChannel));
+ if (category) {
+ BDFDB.ArrayUtils.remove(category.dms, draggedChannel, true);
+ category.dms.splice(categoryId != undefined ? 0 : category.dms.indexOf(releasedChannel) + 1, 0, draggedChannel);
+ BDFDB.DataUtils.save(category, this, "dmCategories", category.id);
+ }
+ draggedChannel = null;
+ releasedChannel = null;
+ }
+ if (draggedCategory && releasedCategory) {
+ let maybedDraggedCategory = categories.find(n => n.id == draggedCategory);
+ let maybedReleasedCategory = categories.find(n => n.id == releasedCategory);
+ if (maybedDraggedCategory && maybedReleasedCategory) {
+ BDFDB.ArrayUtils.remove(categories, maybedDraggedCategory, true);
+ categories.splice(categories.indexOf(maybedReleasedCategory) + 1, 0, maybedDraggedCategory);
+ let newCategories = {}, newPos = 0;
+ for (let category of [].concat(categories).reverse()) newCategories[category.id] = Object.assign(category, {pos:newPos++});
+ BDFDB.DataUtils.save(newCategories, this, "dmCategories");
+ }
+ draggedCategory = null;
+ releasedCategory = null;
+ }
+ e.instance.props.pinnedChannelIds = {};
+ for (let category of [].concat(categories).reverse()) {
+ e.instance.props.pinnedChannelIds[category.id] = [];
+ for (let id of this.sortDMsByTime(this.filterDMs(category.dms), "dmCategories").reverse()) {
+ BDFDB.ArrayUtils.remove(e.instance.props.privateChannelIds, id, true);
+ if (!category.collapsed || e.instance.props.selectedChannelId == id) {
+ e.instance.props.privateChannelIds.unshift(id);
+ e.instance.props.pinnedChannelIds[category.id].push(id);
+ }
+ }
+ }
+ }
+ else {
+ e.returnvalue.props.sections = [];
+ e.returnvalue.props.sections.push(e.instance.state.preRenderedChildren);
+ let shownPinnedIds = BDFDB.ObjectUtils.toArray(e.instance.props.pinnedChannelIds).reverse();
+ for (let ids of shownPinnedIds) e.returnvalue.props.sections.push(ids.length || 1);
+ e.returnvalue.props.sections.push(e.instance.props.privateChannelIds.length - shownPinnedIds.flat().length);
+
+ let sectionHeight = e.returnvalue.props.sectionHeight;
+ let sectionHeightFunc = typeof sectionHeight != "function" ? _ => sectionHeight : sectionHeight;
+ e.returnvalue.props.sectionHeight = (...args) => {
+ if (args[0] != 0 && args[0] != e.returnvalue.props.sections.length - 1) {
+ let category = categories[args[0] - 1];
+ if (category) return 40;
+ }
+ return sectionHeightFunc(...args);
+ };
+
+ let rowHeight = e.returnvalue.props.rowHeight;
+ let rowHeightFunc = typeof rowHeight != "function" ? _ => rowHeight : rowHeight;
+ e.returnvalue.props.rowHeight = (...args) => {
+ if (args[0] != 0 && args[0] != e.returnvalue.props.sections.length - 1) {
+ let category = categories[args[0] - 1];
+ if (category && (category.collapsed || category.id == draggedCategory)) return 0;
+ }
+ return rowHeightFunc(...args);
+ };
+ }
+
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ BDFDB.ModuleUtils.unpatch(this, e.instance, "renderSection");
+ BDFDB.ModuleUtils.patch(this, e.instance, "renderSection", {after: e2 => {
+ if (e2.methodArguments[0].section != 0 && e2.methodArguments[0].section != e.instance.props.listRef.current.props.sections.length - 1) {
+ let category = categories[e2.methodArguments[0].section - 1];
+ if (category && draggedCategory != category.id) {
+ let color = BDFDB.ColorUtils.convert(category.color, "RGBA");
+ let foundDMs = this.filterDMs(category.dms);
+ let unreadAmount = settings.showCategoryUnread && BDFDB.ArrayUtils.sum(foundDMs.map(id => BDFDB.LibraryModules.UnreadChannelUtils.getMentionCount(id)));
+ e2.returnValue = [
+ BDFDB.ReactUtils.createElement("h2", {
+ className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.dmchannelheadercontainer, BDFDB.disCN._pindmspinnedchannelsheadercontainer, category.collapsed && BDFDB.disCN._pindmspinnedchannelsheadercollapsed, color && BDFDB.disCN._pindmspinnedchannelsheadercolored, BDFDB.disCN.namecontainernamecontainer),
+ categoryId: category.id,
+ onMouseDown: event => {
+ event = event.nativeEvent || event;
+ let node = BDFDB.DOMUtils.getParent(BDFDB.dotCN._pindmspinnedchannelsheadercontainer, event.target).cloneNode(true);
+ let mousemove = event2 => {
+ if (Math.sqrt((event.pageX - event2.pageX)**2) > 20 || Math.sqrt((event.pageY - event2.pageY)**2) > 20) {
+ BDFDB.ListenerUtils.stopEvent(event);
+ draggedCategory = category.id;
+ this.updateContainer("dmCategories");
+ let dragPreview = this.createDragPreview(node, event2);
+ document.removeEventListener("mousemove", mousemove);
+ document.removeEventListener("mouseup", mouseup);
+ let dragging = event3 => {
+ this.updateDragPreview(dragPreview, event3);
+ let placeholder = BDFDB.DOMUtils.getParent(BDFDB.dotCN._pindmsdmchannelplaceholder, event3.target);
+ let categoryNode = BDFDB.DOMUtils.getParent(BDFDB.dotCN._pindmspinnedchannelsheadercontainer, placeholder ? placeholder.previousSibling : event3.target);
+ let maybeHoveredCategory = categoryNode && categoryNode.getAttribute("categoryId");
+ let update = maybeHoveredCategory != hoveredCategory;
+ if (maybeHoveredCategory) hoveredCategory = maybeHoveredCategory;
+ else hoveredCategory = null;
+ if (update) this.updateContainer("dmCategories");
+ };
+ let releasing = event3 => {
+ BDFDB.DOMUtils.remove(dragPreview);
+ if (hoveredCategory) releasedCategory = hoveredCategory;
+ else draggedCategory = null;
+ hoveredCategory = null;
+ this.updateContainer("dmCategories");
+ document.removeEventListener("mousemove", dragging);
+ document.removeEventListener("mouseup", releasing);
+ };
+ document.addEventListener("mousemove", dragging);
+ document.addEventListener("mouseup", releasing);
+ }
+ };
+ let mouseup = _ => {
+ document.removeEventListener("mousemove", mousemove);
+ document.removeEventListener("mouseup", mouseup);
+ };
+ document.addEventListener("mousemove", mousemove);
+ document.addEventListener("mouseup", mouseup);
+ },
+ onClick: _ => {
+ if (foundDMs.length || !category.collapsed) {
+ category.collapsed = !category.collapsed;
+ BDFDB.DataUtils.save(category, this, "dmCategories", category.id);
+ this.updateContainer("dmCategories");
+ }
+ },
+ onContextMenu: event => {
+ BDFDB.ContextMenuUtils.open(this, event, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
+ children: [
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: BDFDB.LanguageUtils.LanguageStrings.CATEGORY_SETTINGS,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "category-settings"),
+ action: event2 => {
+ BDFDB.ContextMenuUtils.close(BDFDB.DOMUtils.getParent(BDFDB.dotCN.contextmenu, event2.target));
+ this.openCategorySettingsModal(category, "dmCategories");
+ }
+ }),
+ BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
+ label: BDFDB.LanguageUtils.LanguageStrings.DELETE_CATEGORY,
+ id: BDFDB.ContextMenuUtils.createItemId(this.name, "remove-category"),
+ color: BDFDB.LibraryComponents.MenuItems.Colors.DANGER,
+ action: event2 => {
+ BDFDB.ContextMenuUtils.close(BDFDB.DOMUtils.getParent(BDFDB.dotCN.contextmenu, event2.target));
+ BDFDB.DataUtils.remove(this, "dmCategories", category.id);
+ this.updateContainer("dmCategories");
+ }
+ })
+ ]
+ }));
+ },
+ children: [
+ BDFDB.ObjectUtils.is(color) ? BDFDB.ReactUtils.createElement("span", {
+ className: BDFDB.disCN.dmchannelheadertext,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextGradientElement, {
+ gradient: BDFDB.ColorUtils.createGradient(color),
+ children: category.name
+ })
+ }) : BDFDB.ReactUtils.createElement("span", {
+ className: BDFDB.disCN.dmchannelheadertext,
+ style: {color: color},
+ children: category.name,
+ }),
+ unreadAmount ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.NumberBadge, {
+ className: BDFDB.disCN._pindmspinnedchannelsheaderamount,
+ count: unreadAmount,
+ style: {backgroundColor: BDFDB.DiscordConstants.Colors.STATUS_RED}
+ }) : null,
+ settings.showCategoryAmount ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.NumberBadge, {
+ className: BDFDB.disCN._pindmspinnedchannelsheaderamount,
+ count: foundDMs.length,
+ style: {backgroundColor: BDFDB.DiscordConstants.Colors.BRAND}
+ }) : null,
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCNS._pindmspinnedchannelsheaderarrow + BDFDB.disCNS.channelheadericonwrapper + BDFDB.disCN.channelheadericonclickable,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCNS._pindmspinnedchannelsheaderarrow + BDFDB.disCN.channelheadericon,
+ nativeClass: true,
+ iconSVG: `<svg width="24" height="24" viewBox="4 4 16 16"><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M16.59 8.59004L12 13.17L7.41 8.59004L6 10L12 16L18 10L16.59 8.59004Z"></path></svg>`
+ })
+ })
+ ].filter(n => n)
+ }),
+ hoveredChannel == "header_" + category.id && BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCNS.dmchannel + BDFDB.disCNS._pindmsdmchannelpinned + BDFDB.disCNS._pindmsdmchannelplaceholder + BDFDB.disCN.namecontainernamecontainer,
+ children: BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.namecontainerlayout
+ })
+ })
+ ].filter(n => n);
+ }
+ else e2.returnValue = null;
+ }
+ }}, {force: true, noCache: true});
+
+ let pinnedIds = BDFDB.ObjectUtils.toArray(e.instance.props.pinnedChannelIds).reverse();
+ BDFDB.ModuleUtils.unpatch(this, e.instance, "renderDM");
+ BDFDB.ModuleUtils.patch(this, e.instance, "renderDM", {before: e2 => {
+ if (e2.methodArguments[0] != 0) e2.methodArguments[1] += pinnedIds.slice(0, e2.methodArguments[0] - 1).flat().length;
+ }, after: e2 => {
+ if (e2.methodArguments[0] != 0) {
+ let id = e.instance.props.privateChannelIds[e2.methodArguments[1]];
+ e2.returnValue = e.instance.props.channels[id] ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.PrivateChannelItems[e.instance.props.channels[id].isMultiUserDM() ? "GroupDM" : "DirectMessage"], Object.assign({
+ key: id,
+ channel: e.instance.props.channels[id],
+ selected: e.instance.props.selectedChannelId == id
+ }, (e.instance.props.navigator || e.instance.props.listNavigator).getItemProps({
+ index: e2.methodArguments[2]
+ }))) : null;
+
+ let category = categories[e2.methodArguments[0] - 1];
+ if (category) {
+ if (!id || (category.collapsed && e.instance.props.selectedChannelId != id) || !category.dms.includes(id) || draggedCategory == category.id || draggedChannel == id) e2.returnValue = null;
+ else if (hoveredCategory == category.id && [].concat(category.dms).reverse()[0] == id) e2.returnValue = [
+ e2.returnValue,
+ BDFDB.ReactUtils.createElement("h2", {
+ className: BDFDB.disCNS.dmchannelheadercontainer + BDFDB.disCNS._pindmspinnedchannelsheadercontainer + BDFDB.disCNS._pindmsdmchannelplaceholder + BDFDB.disCN.namecontainernamecontainer
+ })
+ ].filter(n => n);
+ else if (hoveredChannel == id) e2.returnValue = [
+ e2.returnValue,
+ BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCNS.dmchannel + BDFDB.disCNS._pindmsdmchannelpinned + BDFDB.disCNS._pindmsdmchannelplaceholder + BDFDB.disCN.namecontainernamecontainer,
+ children: BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCN.namecontainerlayout
+ })
+ })
+ ].filter(n => n);
+ }
+ }
+ }}, {force: true, noCache: true});
+ }
+ }
+
+ processUnreadDMs (e) {
+ e.instance.props.pinnedPrivateChannelIds = [];
+ let sortedRecents = this.sortAndUpdate("pinnedRecents");
+ if (sortedRecents.length) {
+ e.instance.props.unreadPrivateChannelIds = [];
+ for (let pos in sortedRecents) {
+ let id = sortedRecents[pos];
+ if (e.instance.props.channels[id]) {
+ if (!e.instance.props.pinnedPrivateChannelIds.includes(id)) e.instance.props.pinnedPrivateChannelIds.push(id);
+ if (!e.instance.props.unreadPrivateChannelIds.includes(id)) e.instance.props.unreadPrivateChannelIds.push(id);
+ }
+ }
+ e.instance.props.unreadPrivateChannelIds = e.instance.props.unreadPrivateChannelIds.concat(BDFDB.LibraryModules.DirectMessageUnreadStore.getUnreadPrivateChannelIds());
+ if (e.returnvalue) {
+ if (draggedChannel && releasedChannel) {
+ let pinnedPrivateChannelIds = [].concat(e.instance.props.pinnedPrivateChannelIds), newData = {};
+ BDFDB.ArrayUtils.remove(pinnedPrivateChannelIds, draggedChannel, true);
+ pinnedPrivateChannelIds.splice(pinnedPrivateChannelIds.indexOf(releasedChannel) + 1, 0, draggedChannel);
+ for (let pos in pinnedPrivateChannelIds) newData[pinnedPrivateChannelIds[pos]] = parseInt(pos);
+ BDFDB.DataUtils.save(newData, this, "pinnedRecents");
+ draggedChannel = null;
+ releasedChannel = null;
+ BDFDB.ReactUtils.forceUpdate(e.instance);
+ }
+ if (draggedChannel) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {filter: child => BDFDB.ReactUtils.getValue(child, "props.channel.id") == draggedChannel});
+ children.splice(index, 1);
+ }
+ if (this.hoveredChannel) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {filter: child => BDFDB.ReactUtils.getValue(child, "props.channel.id") == this.hoveredChannel});
+ children.splice(index + 1, 0, BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCNS.guildouter + BDFDB.disCN._pindmsrecentplaceholder,
+ children: BDFDB.ReactUtils.createElement("div", {
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.GuildComponents.Items.DragPlaceholder, {})
+ })
+ }));
+ }
+ }
+ }
+ else e.instance.props.unreadPrivateChannelIds = BDFDB.LibraryModules.DirectMessageUnreadStore.getUnreadPrivateChannelIds();
+ }
+
+ processPrivateChannel (e) {
+ if (e.instance.props.channel) {
+ let category = this.getCategory(e.instance.props.channel.id, "dmCategories");
+ if (category) {
+ if (e.node) {
+ BDFDB.DOMUtils.addClass(e.node, BDFDB.disCN._pindmsdmchannelpinned);
+ e.node.removeEventListener("mousedown", e.node.PinDMsMouseDownListener);
+ if (!BDFDB.DataUtils.get(this, "settings", "sortInRecentOrder")) {
+ e.node.setAttribute("draggable", false);
+ e.node.PinDMsMouseDownListener = event => {
+ if (!BDFDB.BDUtils.isPluginEnabled("PinDMs")) e.node.removeEventListener("mousedown", e.node.PinDMsMouseDownListener);
+ else {
+ event = event.nativeEvent || event;
+ let mousemove = event2 => {
+ if (Math.sqrt((event.pageX - event2.pageX)**2) > 20 || Math.sqrt((event.pageY - event2.pageY)**2) > 20) {
+ BDFDB.ListenerUtils.stopEvent(event);
+ draggedChannel = e.instance.props.channel.id;
+ this.updateContainer("dmCategories");
+ let dragPreview = this.createDragPreview(e.node, event2);
+ document.removeEventListener("mousemove", mousemove);
+ document.removeEventListener("mouseup", mouseup);
+ let dragging = event3 => {
+ this.updateDragPreview(dragPreview, event3);
+ let maybeHoveredChannel = null;
+ let categoryNode = BDFDB.DOMUtils.getParent(BDFDB.dotCN._pindmspinnedchannelsheadercontainer, event3.target);
+ if (categoryNode) {
+ let hoveredCategoryId = categoryNode.getAttribute("categoryid");
+ if (hoveredCategoryId && hoveredCategoryId == category.id) maybeHoveredChannel = "header_" + category.id;
+ }
+ else {
+ let placeholder = BDFDB.DOMUtils.getParent(BDFDB.dotCN._pindmsdmchannelplaceholder, event3.target);
+ maybeHoveredChannel = (BDFDB.ReactUtils.findValue(BDFDB.DOMUtils.getParent(BDFDB.dotCN._pindmsdmchannelpinned, placeholder ? placeholder.previousSibling : event3.target), "channel", {up: true}) || {}).id;
+ let maybeHoveredCategory = maybeHoveredChannel && this.getCategory(maybeHoveredChannel, "dmCategories");
+ if (!maybeHoveredCategory || maybeHoveredCategory.id != category.id) maybeHoveredChannel = null;
+ };
+ let update = maybeHoveredChannel != hoveredChannel;
+ if (maybeHoveredChannel) hoveredChannel = maybeHoveredChannel;
+ else hoveredChannel = null;
+ if (update) this.updateContainer("dmCategories");
+ };
+ let releasing = event3 => {
+ BDFDB.DOMUtils.remove(dragPreview);
+ if (hoveredChannel) releasedChannel = hoveredChannel;
+ else draggedChannel = null;
+ hoveredChannel = null;
+ this.updateContainer("dmCategories");
+ document.removeEventListener("mousemove", dragging);
+ document.removeEventListener("mouseup", releasing);
+ };
+ document.addEventListener("mousemove", dragging);
+ document.addEventListener("mouseup", releasing);
+ }
+ };
+ let mouseup = _ => {
+ document.removeEventListener("mousemove", mousemove);
+ document.removeEventListener("mouseup", mouseup);
+ };
+ document.addEventListener("mousemove", mousemove);
+ document.addEventListener("mouseup", mouseup);
+ }
+ };
+ e.node.addEventListener("mousedown", e.node.PinDMsMouseDownListener);
+ }
+ }
+ if (e.returnvalue) e.returnvalue.props.children = [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: BDFDB.LanguageUtils.LanguageStrings.UNPIN,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, {
+ className: BDFDB.disCN._pindmsunpinbutton,
+ onClick: event => {
+ BDFDB.ListenerUtils.stopEvent(event);
+ this.removeFromCategory(e.instance.props.channel.id, category, "dmCategories");
+ },
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCN._pindmsunpinicon,
+ name: BDFDB.LibraryComponents.SvgIcon.Names.PIN
+ })
+ })
+ }),
+ e.returnvalue.props.children
+ ].flat(10).filter(n => n);
+ }
+ }
+ }
+
+ processDirectMessage (e) {
+ if (e.instance.props.channel) {
+ if (e.node && e.methodname == "componentDidMount") {
+ BDFDB.DOMUtils.removeClass(e.node, BDFDB.disCN._pindmsrecentpinned);
+ e.node.removeEventListener("contextmenu", e.node.PinDMsContextMenuListener);
+ e.node.PinDMsContextMenuListener = event => {BDFDB.DMUtils.openMenu(e.instance.props.channel.id, event);};
+ e.node.addEventListener("contextmenu", e.node.PinDMsContextMenuListener);
+ if (this.isPinned(e.instance.props.channel.id, "pinnedRecents")) {
+ BDFDB.DOMUtils.addClass(e.node, BDFDB.disCN._pindmsrecentpinned);
+ e.node.removeEventListener("mousedown", e.node.PinDMsMouseDownListener);
+ if (!BDFDB.DataUtils.get(this, "settings", "sortInRecentOrderGuild")) {
+ for (let child of e.node.querySelectorAll("a")) child.setAttribute("draggable", false);
+ e.node.PinDMsMouseDownListener = event => {
+ let mousemove = event2 => {
+ if (Math.sqrt((event.pageX - event2.pageX)**2) > 20 || Math.sqrt((event.pageY - event2.pageY)**2) > 20) {
+ BDFDB.ListenerUtils.stopEvent(event);
+ draggedChannel = e.instance.props.channel.id;
+ BDFDB.ModuleUtils.forceAllUpdates(this, "UnreadDMs");
+ let dragPreview = this.createDragPreview(e.node, event2);
+ document.removeEventListener("mousemove", mousemove);
+ document.removeEventListener("mouseup", mouseup);
+ let dragging = event3 => {
+ this.updateDragPreview(dragPreview, event3);
+ let placeholder = BDFDB.DOMUtils.getParent(BDFDB.dotCN._pindmsrecentplaceholder, event3.target);
+ let maybeHoveredChannel = (BDFDB.ReactUtils.findValue(BDFDB.DOMUtils.getParent(BDFDB.dotCN._pindmsrecentpinned, placeholder ? placeholder.previousSibling : event3.target), "channel", {up: true}) || {}).id;
+ let update = maybeHoveredChannel != hoveredChannel;
+ if (maybeHoveredChannel) hoveredChannel = maybeHoveredChannel;
+ else hoveredChannel = null;
+ if (update) BDFDB.ModuleUtils.forceAllUpdates(this, "UnreadDMs");
+ };
+ let releasing = event3 => {
+ BDFDB.DOMUtils.remove(dragPreview);
+ if (hoveredChannel) releasedChannel = hoveredChannel;
+ else draggedChannel = null;
+ hoveredChannel = null;
+ BDFDB.ModuleUtils.forceAllUpdates(this, "UnreadDMs");
+ document.removeEventListener("mousemove", dragging);
+ document.removeEventListener("mouseup", releasing);
+ };
+ document.addEventListener("mousemove", dragging);
+ document.addEventListener("mouseup", releasing);
+ }
+ };
+ let mouseup = _ => {
+ document.removeEventListener("mousemove", mousemove);
+ document.removeEventListener("mouseup", mouseup);
+ };
+ document.addEventListener("mousemove", mousemove);
+ document.addEventListener("mouseup", mouseup);
+ };
+ e.node.addEventListener("mousedown", e.node.PinDMsMouseDownListener);
+ }
+ }
+ }
+ if (e.node && e.methodname == "componentWillUnmount") {
+ BDFDB.ModuleUtils.forceAllUpdates(this, "PrivateChannelsList");
+ }
+ if (e.returnvalue && this.isPinned(e.instance.props.channel.id, "pinnedRecents") && BDFDB.DataUtils.get(this, "settings", "showPinIcon")) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name:"BlobMask"});
+ if (index > -1) children[index].props.upperLeftBadge = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.IconBadge, {
+ className: BDFDB.disCN.guildiconbadge,
+ disableColor: true,
+ style: {transform: "scale(-1, 1)"},
+ icon: BDFDB.LibraryComponents.SvgIcon.Names.NOVA_PIN
+ });
+ }
+ }
+ }
+
+ generateID (type) {
+ if (!type) return null;
+ let categories = BDFDB.DataUtils.load(this, type);
+ let id = Math.round(Math.random() * 10000000000000000);
+ return categories[id] ? this.generateID() : id;
+ }
+
+ filterDMs (dms) {
+ return dms.filter(id => BDFDB.LibraryModules.ChannelStore.getChannel(id));
+ }
+
+ addToCategory (id, category, type) {
+ if (!id || !category || !type) return;
+ let wasEmpty = !this.filterDMs(category.dms).length;
+ if (!category.dms.includes(id)) category.dms.unshift(id);
+ if (wasEmpty && category.dms.length) category.collapsed = false;
+ BDFDB.DataUtils.save(category, this, type, category.id);
+ this.updateContainer(type);
+ }
+
+ removeFromCategory (id, category, type) {
+ if (!id || !category || !type) return;
+ BDFDB.ArrayUtils.remove(category.dms, id, true);
+ if (!this.filterDMs(category.dms).length) category.collapsed = true;
+ BDFDB.DataUtils.save(category, this, type, category.id);
+ this.updateContainer(type);
+ }
+
+ getCategory (id, type) {
+ if (!id || !type) return null;
+ let categories = BDFDB.DataUtils.load(this, type);
+ for (let catId in categories) if (categories[catId].dms.includes(id)) return categories[catId];
+ return null;
+ }
+
+ sortAndUpdateCategories (type, reverse) {
+ let data = BDFDB.ObjectUtils.sort(BDFDB.DataUtils.load(this, type), "pos"), newData = {};
+ let sorted = [], pos = 0, sort = id => {
+ if (sorted[pos] === undefined) {
+ newData[id] = Object.assign({}, data[id], {pos});
+ sorted[pos] = newData[id];
+ }
+ else {
+ pos++;
+ sort(id);
+ }
+ };
+ for (let id in data) sort(id);
+ if (!BDFDB.equals(data, newData)) BDFDB.DataUtils.save(newData, this, type);
+ return (reverse ? sorted.reverse() : sorted).filter(n => n);
+ }
+
+ sortDMsByTime (dms, type) {
+ if (dms.length > 1 && BDFDB.DataUtils.get(this, "settings", type == "dmCategories" ? "sortInRecentOrder" : "sortInRecentOrderGuild")) {
+ let timestamps = BDFDB.LibraryModules.DirectMessageStore.getPrivateChannelTimestamps();
+ return [].concat(dms).sort(function (x, y) {return timestamps[x] > timestamps[y] ? -1 : timestamps[x] < timestamps[y] ? 1 : 0;});
+ }
+ else return dms;
+ }
+
+ openCategorySettingsModal (data, type, isNew) {
+ if (BDFDB.ObjectUtils.is(data) && type) BDFDB.ModalUtils.open(this, {
+ size: "MEDIUM",
+ header: BDFDB.LanguageUtils.LanguageStrings.CATEGORY_SETTINGS,
+ subheader: data.name,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: BDFDB.LanguageUtils.LanguageStrings.CATEGORY_NAME,
+ className: BDFDB.disCN.marginbottom20 + " input-categoryname",
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
+ value: data.name,
+ placeholder: data.name,
+ autoFocus: true
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, {
+ className: BDFDB.disCN.dividerdefault
+ })
+ ]
+ }),
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
+ title: this.labels.modal_colorpicker1_text,
+ className: BDFDB.disCN.marginbottom20,
+ children: [
+ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ColorSwatches, {
+ color: data.color,
+ number: 1
+ })
+ ]
+ })
+ ],
+ buttons: [{
+ contents: isNew ? BDFDB.LanguageUtils.LanguageStrings.CREATE : BDFDB.LanguageUtils.LanguageStrings.SAVE,
+ color: "BRAND",
+ close: true,
+ click: modal => {
+ data.name = modal.querySelector(".input-categoryname " + BDFDB.dotCN.input).value.trim() || data.name;
+
+ data.color = BDFDB.ColorUtils.getSwatchColor(modal, 1);
+ if (data.color != null && !BDFDB.ObjectUtils.is(data.color)) {
+ if (data.color[0] < 30 && data.color[1] < 30 && data.color[2] < 30) data.color = BDFDB.ColorUtils.change(data.color, 30);
+ else if (data.color[0] > 225 && data.color[1] > 225 && data.color[2] > 225) data.color = BDFDB.ColorUtils.change(data.color, -30);
+ }
+
+ BDFDB.DataUtils.save(data, this, type, data.id);
+
+ this.updateContainer(type);
+ }
+ }]
+ });
+ }
+
+ addPin (newid, type) {
+ if (!newid) return;
+ let pinnedDMs = BDFDB.DataUtils.load(this, type);
+ for (let id in pinnedDMs) pinnedDMs[id] = pinnedDMs[id] + 1;
+ pinnedDMs[newid] = 0;
+ BDFDB.DataUtils.save(pinnedDMs, this, type);
+ this.updateContainer(type);
+ }
+
+ removePin (id, type) {
+ if (!id) return;
+ BDFDB.DataUtils.remove(this, type, id);
+ this.updateContainer(type);
+ }
+
+ isPinned (id, type) {
+ return BDFDB.DataUtils.load(this, type, id) != undefined;
+ }
+
+ updateContainer (type) {
+ switch (type) {
+ case "dmCategories":
+ BDFDB.ModuleUtils.forceAllUpdates(this, "PrivateChannelsList");
+ break;
+ case "pinnedRecents":
+ BDFDB.ModuleUtils.forceAllUpdates(this, "UnreadDMs");
+ break;
+ }
+ }
+
+ sortAndUpdate (type) {
+ let data = BDFDB.DataUtils.load(this, type), newData = {};
+ delete data[""];
+ delete data["null"];
+ let sortedDMs = [], existingDMs = [], sortDM = (id, pos) => {
+ if (sortedDMs[pos] === undefined) sortedDMs[pos] = id;
+ else sortDM(id, pos + 1);
+ };
+ for (let id in data) sortDM(id, data[id]);
+ sortedDMs = sortedDMs.filter(n => n);
+ for (let pos in sortedDMs) {
+ newData[sortedDMs[pos]] = parseInt(pos);
+ if (BDFDB.LibraryModules.ChannelStore.getChannel(sortedDMs[pos])) existingDMs.push(sortedDMs[pos]);
+ }
+ if (!BDFDB.equals(data, newData)) BDFDB.DataUtils.save(newData, this, type);
+ return this.sortDMsByTime(existingDMs, type);
+ }
+
+ forceUpdateAll (stopped) {
+ BDFDB.ReactUtils.forceUpdate(BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.app), {name:"FluxContainer(PrivateChannels)", all:true, unlimited:true}));
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ }
+
+ createDragPreview (div, event) {
+ if (!Node.prototype.isPrototypeOf(div)) return;
+ let dragPreview = div.cloneNode(true);
+ BDFDB.DOMUtils.addClass(dragPreview, BDFDB.disCN._pindmsdragpreview);
+ BDFDB.DOMUtils.remove(dragPreview.querySelector(BDFDB.dotCNC.guildlowerbadge + BDFDB.dotCNC.guildupperbadge + BDFDB.dotCN.guildpillwrapper));
+ document.querySelector(BDFDB.dotCN.appmount).appendChild(dragPreview);
+ let rects = BDFDB.DOMUtils.getRects(dragPreview);
+ BDFDB.DOMUtils.hide(dragPreview);
+ dragPreview.style.setProperty("pointer-events", "none", "important");
+ dragPreview.style.setProperty("left", event.clientX - (rects.width/2) + "px", "important");
+ dragPreview.style.setProperty("top", event.clientY - (rects.height/2) + "px", "important");
+ return dragPreview;
+ }
+
+ updateDragPreview (dragPreview, event) {
+ if (!Node.prototype.isPrototypeOf(dragPreview)) return;
+ BDFDB.DOMUtils.show(dragPreview);
+ let rects = BDFDB.DOMUtils.getRects(dragPreview);
+ dragPreview.style.setProperty("left", event.clientX - (rects.width/2) + "px", "important");
+ dragPreview.style.setProperty("top", event.clientY - (rects.height/2) + "px", "important");
+ }
+
+ setLabelsByLanguage () {
+ switch (BDFDB.LanguageUtils.getLanguage().id) {
+ case "hr": //croatian
+ return {
+ context_pindm_text: "Prikljucite Izravnu Poruku",
+ context_pinchannel_text: "Priložite popisu kanala",
+ context_unpinchannel_text: "Ukloni s popisa kanala",
+ context_addtonewcategory_text: "Dodavanje u novu kategoriju",
+ context_pinguild_text: "Priložite popisu poslužitelja",
+ context_unpinguild_text: "Ukloni s popisa poslužitelja",
+ header_pinneddms_text: "Prikvačene Izravne Poruke",
+ modal_colorpicker1_text: "Boja kategorije"
+ };
+ case "da": //danish
+ return {
+ context_pindm_text: "Fastgør PB",
+ context_pinchannel_text: "Vedhæft til kanalliste",
+ context_unpinchannel_text: "Fjern fra kanalliste",
+ context_addtonewcategory_text: "Føj til ny kategori",
+ context_pinguild_text: "Vedhæft til serverliste",
+ context_unpinguild_text: "Fjern fra serverliste",
+ header_pinneddms_text: "Pinned Privat Beskeder",
+ modal_colorpicker1_text: "Kategori farve"
+ };
+ case "de": //german
+ return {
+ context_pindm_text: "Direktnachricht anheften",
+ context_pinchannel_text: "An Kanalliste anheften",
+ context_unpinchannel_text: "Von Kanalliste loslösen",
+ context_addtonewcategory_text: "Zur neuen Kategorie hinzufügen",
+ context_pinguild_text: "An Serverliste anheften",
+ context_unpinguild_text: "Von Serverliste loslösen",
+ header_pinneddms_text: "Gepinnte Direktnachrichten",
+ modal_colorpicker1_text: "Kategoriefarbe"
+ };
+ case "es": //spanish
+ return {
+ context_pindm_text: "Anclar MD",
+ context_pinchannel_text: "Adjuntar a la lista de canales",
+ context_unpinchannel_text: "Deshazte de la lista de canales",
+ context_addtonewcategory_text: "Agregar a nueva categoría",
+ context_pinguild_text: "Adjuntar a la lista de servidores",
+ context_unpinguild_text: "Deshazte de la lista de servidores",
+ header_pinneddms_text: "Mensajes Directos Fijados",
+ modal_colorpicker1_text: "Color de la categoría"
+ };
+ case "fr": //french
+ return {
+ context_pindm_text: "Épingler MP",
+ context_pinchannel_text: "Épingler à la liste des salons",
+ context_unpinchannel_text: "Détacher de la liste des salons",
+ context_addtonewcategory_text: "Ajouter à une nouvelle catégorie",
+ context_pinguild_text: "Épingler à la liste de serveurs",
+ context_unpinguild_text: "Détacher de la liste de serveurs",
+ header_pinneddms_text: "Messages Prives Épinglés",
+ modal_colorpicker1_text: "Couleur de la catégorie"
+ };
+ case "it": //italian
+ return {
+ context_pindm_text: "Fissa il messaggio diretto",
+ context_pinchannel_text: "Allega alla lista dei canali",
+ context_unpinchannel_text: "Rimuovi dalla lista dei canali",
+ context_addtonewcategory_text: "Aggiungi a nuova categoria",
+ context_pinguild_text: "Allega alla lista dei server",
+ context_unpinguild_text: "Rimuovi dalla lista dei server",
+ header_pinneddms_text: "Messaggi Diretti Aggiunti",
+ modal_colorpicker1_text: "Colore della categoria"
+ };
+ case "nl": //dutch
+ return {
+ context_pindm_text: "PB pinnen",
+ context_pinchannel_text: "Pin naar de kanalenlijst",
+ context_unpinchannel_text: "Losmaken van kanalenlijst",
+ context_addtonewcategory_text: "Toevoegen aan nieuwe categorie",
+ context_pinguild_text: "Pin naar de serverlijst",
+ context_unpinguild_text: "Losmaken van serverlijst",
+ header_pinneddms_text: "Vastgezette Persoonluke Berichten",
+ modal_colorpicker1_text: "Categorie kleur"
+ };
+ case "no": //norwegian
+ return {
+ context_pindm_text: "Fest DM",
+ context_pinchannel_text: "Fest på kanalliste",
+ context_unpinchannel_text: "Fjern fra kanalliste",
+ context_addtonewcategory_text: "Legg til i ny kategori",
+ context_pinguild_text: "Fest på serverliste",
+ context_unpinguild_text: "Fjern fra serverlisten",
+ header_pinneddms_text: "Pinned Direktemeldinger",
+ modal_colorpicker1_text: "Kategorifarge"
+ };
+ case "pl": //polish
+ return {
+ context_pindm_text: "Przypnij PW",
+ context_pinchannel_text: "Dołącz do listy kanałów",
+ context_unpinchannel_text: "Usuń z listy kanałów",
+ context_addtonewcategory_text: "Dodaj do nowej kategorii",
+ context_pinguild_text: "Dołącz do listy serwerów",
+ context_unpinguild_text: "Usuń z listy serwerów",
+ header_pinneddms_text: "Prywatne Wiadomości Bezpośrednie",
+ modal_colorpicker1_text: "Kolor kategorii"
+ };
+ case "pt-BR": //portuguese (brazil)
+ return {
+ context_pindm_text: "Fixar MD",
+ context_pinchannel_text: "Anexar à lista de canais",
+ context_unpinchannel_text: "Remover da lista de canais",
+ context_addtonewcategory_text: "Adicionar à nova categoria",
+ context_pinguild_text: "Anexar à lista de servidores",
+ context_unpinguild_text: "Remover da lista de servidores",
+ header_pinneddms_text: "Mensagens diretas fixadas",
+ modal_colorpicker1_text: "Cor da categoria"
+ };
+ case "fi": //finnish
+ return {
+ context_pindm_text: "Kiinnitä yksityisviestit",
+ context_pinchannel_text: "Liitä kanavaluetteloon",
+ context_unpinchannel_text: "Poista kanavaluettelosta",
+ context_addtonewcategory_text: "Lisää uuteen luokkaan",
+ context_pinguild_text: "Liitä palvelinluetteloon",
+ context_unpinguild_text: "Poista palvelinluettelosta",
+ header_pinneddms_text: "Liitetyt yksityisviestit",
+ modal_colorpicker1_text: "Luokan väri"
+ };
+ case "sv": //swedish
+ return {
+ context_pindm_text: "Fäst DM",
+ context_pinchannel_text: "Fäst till kanallista",
+ context_unpinchannel_text: "Ta bort från kanallistan",
+ context_addtonewcategory_text: "Lägg till i ny kategori",
+ context_pinguild_text: "Fäst till servernlista",
+ context_unpinguild_text: "Ta bort från servernlista",
+ header_pinneddms_text: "Inlagda Direktmeddelanden",
+ modal_colorpicker1_text: "Kategori färg"
+ };
+ case "tr": //turkish
+ return {
+ context_pindm_text: "DM'yi Sabitle",
+ context_pinchannel_text: "Kanal listesine ekle",
+ context_unpinchannel_text: "Kanal listesinden kaldır",
+ context_addtonewcategory_text: "Yeni kategoriye ekle",
+ context_pinguild_text: "Sunucu listesine ekle",
+ context_unpinguild_text: "Sunucu listesinden kaldır",
+ header_pinneddms_text: "Direkt Mesajlar Sabitleyin",
+ modal_colorpicker1_text: "Kategori rengi"
+ };
+ case "cs": //czech
+ return {
+ context_pindm_text: "Připnout PZ",
+ context_pinchannel_text: "Připojení k seznamu kanálů",
+ context_unpinchannel_text: "Odstranit ze seznamu kanálů",
+ context_addtonewcategory_text: "Přidat do nové kategorie",
+ context_pinguild_text: "Připojit ke seznamu serverů",
+ context_unpinguild_text: "Odstranit ze seznamu serverů",
+ header_pinneddms_text: "Připojené Přímá Zpráva",
+ modal_colorpicker1_text: "Barva kategorie"
+ };
+ case "bg": //bulgarian
+ return {
+ context_pindm_text: "Закачени ДС",
+ context_pinchannel_text: "Прикачете към списъка с канали",
+ context_unpinchannel_text: "Премахване от списъка с канали",
+ context_addtonewcategory_text: "Добавяне към нова категория",
+ context_pinguild_text: "Прикачване към списъка със сървъри",
+ context_unpinguild_text: "Премахване от списъка със сървъри",
+ header_pinneddms_text: "Свързани директни съобщения",
+ modal_colorpicker1_text: "Цвят на категорията"
+ };
+ case "ru": //russian
+ return {
+ context_pindm_text: "Закрепить ЛС",
+ context_pinchannel_text: "Прикрепить к списку каналов",
+ context_unpinchannel_text: "Удалить из списка каналов",
+ context_addtonewcategory_text: "Добавить в новую категорию",
+ context_pinguild_text: "Присоединить к списку серверов",
+ context_unpinguild_text: "Удалить из списка серверов",
+ header_pinneddms_text: "Прикрепленные Личные Сообщения",
+ modal_colorpicker1_text: "Цвет категории"
+ };
+ case "uk": //ukrainian
+ return {
+ context_pindm_text: "Закріпити ОП",
+ context_pinchannel_text: "Додайте до списку каналів",
+ context_unpinchannel_text: "Видалити зі списку каналів",
+ context_addtonewcategory_text: "Додати до нової категорії",
+ context_pinguild_text: "Додайте до списку серверів",
+ context_unpinguild_text: "Видалити зі списку серверів",
+ header_pinneddms_text: "Прикріплені oсобисті повідомлення",
+ modal_colorpicker1_text: "Колір категорії"
+ };
+ case "ja": //japanese
+ return {
+ context_pindm_text: "DMピン",
+ context_pinchannel_text: "チャンネルリストに添付",
+ context_unpinchannel_text: "チャンネルリストから削除",
+ context_addtonewcategory_text: "新しいカテゴリに追加",
+ context_pinguild_text: "サーバーリストに添付",
+ context_unpinguild_text: "サーバーリストから削除",
+ header_pinneddms_text: "固定された直接メッセージ",
+ modal_colorpicker1_text: "カテゴリーの色"
+ };
+ case "zh-TW": //chinese (traditional)
+ return {
+ context_pindm_text: "引腳直接留言",
+ context_pinchannel_text: "附加到頻道列表",
+ context_unpinchannel_text: "從頻道列表中刪除",
+ context_addtonewcategory_text: "添加到新類別",
+ context_pinguild_text: "附加到服務器列表",
+ context_unpinguild_text: "從服務器列表中刪除",
+ header_pinneddms_text: "固定私人信息",
+ modal_colorpicker1_text: "類別顏色"
+ };
+ case "ko": //korean
+ return {
+ context_pindm_text: "비공개 메시지 고정",
+ context_pinchannel_text: "채널 목록에 첨부",
+ context_unpinchannel_text: "채널 목록에서 삭제",
+ context_addtonewcategory_text: "새 카테고리에 추가",
+ context_pinguild_text: "서버 목록에 첨부",
+ context_unpinguild_text: "서버 목록에서 제거",
+ header_pinneddms_text: "고정 된 비공개 메시지",
+ modal_colorpicker1_text: "카테고리 색상"
+ };
+ default: //default: english
+ return {
+ context_pindm_text: "Pin DM",
+ context_pinchannel_text: "Pin to Channellist",
+ context_unpinchannel_text: "Unpin from Channellist",
+ context_addtonewcategory_text: "Add to new Category",
+ context_pinguild_text: "Pin to Serverlist",
+ context_unpinguild_text: "Unpin from Serverlist",
+ header_pinneddms_text: "Pinned Direct Messages",
+ modal_colorpicker1_text: "Categorycolor"
+ };
+ }
+ }
+ }
+})(); \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/QuickMention.plugin.js b/.config/BetterDiscord/plugins/QuickMention.plugin.js
new file mode 100644
index 0000000..64541e8
--- /dev/null
+++ b/.config/BetterDiscord/plugins/QuickMention.plugin.js
@@ -0,0 +1,78 @@
+//META{"name":"QuickMention","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/QuickMention","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/QuickMention/QuickMention.plugin.js"}*//
+
+var QuickMention = (_ => {
+ return class QuickMention {
+ getName () {return "QuickMention";}
+
+ getVersion () {return "1.0.0";}
+
+ getAuthor () {return "DevilBro";}
+
+ getDescription () {return "Adds a mention entry to the message option toolbar.";}
+
+ // Legacy
+ load () {}
+
+ start () {
+ if (!window.BDFDB) window.BDFDB = {myPlugins:{}};
+ if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this;
+ let libraryScript = document.querySelector("head script#BDFDBLibraryScript");
+ if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) {
+ if (libraryScript) libraryScript.remove();
+ libraryScript = document.createElement("script");
+ libraryScript.setAttribute("id", "BDFDBLibraryScript");
+ libraryScript.setAttribute("type", "text/javascript");
+ libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js");
+ libraryScript.setAttribute("date", performance.now());
+ libraryScript.addEventListener("load", _ => {this.initialize();});
+ document.head.appendChild(libraryScript);
+ }
+ else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize();
+ this.startTimeout = setTimeout(_ => {
+ try {return this.initialize();}
+ catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);}
+ }, 30000);
+ }
+
+ initialize () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ if (this.started) return;
+ BDFDB.PluginUtils.init(this);
+ }
+ else {
+ console.error(`%c[${this.getName()}]%c`, 'color: #3a71c1; font-weight: 700;', '', 'Fatal Error: Could not load BD functions!');
+ }
+ }
+
+ stop () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ this.stopping = true;
+
+ BDFDB.PluginUtils.clear(this);
+ }
+ }
+
+
+ // Begin of own functions
+
+ onMessageOptionToolbar (e) {
+ if (e.instance.props.message.author.id != BDFDB.UserUtils.me.id && e.instance.props.message.type == BDFDB.DiscordConstants.MessageTypes.DEFAULT && BDFDB.UserUtils.can("SEND_MESSAGES")) e.returnvalue.props.children.unshift(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ key: "mention",
+ text: BDFDB.LanguageUtils.LanguageStrings.MENTION,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, {
+ className: BDFDB.disCN.messagetoolbarbutton,
+ onClick: _ => {
+ BDFDB.LibraryModules.DispatchUtils.ComponentDispatch.dispatchToLastSubscribed(BDFDB.DiscordConstants.ComponentActions.INSERT_TEXT, {
+ content: `<@!${e.instance.props.message.author.id}>`
+ });
+ },
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
+ className: BDFDB.disCNS.messagetoolbaricon,
+ nativeClass: true,
+ name: BDFDB.LibraryComponents.SvgIcon.Names.NOVA_AT
+ })
+ })
+ }));
+ }
+ }
+})(); \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/RemoveNicknames.config.json b/.config/BetterDiscord/plugins/RemoveNicknames.config.json
new file mode 100644
index 0000000..1818ecb
--- /dev/null
+++ b/.config/BetterDiscord/plugins/RemoveNicknames.config.json
@@ -0,0 +1,17 @@
+{
+ "changelog": {
+ "currentversion": "1.3.0"
+ },
+ "settings": {
+ "addNickname": false,
+ "changeInAutoComplete": true,
+ "changeInChatWindow": true,
+ "changeInMemberList": true,
+ "changeInMentions": true,
+ "changeInTyping": true,
+ "changeInVoiceChat": true,
+ "replaceBots": true,
+ "replaceOwn": false,
+ "swapPositions": false
+ }
+} \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/RemoveNicknames.plugin.js b/.config/BetterDiscord/plugins/RemoveNicknames.plugin.js
new file mode 100644
index 0000000..0390456
--- /dev/null
+++ b/.config/BetterDiscord/plugins/RemoveNicknames.plugin.js
@@ -0,0 +1,201 @@
+//META{"name":"RemoveNicknames","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/RemoveNicknames","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/RemoveNicknames/RemoveNicknames.plugin.js"}*//
+
+var RemoveNicknames = (_ => {
+ return class RemoveNicknames {
+ getName () {return "RemoveNicknames";}
+
+ getVersion () {return "1.3.0";}
+
+ getAuthor () {return "DevilBro";}
+
+ getDescription () {return "Replace all nicknames with the actual accountnames.";}
+
+ constructor () {
+ this.changelog = {
+ "fixed":[["Typing List","Works now"]]
+ };
+
+ this.patchedModules = {
+ before: {
+ AutocompleteUserResult: "render",
+ VoiceUser: "render",
+ MemberListItem: "render",
+ Message: "default",
+ MessageContent: "type",
+ },
+ after: {
+ TypingUsers: "render"
+ }
+ };
+ }
+
+ initConstructor () {
+ this.defaults = {
+ settings: {
+ replaceOwn: {value:false, inner:false, description:"Replace your own name:"},
+ replaceBots: {value:true, inner:false, description:"Replace the nickname of bots:"},
+ addNickname: {value:false, inner:false, description:"Add nickname as parentheses:"},
+ swapPositions: {value:false, inner:false, description:"Swap the position of username and nickname:"},
+ changeInChatWindow: {value:true, inner:true, description:"Messages"},
+ changeInMentions: {value:true, inner:true, description:"Mentions"},
+ changeInVoiceChat: {value:true, inner:true, description:"Voice Channels"},
+ changeInMemberList: {value:true, inner:true, description:"Member List"},
+ changeInTyping: {value:true, inner:true, description:"Typing List"},
+ changeInAutoComplete: {value:true, inner:true, description:"Autocomplete Menu"}
+ }
+ };
+ }
+
+ getSettingsPanel () {
+ if (!window.BDFDB || typeof BDFDB != "object" || !BDFDB.loaded || !this.started) return;
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ let settingsPanel, settingsItems = [], innerItems = [];
+
+ for (let key in settings) (!this.defaults.settings[key].inner ? settingsItems : innerItems).push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
+ className: BDFDB.disCN.marginbottom8,
+ type: "Switch",
+ plugin: this,
+ keys: ["settings", key],
+ label: this.defaults.settings[key].description,
+ value: settings[key]
+ }));
+ settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelInner, {
+ title: "Remove Nicknames in:",
+ first: settingsItems.length == 0,
+ last: true,
+ children: innerItems
+ }));
+
+ return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, settingsItems);
+ }
+
+ // Legacy
+ load () {}
+
+ start () {
+ if (!window.BDFDB) window.BDFDB = {myPlugins:{}};
+ if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this;
+ let libraryScript = document.querySelector("head script#BDFDBLibraryScript");
+ if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) {
+ if (libraryScript) libraryScript.remove();
+ libraryScript = document.createElement("script");
+ libraryScript.setAttribute("id", "BDFDBLibraryScript");
+ libraryScript.setAttribute("type", "text/javascript");
+ libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js");
+ libraryScript.setAttribute("date", performance.now());
+ libraryScript.addEventListener("load", _ => {this.initialize();});
+ document.head.appendChild(libraryScript);
+ }
+ else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize();
+ this.startTimeout = setTimeout(_ => {
+ try {return this.initialize();}
+ catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);}
+ }, 30000);
+ }
+
+ initialize () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ if (this.started) return;
+ BDFDB.PluginUtils.init(this);
+
+ this.forceUpdateAll();
+ }
+ else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!");
+ }
+
+
+ stop () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ this.stopping = true;
+
+ this.forceUpdateAll();
+
+ BDFDB.PluginUtils.clear(this);
+ }
+ }
+
+
+ // Begin of own functions
+
+ onSettingsClosed (e) {
+ if (this.SettingsUpdated) {
+ delete this.SettingsUpdated;
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ }
+ }
+
+ processAutocompleteUserResult (e) {
+ if (e.instance.props.user && e.instance.props.nick && BDFDB.DataUtils.get(this, "settings", "changeInAutoComplete")) {
+ let newName = this.getNewName(e.instance.props.user);
+ if (newName) e.instance.props.nick = newName;
+ }
+ }
+
+ processVoiceUser (e) {
+ if (e.instance.props.user && e.instance.props.nick && BDFDB.DataUtils.get(this, "settings", "changeInVoiceChat")) {
+ let newName = this.getNewName(e.instance.props.user);
+ if (newName) e.instance.props.nick = newName;
+ }
+ }
+
+ processMemberListItem (e) {
+ if (e.instance.props.user && e.instance.props.nick && BDFDB.DataUtils.get(this, "settings", "changeInMemberList")) {
+ let newName = this.getNewName(e.instance.props.user);
+ if (newName) e.instance.props.nick = newName;
+ }
+ }
+
+ processTypingUsers (e) {
+ if (BDFDB.ObjectUtils.is(e.instance.props.typingUsers) && Object.keys(e.instance.props.typingUsers).length && BDFDB.DataUtils.get(this, "settings", "changeInTyping")) {
+ let users = Object.keys(e.instance.props.typingUsers).filter(id => id != BDFDB.UserUtils.me.id).filter(id => !BDFDB.LibraryModules.FriendUtils.isBlocked(id)).map(id => BDFDB.LibraryModules.UserStore.getUser(id)).filter(user => user);
+ if (users.length) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props: [["className", BDFDB.disCN.typingtext]]});
+ if (index > -1 && BDFDB.ArrayUtils.is(children[index].props.children)) for (let child of children[index].props.children) if (child.type == "strong") {
+ let newName = this.getNewName(users.shift());
+ if (newName) BDFDB.ReactUtils.setChild(child, newName);
+ }
+ }
+ }
+ }
+
+ processMessage (e) {
+ let header = e.instance.props.childrenHeader;
+ if (header && header.props && header.props.message && header.props.message.nick) {
+ let newName = this.getNewName(header.props.message.author);
+ if (newName) header.props.message = new BDFDB.DiscordObjects.Message(Object.assign({}, header.props.message, {nick: newName}));
+ }
+ }
+
+ processMessageContent (e) {
+ if (BDFDB.ArrayUtils.is(e.instance.props.content) && BDFDB.DataUtils.get(this, "settings", "changeInMentions")) for (let ele of e.instance.props.content) {
+ if (BDFDB.ReactUtils.isValidElement(ele) && ele.type && (ele.type.displayName || "").toLowerCase().indexOf("popout") > -1 && typeof ele.props.render == "function") {
+ if (BDFDB.ReactUtils.getValue(ele, "props.children.type.displayName") == "Mention") {
+ let newName = this.getNewName(BDFDB.LibraryModules.UserStore.getUser(ele.props.render().props.userId));
+ if (newName) ele.props.children.props.children[0] = "@" + newName;
+ }
+ }
+ }
+ if (e.instance.props.message.type != BDFDB.DiscordConstants.MessageTypes.DEFAULT && e.instance.props.message.nick && BDFDB.DataUtils.get(this, "settings", "changeInChatWindow")) {
+ let newName = this.getNewName(e.instance.props.message.author);
+ if (newName) {
+ e.instance.props.message = new BDFDB.DiscordObjects.Message(Object.assign({}, e.instance.props.message, {nick: newName}));
+ e.instance.props.children.props.message = e.instance.props.message;
+ }
+ }
+ }
+
+ getNewName (user, wrapper) {
+ if (!user) return null;
+ let settings = BDFDB.DataUtils.get(this, "settings");
+ let member = BDFDB.LibraryModules.MemberStore.getMember(BDFDB.LibraryModules.LastGuildStore.getGuildId(), user.id) || {};
+ if (!member.nick || user.id == BDFDB.UserUtils.me.id && !settings.replaceOwn || user.bot && !settings.replaceBots) return null;
+ let username = (BDFDB.BDUtils.isPluginEnabled("EditUsers") && BDFDB.DataUtils.load("EditUsers", "users", user.id) || {}).name || user.username;
+ return settings.addNickname ? (settings.swapPositions ? (member.nick + " (" + username + ")") : (username + " (" + member.nick + ")")) : username;
+ }
+
+ forceUpdateAll () {
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ BDFDB.MessageUtils.rerenderAll();
+ }
+ }
+})(); \ No newline at end of file
diff --git a/.config/BetterDiscord/plugins/SendLargeMessages.plugin.js b/.config/BetterDiscord/plugins/SendLargeMessages.plugin.js
new file mode 100644
index 0000000..3827ee2
--- /dev/null
+++ b/.config/BetterDiscord/plugins/SendLargeMessages.plugin.js
@@ -0,0 +1,259 @@
+//META{"name":"SendLargeMessages","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/SendLargeMessages","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/SendLargeMessages/SendLargeMessages.plugin.js"}*//
+
+var SendLargeMessages = (_ => {
+ return class SendLargeMessages {
+ getName () {return "SendLargeMessages";}
+
+ getVersion () {return "1.6.4";}
+
+ getAuthor () {return "DevilBro";}
+
+ getDescription () {return "Opens a popout when your message is too large, which allows you to automatically send the message in several smaller messages.";}
+
+ constructor () {
+ this.patchedModules = {
+ before: {
+ ChannelTextAreaForm: "render",
+ ChannelEditorContainer: "render"
+ },
+ after: {
+ ChannelTextAreaContainer: "render",
+ }
+ };
+ }
+
+ initConstructor () {
+ this.messageDelay = 1000; //changing at own risk, might result in bans or mutes
+
+ this.css = `
+ .${this.name}-modal textarea {
+ height: 50vh;
+ }`;
+ }
+
+ // Legacy
+ load () {}
+
+ start () {
+ if (!window.BDFDB) window.BDFDB = {myPlugins:{}};
+ if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this;
+ let libraryScript = document.querySelector("head script#BDFDBLibraryScript");
+ if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) {
+ if (libraryScript) libraryScript.remove();
+ libraryScript = document.createElement("script");
+ libraryScript.setAttribute("id", "BDFDBLibraryScript");
+ libraryScript.setAttribute("type", "text/javascript");
+ libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js");
+ libraryScript.setAttribute("date", performance.now());
+ libraryScript.addEventListener("load", _ => {this.initialize();});
+ document.head.appendChild(libraryScript);
+ }
+ else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize();
+ this.startTimeout = setTimeout(_ => {
+ try {return this.initialize();}
+ catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);}
+ }, 30000);
+ }
+
+ initialize () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ if (this.started) return;
+ BDFDB.PluginUtils.init(this);
+
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+ }
+ else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!");
+ }
+
+
+ stop () {
+ if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) {
+ this.stopping = true;
+
+ BDFDB.ModuleUtils.forceAllUpdates(this);
+
+ BDFDB.PluginUtils.clear(this);
+ }
+ }
+
+
+ // Begin of own functions
+
+ processChannelTextAreaForm (e) {
+ if (!BDFDB.ModuleUtils.isPatched(this, e.instance, "handleSendMessage")) BDFDB.ModuleUtils.patch(this, e.instance, "handleSendMessage", {instead: e2 => {
+ if (e2.methodArguments[0].length > BDFDB.DiscordConstants.MAX_MESSAGE_LENGTH) {
+ e2.stopOriginalMethodCall();
+ let messages = this.formatText(e2.methodArguments[0]);
+ messages.filter(n => n).forEach((message, i) => {
+ BDFDB.TimeUtils.timeout(_ => {
+ e2.originalMethod(message);
+ if (i >= messages.length-1) BDFDB.NotificationUtils.toast(this.labels.toast_allsent_text, {type:"success"});
+ }, this.messageDelay * i);
+ });
+ return Promise.resolve({
+ shouldClear: true,
+ shouldRefocus: true
+ });
+ }
+ else return e2.callOriginalMethodAfterwards();
+ }}, {force: true, noCache: true});
+ }
+
+ processChannelTextAreaContainer (e) {
+ if (e.returnvalue.ref && e.returnvalue.ref.current && BDFDB.DOMUtils.getParent(BDFDB.dotCN.chatform, e.returnvalue.ref.current)) {
+ let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name: "SlateCharacterCount"});
+ if (index > -1) {
+ let text = BDFDB.LibraryModules.SlateSelectionUtils.serialize(children[index].props.document, "raw");
+ if (text.length > BDFDB.DiscordConstants.MAX_MESSAGE_LENGTH) children[index] = BDFDB.ReactUtils.createElement("div", {
+ className: BDFDB.disCNS.textareacharcounter + BDFDB.disCN.textareacharcountererror,
+ children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
+ text: Math.ceil(text.length / BDFDB.DiscordConstants.MAX_MESSAGE_LENGTH * (39/40)) + " " + BDFDB.LanguageUtils.LanguageStrings.MESSAGES,
+ children: BDFDB.ReactUtils.createElement("span", {
+ children: BDFDB.DiscordConstants.MAX_MESSAGE_LENGTH - text.length
+ })
+ })
+ });
+ }
+ }
+ }
+
+ processChannelEditorContainer (e) {
+ if (e.instance.props.type && e.instance.props.type == BDFDB.DiscordConstants.TextareaTypes.NORMAL) e.instance.props.shouldUploadLongMessages = false;
+ }
+
+ formatText (text) {
+ text = text.replace(/\t/g, " ");
+ let longwords = text.match(/[\S]{1800,}/gm);
+ if (longwords) for (let longword of longwords) {
+ let count1 = 0;
+ let shortwords = [];
+ longword.split("").forEach(c => {
+ if (shortwords[count1] && shortwords[count1].length >= BDFDB.DiscordConstants.MAX_MESSAGE_LENGTH * (19/20)) count1++;
+ shortwords[count1] = shortwords[count1] ? shortwords[count1] + c : c;
+ });
+ text = text.replace(longword, shortwords.join(" "));
+ }
+ let messages = [];
+ let count2 = 0;
+ text.split(" ").forEach((word) => {
+ if (messages[count2] && (messages[count2] + "" + word).length > BDFDB.DiscordConstants.MAX_MESSAGE_LENGTH * (39/40)) count2++;
+ messages[count2] = messages[count2] ? messages[count2] + " " + word : word;
+ });
+
+ let insertCodeBlock = null, insertCodeLine = null;
+ for (let j = 0; j < messages.length; j++) {
+ if (insertCodeBlock) {
+ messages[j] = insertCodeBlock + messages[j];
+ insertCodeBlock = null;
+ }
+ else if (insertCodeLine) {
+ messages[j] = insertCodeLine + messages[j];
+ insertCodeLine = null;
+ }
+
+ let codeBlocks = messages[j].match(/`{3,}[\S]*\n|`{3,}/gm);
+ let codeLines = messages[j].match(/[^`]{0,1}`{1,2}[^`]|[^`]`{1,2}[^`]{0,1}/gm);
+
+ if (codeBlocks && codeBlocks.length % 2 == 1) {
+ messages[j] = messages[j] + "```";
+ insertCodeBlock = codeBlocks[codeBlocks.length-1] + "\n";
+ }
+ else if (codeLines && codeLines.length % 2 == 1) {
+ insertCodeLine = codeLines[codeLines.length-1].replace(/[^`]/g, "");
+ messages[j] = messages[j] + insertCodeLine;
+ }
+ }
+
+ return messages;
+ }
+
+ setLabelsByLanguage () {
+ switch (BDFDB.LanguageUtils.getLanguage().id) {
+ case "hr": //croatian
+ return {
+ toast_allsent_text: "Sve veliku poslane."
+ };
+ case "da": //danish
+ return {
+ toast_allsent_text: "Alle beskeder sendes."
+ };
+ case "de": //german
+ return {
+ toast_allsent_text: "Alle Nachrichten versendet."
+ };
+ case "es": //spanish
+ return {
+ toast_allsent_text: "Todos los mensajes enviados."
+ };
+ case "fr": //french
+ return {
+ toast_allsent_text: "Tous les messages envoyés"
+ };
+ case "it": //italian
+ return {
+ toast_allsent_text: "Tutti i messaggi inviati."
+ };
+ case "nl": //dutch
+ return {
+ toast_allsent_text: "Alle berichten verzonden."
+ };
+ case "no": //norwegian
+ return {
+ toast_allsent_text: "Alle meldinger sendt."
+ };
+ case "pl": //polish
+ return {
+ toast_allsent_text: "Wszystkie wiadomości zostały wysłane."
+ };
+ case "pt-BR": //portuguese (brazil)
+ return {
+ toast_allsent_text: "Todas as mensagens enviadas."
+ };
+ case "fi": //finnish
+ return {
+ toast_allsent_text: "Kaikki lähetetyt viestit."
+ };
+ case "sv": //swedish
+ return {
+ toast_allsent_text: "Alla meddelanden skickade."
+ };
+ case "tr": //turkish
+ return {
+ toast_allsent_text: "Tüm mesajlar gönderildi."
+ };
+ case "cs": //czech
+ return {
+ toast_allsent_text: "Všechny zprávy byly odeslány."
+ };
+ case "bg": //bulgarian
+ return {
+ toast_allsent_text: "Всички изпратени съобщения."
+ };
+ case "ru": //russian
+ return {
+ toast_allsent_text: "Все отправленные сообщения."
+ };
+ case "uk": //ukrainian
+ return {
+ toast_allsent_text: "Всі повідомлення надіслано."
+ };
+ case "ja": //japanese
+ return {
+ toast_allsent_text: "すべてのメッセージが送信されました。"
+ };
+ case "zh-TW": //chinese (traditional)
+ return {
+ toast_allsent_text: "發送的所有消息。"
+ };
+ case "ko": //korean
+ return {
+ toast_allsent_text: "모든 메시지가 전송되었습니다."
+ };
+ default: //default: english
+ return {
+ toast_allsent_text: "All messages sent."
+ };
+ }
+ }
+ }
+})(); \ No newline at end of file
diff --git a/.config/BetterDiscord/themes/Material-Discord.theme.css b/.config/BetterDiscord/themes/Material-Discord.theme.css
new file mode 100644
index 0000000..c3f0ced
--- /dev/null
+++ b/.config/BetterDiscord/themes/Material-Discord.theme.css
@@ -0,0 +1,3 @@
+//META{"name":"Material_Discord","description":"A theme based on Google's Material Design","author":"CapnKitten","version":"2.1.4.1"}*//{}
+
+@import url(https://capnkitten.github.io/BetterDiscord/Material-Discord/css/source.css?v=2.1.4.1);
diff --git a/.config/BetterDiscord/themes/MinimalCord.theme.css b/.config/BetterDiscord/themes/MinimalCord.theme.css
new file mode 100644
index 0000000..87e66f9
--- /dev/null
+++ b/.config/BetterDiscord/themes/MinimalCord.theme.css
@@ -0,0 +1,138 @@
+/**
+ * @name MinimalCord
+ * @author Gibbu#1211
+ * @version 1.0.0
+ * @description Changes Discord enough to give it a fresh feel while also making it darker. Supports both Light and Dark themes.
+ * @source https://github.com/Gibbu/BetterDiscord-Themes/tree/master/MinimalCord
+ * @website https://www.gibbu.me
+*/
+
+@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900');
+@import url("https://gibbu.github.io/BetterDiscord-Themes/MinimalCord/base.css");
+
+/* Black Box user tags */
+@import url('https://monstrousdev.github.io/themes/addons/user-tags.css');
+
+:root {
+ /* --accent: 233, 86, 120; DEFAULT: 22, 188, 249 */
+ --chat-avatar-size: 48px; /* Changes size of chat avatars | DEFAULT: 40px */
+ --search-results-width: 25vw; /* Width of server results. For a fixed width use px. | DEFAULT: 25vw */
+ --emoji-picker-height: 260px; /* Height of Emoji picker | DEFAULT: 260px */
+ --font: 'JetBrainsMono NF';
+}
+
+.theme-dark {
+ --foreground-1: var(--pywal-bg0) !important;
+ --foreground-2: var(--pywal-bg1) !important;
+ --foreground-divider: var(--pywal-color15) !important;
+ --background-primary: var(--pywal-bg3) !important;
+ --background-secondary: var(--pywal-bg2) !important;
+ --background-tertiary: var(--pywal-bg0) !important;
+ --background-floating: var(--pywal-bg0) !important;
+ --background-1: var(--pywal-bg0) !important;
+ --background-2: var(--pywal-serverside) !important;
+ --box-shadow: 0 3px 30px -5px #0008 !important;
+ --scrollbar: var(--pywal-bg1) !important;
+ --tooltips: var(--pywal-bg2) !important;
+ --pages-box: var(--message-box) !important;
+ --pages-box-footer: var(--pywal-bg0) !important;
+ --pages-separator: #ffffff0d !important;
+ --user-popout: var(--dropdown) !important;
+ --user-popout-body: var(--pywal-bg0) !important;
+ --user-popout-message: var(--pywal-color0) !important;
+ --settings-box: var(--message-box) !important;
+ --settings-box-hover: var(--pywal-color0) !important;
+ --settings-box-footer: var(--pywal-color0) !important;
+ --settings-input: var(--pywal-color0) !important;
+ --settings-paginator: #00000033 !important;
+ --settings-separator: #ffffff0d !important;
+ --dropdown: var(--pywal-bg1) !important;
+ --dropdown-header: var(--pywal-bg3) !important;
+ --dropdown-item-hover: var(--pywal-bg1) !important;
+ --message-box: #0000 !important;
+ --message-box-divider: var(--pywal-bg2) !important;
+ --message-embed: var(--pywal-bg2) !important;
+ --message-button: var(--pywal-bg3) !important;
+ --server-background: var(--pywal-serverside) !important;
+ --server-selected: var(--pywal-serverside) !important;
+ --search-filter-hover: var(--pywal-bg1) !important;
+ --search-result-header: var(--pywal-bg1) !important;
+ --channel-selected: var(--pywal-bg2) !important;
+ --channel-selected-bg: var(--channel-selected) !important;
+ --channel-hovered: var(--pywal-bg1) !important;
+ --channel-hovered-bg: var(--pywal-bg1) !important;
+ --modal-connection: var(--background-1) !important;
+ --modal-list: var(--pywal-color0) !important;
+ --modal-badge-invert: invert(0) !important;
+ --modal-bg1-search: var(--search-filter-hover) !important;
+ --text-1: #fff !important;
+ --text-2: #c9c9c9 !important;
+ --text-3: #999a9b !important;
+ --text-4: #717173 !important;
+ --text-dark: #565759 !important;
+}
+
+.da-attachWrapper, .da-buttons {
+ width: 0;
+ overflow: hidden;
+}
+
+.da-channelTextArea {
+ margin-top: 0 !important;
+ margin-bottom: 8px !important;
+}
+
+.da-form {
+ margin-left: 0 !important;
+ margin-right: 0 !important;
+}
+
+.da-username {
+ filter: saturate(0.4) brightness(2);
+}
+
+.da-itemCard > div > div {
+ background: var(--background-secondary) !important;
+}
+
+.da-channelTextArea > div {
+ box-shadow: none !important;
+ background: var(--foreground-1) !important;
+}
+
+.da-form::before {
+ background: transparent !important;
+}
+
+.da-nowPlayingColumn {
+ background: var(--background-1) !important;
+}
+
+.da-videoControls {
+ margin: 0px !important;
+}
+
+body {
+ background: var(--pywal-transparent50) !important;
+}
+
+.da-privateChannelsHeaderContainer .da-numberBadge.da-base {
+ background-color: var(--pywal-color6) !important;
+ color: var(--pywal-readableOn-color6) !important;
+}
+
+.da-circleIcon,
+.da-circleIconButton {
+ color: var(--pywal-readable-color6) !important;
+}
+
+section.da-container > .da-children > .da-tabBar > .da-item:not(.da-themed),
+.bd-guild .da-item,
+.da-tutorialContainer .da-pill .da-item {
+ background-color: var(--pywal-readable-color6) !important;
+ color: var(--pywal-readableOn-color6) !important;
+}
+
+.da-actionButton {
+ background-color: transparent !important;
+}
diff --git a/.config/BetterDiscord/themes/glass.theme.css b/.config/BetterDiscord/themes/glass.theme.css
new file mode 100644
index 0000000..a6dfb72
--- /dev/null
+++ b/.config/BetterDiscord/themes/glass.theme.css
@@ -0,0 +1,40 @@
+/**
+ * @name Glass
+ * @author Loekaars#8205
+ * @version 1.0.0
+ * @description Transparency required
+*/
+
+body #app-mount,
+body #app-mount .da-app .da-app,
+body #app-mount .da-app .da-app .da-layers .da-layer,
+.da-scroller,
+.da-bg,
+.da-container > nav {
+ background: transparent !important;
+}
+
+.da-sidebar .da-panels .da-container,
+.da-sidebar .da-panels {
+ background: transparent !important;
+}
+
+:root {
+ --server-selected: transparent !important;
+ --server-folders: var(--pywal-serverfolder-transparent) !important;
+ --server-background: transparent !important;
+}
+
+.da-expandedFolderBackground {
+ border-radius: 16px !important;
+}
+
+body,
+.da-sidebar {
+ background: var(--pywal-serverfolder-transparent) !important;
+}
+
+.da-folder {
+ background: transparent !important;
+}
+
diff --git a/.config/BetterDiscord/themes/pywal.theme.css b/.config/BetterDiscord/themes/pywal.theme.css
new file mode 100644
index 0000000..19b01e4
--- /dev/null
+++ b/.config/BetterDiscord/themes/pywal.theme.css
@@ -0,0 +1,75 @@
+/**
+ * @name pywal
+ * @author Loekaars#8205
+ * @version 1.0.0
+ * @description Cool beans
+*/
+
+:root {
+ --pywal-shade0: #18191C;
+ --pywal-shade1: #1D1E21;
+ --pywal-shade2: #1F2024;
+ --pywal-shade3: #222227;
+ --pywal-shade4: #1F202A;
+
+ --accent: 102, 97, 90;
+
+ --pywal-serverside: #121213;
+ --pywal-serverfolder-transparent: hsla(240, 2.4%, 7.2%, 0.55);
+
+ --pywal-bg0: #141415;
+ --pywal-bg1: #181819;
+ --pywal-bg2: #1C1C1D;
+ --pywal-bg3: #202022;
+
+ --pywal-color0: #141415;
+ --pywal-color1: #292b37;
+ --pywal-color2: #3c2a27;
+ --pywal-color3: #3b3033;
+ --pywal-color4: #3f3330;
+ --pywal-color5: #4b4240;
+ --pywal-color6: #66615a;
+ --pywal-color7: #c4c4c4;
+ --pywal-color8: #4e4e4f;
+ --pywal-color9: #292b37;
+ --pywal-color10: #3c2a27;
+ --pywal-color11: #3b3033;
+ --pywal-color12: #3f3330;
+ --pywal-color13: #4b4240;
+ --pywal-color14: #66615a;
+ --pywal-color15: #c4c4c4;
+
+ --pywal-readable-color0: #6B6B70;
+ --pywal-readable-color1: #585B71;
+ --pywal-readable-color2: #745550;
+ --pywal-readable-color3: #67565A;
+ --pywal-readable-color4: #665450;
+ --pywal-readable-color5: #4B4240;
+ --pywal-readable-color6: #66615A;
+ --pywal-readable-color7: #C4C4C4;
+ --pywal-readable-color8: #4E4E4F;
+ --pywal-readable-color9: #585B71;
+ --pywal-readable-color10: #745550;
+ --pywal-readable-color11: #67565A;
+ --pywal-readable-color12: #665450;
+ --pywal-readable-color13: #4B4240;
+ --pywal-readable-color14: #66615A;
+ --pywal-readable-color15: #C4C4C4;
+
+ --pywal-readableOn-color0: #c4c4c4;
+ --pywal-readableOn-color1: #c4c4c4;
+ --pywal-readableOn-color2: #c4c4c4;
+ --pywal-readableOn-color3: #c4c4c4;
+ --pywal-readableOn-color4: #c4c4c4;
+ --pywal-readableOn-color5: #c4c4c4;
+ --pywal-readableOn-color6: #c4c4c4;
+ --pywal-readableOn-color7: #141415;
+ --pywal-readableOn-color8: #c4c4c4;
+ --pywal-readableOn-color9: #c4c4c4;
+ --pywal-readableOn-color10: #c4c4c4;
+ --pywal-readableOn-color11: #c4c4c4;
+ --pywal-readableOn-color12: #c4c4c4;
+ --pywal-readableOn-color13: #c4c4c4;
+ --pywal-readableOn-color14: #c4c4c4;
+ --pywal-readableOn-color15: #141415;
+} \ No newline at end of file
diff --git a/.config/brave-flags.conf b/.config/brave-flags.conf
new file mode 100644
index 0000000..cb077db
--- /dev/null
+++ b/.config/brave-flags.conf
@@ -0,0 +1 @@
+--force-dark-mode --load-extension=/home/loek/.cache/wal/chromium
diff --git a/.config/chromium-flags.conf b/.config/chromium-flags.conf
new file mode 100644
index 0000000..6fc50e8
--- /dev/null
+++ b/.config/chromium-flags.conf
@@ -0,0 +1 @@
+--force-dark-mode --load-extension="/home/loek/.cache/wal/chromium/"
diff --git a/.config/coc/commands b/.config/coc/commands
new file mode 100644
index 0000000..d173b4d
--- /dev/null
+++ b/.config/coc/commands
@@ -0,0 +1,3 @@
+clangd.install
+snippets.editSnippets
+workspace.showOutput \ No newline at end of file
diff --git a/.config/coc/extensions/package.json b/.config/coc/extensions/package.json
new file mode 100644
index 0000000..432cb8a
--- /dev/null
+++ b/.config/coc/extensions/package.json
@@ -0,0 +1,14 @@
+{
+ "dependencies": {
+ "coc-clangd": ">=0.4.10",
+ "coc-css": ">=1.2.3",
+ "coc-emmet": ">=1.1.4",
+ "coc-html": ">=1.2.4",
+ "coc-json": ">=1.2.6",
+ "coc-neosnippet": ">=1.2.2",
+ "coc-python": ">=1.2.12",
+ "coc-snippets": ">=2.1.28",
+ "coc-tsserver": ">=1.5.2",
+ "coc-vimtex": ">=1.0.3"
+ }
+} \ No newline at end of file
diff --git a/.config/coc/snippets-mru b/.config/coc/snippets-mru
new file mode 100644
index 0000000..b58504a
--- /dev/null
+++ b/.config/coc/snippets-mru
@@ -0,0 +1,9 @@
+newdocument
+newhtml
+for
+try/except/else
+try/except
+\begin
+gerrit
+nopagenumbers
+snippet \ No newline at end of file
diff --git a/.config/coc/ultisnips/css.snippets b/.config/coc/ultisnips/css.snippets
new file mode 100644
index 0000000..fc67c2e
--- /dev/null
+++ b/.config/coc/ultisnips/css.snippets
@@ -0,0 +1,5 @@
+snippet resetFontWeight "reset font-weight for mobile browsers"
+h1, h2, h3, h4, h5, h6 {
+ font-weight: normal !important;
+}
+endsnippet
diff --git a/.config/coc/ultisnips/html.snippets b/.config/coc/ultisnips/html.snippets
new file mode 100644
index 0000000..1730a7a
--- /dev/null
+++ b/.config/coc/ultisnips/html.snippets
@@ -0,0 +1,16 @@
+snippet newhtml "HTML Starting Point"
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <meta http-equiv='X-UA-Compatible' content='IE=edge'>
+ <title>${1:Page Title}</title>
+ <meta name='viewport' content='width=device-width, initial-scale=1'>
+ <link rel='stylesheet' type='text/css' media='screen' href='${2:style.css}'>
+ <script src='${3:script.js}'></script>
+</head>
+<body>
+ ${0}
+</body>
+</html>
+endsnippet
diff --git a/.config/coc/ultisnips/tex.snippets b/.config/coc/ultisnips/tex.snippets
new file mode 100644
index 0000000..056f562
--- /dev/null
+++ b/.config/coc/ultisnips/tex.snippets
@@ -0,0 +1,34 @@
+snippet newdocument "Starting point for a new LaTeX document"
+\\documentclass[12pt, a4paper, hidelinks]{article}
+\\setlength{\\marginparwidth}{2.54cm}
+
+% Packages
+\\usepackage{fullpage}
+
+% Skip
+\\bigskipamount=.7cm
+\\medskipamount=.4cm
+\\parindent=.3cm
+
+% Document
+\\begin{document}
+ ${0}
+\\end{document}
+endsnippet
+
+snippet nonumberedchapters "Removes chapter numbers"
+% Unnumber chapter headings
+\\makeatletter
+\\def\\@seccntformat#1{
+ \\expandafter\\ifx\\csname c@#1\\endcsname\\c@section\\else
+ \\csname the#1\\endcsname\\quad
+ \\fi}
+\\makeatother
+endsnippet
+
+snippet beginend "begin/end snippet"
+\\begin\{${1}\}
+${0}
+\\end\{${1}\}
+endsnippet
+
diff --git a/.config/i3/config b/.config/i3/config
new file mode 100644
index 0000000..7e6afb4
--- /dev/null
+++ b/.config/i3/config
@@ -0,0 +1,165 @@
+# variables
+set $mod Mod4
+set $inner_gaps 10
+set $outer_gaps 0
+
+# set gaps
+gaps inner $inner_gaps
+gaps outer $outer_gaps
+
+# border color
+# pywal-start
+client.focused #66615a #141415 #c4c4c4 #66615a #585653
+client.focused_inactive #5E5E5F #4e4e4f #c4c4c4 #181819 #141415
+client.unfocused #141415 #101011 #9D9D9D #141415 #101011
+client.urgent #141415 #e95678 #c4c4c4 #e95678 #e95678
+client.placeholder #000000 #0c0c0c #c4c4c4 #000000 #0c0c0c
+client.background #c4c4c4
+
+# pywal-end
+
+# font
+font pango:Fira Code 9
+
+# no idea what these do but they were in here by default
+exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
+exec --no-startup-id nm-applet
+set $refresh_i3status killall -SIGUSR1 i3status
+
+# Voulme keybinds
+bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +5% && $refresh_i3status
+bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -5% && $refresh_i3status
+bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle && $refresh_i3status
+
+# window interactions
+floating_modifier $mod
+bindsym $mod+q kill
+
+# Replace window decorations with a border
+for_window [class=".*"] border pixel 1
+
+# Keybinds
+bindsym $mod+Escape exec "dm-tool lock"
+bindsym $mod+space exec "rofi -show drun -show-icons"
+bindsym $mod+Return exec konsole
+bindsym $mod+p exec bwmenu
+bindsym Print exec "flameshot gui"
+
+bindsym XF86AudioPlay exec "playerctl play-pause"
+bindsym XF86AudioNext exec "playerctl next"
+bindsym XF86AudioPrev exec "playerctl previous"
+
+# 60% keyboard media controls
+bindsym Mod1+Mod4+k exec "mpc toggle"
+bindsym Mod1+Mod4+l exec "mpc next"
+bindsym Mod1+Mod4+j exec "mpc prev"
+bindsym Mod1+Mod4+minus exec "pactl set-sink-volume @DEFAULT_SINK@ -5%"
+bindsym Mod1+Mod4+equal exec "pactl set-sink-volume @DEFAULT_SINK@ +5%"
+
+# Gaps
+bindsym $mod+i gaps inner current plus 5
+bindsym $mod+Shift+i gaps inner current minus 5
+bindsym $mod+o gaps outer current plus 5
+bindsym $mod+Shift+o gaps outer current minus 5
+bindsym $mod+n gaps inner current set -1; gaps outer current set 0
+bindsym $mod+d gaps inner current set $inner_gaps; gaps outer current set $outer_gaps;
+
+# Autostart
+exec "polybar main -c ~/.config/polybar/config.ini &"
+exec "node ~/.local/share/bin/pywal/wall.js &"
+exec "exec picom --experimental-backends &"
+exec "node ~/.local/share/bin/pester/index.js &"
+exec "flameshot &"
+
+# change focus
+bindsym $mod+h focus left
+bindsym $mod+j focus down
+bindsym $mod+k focus up
+bindsym $mod+l focus right
+
+# move focused window
+bindsym $mod+Shift+h move left
+bindsym $mod+Shift+j move down
+bindsym $mod+Shift+k move up
+bindsym $mod+Shift+l move right
+
+# resize focused window
+bindsym $mod+bracketright resize grow height 10 px
+bindsym $mod+bracketleft resize shrink height 10 px
+bindsym $mod+period resize grow width 10 px
+bindsym $mod+comma resize shrink width 10 px
+
+# split in horizontal orientation
+bindsym $mod+bar split h
+bindsym $mod+minus split v
+
+# enter fullscreen mode for the focused container
+bindsym $mod+f fullscreen toggle
+
+# toggle tiling / floating
+bindsym $mod+Shift+space floating toggle
+
+# change focus between tiling / floating windows
+bindsym $mod+Alt+space focus mode_toggle
+
+# focus the parent container
+bindsym $mod+Shift+a focus parent
+
+# focus the child container
+#bindsym $mod+d focus child
+
+# disable mouse teleports
+mouse_warping none
+
+# workspaces
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+set $ws5 "5"
+set $ws6 "6"
+set $ws7 "7"
+set $ws8 "8"
+
+# switch to workspace
+bindsym $mod+1 workspace number $ws1
+bindsym $mod+2 workspace number $ws2
+bindsym $mod+3 workspace number $ws3
+bindsym $mod+4 workspace number $ws4
+bindsym $mod+5 workspace number $ws5
+bindsym $mod+6 workspace number $ws6
+bindsym $mod+7 workspace number $ws7
+bindsym $mod+8 workspace number $ws8
+
+# move focused container to workspace
+bindsym $mod+Shift+1 move container to workspace number $ws1
+bindsym $mod+Shift+2 move container to workspace number $ws2
+bindsym $mod+Shift+3 move container to workspace number $ws3
+bindsym $mod+Shift+4 move container to workspace number $ws4
+bindsym $mod+Shift+5 move container to workspace number $ws5
+bindsym $mod+Shift+6 move container to workspace number $ws6
+bindsym $mod+Shift+7 move container to workspace number $ws7
+bindsym $mod+Shift+8 move container to workspace number $ws8
+
+# always floating windows
+for_window [class="Steam"] floating enable
+for_window [class="cinquo"] floating enable
+for_window [title="Farge"] floating enable
+for_window [window_type=notification] floating enable
+
+for_window [class="Unturned.x86_64"] floating disable
+for_window [class="Unturned.x86_64"] fullscreen disable
+
+# reload the configuration file
+bindsym $mod+Shift+c reload
+# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
+bindsym $mod+Shift+r restart
+# exit i3 (logs you out of your X session)
+# bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
+
+# bar {
+# mode hide
+# hidden_state hide
+# modifier none
+# tray_output none
+# }
diff --git a/.config/i3/config.backup b/.config/i3/config.backup
new file mode 100644
index 0000000..4d3a098
--- /dev/null
+++ b/.config/i3/config.backup
@@ -0,0 +1,140 @@
+# variables
+set $mod Mod4
+set $inner_gaps 10
+set $outer_gaps 0
+set $polybar_gap 30
+
+# set gaps
+gaps inner $inner_gaps
+gaps outer $outer_gaps
+
+# border color
+# pywal-start
+client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
+client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
+client.unfocused #333333 #222222 #888888 #292d2e #222222
+client.urgent #2f343a #900000 #ffffff #900000 #900000
+client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
+
+client.background #ffffff
+# pywal-end
+
+# font
+font pango:Fira Code 9
+
+# no idea what these do but they were in here by default
+exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
+exec --no-startup-id nm-applet
+set $refresh_i3status killall -SIGUSR1 i3status
+
+# Voulme keybinds
+bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +5% && $refresh_i3status
+bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -5% && $refresh_i3status
+bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle && $refresh_i3status
+
+# window interactions
+floating_modifier $mod
+bindsym $mod+q kill
+
+# Replace window decorations with a border
+# for_window [class=".*"] border pixel 1
+
+# Keybinds
+bindsym $mod+Escape exec "dm-tool lock"
+bindsym $mod+space exec "rofi -show drun -show-icons"
+bindsym $mod+Return exec konsole
+bindsym Print exec "flameshot gui"
+
+bindsym XF86AudioPlay exec "playerctl play-pause"
+bindsym XF86AudioNext exec "playerctl next"
+bindsym XF86AudioPrev exec "playerctl previous"
+
+# Gaps
+bindsym $mod+i gaps inner current plus 5
+bindsym $mod+Shift+i gaps inner current minus 5
+bindsym $mod+o gaps outer current plus 5
+bindsym $mod+Shift+o gaps outer current minus 5
+bindsym $mod+n gaps inner current set -1; gaps outer current set 0
+bindsym $mod+d gaps inner current set $inner_gaps; gaps outer current set $outer_gaps;
+bindsym $mod+p gaps top current set $polybar_gap
+
+# Autostart
+exec "polybar main -c ~/.config/polybar/config.ini &"
+exec "node ~/scripts/pywal/wall.js"
+exec "exec picom &"
+
+exec "deadd-notification-center &"
+
+exec "node ~/pester/index.js &"
+exec "flameshot &"
+exec "dropbox &"
+# exec "teams &"
+
+# change focus
+bindsym $mod+h focus left
+bindsym $mod+j focus down
+bindsym $mod+k focus up
+bindsym $mod+l focus right
+
+# move focused window
+bindsym $mod+Shift+h move left
+bindsym $mod+Shift+j move down
+bindsym $mod+Shift+k move up
+bindsym $mod+Shift+l move right
+
+# resize focused window
+bindsym $mod+bracketright resize grow height 10 px
+bindsym $mod+bracketleft resize shrink height 10 px
+bindsym $mod+period resize grow width 10 px
+bindsym $mod+comma resize shrink width 10 px
+
+# split in horizontal orientation
+bindsym $mod+bar split h
+bindsym $mod+minus split v
+
+# enter fullscreen mode for the focused container
+bindsym $mod+f fullscreen toggle
+
+# toggle tiling / floating
+bindsym $mod+Shift+space floating toggle
+
+# change focus between tiling / floating windows
+# bindsym $mod+space focus mode_toggle
+
+# focus the parent container
+bindsym $mod+a focus parent
+
+# focus the child container
+#bindsym $mod+d focus child
+
+# workspaces
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+
+# switch to workspace
+bindsym $mod+1 workspace number $ws1
+bindsym $mod+2 workspace number $ws2
+bindsym $mod+3 workspace number $ws3
+bindsym $mod+4 workspace number $ws4
+
+# move focused container to workspace
+bindsym $mod+Shift+1 move container to workspace number $ws1
+bindsym $mod+Shift+2 move container to workspace number $ws2
+bindsym $mod+Shift+3 move container to workspace number $ws3
+bindsym $mod+Shift+4 move container to workspace number $ws4
+
+# reload the configuration file
+bindsym $mod+Shift+c reload
+# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
+bindsym $mod+Shift+r restart
+# exit i3 (logs you out of your X session)
+bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
+
+bar {
+ mode hide
+ hidden_state hide
+ modifier none
+ tray_output none
+}
diff --git a/.config/konsolerc b/.config/konsolerc
new file mode 100644
index 0000000..c42e4b7
--- /dev/null
+++ b/.config/konsolerc
@@ -0,0 +1,25 @@
+[Desktop Entry]
+DefaultProfile=Loekaars.profile
+
+[DownloadDialog Settings]
+Height 1080=400
+Width 1920=700
+
+[Favorite Profiles]
+Favorites=
+
+[KonsoleWindow]
+SaveGeometryOnExit=false
+ShowMenuBarByDefault=false
+
+[MainWindow]
+MenuBar=Disabled
+State=AAAA/wAAAAD9AAAAAAAAB2oAAAQSAAAABAAAAAQAAAAIAAAACPwAAAAA
+ToolBarsMovable=Disabled
+
+[Notification Messages]
+CloseAllTabs=true
+ShowPasteUnprintableWarning=false
+
+[Shortcut Schemes]
+Current Scheme=Default
diff --git a/.config/nvim/autoload/plug.vim b/.config/nvim/autoload/plug.vim
new file mode 100644
index 0000000..25be27f
--- /dev/null
+++ b/.config/nvim/autoload/plug.vim
@@ -0,0 +1,2665 @@
+" vim-plug: Vim plugin manager
+" ============================
+"
+" Download plug.vim and put it in ~/.vim/autoload
+"
+" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
+" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
+"
+" Edit your .vimrc
+"
+" call plug#begin('~/.vim/plugged')
+"
+" " Make sure you use single quotes
+"
+" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align
+" Plug 'junegunn/vim-easy-align'
+"
+" " Any valid git URL is allowed
+" Plug 'https://github.com/junegunn/vim-github-dashboard.git'
+"
+" " Multiple Plug commands can be written in a single line using | separators
+" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
+"
+" " On-demand loading
+" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
+" Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
+"
+" " Using a non-master branch
+" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
+"
+" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above)
+" Plug 'fatih/vim-go', { 'tag': '*' }
+"
+" " Plugin options
+" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }
+"
+" " Plugin outside ~/.vim/plugged with post-update hook
+" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
+"
+" " Unmanaged plugin (manually installed and updated)
+" Plug '~/my-prototype-plugin'
+"
+" " Initialize plugin system
+" call plug#end()
+"
+" Then reload .vimrc and :PlugInstall to install plugins.
+"
+" Plug options:
+"
+"| Option | Description |
+"| ----------------------- | ------------------------------------------------ |
+"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use |
+"| `rtp` | Subdirectory that contains Vim plugin |
+"| `dir` | Custom directory for the plugin |
+"| `as` | Use different name for the plugin |
+"| `do` | Post-update hook (string or funcref) |
+"| `on` | On-demand loading: Commands or `<Plug>`-mappings |
+"| `for` | On-demand loading: File types |
+"| `frozen` | Do not update unless explicitly specified |
+"
+" More information: https://github.com/junegunn/vim-plug
+"
+"
+" Copyright (c) 2017 Junegunn Choi
+"
+" MIT License
+"
+" 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.
+
+if exists('g:loaded_plug')
+ finish
+endif
+let g:loaded_plug = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+let s:plug_src = 'https://github.com/junegunn/vim-plug.git'
+let s:plug_tab = get(s:, 'plug_tab', -1)
+let s:plug_buf = get(s:, 'plug_buf', -1)
+let s:mac_gui = has('gui_macvim') && has('gui_running')
+let s:is_win = has('win32')
+let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win)
+let s:vim8 = has('patch-8.0.0039') && exists('*job_start')
+if s:is_win && &shellslash
+ set noshellslash
+ let s:me = resolve(expand('<sfile>:p'))
+ set shellslash
+else
+ let s:me = resolve(expand('<sfile>:p'))
+endif
+let s:base_spec = { 'branch': 'master', 'frozen': 0 }
+let s:TYPE = {
+\ 'string': type(''),
+\ 'list': type([]),
+\ 'dict': type({}),
+\ 'funcref': type(function('call'))
+\ }
+let s:loaded = get(s:, 'loaded', {})
+let s:triggers = get(s:, 'triggers', {})
+
+if s:is_win
+ function! s:plug_call(fn, ...)
+ let shellslash = &shellslash
+ try
+ set noshellslash
+ return call(a:fn, a:000)
+ finally
+ let &shellslash = shellslash
+ endtry
+ endfunction
+else
+ function! s:plug_call(fn, ...)
+ return call(a:fn, a:000)
+ endfunction
+endif
+
+function! s:plug_getcwd()
+ return s:plug_call('getcwd')
+endfunction
+
+function! s:plug_fnamemodify(fname, mods)
+ return s:plug_call('fnamemodify', a:fname, a:mods)
+endfunction
+
+function! s:plug_expand(fmt)
+ return s:plug_call('expand', a:fmt, 1)
+endfunction
+
+function! s:plug_tempname()
+ return s:plug_call('tempname')
+endfunction
+
+function! plug#begin(...)
+ if a:0 > 0
+ let s:plug_home_org = a:1
+ let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p'))
+ elseif exists('g:plug_home')
+ let home = s:path(g:plug_home)
+ elseif !empty(&rtp)
+ let home = s:path(split(&rtp, ',')[0]) . '/plugged'
+ else
+ return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
+ endif
+ if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp
+ return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.')
+ endif
+
+ let g:plug_home = home
+ let g:plugs = {}
+ let g:plugs_order = []
+ let s:triggers = {}
+
+ call s:define_commands()
+ return 1
+endfunction
+
+function! s:define_commands()
+ command! -nargs=+ -bar Plug call plug#(<args>)
+ if !executable('git')
+ return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.')
+ endif
+ if has('win32')
+ \ && &shellslash
+ \ && (&shell =~# 'cmd\.exe' || &shell =~# 'powershell\.exe')
+ return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.')
+ endif
+ if !has('nvim')
+ \ && (has('win32') || has('win32unix'))
+ \ && !has('multi_byte')
+ return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.')
+ endif
+ command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(<bang>0, [<f-args>])
+ command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(<bang>0, [<f-args>])
+ command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0)
+ command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif
+ command! -nargs=0 -bar PlugStatus call s:status()
+ command! -nargs=0 -bar PlugDiff call s:diff()
+ command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(<bang>0, <f-args>)
+endfunction
+
+function! s:to_a(v)
+ return type(a:v) == s:TYPE.list ? a:v : [a:v]
+endfunction
+
+function! s:to_s(v)
+ return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n"
+endfunction
+
+function! s:glob(from, pattern)
+ return s:lines(globpath(a:from, a:pattern))
+endfunction
+
+function! s:source(from, ...)
+ let found = 0
+ for pattern in a:000
+ for vim in s:glob(a:from, pattern)
+ execute 'source' s:esc(vim)
+ let found = 1
+ endfor
+ endfor
+ return found
+endfunction
+
+function! s:assoc(dict, key, val)
+ let a:dict[a:key] = add(get(a:dict, a:key, []), a:val)
+endfunction
+
+function! s:ask(message, ...)
+ call inputsave()
+ echohl WarningMsg
+ let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) '))
+ echohl None
+ call inputrestore()
+ echo "\r"
+ return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0
+endfunction
+
+function! s:ask_no_interrupt(...)
+ try
+ return call('s:ask', a:000)
+ catch
+ return 0
+ endtry
+endfunction
+
+function! s:lazy(plug, opt)
+ return has_key(a:plug, a:opt) &&
+ \ (empty(s:to_a(a:plug[a:opt])) ||
+ \ !isdirectory(a:plug.dir) ||
+ \ len(s:glob(s:rtp(a:plug), 'plugin')) ||
+ \ len(s:glob(s:rtp(a:plug), 'after/plugin')))
+endfunction
+
+function! plug#end()
+ if !exists('g:plugs')
+ return s:err('plug#end() called without calling plug#begin() first')
+ endif
+
+ if exists('#PlugLOD')
+ augroup PlugLOD
+ autocmd!
+ augroup END
+ augroup! PlugLOD
+ endif
+ let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
+
+ if exists('g:did_load_filetypes')
+ filetype off
+ endif
+ for name in g:plugs_order
+ if !has_key(g:plugs, name)
+ continue
+ endif
+ let plug = g:plugs[name]
+ if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for')
+ let s:loaded[name] = 1
+ continue
+ endif
+
+ if has_key(plug, 'on')
+ let s:triggers[name] = { 'map': [], 'cmd': [] }
+ for cmd in s:to_a(plug.on)
+ if cmd =~? '^<Plug>.\+'
+ if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
+ call s:assoc(lod.map, cmd, name)
+ endif
+ call add(s:triggers[name].map, cmd)
+ elseif cmd =~# '^[A-Z]'
+ let cmd = substitute(cmd, '!*$', '', '')
+ if exists(':'.cmd) != 2
+ call s:assoc(lod.cmd, cmd, name)
+ endif
+ call add(s:triggers[name].cmd, cmd)
+ else
+ call s:err('Invalid `on` option: '.cmd.
+ \ '. Should start with an uppercase letter or `<Plug>`.')
+ endif
+ endfor
+ endif
+
+ if has_key(plug, 'for')
+ let types = s:to_a(plug.for)
+ if !empty(types)
+ augroup filetypedetect
+ call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
+ augroup END
+ endif
+ for type in types
+ call s:assoc(lod.ft, type, name)
+ endfor
+ endif
+ endfor
+
+ for [cmd, names] in items(lod.cmd)
+ execute printf(
+ \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)',
+ \ cmd, string(cmd), string(names))
+ endfor
+
+ for [map, names] in items(lod.map)
+ for [mode, map_prefix, key_prefix] in
+ \ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
+ execute printf(
+ \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>',
+ \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
+ endfor
+ endfor
+
+ for [ft, names] in items(lod.ft)
+ augroup PlugLOD
+ execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
+ \ ft, string(ft), string(names))
+ augroup END
+ endfor
+
+ call s:reorg_rtp()
+ filetype plugin indent on
+ if has('vim_starting')
+ if has('syntax') && !exists('g:syntax_on')
+ syntax enable
+ end
+ else
+ call s:reload_plugins()
+ endif
+endfunction
+
+function! s:loaded_names()
+ return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)')
+endfunction
+
+function! s:load_plugin(spec)
+ call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim')
+endfunction
+
+function! s:reload_plugins()
+ for name in s:loaded_names()
+ call s:load_plugin(g:plugs[name])
+ endfor
+endfunction
+
+function! s:trim(str)
+ return substitute(a:str, '[\/]\+$', '', '')
+endfunction
+
+function! s:version_requirement(val, min)
+ for idx in range(0, len(a:min) - 1)
+ let v = get(a:val, idx, 0)
+ if v < a:min[idx] | return 0
+ elseif v > a:min[idx] | return 1
+ endif
+ endfor
+ return 1
+endfunction
+
+function! s:git_version_requirement(...)
+ if !exists('s:git_version')
+ let s:git_version = map(split(split(s:system(['git', '--version']))[2], '\.'), 'str2nr(v:val)')
+ endif
+ return s:version_requirement(s:git_version, a:000)
+endfunction
+
+function! s:progress_opt(base)
+ return a:base && !s:is_win &&
+ \ s:git_version_requirement(1, 7, 1) ? '--progress' : ''
+endfunction
+
+function! s:rtp(spec)
+ return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
+endfunction
+
+if s:is_win
+ function! s:path(path)
+ return s:trim(substitute(a:path, '/', '\', 'g'))
+ endfunction
+
+ function! s:dirpath(path)
+ return s:path(a:path) . '\'
+ endfunction
+
+ function! s:is_local_plug(repo)
+ return a:repo =~? '^[a-z]:\|^[%~]'
+ endfunction
+
+ " Copied from fzf
+ function! s:wrap_cmds(cmds)
+ let cmds = [
+ \ '@echo off',
+ \ 'setlocal enabledelayedexpansion']
+ \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])
+ \ + ['endlocal']
+ if has('iconv')
+ if !exists('s:codepage')
+ let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)
+ endif
+ return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage))
+ endif
+ return map(cmds, 'v:val."\r"')
+ endfunction
+
+ function! s:batchfile(cmd)
+ let batchfile = s:plug_tempname().'.bat'
+ call writefile(s:wrap_cmds(a:cmd), batchfile)
+ let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0})
+ if &shell =~# 'powershell\.exe'
+ let cmd = '& ' . cmd
+ endif
+ return [batchfile, cmd]
+ endfunction
+else
+ function! s:path(path)
+ return s:trim(a:path)
+ endfunction
+
+ function! s:dirpath(path)
+ return substitute(a:path, '[/\\]*$', '/', '')
+ endfunction
+
+ function! s:is_local_plug(repo)
+ return a:repo[0] =~ '[/$~]'
+ endfunction
+endif
+
+function! s:err(msg)
+ echohl ErrorMsg
+ echom '[vim-plug] '.a:msg
+ echohl None
+endfunction
+
+function! s:warn(cmd, msg)
+ echohl WarningMsg
+ execute a:cmd 'a:msg'
+ echohl None
+endfunction
+
+function! s:esc(path)
+ return escape(a:path, ' ')
+endfunction
+
+function! s:escrtp(path)
+ return escape(a:path, ' ,')
+endfunction
+
+function! s:remove_rtp()
+ for name in s:loaded_names()
+ let rtp = s:rtp(g:plugs[name])
+ execute 'set rtp-='.s:escrtp(rtp)
+ let after = globpath(rtp, 'after')
+ if isdirectory(after)
+ execute 'set rtp-='.s:escrtp(after)
+ endif
+ endfor
+endfunction
+
+function! s:reorg_rtp()
+ if !empty(s:first_rtp)
+ execute 'set rtp-='.s:first_rtp
+ execute 'set rtp-='.s:last_rtp
+ endif
+
+ " &rtp is modified from outside
+ if exists('s:prtp') && s:prtp !=# &rtp
+ call s:remove_rtp()
+ unlet! s:middle
+ endif
+
+ let s:middle = get(s:, 'middle', &rtp)
+ let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])')
+ let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)')
+ let rtp = join(map(rtps, 'escape(v:val, ",")'), ',')
+ \ . ','.s:middle.','
+ \ . join(map(afters, 'escape(v:val, ",")'), ',')
+ let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g')
+ let s:prtp = &rtp
+
+ if !empty(s:first_rtp)
+ execute 'set rtp^='.s:first_rtp
+ execute 'set rtp+='.s:last_rtp
+ endif
+endfunction
+
+function! s:doautocmd(...)
+ if exists('#'.join(a:000, '#'))
+ execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '<nomodeline>' : '') join(a:000)
+ endif
+endfunction
+
+function! s:dobufread(names)
+ for name in a:names
+ let path = s:rtp(g:plugs[name])
+ for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin']
+ if len(finddir(dir, path))
+ if exists('#BufRead')
+ doautocmd BufRead
+ endif
+ return
+ endif
+ endfor
+ endfor
+endfunction
+
+function! plug#load(...)
+ if a:0 == 0
+ return s:err('Argument missing: plugin name(s) required')
+ endif
+ if !exists('g:plugs')
+ return s:err('plug#begin was not called')
+ endif
+ let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000
+ let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)')
+ if !empty(unknowns)
+ let s = len(unknowns) > 1 ? 's' : ''
+ return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
+ end
+ let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)')
+ if !empty(unloaded)
+ for name in unloaded
+ call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ endfor
+ call s:dobufread(unloaded)
+ return 1
+ end
+ return 0
+endfunction
+
+function! s:remove_triggers(name)
+ if !has_key(s:triggers, a:name)
+ return
+ endif
+ for cmd in s:triggers[a:name].cmd
+ execute 'silent! delc' cmd
+ endfor
+ for map in s:triggers[a:name].map
+ execute 'silent! unmap' map
+ execute 'silent! iunmap' map
+ endfor
+ call remove(s:triggers, a:name)
+endfunction
+
+function! s:lod(names, types, ...)
+ for name in a:names
+ call s:remove_triggers(name)
+ let s:loaded[name] = 1
+ endfor
+ call s:reorg_rtp()
+
+ for name in a:names
+ let rtp = s:rtp(g:plugs[name])
+ for dir in a:types
+ call s:source(rtp, dir.'/**/*.vim')
+ endfor
+ if a:0
+ if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2))
+ execute 'runtime' a:1
+ endif
+ call s:source(rtp, a:2)
+ endif
+ call s:doautocmd('User', name)
+ endfor
+endfunction
+
+function! s:lod_ft(pat, names)
+ let syn = 'syntax/'.a:pat.'.vim'
+ call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn)
+ execute 'autocmd! PlugLOD FileType' a:pat
+ call s:doautocmd('filetypeplugin', 'FileType')
+ call s:doautocmd('filetypeindent', 'FileType')
+endfunction
+
+function! s:lod_cmd(cmd, bang, l1, l2, args, names)
+ call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ call s:dobufread(a:names)
+ execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
+endfunction
+
+function! s:lod_map(map, names, with_prefix, prefix)
+ call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ call s:dobufread(a:names)
+ let extra = ''
+ while 1
+ let c = getchar(0)
+ if c == 0
+ break
+ endif
+ let extra .= nr2char(c)
+ endwhile
+
+ if a:with_prefix
+ let prefix = v:count ? v:count : ''
+ let prefix .= '"'.v:register.a:prefix
+ if mode(1) == 'no'
+ if v:operator == 'c'
+ let prefix = "\<esc>" . prefix
+ endif
+ let prefix .= v:operator
+ endif
+ call feedkeys(prefix, 'n')
+ endif
+ call feedkeys(substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
+endfunction
+
+function! plug#(repo, ...)
+ if a:0 > 1
+ return s:err('Invalid number of arguments (1..2)')
+ endif
+
+ try
+ let repo = s:trim(a:repo)
+ let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec
+ let name = get(opts, 'as', s:plug_fnamemodify(repo, ':t:s?\.git$??'))
+ let spec = extend(s:infer_properties(name, repo), opts)
+ if !has_key(g:plugs, name)
+ call add(g:plugs_order, name)
+ endif
+ let g:plugs[name] = spec
+ let s:loaded[name] = get(s:loaded, name, 0)
+ catch
+ return s:err(v:exception)
+ endtry
+endfunction
+
+function! s:parse_options(arg)
+ let opts = copy(s:base_spec)
+ let type = type(a:arg)
+ if type == s:TYPE.string
+ let opts.tag = a:arg
+ elseif type == s:TYPE.dict
+ call extend(opts, a:arg)
+ if has_key(opts, 'dir')
+ let opts.dir = s:dirpath(s:plug_expand(opts.dir))
+ endif
+ else
+ throw 'Invalid argument type (expected: string or dictionary)'
+ endif
+ return opts
+endfunction
+
+function! s:infer_properties(name, repo)
+ let repo = a:repo
+ if s:is_local_plug(repo)
+ return { 'dir': s:dirpath(s:plug_expand(repo)) }
+ else
+ if repo =~ ':'
+ let uri = repo
+ else
+ if repo !~ '/'
+ throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo)
+ endif
+ let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git')
+ let uri = printf(fmt, repo)
+ endif
+ return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri }
+ endif
+endfunction
+
+function! s:install(force, names)
+ call s:update_impl(0, a:force, a:names)
+endfunction
+
+function! s:update(force, names)
+ call s:update_impl(1, a:force, a:names)
+endfunction
+
+function! plug#helptags()
+ if !exists('g:plugs')
+ return s:err('plug#begin was not called')
+ endif
+ for spec in values(g:plugs)
+ let docd = join([s:rtp(spec), 'doc'], '/')
+ if isdirectory(docd)
+ silent! execute 'helptags' s:esc(docd)
+ endif
+ endfor
+ return 1
+endfunction
+
+function! s:syntax()
+ syntax clear
+ syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
+ syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
+ syn match plugNumber /[0-9]\+[0-9.]*/ contained
+ syn match plugBracket /[[\]]/ contained
+ syn match plugX /x/ contained
+ syn match plugDash /^-/
+ syn match plugPlus /^+/
+ syn match plugStar /^*/
+ syn match plugMessage /\(^- \)\@<=.*/
+ syn match plugName /\(^- \)\@<=[^ ]*:/
+ syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/
+ syn match plugTag /(tag: [^)]\+)/
+ syn match plugInstall /\(^+ \)\@<=[^:]*/
+ syn match plugUpdate /\(^* \)\@<=[^:]*/
+ syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag
+ syn match plugEdge /^ \X\+$/
+ syn match plugEdge /^ \X*/ contained nextgroup=plugSha
+ syn match plugSha /[0-9a-f]\{7,9}/ contained
+ syn match plugRelDate /([^)]*)$/ contained
+ syn match plugNotLoaded /(not loaded)$/
+ syn match plugError /^x.*/
+ syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
+ syn match plugH2 /^.*:\n-\+$/
+ syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
+ hi def link plug1 Title
+ hi def link plug2 Repeat
+ hi def link plugH2 Type
+ hi def link plugX Exception
+ hi def link plugBracket Structure
+ hi def link plugNumber Number
+
+ hi def link plugDash Special
+ hi def link plugPlus Constant
+ hi def link plugStar Boolean
+
+ hi def link plugMessage Function
+ hi def link plugName Label
+ hi def link plugInstall Function
+ hi def link plugUpdate Type
+
+ hi def link plugError Error
+ hi def link plugDeleted Ignore
+ hi def link plugRelDate Comment
+ hi def link plugEdge PreProc
+ hi def link plugSha Identifier
+ hi def link plugTag Constant
+
+ hi def link plugNotLoaded Comment
+endfunction
+
+function! s:lpad(str, len)
+ return a:str . repeat(' ', a:len - len(a:str))
+endfunction
+
+function! s:lines(msg)
+ return split(a:msg, "[\r\n]")
+endfunction
+
+function! s:lastline(msg)
+ return get(s:lines(a:msg), -1, '')
+endfunction
+
+function! s:new_window()
+ execute get(g:, 'plug_window', 'vertical topleft new')
+endfunction
+
+function! s:plug_window_exists()
+ let buflist = tabpagebuflist(s:plug_tab)
+ return !empty(buflist) && index(buflist, s:plug_buf) >= 0
+endfunction
+
+function! s:switch_in()
+ if !s:plug_window_exists()
+ return 0
+ endif
+
+ if winbufnr(0) != s:plug_buf
+ let s:pos = [tabpagenr(), winnr(), winsaveview()]
+ execute 'normal!' s:plug_tab.'gt'
+ let winnr = bufwinnr(s:plug_buf)
+ execute winnr.'wincmd w'
+ call add(s:pos, winsaveview())
+ else
+ let s:pos = [winsaveview()]
+ endif
+
+ setlocal modifiable
+ return 1
+endfunction
+
+function! s:switch_out(...)
+ call winrestview(s:pos[-1])
+ setlocal nomodifiable
+ if a:0 > 0
+ execute a:1
+ endif
+
+ if len(s:pos) > 1
+ execute 'normal!' s:pos[0].'gt'
+ execute s:pos[1] 'wincmd w'
+ call winrestview(s:pos[2])
+ endif
+endfunction
+
+function! s:finish_bindings()
+ nnoremap <silent> <buffer> R :call <SID>retry()<cr>
+ nnoremap <silent> <buffer> D :PlugDiff<cr>
+ nnoremap <silent> <buffer> S :PlugStatus<cr>
+ nnoremap <silent> <buffer> U :call <SID>status_update()<cr>
+ xnoremap <silent> <buffer> U :call <SID>status_update()<cr>
+ nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
+ nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
+endfunction
+
+function! s:prepare(...)
+ if empty(s:plug_getcwd())
+ throw 'Invalid current working directory. Cannot proceed.'
+ endif
+
+ for evar in ['$GIT_DIR', '$GIT_WORK_TREE']
+ if exists(evar)
+ throw evar.' detected. Cannot proceed.'
+ endif
+ endfor
+
+ call s:job_abort()
+ if s:switch_in()
+ if b:plug_preview == 1
+ pc
+ endif
+ enew
+ else
+ call s:new_window()
+ endif
+
+ nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr>
+ if a:0 == 0
+ call s:finish_bindings()
+ endif
+ let b:plug_preview = -1
+ let s:plug_tab = tabpagenr()
+ let s:plug_buf = winbufnr(0)
+ call s:assign_name()
+
+ for k in ['<cr>', 'L', 'o', 'X', 'd', 'dd']
+ execute 'silent! unmap <buffer>' k
+ endfor
+ setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell
+ if exists('+colorcolumn')
+ setlocal colorcolumn=
+ endif
+ setf vim-plug
+ if exists('g:syntax_on')
+ call s:syntax()
+ endif
+endfunction
+
+function! s:assign_name()
+ " Assign buffer name
+ let prefix = '[Plugins]'
+ let name = prefix
+ let idx = 2
+ while bufexists(name)
+ let name = printf('%s (%s)', prefix, idx)
+ let idx = idx + 1
+ endwhile
+ silent! execute 'f' fnameescape(name)
+endfunction
+
+function! s:chsh(swap)
+ let prev = [&shell, &shellcmdflag, &shellredir]
+ if !s:is_win
+ set shell=sh
+ endif
+ if a:swap
+ if &shell =~# 'powershell\.exe' || &shell =~# 'pwsh$'
+ let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s'
+ elseif &shell =~# 'sh' || &shell =~# 'cmd\.exe'
+ set shellredir=>%s\ 2>&1
+ endif
+ endif
+ return prev
+endfunction
+
+function! s:bang(cmd, ...)
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(a:0)
+ " FIXME: Escaping is incomplete. We could use shellescape with eval,
+ " but it won't work on Windows.
+ let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd
+ if s:is_win
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%')
+ execute "normal! :execute g:_plug_bang\<cr>\<cr>"
+ finally
+ unlet g:_plug_bang
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ call delete(batchfile)
+ endif
+ endtry
+ return v:shell_error ? 'Exit status: ' . v:shell_error : ''
+endfunction
+
+function! s:regress_bar()
+ let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '')
+ call s:progress_bar(2, bar, len(bar))
+endfunction
+
+function! s:is_updated(dir)
+ return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir))
+endfunction
+
+function! s:do(pull, force, todo)
+ for [name, spec] in items(a:todo)
+ if !isdirectory(spec.dir)
+ continue
+ endif
+ let installed = has_key(s:update.new, name)
+ let updated = installed ? 0 :
+ \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir))
+ if a:force || installed || updated
+ execute 'cd' s:esc(spec.dir)
+ call append(3, '- Post-update hook for '. name .' ... ')
+ let error = ''
+ let type = type(spec.do)
+ if type == s:TYPE.string
+ if spec.do[0] == ':'
+ if !get(s:loaded, name, 0)
+ let s:loaded[name] = 1
+ call s:reorg_rtp()
+ endif
+ call s:load_plugin(spec)
+ try
+ execute spec.do[1:]
+ catch
+ let error = v:exception
+ endtry
+ if !s:plug_window_exists()
+ cd -
+ throw 'Warning: vim-plug was terminated by the post-update hook of '.name
+ endif
+ else
+ let error = s:bang(spec.do)
+ endif
+ elseif type == s:TYPE.funcref
+ try
+ call s:load_plugin(spec)
+ let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
+ call spec.do({ 'name': name, 'status': status, 'force': a:force })
+ catch
+ let error = v:exception
+ endtry
+ else
+ let error = 'Invalid hook type'
+ endif
+ call s:switch_in()
+ call setline(4, empty(error) ? (getline(4) . 'OK')
+ \ : ('x' . getline(4)[1:] . error))
+ if !empty(error)
+ call add(s:update.errors, name)
+ call s:regress_bar()
+ endif
+ cd -
+ endif
+ endfor
+endfunction
+
+function! s:hash_match(a, b)
+ return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
+endfunction
+
+function! s:checkout(spec)
+ let sha = a:spec.commit
+ let output = s:system(['git', 'rev-parse', 'HEAD'], a:spec.dir)
+ if !v:shell_error && !s:hash_match(sha, s:lines(output)[0])
+ let output = s:system(
+ \ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
+ endif
+ return output
+endfunction
+
+function! s:finish(pull)
+ let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen'))
+ if new_frozen
+ let s = new_frozen > 1 ? 's' : ''
+ call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s))
+ endif
+ call append(3, '- Finishing ... ') | 4
+ redraw
+ call plug#helptags()
+ call plug#end()
+ call setline(4, getline(4) . 'Done!')
+ redraw
+ let msgs = []
+ if !empty(s:update.errors)
+ call add(msgs, "Press 'R' to retry.")
+ endif
+ if a:pull && len(s:update.new) < len(filter(getline(5, '$'),
+ \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'"))
+ call add(msgs, "Press 'D' to see the updated changes.")
+ endif
+ echo join(msgs, ' ')
+ call s:finish_bindings()
+endfunction
+
+function! s:retry()
+ if empty(s:update.errors)
+ return
+ endif
+ echo
+ call s:update_impl(s:update.pull, s:update.force,
+ \ extend(copy(s:update.errors), [s:update.threads]))
+endfunction
+
+function! s:is_managed(name)
+ return has_key(g:plugs[a:name], 'uri')
+endfunction
+
+function! s:names(...)
+ return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)'))
+endfunction
+
+function! s:check_ruby()
+ silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'")
+ if !exists('g:plug_ruby')
+ redraw!
+ return s:warn('echom', 'Warning: Ruby interface is broken')
+ endif
+ let ruby_version = split(g:plug_ruby, '\.')
+ unlet g:plug_ruby
+ return s:version_requirement(ruby_version, [1, 8, 7])
+endfunction
+
+function! s:update_impl(pull, force, args) abort
+ let sync = index(a:args, '--sync') >= 0 || has('vim_starting')
+ let args = filter(copy(a:args), 'v:val != "--sync"')
+ let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
+ \ remove(args, -1) : get(g:, 'plug_threads', 16)
+
+ let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
+ let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
+ \ filter(managed, 'index(args, v:key) >= 0')
+
+ if empty(todo)
+ return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install'))
+ endif
+
+ if !s:is_win && s:git_version_requirement(2, 3)
+ let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : ''
+ let $GIT_TERMINAL_PROMPT = 0
+ for plug in values(todo)
+ let plug.uri = substitute(plug.uri,
+ \ '^https://git::@github\.com', 'https://github.com', '')
+ endfor
+ endif
+
+ if !isdirectory(g:plug_home)
+ try
+ call mkdir(g:plug_home, 'p')
+ catch
+ return s:err(printf('Invalid plug directory: %s. '.
+ \ 'Try to call plug#begin with a valid directory', g:plug_home))
+ endtry
+ endif
+
+ if has('nvim') && !exists('*jobwait') && threads > 1
+ call s:warn('echom', '[vim-plug] Update Neovim for parallel installer')
+ endif
+
+ let use_job = s:nvim || s:vim8
+ let python = (has('python') || has('python3')) && !use_job
+ let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby()
+
+ let s:update = {
+ \ 'start': reltime(),
+ \ 'all': todo,
+ \ 'todo': copy(todo),
+ \ 'errors': [],
+ \ 'pull': a:pull,
+ \ 'force': a:force,
+ \ 'new': {},
+ \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1,
+ \ 'bar': '',
+ \ 'fin': 0
+ \ }
+
+ call s:prepare(1)
+ call append(0, ['', ''])
+ normal! 2G
+ silent! redraw
+
+ let s:clone_opt = []
+ if get(g:, 'plug_shallow', 1)
+ call extend(s:clone_opt, ['--depth', '1'])
+ if s:git_version_requirement(1, 7, 10)
+ call add(s:clone_opt, '--no-single-branch')
+ endif
+ endif
+
+ if has('win32unix') || has('wsl')
+ call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input'])
+ endif
+
+ let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : ''
+
+ " Python version requirement (>= 2.7)
+ if python && !has('python3') && !ruby && !use_job && s:update.threads > 1
+ redir => pyv
+ silent python import platform; print platform.python_version()
+ redir END
+ let python = s:version_requirement(
+ \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6])
+ endif
+
+ if (python || ruby) && s:update.threads > 1
+ try
+ let imd = &imd
+ if s:mac_gui
+ set noimd
+ endif
+ if ruby
+ call s:update_ruby()
+ else
+ call s:update_python()
+ endif
+ catch
+ let lines = getline(4, '$')
+ let printed = {}
+ silent! 4,$d _
+ for line in lines
+ let name = s:extract_name(line, '.', '')
+ if empty(name) || !has_key(printed, name)
+ call append('$', line)
+ if !empty(name)
+ let printed[name] = 1
+ if line[0] == 'x' && index(s:update.errors, name) < 0
+ call add(s:update.errors, name)
+ end
+ endif
+ endif
+ endfor
+ finally
+ let &imd = imd
+ call s:update_finish()
+ endtry
+ else
+ call s:update_vim()
+ while use_job && sync
+ sleep 100m
+ if s:update.fin
+ break
+ endif
+ endwhile
+ endif
+endfunction
+
+function! s:log4(name, msg)
+ call setline(4, printf('- %s (%s)', a:msg, a:name))
+ redraw
+endfunction
+
+function! s:update_finish()
+ if exists('s:git_terminal_prompt')
+ let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt
+ endif
+ if s:switch_in()
+ call append(3, '- Updating ...') | 4
+ for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))'))
+ let [pos, _] = s:logpos(name)
+ if !pos
+ continue
+ endif
+ if has_key(spec, 'commit')
+ call s:log4(name, 'Checking out '.spec.commit)
+ let out = s:checkout(spec)
+ elseif has_key(spec, 'tag')
+ let tag = spec.tag
+ if tag =~ '\*'
+ let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir))
+ if !v:shell_error && !empty(tags)
+ let tag = tags[0]
+ call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag))
+ call append(3, '')
+ endif
+ endif
+ call s:log4(name, 'Checking out '.tag)
+ let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
+ else
+ let branch = get(spec, 'branch', 'master')
+ call s:log4(name, 'Merging origin/'.s:esc(branch))
+ let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1'
+ \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir)
+ endif
+ if !v:shell_error && filereadable(spec.dir.'/.gitmodules') &&
+ \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
+ call s:log4(name, 'Updating submodules. This may take a while.')
+ let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
+ endif
+ let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
+ if v:shell_error
+ call add(s:update.errors, name)
+ call s:regress_bar()
+ silent execute pos 'd _'
+ call append(4, msg) | 4
+ elseif !empty(out)
+ call setline(pos, msg[0])
+ endif
+ redraw
+ endfor
+ silent 4 d _
+ try
+ call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")'))
+ catch
+ call s:warn('echom', v:exception)
+ call s:warn('echo', '')
+ return
+ endtry
+ call s:finish(s:update.pull)
+ call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.')
+ call s:switch_out('normal! gg')
+ endif
+endfunction
+
+function! s:job_abort()
+ if (!s:nvim && !s:vim8) || !exists('s:jobs')
+ return
+ endif
+
+ for [name, j] in items(s:jobs)
+ if s:nvim
+ silent! call jobstop(j.jobid)
+ elseif s:vim8
+ silent! call job_stop(j.jobid)
+ endif
+ if j.new
+ call s:rm_rf(g:plugs[name].dir)
+ endif
+ endfor
+ let s:jobs = {}
+endfunction
+
+function! s:last_non_empty_line(lines)
+ let len = len(a:lines)
+ for idx in range(len)
+ let line = a:lines[len-idx-1]
+ if !empty(line)
+ return line
+ endif
+ endfor
+ return ''
+endfunction
+
+function! s:job_out_cb(self, data) abort
+ let self = a:self
+ let data = remove(self.lines, -1) . a:data
+ let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]')
+ call extend(self.lines, lines)
+ " To reduce the number of buffer updates
+ let self.tick = get(self, 'tick', -1) + 1
+ if !self.running || self.tick % len(s:jobs) == 0
+ let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-')
+ let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines)
+ call s:log(bullet, self.name, result)
+ endif
+endfunction
+
+function! s:job_exit_cb(self, data) abort
+ let a:self.running = 0
+ let a:self.error = a:data != 0
+ call s:reap(a:self.name)
+ call s:tick()
+endfunction
+
+function! s:job_cb(fn, job, ch, data)
+ if !s:plug_window_exists() " plug window closed
+ return s:job_abort()
+ endif
+ call call(a:fn, [a:job, a:data])
+endfunction
+
+function! s:nvim_cb(job_id, data, event) dict abort
+ return (a:event == 'stdout' || a:event == 'stderr') ?
+ \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) :
+ \ s:job_cb('s:job_exit_cb', self, 0, a:data)
+endfunction
+
+function! s:spawn(name, cmd, opts)
+ let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''],
+ \ 'new': get(a:opts, 'new', 0) }
+ let s:jobs[a:name] = job
+
+ if s:nvim
+ if has_key(a:opts, 'dir')
+ let job.cwd = a:opts.dir
+ endif
+ let argv = a:cmd
+ call extend(job, {
+ \ 'on_stdout': function('s:nvim_cb'),
+ \ 'on_stderr': function('s:nvim_cb'),
+ \ 'on_exit': function('s:nvim_cb'),
+ \ })
+ let jid = s:plug_call('jobstart', argv, job)
+ if jid > 0
+ let job.jobid = jid
+ else
+ let job.running = 0
+ let job.error = 1
+ let job.lines = [jid < 0 ? argv[0].' is not executable' :
+ \ 'Invalid arguments (or job table is full)']
+ endif
+ elseif s:vim8
+ let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})'))
+ if has_key(a:opts, 'dir')
+ let cmd = s:with_cd(cmd, a:opts.dir, 0)
+ endif
+ let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd]
+ let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
+ \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]),
+ \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]),
+ \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]),
+ \ 'err_mode': 'raw',
+ \ 'out_mode': 'raw'
+ \})
+ if job_status(jid) == 'run'
+ let job.jobid = jid
+ else
+ let job.running = 0
+ let job.error = 1
+ let job.lines = ['Failed to start job']
+ endif
+ else
+ let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd]))
+ let job.error = v:shell_error != 0
+ let job.running = 0
+ endif
+endfunction
+
+function! s:reap(name)
+ let job = s:jobs[a:name]
+ if job.error
+ call add(s:update.errors, a:name)
+ elseif get(job, 'new', 0)
+ let s:update.new[a:name] = 1
+ endif
+ let s:update.bar .= job.error ? 'x' : '='
+
+ let bullet = job.error ? 'x' : '-'
+ let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines)
+ call s:log(bullet, a:name, empty(result) ? 'OK' : result)
+ call s:bar()
+
+ call remove(s:jobs, a:name)
+endfunction
+
+function! s:bar()
+ if s:switch_in()
+ let total = len(s:update.all)
+ call setline(1, (s:update.pull ? 'Updating' : 'Installing').
+ \ ' plugins ('.len(s:update.bar).'/'.total.')')
+ call s:progress_bar(2, s:update.bar, total)
+ call s:switch_out()
+ endif
+endfunction
+
+function! s:logpos(name)
+ let max = line('$')
+ for i in range(4, max > 4 ? max : 4)
+ if getline(i) =~# '^[-+x*] '.a:name.':'
+ for j in range(i + 1, max > 5 ? max : 5)
+ if getline(j) !~ '^ '
+ return [i, j - 1]
+ endif
+ endfor
+ return [i, i]
+ endif
+ endfor
+ return [0, 0]
+endfunction
+
+function! s:log(bullet, name, lines)
+ if s:switch_in()
+ let [b, e] = s:logpos(a:name)
+ if b > 0
+ silent execute printf('%d,%d d _', b, e)
+ if b > winheight('.')
+ let b = 4
+ endif
+ else
+ let b = 4
+ endif
+ " FIXME For some reason, nomodifiable is set after :d in vim8
+ setlocal modifiable
+ call append(b - 1, s:format_message(a:bullet, a:name, a:lines))
+ call s:switch_out()
+ endif
+endfunction
+
+function! s:update_vim()
+ let s:jobs = {}
+
+ call s:bar()
+ call s:tick()
+endfunction
+
+function! s:tick()
+ let pull = s:update.pull
+ let prog = s:progress_opt(s:nvim || s:vim8)
+while 1 " Without TCO, Vim stack is bound to explode
+ if empty(s:update.todo)
+ if empty(s:jobs) && !s:update.fin
+ call s:update_finish()
+ let s:update.fin = 1
+ endif
+ return
+ endif
+
+ let name = keys(s:update.todo)[0]
+ let spec = remove(s:update.todo, name)
+ let new = empty(globpath(spec.dir, '.git', 1))
+
+ call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
+ redraw
+
+ let has_tag = has_key(spec, 'tag')
+ if !new
+ let [error, _] = s:git_validate(spec, 0)
+ if empty(error)
+ if pull
+ let cmd = ['git', 'fetch']
+ if has_tag && !empty(globpath(spec.dir, '.git/shallow'))
+ call extend(cmd, ['--depth', '99999999'])
+ endif
+ if !empty(prog)
+ call add(cmd, prog)
+ endif
+ call s:spawn(name, cmd, { 'dir': spec.dir })
+ else
+ let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
+ endif
+ else
+ let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 }
+ endif
+ else
+ let cmd = ['git', 'clone']
+ if !has_tag
+ call extend(cmd, s:clone_opt)
+ endif
+ if !empty(prog)
+ call add(cmd, prog)
+ endif
+ call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 })
+ endif
+
+ if !s:jobs[name].running
+ call s:reap(name)
+ endif
+ if len(s:jobs) >= s:update.threads
+ break
+ endif
+endwhile
+endfunction
+
+function! s:update_python()
+let py_exe = has('python') ? 'python' : 'python3'
+execute py_exe "<< EOF"
+import datetime
+import functools
+import os
+try:
+ import queue
+except ImportError:
+ import Queue as queue
+import random
+import re
+import shutil
+import signal
+import subprocess
+import tempfile
+import threading as thr
+import time
+import traceback
+import vim
+
+G_NVIM = vim.eval("has('nvim')") == '1'
+G_PULL = vim.eval('s:update.pull') == '1'
+G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
+G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
+G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt'))
+G_PROGRESS = vim.eval('s:progress_opt(1)')
+G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
+G_STOP = thr.Event()
+G_IS_WIN = vim.eval('s:is_win') == '1'
+
+class PlugError(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+class CmdTimedOut(PlugError):
+ pass
+class CmdFailed(PlugError):
+ pass
+class InvalidURI(PlugError):
+ pass
+class Action(object):
+ INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-']
+
+class Buffer(object):
+ def __init__(self, lock, num_plugs, is_pull):
+ self.bar = ''
+ self.event = 'Updating' if is_pull else 'Installing'
+ self.lock = lock
+ self.maxy = int(vim.eval('winheight(".")'))
+ self.num_plugs = num_plugs
+
+ def __where(self, name):
+ """ Find first line with name in current buffer. Return line num. """
+ found, lnum = False, 0
+ matcher = re.compile('^[-+x*] {0}:'.format(name))
+ for line in vim.current.buffer:
+ if matcher.search(line) is not None:
+ found = True
+ break
+ lnum += 1
+
+ if not found:
+ lnum = -1
+ return lnum
+
+ def header(self):
+ curbuf = vim.current.buffer
+ curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs)
+
+ num_spaces = self.num_plugs - len(self.bar)
+ curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ')
+
+ with self.lock:
+ vim.command('normal! 2G')
+ vim.command('redraw')
+
+ def write(self, action, name, lines):
+ first, rest = lines[0], lines[1:]
+ msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)]
+ msg.extend([' ' + line for line in rest])
+
+ try:
+ if action == Action.ERROR:
+ self.bar += 'x'
+ vim.command("call add(s:update.errors, '{0}')".format(name))
+ elif action == Action.DONE:
+ self.bar += '='
+
+ curbuf = vim.current.buffer
+ lnum = self.__where(name)
+ if lnum != -1: # Found matching line num
+ del curbuf[lnum]
+ if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]):
+ lnum = 3
+ else:
+ lnum = 3
+ curbuf.append(msg, lnum)
+
+ self.header()
+ except vim.error:
+ pass
+
+class Command(object):
+ CD = 'cd /d' if G_IS_WIN else 'cd'
+
+ def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None):
+ self.cmd = cmd
+ if cmd_dir:
+ self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd)
+ self.timeout = timeout
+ self.callback = cb if cb else (lambda msg: None)
+ self.clean = clean if clean else (lambda: None)
+ self.proc = None
+
+ @property
+ def alive(self):
+ """ Returns true only if command still running. """
+ return self.proc and self.proc.poll() is None
+
+ def execute(self, ntries=3):
+ """ Execute the command with ntries if CmdTimedOut.
+ Returns the output of the command if no Exception.
+ """
+ attempt, finished, limit = 0, False, self.timeout
+
+ while not finished:
+ try:
+ attempt += 1
+ result = self.try_command()
+ finished = True
+ return result
+ except CmdTimedOut:
+ if attempt != ntries:
+ self.notify_retry()
+ self.timeout += limit
+ else:
+ raise
+
+ def notify_retry(self):
+ """ Retry required for command, notify user. """
+ for count in range(3, 0, -1):
+ if G_STOP.is_set():
+ raise KeyboardInterrupt
+ msg = 'Timeout. Will retry in {0} second{1} ...'.format(
+ count, 's' if count != 1 else '')
+ self.callback([msg])
+ time.sleep(1)
+ self.callback(['Retrying ...'])
+
+ def try_command(self):
+ """ Execute a cmd & poll for callback. Returns list of output.
+ Raises CmdFailed -> return code for Popen isn't 0
+ Raises CmdTimedOut -> command exceeded timeout without new output
+ """
+ first_line = True
+
+ try:
+ tfile = tempfile.NamedTemporaryFile(mode='w+b')
+ preexec_fn = not G_IS_WIN and os.setsid or None
+ self.proc = subprocess.Popen(self.cmd, stdout=tfile,
+ stderr=subprocess.STDOUT,
+ stdin=subprocess.PIPE, shell=True,
+ preexec_fn=preexec_fn)
+ thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,))
+ thrd.start()
+
+ thread_not_started = True
+ while thread_not_started:
+ try:
+ thrd.join(0.1)
+ thread_not_started = False
+ except RuntimeError:
+ pass
+
+ while self.alive:
+ if G_STOP.is_set():
+ raise KeyboardInterrupt
+
+ if first_line or random.random() < G_LOG_PROB:
+ first_line = False
+ line = '' if G_IS_WIN else nonblock_read(tfile.name)
+ if line:
+ self.callback([line])
+
+ time_diff = time.time() - os.path.getmtime(tfile.name)
+ if time_diff > self.timeout:
+ raise CmdTimedOut(['Timeout!'])
+
+ thrd.join(0.5)
+
+ tfile.seek(0)
+ result = [line.decode('utf-8', 'replace').rstrip() for line in tfile]
+
+ if self.proc.returncode != 0:
+ raise CmdFailed([''] + result)
+
+ return result
+ except:
+ self.terminate()
+ raise
+
+ def terminate(self):
+ """ Terminate process and cleanup. """
+ if self.alive:
+ if G_IS_WIN:
+ os.kill(self.proc.pid, signal.SIGINT)
+ else:
+ os.killpg(self.proc.pid, signal.SIGTERM)
+ self.clean()
+
+class Plugin(object):
+ def __init__(self, name, args, buf_q, lock):
+ self.name = name
+ self.args = args
+ self.buf_q = buf_q
+ self.lock = lock
+ self.tag = args.get('tag', 0)
+
+ def manage(self):
+ try:
+ if os.path.exists(self.args['dir']):
+ self.update()
+ else:
+ self.install()
+ with self.lock:
+ thread_vim_command("let s:update.new['{0}'] = 1".format(self.name))
+ except PlugError as exc:
+ self.write(Action.ERROR, self.name, exc.msg)
+ except KeyboardInterrupt:
+ G_STOP.set()
+ self.write(Action.ERROR, self.name, ['Interrupted!'])
+ except:
+ # Any exception except those above print stack trace
+ msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip())
+ self.write(Action.ERROR, self.name, msg.split('\n'))
+ raise
+
+ def install(self):
+ target = self.args['dir']
+ if target[-1] == '\\':
+ target = target[0:-1]
+
+ def clean(target):
+ def _clean():
+ try:
+ shutil.rmtree(target)
+ except OSError:
+ pass
+ return _clean
+
+ self.write(Action.INSTALL, self.name, ['Installing ...'])
+ callback = functools.partial(self.write, Action.INSTALL, self.name)
+ cmd = 'git clone {0} {1} {2} {3} 2>&1'.format(
+ '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'],
+ esc(target))
+ com = Command(cmd, None, G_TIMEOUT, callback, clean(target))
+ result = com.execute(G_RETRIES)
+ self.write(Action.DONE, self.name, result[-1:])
+
+ def repo_uri(self):
+ cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url'
+ command = Command(cmd, self.args['dir'], G_TIMEOUT,)
+ result = command.execute(G_RETRIES)
+ return result[-1]
+
+ def update(self):
+ actual_uri = self.repo_uri()
+ expect_uri = self.args['uri']
+ regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$')
+ ma = regex.match(actual_uri)
+ mb = regex.match(expect_uri)
+ if ma is None or mb is None or ma.groups() != mb.groups():
+ msg = ['',
+ 'Invalid URI: {0}'.format(actual_uri),
+ 'Expected {0}'.format(expect_uri),
+ 'PlugClean required.']
+ raise InvalidURI(msg)
+
+ if G_PULL:
+ self.write(Action.UPDATE, self.name, ['Updating ...'])
+ callback = functools.partial(self.write, Action.UPDATE, self.name)
+ fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else ''
+ cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS)
+ com = Command(cmd, self.args['dir'], G_TIMEOUT, callback)
+ result = com.execute(G_RETRIES)
+ self.write(Action.DONE, self.name, result[-1:])
+ else:
+ self.write(Action.DONE, self.name, ['Already installed'])
+
+ def write(self, action, name, msg):
+ self.buf_q.put((action, name, msg))
+
+class PlugThread(thr.Thread):
+ def __init__(self, tname, args):
+ super(PlugThread, self).__init__()
+ self.tname = tname
+ self.args = args
+
+ def run(self):
+ thr.current_thread().name = self.tname
+ buf_q, work_q, lock = self.args
+
+ try:
+ while not G_STOP.is_set():
+ name, args = work_q.get_nowait()
+ plug = Plugin(name, args, buf_q, lock)
+ plug.manage()
+ work_q.task_done()
+ except queue.Empty:
+ pass
+
+class RefreshThread(thr.Thread):
+ def __init__(self, lock):
+ super(RefreshThread, self).__init__()
+ self.lock = lock
+ self.running = True
+
+ def run(self):
+ while self.running:
+ with self.lock:
+ thread_vim_command('noautocmd normal! a')
+ time.sleep(0.33)
+
+ def stop(self):
+ self.running = False
+
+if G_NVIM:
+ def thread_vim_command(cmd):
+ vim.session.threadsafe_call(lambda: vim.command(cmd))
+else:
+ def thread_vim_command(cmd):
+ vim.command(cmd)
+
+def esc(name):
+ return '"' + name.replace('"', '\"') + '"'
+
+def nonblock_read(fname):
+ """ Read a file with nonblock flag. Return the last line. """
+ fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK)
+ buf = os.read(fread, 100000).decode('utf-8', 'replace')
+ os.close(fread)
+
+ line = buf.rstrip('\r\n')
+ left = max(line.rfind('\r'), line.rfind('\n'))
+ if left != -1:
+ left += 1
+ line = line[left:]
+
+ return line
+
+def main():
+ thr.current_thread().name = 'main'
+ nthreads = int(vim.eval('s:update.threads'))
+ plugs = vim.eval('s:update.todo')
+ mac_gui = vim.eval('s:mac_gui') == '1'
+
+ lock = thr.Lock()
+ buf = Buffer(lock, len(plugs), G_PULL)
+ buf_q, work_q = queue.Queue(), queue.Queue()
+ for work in plugs.items():
+ work_q.put(work)
+
+ start_cnt = thr.active_count()
+ for num in range(nthreads):
+ tname = 'PlugT-{0:02}'.format(num)
+ thread = PlugThread(tname, (buf_q, work_q, lock))
+ thread.start()
+ if mac_gui:
+ rthread = RefreshThread(lock)
+ rthread.start()
+
+ while not buf_q.empty() or thr.active_count() != start_cnt:
+ try:
+ action, name, msg = buf_q.get(True, 0.25)
+ buf.write(action, name, ['OK'] if not msg else msg)
+ buf_q.task_done()
+ except queue.Empty:
+ pass
+ except KeyboardInterrupt:
+ G_STOP.set()
+
+ if mac_gui:
+ rthread.stop()
+ rthread.join()
+
+main()
+EOF
+endfunction
+
+function! s:update_ruby()
+ ruby << EOF
+ module PlugStream
+ SEP = ["\r", "\n", nil]
+ def get_line
+ buffer = ''
+ loop do
+ char = readchar rescue return
+ if SEP.include? char.chr
+ buffer << $/
+ break
+ else
+ buffer << char
+ end
+ end
+ buffer
+ end
+ end unless defined?(PlugStream)
+
+ def esc arg
+ %["#{arg.gsub('"', '\"')}"]
+ end
+
+ def killall pid
+ pids = [pid]
+ if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
+ pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil }
+ else
+ unless `which pgrep 2> /dev/null`.empty?
+ children = pids
+ until children.empty?
+ children = children.map { |pid|
+ `pgrep -P #{pid}`.lines.map { |l| l.chomp }
+ }.flatten
+ pids += children
+ end
+ end
+ pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
+ end
+ end
+
+ def compare_git_uri a, b
+ regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$}
+ regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1)
+ end
+
+ require 'thread'
+ require 'fileutils'
+ require 'timeout'
+ running = true
+ iswin = VIM::evaluate('s:is_win').to_i == 1
+ pull = VIM::evaluate('s:update.pull').to_i == 1
+ base = VIM::evaluate('g:plug_home')
+ all = VIM::evaluate('s:update.todo')
+ limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
+ tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
+ nthr = VIM::evaluate('s:update.threads').to_i
+ maxy = VIM::evaluate('winheight(".")').to_i
+ vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/
+ cd = iswin ? 'cd /d' : 'cd'
+ tot = VIM::evaluate('len(s:update.todo)') || 0
+ bar = ''
+ skip = 'Already installed'
+ mtx = Mutex.new
+ take1 = proc { mtx.synchronize { running && all.shift } }
+ logh = proc {
+ cnt = bar.length
+ $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
+ $curbuf[2] = '[' + bar.ljust(tot) + ']'
+ VIM::command('normal! 2G')
+ VIM::command('redraw')
+ }
+ where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
+ log = proc { |name, result, type|
+ mtx.synchronize do
+ ing = ![true, false].include?(type)
+ bar += type ? '=' : 'x' unless ing
+ b = case type
+ when :install then '+' when :update then '*'
+ when true, nil then '-' else
+ VIM::command("call add(s:update.errors, '#{name}')")
+ 'x'
+ end
+ result =
+ if type || type.nil?
+ ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"]
+ elsif result =~ /^Interrupted|^Timeout/
+ ["#{b} #{name}: #{result}"]
+ else
+ ["#{b} #{name}"] + result.lines.map { |l| " " << l }
+ end
+ if lnum = where.call(name)
+ $curbuf.delete lnum
+ lnum = 4 if ing && lnum > maxy
+ end
+ result.each_with_index do |line, offset|
+ $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
+ end
+ logh.call
+ end
+ }
+ bt = proc { |cmd, name, type, cleanup|
+ tried = timeout = 0
+ begin
+ tried += 1
+ timeout += limit
+ fd = nil
+ data = ''
+ if iswin
+ Timeout::timeout(timeout) do
+ tmp = VIM::evaluate('tempname()')
+ system("(#{cmd}) > #{tmp}")
+ data = File.read(tmp).chomp
+ File.unlink tmp rescue nil
+ end
+ else
+ fd = IO.popen(cmd).extend(PlugStream)
+ first_line = true
+ log_prob = 1.0 / nthr
+ while line = Timeout::timeout(timeout) { fd.get_line }
+ data << line
+ log.call name, line.chomp, type if name && (first_line || rand < log_prob)
+ first_line = false
+ end
+ fd.close
+ end
+ [$? == 0, data.chomp]
+ rescue Timeout::Error, Interrupt => e
+ if fd && !fd.closed?
+ killall fd.pid
+ fd.close
+ end
+ cleanup.call if cleanup
+ if e.is_a?(Timeout::Error) && tried < tries
+ 3.downto(1) do |countdown|
+ s = countdown > 1 ? 's' : ''
+ log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
+ sleep 1
+ end
+ log.call name, 'Retrying ...', type
+ retry
+ end
+ [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
+ end
+ }
+ main = Thread.current
+ threads = []
+ watcher = Thread.new {
+ if vim7
+ while VIM::evaluate('getchar(1)')
+ sleep 0.1
+ end
+ else
+ require 'io/console' # >= Ruby 1.9
+ nil until IO.console.getch == 3.chr
+ end
+ mtx.synchronize do
+ running = false
+ threads.each { |t| t.raise Interrupt } unless vim7
+ end
+ threads.each { |t| t.join rescue nil }
+ main.kill
+ }
+ refresh = Thread.new {
+ while true
+ mtx.synchronize do
+ break unless running
+ VIM::command('noautocmd normal! a')
+ end
+ sleep 0.2
+ end
+ } if VIM::evaluate('s:mac_gui') == 1
+
+ clone_opt = VIM::evaluate('s:clone_opt').join(' ')
+ progress = VIM::evaluate('s:progress_opt(1)')
+ nthr.times do
+ mtx.synchronize do
+ threads << Thread.new {
+ while pair = take1.call
+ name = pair.first
+ dir, uri, tag = pair.last.values_at *%w[dir uri tag]
+ exists = File.directory? dir
+ ok, result =
+ if exists
+ chdir = "#{cd} #{iswin ? dir : esc(dir)}"
+ ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil
+ current_uri = data.lines.to_a.last
+ if !ret
+ if data =~ /^Interrupted|^Timeout/
+ [false, data]
+ else
+ [false, [data.chomp, "PlugClean required."].join($/)]
+ end
+ elsif !compare_git_uri(current_uri, uri)
+ [false, ["Invalid URI: #{current_uri}",
+ "Expected: #{uri}",
+ "PlugClean required."].join($/)]
+ else
+ if pull
+ log.call name, 'Updating ...', :update
+ fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : ''
+ bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil
+ else
+ [true, skip]
+ end
+ end
+ else
+ d = esc dir.sub(%r{[\\/]+$}, '')
+ log.call name, 'Installing ...', :install
+ bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc {
+ FileUtils.rm_rf dir
+ }
+ end
+ mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok
+ log.call name, result, ok
+ end
+ } if running
+ end
+ end
+ threads.each { |t| t.join rescue nil }
+ logh.call
+ refresh.kill if refresh
+ watcher.kill
+EOF
+endfunction
+
+function! s:shellesc_cmd(arg, script)
+ let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g')
+ return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g')
+endfunction
+
+function! s:shellesc_ps1(arg)
+ return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'"
+endfunction
+
+function! s:shellesc_sh(arg)
+ return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'"
+endfunction
+
+" Escape the shell argument based on the shell.
+" Vim and Neovim's shellescape() are insufficient.
+" 1. shellslash determines whether to use single/double quotes.
+" Double-quote escaping is fragile for cmd.exe.
+" 2. It does not work for powershell.
+" 3. It does not work for *sh shells if the command is executed
+" via cmd.exe (ie. cmd.exe /c sh -c command command_args)
+" 4. It does not support batchfile syntax.
+"
+" Accepts an optional dictionary with the following keys:
+" - shell: same as Vim/Neovim 'shell' option.
+" If unset, fallback to 'cmd.exe' on Windows or 'sh'.
+" - script: If truthy and shell is cmd.exe, escape for batchfile syntax.
+function! plug#shellescape(arg, ...)
+ if a:arg =~# '^[A-Za-z0-9_/:.-]\+$'
+ return a:arg
+ endif
+ let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
+ let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
+ let script = get(opts, 'script', 1)
+ if shell =~# 'cmd\.exe'
+ return s:shellesc_cmd(a:arg, script)
+ elseif shell =~# 'powershell\.exe' || shell =~# 'pwsh$'
+ return s:shellesc_ps1(a:arg)
+ endif
+ return s:shellesc_sh(a:arg)
+endfunction
+
+function! s:glob_dir(path)
+ return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
+endfunction
+
+function! s:progress_bar(line, bar, total)
+ call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
+endfunction
+
+function! s:compare_git_uri(a, b)
+ " See `git help clone'
+ " https:// [user@] github.com[:port] / junegunn/vim-plug [.git]
+ " [git@] github.com[:port] : junegunn/vim-plug [.git]
+ " file:// / junegunn/vim-plug [/]
+ " / junegunn/vim-plug [/]
+ let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$'
+ let ma = matchlist(a:a, pat)
+ let mb = matchlist(a:b, pat)
+ return ma[1:2] ==# mb[1:2]
+endfunction
+
+function! s:format_message(bullet, name, message)
+ if a:bullet != 'x'
+ return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))]
+ else
+ let lines = map(s:lines(a:message), '" ".v:val')
+ return extend([printf('x %s:', a:name)], lines)
+ endif
+endfunction
+
+function! s:with_cd(cmd, dir, ...)
+ let script = a:0 > 0 ? a:1 : 1
+ return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd)
+endfunction
+
+function! s:system(cmd, ...)
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ if type(a:cmd) == s:TYPE.list
+ " Neovim's system() supports list argument to bypass the shell
+ " but it cannot set the working directory for the command.
+ " Assume that the command does not rely on the shell.
+ if has('nvim') && a:0 == 0
+ return system(a:cmd)
+ endif
+ let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
+ if &shell =~# 'powershell\.exe'
+ let cmd = '& ' . cmd
+ endif
+ else
+ let cmd = a:cmd
+ endif
+ if a:0 > 0
+ let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list)
+ endif
+ if s:is_win && type(a:cmd) != s:TYPE.list
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ return system(cmd)
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ call delete(batchfile)
+ endif
+ endtry
+endfunction
+
+function! s:system_chomp(...)
+ let ret = call('s:system', a:000)
+ return v:shell_error ? '' : substitute(ret, '\n$', '', '')
+endfunction
+
+function! s:git_validate(spec, check_branch)
+ let err = ''
+ if isdirectory(a:spec.dir)
+ let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir))
+ let remote = result[-1]
+ if v:shell_error
+ let err = join([remote, 'PlugClean required.'], "\n")
+ elseif !s:compare_git_uri(remote, a:spec.uri)
+ let err = join(['Invalid URI: '.remote,
+ \ 'Expected: '.a:spec.uri,
+ \ 'PlugClean required.'], "\n")
+ elseif a:check_branch && has_key(a:spec, 'commit')
+ let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir))
+ let sha = result[-1]
+ if v:shell_error
+ let err = join(add(result, 'PlugClean required.'), "\n")
+ elseif !s:hash_match(sha, a:spec.commit)
+ let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
+ \ a:spec.commit[:6], sha[:6]),
+ \ 'PlugUpdate required.'], "\n")
+ endif
+ elseif a:check_branch
+ let branch = result[0]
+ " Check tag
+ if has_key(a:spec, 'tag')
+ let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
+ if a:spec.tag !=# tag && a:spec.tag !~ '\*'
+ let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
+ \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
+ endif
+ " Check branch
+ elseif a:spec.branch !=# branch
+ let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
+ \ branch, a:spec.branch)
+ endif
+ if empty(err)
+ let [ahead, behind] = split(s:lastline(s:system([
+ \ 'git', 'rev-list', '--count', '--left-right',
+ \ printf('HEAD...origin/%s', a:spec.branch)
+ \ ], a:spec.dir)), '\t')
+ if !v:shell_error && ahead
+ if behind
+ " Only mention PlugClean if diverged, otherwise it's likely to be
+ " pushable (and probably not that messed up).
+ let err = printf(
+ \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
+ \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind)
+ else
+ let err = printf("Ahead of origin/%s by %d commit(s).\n"
+ \ .'Cannot update until local changes are pushed.',
+ \ a:spec.branch, ahead)
+ endif
+ endif
+ endif
+ endif
+ else
+ let err = 'Not found'
+ endif
+ return [err, err =~# 'PlugClean']
+endfunction
+
+function! s:rm_rf(dir)
+ if isdirectory(a:dir)
+ call s:system(s:is_win
+ \ ? 'rmdir /S /Q '.plug#shellescape(a:dir)
+ \ : ['rm', '-rf', a:dir])
+ endif
+endfunction
+
+function! s:clean(force)
+ call s:prepare()
+ call append(0, 'Searching for invalid plugins in '.g:plug_home)
+ call append(1, '')
+
+ " List of valid directories
+ let dirs = []
+ let errs = {}
+ let [cnt, total] = [0, len(g:plugs)]
+ for [name, spec] in items(g:plugs)
+ if !s:is_managed(name)
+ call add(dirs, spec.dir)
+ else
+ let [err, clean] = s:git_validate(spec, 1)
+ if clean
+ let errs[spec.dir] = s:lines(err)[0]
+ else
+ call add(dirs, spec.dir)
+ endif
+ endif
+ let cnt += 1
+ call s:progress_bar(2, repeat('=', cnt), total)
+ normal! 2G
+ redraw
+ endfor
+
+ let allowed = {}
+ for dir in dirs
+ let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1
+ let allowed[dir] = 1
+ for child in s:glob_dir(dir)
+ let allowed[child] = 1
+ endfor
+ endfor
+
+ let todo = []
+ let found = sort(s:glob_dir(g:plug_home))
+ while !empty(found)
+ let f = remove(found, 0)
+ if !has_key(allowed, f) && isdirectory(f)
+ call add(todo, f)
+ call append(line('$'), '- ' . f)
+ if has_key(errs, f)
+ call append(line('$'), ' ' . errs[f])
+ endif
+ let found = filter(found, 'stridx(v:val, f) != 0')
+ end
+ endwhile
+
+ 4
+ redraw
+ if empty(todo)
+ call append(line('$'), 'Already clean.')
+ else
+ let s:clean_count = 0
+ call append(3, ['Directories to delete:', ''])
+ redraw!
+ if a:force || s:ask_no_interrupt('Delete all directories?')
+ call s:delete([6, line('$')], 1)
+ else
+ call setline(4, 'Cancelled.')
+ nnoremap <silent> <buffer> d :set opfunc=<sid>delete_op<cr>g@
+ nmap <silent> <buffer> dd d_
+ xnoremap <silent> <buffer> d :<c-u>call <sid>delete_op(visualmode(), 1)<cr>
+ echo 'Delete the lines (d{motion}) to delete the corresponding directories'
+ endif
+ endif
+ 4
+ setlocal nomodifiable
+endfunction
+
+function! s:delete_op(type, ...)
+ call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0)
+endfunction
+
+function! s:delete(range, force)
+ let [l1, l2] = a:range
+ let force = a:force
+ while l1 <= l2
+ let line = getline(l1)
+ if line =~ '^- ' && isdirectory(line[2:])
+ execute l1
+ redraw!
+ let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
+ let force = force || answer > 1
+ if answer
+ call s:rm_rf(line[2:])
+ setlocal modifiable
+ call setline(l1, '~'.line[1:])
+ let s:clean_count += 1
+ call setline(4, printf('Removed %d directories.', s:clean_count))
+ setlocal nomodifiable
+ endif
+ endif
+ let l1 += 1
+ endwhile
+endfunction
+
+function! s:upgrade()
+ echo 'Downloading the latest version of vim-plug'
+ redraw
+ let tmp = s:plug_tempname()
+ let new = tmp . '/plug.vim'
+
+ try
+ let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp])
+ if v:shell_error
+ return s:err('Error upgrading vim-plug: '. out)
+ endif
+
+ if readfile(s:me) ==# readfile(new)
+ echo 'vim-plug is already up-to-date'
+ return 0
+ else
+ call rename(s:me, s:me . '.old')
+ call rename(new, s:me)
+ unlet g:loaded_plug
+ echo 'vim-plug has been upgraded'
+ return 1
+ endif
+ finally
+ silent! call s:rm_rf(tmp)
+ endtry
+endfunction
+
+function! s:upgrade_specs()
+ for spec in values(g:plugs)
+ let spec.frozen = get(spec, 'frozen', 0)
+ endfor
+endfunction
+
+function! s:status()
+ call s:prepare()
+ call append(0, 'Checking plugins')
+ call append(1, '')
+
+ let ecnt = 0
+ let unloaded = 0
+ let [cnt, total] = [0, len(g:plugs)]
+ for [name, spec] in items(g:plugs)
+ let is_dir = isdirectory(spec.dir)
+ if has_key(spec, 'uri')
+ if is_dir
+ let [err, _] = s:git_validate(spec, 1)
+ let [valid, msg] = [empty(err), empty(err) ? 'OK' : err]
+ else
+ let [valid, msg] = [0, 'Not found. Try PlugInstall.']
+ endif
+ else
+ if is_dir
+ let [valid, msg] = [1, 'OK']
+ else
+ let [valid, msg] = [0, 'Not found.']
+ endif
+ endif
+ let cnt += 1
+ let ecnt += !valid
+ " `s:loaded` entry can be missing if PlugUpgraded
+ if is_dir && get(s:loaded, name, -1) == 0
+ let unloaded = 1
+ let msg .= ' (not loaded)'
+ endif
+ call s:progress_bar(2, repeat('=', cnt), total)
+ call append(3, s:format_message(valid ? '-' : 'x', name, msg))
+ normal! 2G
+ redraw
+ endfor
+ call setline(1, 'Finished. '.ecnt.' error(s).')
+ normal! gg
+ setlocal nomodifiable
+ if unloaded
+ echo "Press 'L' on each line to load plugin, or 'U' to update"
+ nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
+ xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
+ end
+endfunction
+
+function! s:extract_name(str, prefix, suffix)
+ return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$')
+endfunction
+
+function! s:status_load(lnum)
+ let line = getline(a:lnum)
+ let name = s:extract_name(line, '-', '(not loaded)')
+ if !empty(name)
+ call plug#load(name)
+ setlocal modifiable
+ call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
+ setlocal nomodifiable
+ endif
+endfunction
+
+function! s:status_update() range
+ let lines = getline(a:firstline, a:lastline)
+ let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)')
+ if !empty(names)
+ echo
+ execute 'PlugUpdate' join(names)
+ endif
+endfunction
+
+function! s:is_preview_window_open()
+ silent! wincmd P
+ if &previewwindow
+ wincmd p
+ return 1
+ endif
+endfunction
+
+function! s:find_name(lnum)
+ for lnum in reverse(range(1, a:lnum))
+ let line = getline(lnum)
+ if empty(line)
+ return ''
+ endif
+ let name = s:extract_name(line, '-', '')
+ if !empty(name)
+ return name
+ endif
+ endfor
+ return ''
+endfunction
+
+function! s:preview_commit()
+ if b:plug_preview < 0
+ let b:plug_preview = !s:is_preview_window_open()
+ endif
+
+ let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}')
+ if empty(sha)
+ return
+ endif
+
+ let name = s:find_name(line('.'))
+ if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
+ return
+ endif
+
+ if exists('g:plug_pwindow') && !s:is_preview_window_open()
+ execute g:plug_pwindow
+ execute 'e' sha
+ else
+ execute 'pedit' sha
+ wincmd P
+ endif
+ setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha
+ if s:is_win
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ execute 'silent %!' cmd
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ call delete(batchfile)
+ endif
+ endtry
+ setlocal nomodifiable
+ nnoremap <silent> <buffer> q :q<cr>
+ wincmd p
+endfunction
+
+function! s:section(flags)
+ call search('\(^[x-] \)\@<=[^:]\+:', a:flags)
+endfunction
+
+function! s:format_git_log(line)
+ let indent = ' '
+ let tokens = split(a:line, nr2char(1))
+ if len(tokens) != 5
+ return indent.substitute(a:line, '\s*$', '', '')
+ endif
+ let [graph, sha, refs, subject, date] = tokens
+ let tag = matchstr(refs, 'tag: [^,)]\+')
+ let tag = empty(tag) ? ' ' : ' ('.tag.') '
+ return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date)
+endfunction
+
+function! s:append_ul(lnum, text)
+ call append(a:lnum, ['', a:text, repeat('-', len(a:text))])
+endfunction
+
+function! s:diff()
+ call s:prepare()
+ call append(0, ['Collecting changes ...', ''])
+ let cnts = [0, 0]
+ let bar = ''
+ let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)')
+ call s:progress_bar(2, bar, len(total))
+ for origin in [1, 0]
+ let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))'))))
+ if empty(plugs)
+ continue
+ endif
+ call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
+ for [k, v] in plugs
+ let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..'
+ let cmd = ['git', 'log', '--graph', '--color=never']
+ if s:git_version_requirement(2, 10, 0)
+ call add(cmd, '--no-show-signature')
+ endif
+ call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range])
+ if has_key(v, 'rtp')
+ call extend(cmd, ['--', v.rtp])
+ endif
+ let diff = s:system_chomp(cmd, v.dir)
+ if !empty(diff)
+ let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
+ call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
+ let cnts[origin] += 1
+ endif
+ let bar .= '='
+ call s:progress_bar(2, bar, len(total))
+ normal! 2G
+ redraw
+ endfor
+ if !cnts[origin]
+ call append(5, ['', 'N/A'])
+ endif
+ endfor
+ call setline(1, printf('%d plugin(s) updated.', cnts[0])
+ \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : ''))
+
+ if cnts[0] || cnts[1]
+ nnoremap <silent> <buffer> <plug>(plug-preview) :silent! call <SID>preview_commit()<cr>
+ if empty(maparg("\<cr>", 'n'))
+ nmap <buffer> <cr> <plug>(plug-preview)
+ endif
+ if empty(maparg('o', 'n'))
+ nmap <buffer> o <plug>(plug-preview)
+ endif
+ endif
+ if cnts[0]
+ nnoremap <silent> <buffer> X :call <SID>revert()<cr>
+ echo "Press 'X' on each block to revert the update"
+ endif
+ normal! gg
+ setlocal nomodifiable
+endfunction
+
+function! s:revert()
+ if search('^Pending updates', 'bnW')
+ return
+ endif
+
+ let name = s:find_name(line('.'))
+ if empty(name) || !has_key(g:plugs, name) ||
+ \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y'
+ return
+ endif
+
+ call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir)
+ setlocal modifiable
+ normal! "_dap
+ setlocal nomodifiable
+ echo 'Reverted'
+endfunction
+
+function! s:snapshot(force, ...) abort
+ call s:prepare()
+ setf vim
+ call append(0, ['" Generated by vim-plug',
+ \ '" '.strftime("%c"),
+ \ '" :source this file in vim to restore the snapshot',
+ \ '" or execute: vim -S snapshot.vim',
+ \ '', '', 'PlugUpdate!'])
+ 1
+ let anchor = line('$') - 3
+ let names = sort(keys(filter(copy(g:plugs),
+ \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
+ for name in reverse(names)
+ let sha = s:system_chomp(['git', 'rev-parse', '--short', 'HEAD'], g:plugs[name].dir)
+ if !empty(sha)
+ call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
+ redraw
+ endif
+ endfor
+
+ if a:0 > 0
+ let fn = s:plug_expand(a:1)
+ if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?'))
+ return
+ endif
+ call writefile(getline(1, '$'), fn)
+ echo 'Saved as '.a:1
+ silent execute 'e' s:esc(fn)
+ setf vim
+ endif
+endfunction
+
+function! s:split_rtp()
+ return split(&rtp, '\\\@<!,')
+endfunction
+
+let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, ''))
+let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, ''))
+
+if exists('g:plugs')
+ let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
+ call s:upgrade_specs()
+ call s:define_commands()
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/.config/nvim/autoload/plug.vim.old b/.config/nvim/autoload/plug.vim.old
new file mode 100644
index 0000000..ac14332
--- /dev/null
+++ b/.config/nvim/autoload/plug.vim.old
@@ -0,0 +1,2597 @@
+" vim-plug: Vim plugin manager
+" ============================
+"
+" Download plug.vim and put it in ~/.vim/autoload
+"
+" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
+" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
+"
+" Edit your .vimrc
+"
+" call plug#begin('~/.vim/plugged')
+"
+" " Make sure you use single quotes
+"
+" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align
+" Plug 'junegunn/vim-easy-align'
+"
+" " Any valid git URL is allowed
+" Plug 'https://github.com/junegunn/vim-github-dashboard.git'
+"
+" " Multiple Plug commands can be written in a single line using | separators
+" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
+"
+" " On-demand loading
+" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
+" Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
+"
+" " Using a non-master branch
+" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
+"
+" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above)
+" Plug 'fatih/vim-go', { 'tag': '*' }
+"
+" " Plugin options
+" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }
+"
+" " Plugin outside ~/.vim/plugged with post-update hook
+" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
+"
+" " Unmanaged plugin (manually installed and updated)
+" Plug '~/my-prototype-plugin'
+"
+" " Initialize plugin system
+" call plug#end()
+"
+" Then reload .vimrc and :PlugInstall to install plugins.
+"
+" Plug options:
+"
+"| Option | Description |
+"| ----------------------- | ------------------------------------------------ |
+"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use |
+"| `rtp` | Subdirectory that contains Vim plugin |
+"| `dir` | Custom directory for the plugin |
+"| `as` | Use different name for the plugin |
+"| `do` | Post-update hook (string or funcref) |
+"| `on` | On-demand loading: Commands or `<Plug>`-mappings |
+"| `for` | On-demand loading: File types |
+"| `frozen` | Do not update unless explicitly specified |
+"
+" More information: https://github.com/junegunn/vim-plug
+"
+"
+" Copyright (c) 2017 Junegunn Choi
+"
+" MIT License
+"
+" 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.
+
+if exists('g:loaded_plug')
+ finish
+endif
+let g:loaded_plug = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+let s:plug_src = 'https://github.com/junegunn/vim-plug.git'
+let s:plug_tab = get(s:, 'plug_tab', -1)
+let s:plug_buf = get(s:, 'plug_buf', -1)
+let s:mac_gui = has('gui_macvim') && has('gui_running')
+let s:is_win = has('win32')
+let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win)
+let s:vim8 = has('patch-8.0.0039') && exists('*job_start')
+if s:is_win && &shellslash
+ set noshellslash
+ let s:me = resolve(expand('<sfile>:p'))
+ set shellslash
+else
+ let s:me = resolve(expand('<sfile>:p'))
+endif
+let s:base_spec = { 'branch': 'master', 'frozen': 0 }
+let s:TYPE = {
+\ 'string': type(''),
+\ 'list': type([]),
+\ 'dict': type({}),
+\ 'funcref': type(function('call'))
+\ }
+let s:loaded = get(s:, 'loaded', {})
+let s:triggers = get(s:, 'triggers', {})
+
+if s:is_win
+ function! s:plug_call(fn, ...)
+ let shellslash = &shellslash
+ try
+ set noshellslash
+ return call(a:fn, a:000)
+ finally
+ let &shellslash = shellslash
+ endtry
+ endfunction
+else
+ function! s:plug_call(fn, ...)
+ return call(a:fn, a:000)
+ endfunction
+endif
+
+function! s:plug_getcwd()
+ return s:plug_call('getcwd')
+endfunction
+
+function! s:plug_fnamemodify(fname, mods)
+ return s:plug_call('fnamemodify', a:fname, a:mods)
+endfunction
+
+function! s:plug_expand(fmt)
+ return s:plug_call('expand', a:fmt, 1)
+endfunction
+
+function! s:plug_tempname()
+ return s:plug_call('tempname')
+endfunction
+
+function! plug#begin(...)
+ if a:0 > 0
+ let s:plug_home_org = a:1
+ let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p'))
+ elseif exists('g:plug_home')
+ let home = s:path(g:plug_home)
+ elseif !empty(&rtp)
+ let home = s:path(split(&rtp, ',')[0]) . '/plugged'
+ else
+ return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
+ endif
+ if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp
+ return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.')
+ endif
+
+ let g:plug_home = home
+ let g:plugs = {}
+ let g:plugs_order = []
+ let s:triggers = {}
+
+ call s:define_commands()
+ return 1
+endfunction
+
+function! s:define_commands()
+ command! -nargs=+ -bar Plug call plug#(<args>)
+ if !executable('git')
+ return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.')
+ endif
+ if has('win32')
+ \ && &shellslash
+ \ && (&shell =~# 'cmd\.exe' || &shell =~# 'powershell\.exe')
+ return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.')
+ endif
+ if !has('nvim')
+ \ && (has('win32') || has('win32unix'))
+ \ && !has('multi_byte')
+ return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.')
+ endif
+ command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(<bang>0, [<f-args>])
+ command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(<bang>0, [<f-args>])
+ command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0)
+ command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif
+ command! -nargs=0 -bar PlugStatus call s:status()
+ command! -nargs=0 -bar PlugDiff call s:diff()
+ command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(<bang>0, <f-args>)
+endfunction
+
+function! s:to_a(v)
+ return type(a:v) == s:TYPE.list ? a:v : [a:v]
+endfunction
+
+function! s:to_s(v)
+ return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n"
+endfunction
+
+function! s:glob(from, pattern)
+ return s:lines(globpath(a:from, a:pattern))
+endfunction
+
+function! s:source(from, ...)
+ let found = 0
+ for pattern in a:000
+ for vim in s:glob(a:from, pattern)
+ execute 'source' s:esc(vim)
+ let found = 1
+ endfor
+ endfor
+ return found
+endfunction
+
+function! s:assoc(dict, key, val)
+ let a:dict[a:key] = add(get(a:dict, a:key, []), a:val)
+endfunction
+
+function! s:ask(message, ...)
+ call inputsave()
+ echohl WarningMsg
+ let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) '))
+ echohl None
+ call inputrestore()
+ echo "\r"
+ return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0
+endfunction
+
+function! s:ask_no_interrupt(...)
+ try
+ return call('s:ask', a:000)
+ catch
+ return 0
+ endtry
+endfunction
+
+function! s:lazy(plug, opt)
+ return has_key(a:plug, a:opt) &&
+ \ (empty(s:to_a(a:plug[a:opt])) ||
+ \ !isdirectory(a:plug.dir) ||
+ \ len(s:glob(s:rtp(a:plug), 'plugin')) ||
+ \ len(s:glob(s:rtp(a:plug), 'after/plugin')))
+endfunction
+
+function! plug#end()
+ if !exists('g:plugs')
+ return s:err('plug#end() called without calling plug#begin() first')
+ endif
+
+ if exists('#PlugLOD')
+ augroup PlugLOD
+ autocmd!
+ augroup END
+ augroup! PlugLOD
+ endif
+ let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
+
+ if exists('g:did_load_filetypes')
+ filetype off
+ endif
+ for name in g:plugs_order
+ if !has_key(g:plugs, name)
+ continue
+ endif
+ let plug = g:plugs[name]
+ if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for')
+ let s:loaded[name] = 1
+ continue
+ endif
+
+ if has_key(plug, 'on')
+ let s:triggers[name] = { 'map': [], 'cmd': [] }
+ for cmd in s:to_a(plug.on)
+ if cmd =~? '^<Plug>.\+'
+ if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
+ call s:assoc(lod.map, cmd, name)
+ endif
+ call add(s:triggers[name].map, cmd)
+ elseif cmd =~# '^[A-Z]'
+ let cmd = substitute(cmd, '!*$', '', '')
+ if exists(':'.cmd) != 2
+ call s:assoc(lod.cmd, cmd, name)
+ endif
+ call add(s:triggers[name].cmd, cmd)
+ else
+ call s:err('Invalid `on` option: '.cmd.
+ \ '. Should start with an uppercase letter or `<Plug>`.')
+ endif
+ endfor
+ endif
+
+ if has_key(plug, 'for')
+ let types = s:to_a(plug.for)
+ if !empty(types)
+ augroup filetypedetect
+ call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
+ augroup END
+ endif
+ for type in types
+ call s:assoc(lod.ft, type, name)
+ endfor
+ endif
+ endfor
+
+ for [cmd, names] in items(lod.cmd)
+ execute printf(
+ \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)',
+ \ cmd, string(cmd), string(names))
+ endfor
+
+ for [map, names] in items(lod.map)
+ for [mode, map_prefix, key_prefix] in
+ \ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
+ execute printf(
+ \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>',
+ \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
+ endfor
+ endfor
+
+ for [ft, names] in items(lod.ft)
+ augroup PlugLOD
+ execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
+ \ ft, string(ft), string(names))
+ augroup END
+ endfor
+
+ call s:reorg_rtp()
+ filetype plugin indent on
+ if has('vim_starting')
+ if has('syntax') && !exists('g:syntax_on')
+ syntax enable
+ end
+ else
+ call s:reload_plugins()
+ endif
+endfunction
+
+function! s:loaded_names()
+ return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)')
+endfunction
+
+function! s:load_plugin(spec)
+ call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim')
+endfunction
+
+function! s:reload_plugins()
+ for name in s:loaded_names()
+ call s:load_plugin(g:plugs[name])
+ endfor
+endfunction
+
+function! s:trim(str)
+ return substitute(a:str, '[\/]\+$', '', '')
+endfunction
+
+function! s:version_requirement(val, min)
+ for idx in range(0, len(a:min) - 1)
+ let v = get(a:val, idx, 0)
+ if v < a:min[idx] | return 0
+ elseif v > a:min[idx] | return 1
+ endif
+ endfor
+ return 1
+endfunction
+
+function! s:git_version_requirement(...)
+ if !exists('s:git_version')
+ let s:git_version = map(split(split(s:system('git --version'))[2], '\.'), 'str2nr(v:val)')
+ endif
+ return s:version_requirement(s:git_version, a:000)
+endfunction
+
+function! s:progress_opt(base)
+ return a:base && !s:is_win &&
+ \ s:git_version_requirement(1, 7, 1) ? '--progress' : ''
+endfunction
+
+function! s:rtp(spec)
+ return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
+endfunction
+
+if s:is_win
+ function! s:path(path)
+ return s:trim(substitute(a:path, '/', '\', 'g'))
+ endfunction
+
+ function! s:dirpath(path)
+ return s:path(a:path) . '\'
+ endfunction
+
+ function! s:is_local_plug(repo)
+ return a:repo =~? '^[a-z]:\|^[%~]'
+ endfunction
+
+ " Copied from fzf
+ function! s:wrap_cmds(cmds)
+ let cmds = [
+ \ '@echo off',
+ \ 'setlocal enabledelayedexpansion']
+ \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])
+ \ + ['endlocal']
+ if has('iconv')
+ if !exists('s:codepage')
+ let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)
+ endif
+ return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage))
+ endif
+ return map(cmds, 'v:val."\r"')
+ endfunction
+
+ function! s:batchfile(cmd)
+ let batchfile = s:plug_tempname().'.bat'
+ call writefile(s:wrap_cmds(a:cmd), batchfile)
+ let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0})
+ if &shell =~# 'powershell\.exe'
+ let cmd = '& ' . cmd
+ endif
+ return [batchfile, cmd]
+ endfunction
+else
+ function! s:path(path)
+ return s:trim(a:path)
+ endfunction
+
+ function! s:dirpath(path)
+ return substitute(a:path, '[/\\]*$', '/', '')
+ endfunction
+
+ function! s:is_local_plug(repo)
+ return a:repo[0] =~ '[/$~]'
+ endfunction
+endif
+
+function! s:err(msg)
+ echohl ErrorMsg
+ echom '[vim-plug] '.a:msg
+ echohl None
+endfunction
+
+function! s:warn(cmd, msg)
+ echohl WarningMsg
+ execute a:cmd 'a:msg'
+ echohl None
+endfunction
+
+function! s:esc(path)
+ return escape(a:path, ' ')
+endfunction
+
+function! s:escrtp(path)
+ return escape(a:path, ' ,')
+endfunction
+
+function! s:remove_rtp()
+ for name in s:loaded_names()
+ let rtp = s:rtp(g:plugs[name])
+ execute 'set rtp-='.s:escrtp(rtp)
+ let after = globpath(rtp, 'after')
+ if isdirectory(after)
+ execute 'set rtp-='.s:escrtp(after)
+ endif
+ endfor
+endfunction
+
+function! s:reorg_rtp()
+ if !empty(s:first_rtp)
+ execute 'set rtp-='.s:first_rtp
+ execute 'set rtp-='.s:last_rtp
+ endif
+
+ " &rtp is modified from outside
+ if exists('s:prtp') && s:prtp !=# &rtp
+ call s:remove_rtp()
+ unlet! s:middle
+ endif
+
+ let s:middle = get(s:, 'middle', &rtp)
+ let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])')
+ let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)')
+ let rtp = join(map(rtps, 'escape(v:val, ",")'), ',')
+ \ . ','.s:middle.','
+ \ . join(map(afters, 'escape(v:val, ",")'), ',')
+ let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g')
+ let s:prtp = &rtp
+
+ if !empty(s:first_rtp)
+ execute 'set rtp^='.s:first_rtp
+ execute 'set rtp+='.s:last_rtp
+ endif
+endfunction
+
+function! s:doautocmd(...)
+ if exists('#'.join(a:000, '#'))
+ execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '<nomodeline>' : '') join(a:000)
+ endif
+endfunction
+
+function! s:dobufread(names)
+ for name in a:names
+ let path = s:rtp(g:plugs[name])
+ for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin']
+ if len(finddir(dir, path))
+ if exists('#BufRead')
+ doautocmd BufRead
+ endif
+ return
+ endif
+ endfor
+ endfor
+endfunction
+
+function! plug#load(...)
+ if a:0 == 0
+ return s:err('Argument missing: plugin name(s) required')
+ endif
+ if !exists('g:plugs')
+ return s:err('plug#begin was not called')
+ endif
+ let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000
+ let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)')
+ if !empty(unknowns)
+ let s = len(unknowns) > 1 ? 's' : ''
+ return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
+ end
+ let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)')
+ if !empty(unloaded)
+ for name in unloaded
+ call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ endfor
+ call s:dobufread(unloaded)
+ return 1
+ end
+ return 0
+endfunction
+
+function! s:remove_triggers(name)
+ if !has_key(s:triggers, a:name)
+ return
+ endif
+ for cmd in s:triggers[a:name].cmd
+ execute 'silent! delc' cmd
+ endfor
+ for map in s:triggers[a:name].map
+ execute 'silent! unmap' map
+ execute 'silent! iunmap' map
+ endfor
+ call remove(s:triggers, a:name)
+endfunction
+
+function! s:lod(names, types, ...)
+ for name in a:names
+ call s:remove_triggers(name)
+ let s:loaded[name] = 1
+ endfor
+ call s:reorg_rtp()
+
+ for name in a:names
+ let rtp = s:rtp(g:plugs[name])
+ for dir in a:types
+ call s:source(rtp, dir.'/**/*.vim')
+ endfor
+ if a:0
+ if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2))
+ execute 'runtime' a:1
+ endif
+ call s:source(rtp, a:2)
+ endif
+ call s:doautocmd('User', name)
+ endfor
+endfunction
+
+function! s:lod_ft(pat, names)
+ let syn = 'syntax/'.a:pat.'.vim'
+ call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn)
+ execute 'autocmd! PlugLOD FileType' a:pat
+ call s:doautocmd('filetypeplugin', 'FileType')
+ call s:doautocmd('filetypeindent', 'FileType')
+endfunction
+
+function! s:lod_cmd(cmd, bang, l1, l2, args, names)
+ call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ call s:dobufread(a:names)
+ execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
+endfunction
+
+function! s:lod_map(map, names, with_prefix, prefix)
+ call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ call s:dobufread(a:names)
+ let extra = ''
+ while 1
+ let c = getchar(0)
+ if c == 0
+ break
+ endif
+ let extra .= nr2char(c)
+ endwhile
+
+ if a:with_prefix
+ let prefix = v:count ? v:count : ''
+ let prefix .= '"'.v:register.a:prefix
+ if mode(1) == 'no'
+ if v:operator == 'c'
+ let prefix = "\<esc>" . prefix
+ endif
+ let prefix .= v:operator
+ endif
+ call feedkeys(prefix, 'n')
+ endif
+ call feedkeys(substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
+endfunction
+
+function! plug#(repo, ...)
+ if a:0 > 1
+ return s:err('Invalid number of arguments (1..2)')
+ endif
+
+ try
+ let repo = s:trim(a:repo)
+ let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec
+ let name = get(opts, 'as', s:plug_fnamemodify(repo, ':t:s?\.git$??'))
+ let spec = extend(s:infer_properties(name, repo), opts)
+ if !has_key(g:plugs, name)
+ call add(g:plugs_order, name)
+ endif
+ let g:plugs[name] = spec
+ let s:loaded[name] = get(s:loaded, name, 0)
+ catch
+ return s:err(v:exception)
+ endtry
+endfunction
+
+function! s:parse_options(arg)
+ let opts = copy(s:base_spec)
+ let type = type(a:arg)
+ if type == s:TYPE.string
+ let opts.tag = a:arg
+ elseif type == s:TYPE.dict
+ call extend(opts, a:arg)
+ if has_key(opts, 'dir')
+ let opts.dir = s:dirpath(s:plug_expand(opts.dir))
+ endif
+ else
+ throw 'Invalid argument type (expected: string or dictionary)'
+ endif
+ return opts
+endfunction
+
+function! s:infer_properties(name, repo)
+ let repo = a:repo
+ if s:is_local_plug(repo)
+ return { 'dir': s:dirpath(s:plug_expand(repo)) }
+ else
+ if repo =~ ':'
+ let uri = repo
+ else
+ if repo !~ '/'
+ throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo)
+ endif
+ let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git')
+ let uri = printf(fmt, repo)
+ endif
+ return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri }
+ endif
+endfunction
+
+function! s:install(force, names)
+ call s:update_impl(0, a:force, a:names)
+endfunction
+
+function! s:update(force, names)
+ call s:update_impl(1, a:force, a:names)
+endfunction
+
+function! plug#helptags()
+ if !exists('g:plugs')
+ return s:err('plug#begin was not called')
+ endif
+ for spec in values(g:plugs)
+ let docd = join([s:rtp(spec), 'doc'], '/')
+ if isdirectory(docd)
+ silent! execute 'helptags' s:esc(docd)
+ endif
+ endfor
+ return 1
+endfunction
+
+function! s:syntax()
+ syntax clear
+ syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
+ syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
+ syn match plugNumber /[0-9]\+[0-9.]*/ contained
+ syn match plugBracket /[[\]]/ contained
+ syn match plugX /x/ contained
+ syn match plugDash /^-/
+ syn match plugPlus /^+/
+ syn match plugStar /^*/
+ syn match plugMessage /\(^- \)\@<=.*/
+ syn match plugName /\(^- \)\@<=[^ ]*:/
+ syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/
+ syn match plugTag /(tag: [^)]\+)/
+ syn match plugInstall /\(^+ \)\@<=[^:]*/
+ syn match plugUpdate /\(^* \)\@<=[^:]*/
+ syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag
+ syn match plugEdge /^ \X\+$/
+ syn match plugEdge /^ \X*/ contained nextgroup=plugSha
+ syn match plugSha /[0-9a-f]\{7,9}/ contained
+ syn match plugRelDate /([^)]*)$/ contained
+ syn match plugNotLoaded /(not loaded)$/
+ syn match plugError /^x.*/
+ syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
+ syn match plugH2 /^.*:\n-\+$/
+ syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
+ hi def link plug1 Title
+ hi def link plug2 Repeat
+ hi def link plugH2 Type
+ hi def link plugX Exception
+ hi def link plugBracket Structure
+ hi def link plugNumber Number
+
+ hi def link plugDash Special
+ hi def link plugPlus Constant
+ hi def link plugStar Boolean
+
+ hi def link plugMessage Function
+ hi def link plugName Label
+ hi def link plugInstall Function
+ hi def link plugUpdate Type
+
+ hi def link plugError Error
+ hi def link plugDeleted Ignore
+ hi def link plugRelDate Comment
+ hi def link plugEdge PreProc
+ hi def link plugSha Identifier
+ hi def link plugTag Constant
+
+ hi def link plugNotLoaded Comment
+endfunction
+
+function! s:lpad(str, len)
+ return a:str . repeat(' ', a:len - len(a:str))
+endfunction
+
+function! s:lines(msg)
+ return split(a:msg, "[\r\n]")
+endfunction
+
+function! s:lastline(msg)
+ return get(s:lines(a:msg), -1, '')
+endfunction
+
+function! s:new_window()
+ execute get(g:, 'plug_window', 'vertical topleft new')
+endfunction
+
+function! s:plug_window_exists()
+ let buflist = tabpagebuflist(s:plug_tab)
+ return !empty(buflist) && index(buflist, s:plug_buf) >= 0
+endfunction
+
+function! s:switch_in()
+ if !s:plug_window_exists()
+ return 0
+ endif
+
+ if winbufnr(0) != s:plug_buf
+ let s:pos = [tabpagenr(), winnr(), winsaveview()]
+ execute 'normal!' s:plug_tab.'gt'
+ let winnr = bufwinnr(s:plug_buf)
+ execute winnr.'wincmd w'
+ call add(s:pos, winsaveview())
+ else
+ let s:pos = [winsaveview()]
+ endif
+
+ setlocal modifiable
+ return 1
+endfunction
+
+function! s:switch_out(...)
+ call winrestview(s:pos[-1])
+ setlocal nomodifiable
+ if a:0 > 0
+ execute a:1
+ endif
+
+ if len(s:pos) > 1
+ execute 'normal!' s:pos[0].'gt'
+ execute s:pos[1] 'wincmd w'
+ call winrestview(s:pos[2])
+ endif
+endfunction
+
+function! s:finish_bindings()
+ nnoremap <silent> <buffer> R :call <SID>retry()<cr>
+ nnoremap <silent> <buffer> D :PlugDiff<cr>
+ nnoremap <silent> <buffer> S :PlugStatus<cr>
+ nnoremap <silent> <buffer> U :call <SID>status_update()<cr>
+ xnoremap <silent> <buffer> U :call <SID>status_update()<cr>
+ nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
+ nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
+endfunction
+
+function! s:prepare(...)
+ if empty(s:plug_getcwd())
+ throw 'Invalid current working directory. Cannot proceed.'
+ endif
+
+ for evar in ['$GIT_DIR', '$GIT_WORK_TREE']
+ if exists(evar)
+ throw evar.' detected. Cannot proceed.'
+ endif
+ endfor
+
+ call s:job_abort()
+ if s:switch_in()
+ if b:plug_preview == 1
+ pc
+ endif
+ enew
+ else
+ call s:new_window()
+ endif
+
+ nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr>
+ if a:0 == 0
+ call s:finish_bindings()
+ endif
+ let b:plug_preview = -1
+ let s:plug_tab = tabpagenr()
+ let s:plug_buf = winbufnr(0)
+ call s:assign_name()
+
+ for k in ['<cr>', 'L', 'o', 'X', 'd', 'dd']
+ execute 'silent! unmap <buffer>' k
+ endfor
+ setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell
+ if exists('+colorcolumn')
+ setlocal colorcolumn=
+ endif
+ setf vim-plug
+ if exists('g:syntax_on')
+ call s:syntax()
+ endif
+endfunction
+
+function! s:assign_name()
+ " Assign buffer name
+ let prefix = '[Plugins]'
+ let name = prefix
+ let idx = 2
+ while bufexists(name)
+ let name = printf('%s (%s)', prefix, idx)
+ let idx = idx + 1
+ endwhile
+ silent! execute 'f' fnameescape(name)
+endfunction
+
+function! s:chsh(swap)
+ let prev = [&shell, &shellcmdflag, &shellredir]
+ if !s:is_win && a:swap
+ set shell=sh shellredir=>%s\ 2>&1
+ endif
+ return prev
+endfunction
+
+function! s:bang(cmd, ...)
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(a:0)
+ " FIXME: Escaping is incomplete. We could use shellescape with eval,
+ " but it won't work on Windows.
+ let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd
+ if s:is_win
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%')
+ execute "normal! :execute g:_plug_bang\<cr>\<cr>"
+ finally
+ unlet g:_plug_bang
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ call delete(batchfile)
+ endif
+ endtry
+ return v:shell_error ? 'Exit status: ' . v:shell_error : ''
+endfunction
+
+function! s:regress_bar()
+ let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '')
+ call s:progress_bar(2, bar, len(bar))
+endfunction
+
+function! s:is_updated(dir)
+ return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir))
+endfunction
+
+function! s:do(pull, force, todo)
+ for [name, spec] in items(a:todo)
+ if !isdirectory(spec.dir)
+ continue
+ endif
+ let installed = has_key(s:update.new, name)
+ let updated = installed ? 0 :
+ \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir))
+ if a:force || installed || updated
+ execute 'cd' s:esc(spec.dir)
+ call append(3, '- Post-update hook for '. name .' ... ')
+ let error = ''
+ let type = type(spec.do)
+ if type == s:TYPE.string
+ if spec.do[0] == ':'
+ if !get(s:loaded, name, 0)
+ let s:loaded[name] = 1
+ call s:reorg_rtp()
+ endif
+ call s:load_plugin(spec)
+ try
+ execute spec.do[1:]
+ catch
+ let error = v:exception
+ endtry
+ if !s:plug_window_exists()
+ cd -
+ throw 'Warning: vim-plug was terminated by the post-update hook of '.name
+ endif
+ else
+ let error = s:bang(spec.do)
+ endif
+ elseif type == s:TYPE.funcref
+ try
+ let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
+ call spec.do({ 'name': name, 'status': status, 'force': a:force })
+ catch
+ let error = v:exception
+ endtry
+ else
+ let error = 'Invalid hook type'
+ endif
+ call s:switch_in()
+ call setline(4, empty(error) ? (getline(4) . 'OK')
+ \ : ('x' . getline(4)[1:] . error))
+ if !empty(error)
+ call add(s:update.errors, name)
+ call s:regress_bar()
+ endif
+ cd -
+ endif
+ endfor
+endfunction
+
+function! s:hash_match(a, b)
+ return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
+endfunction
+
+function! s:checkout(spec)
+ let sha = a:spec.commit
+ let output = s:system('git rev-parse HEAD', a:spec.dir)
+ if !v:shell_error && !s:hash_match(sha, s:lines(output)[0])
+ let output = s:system(
+ \ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
+ endif
+ return output
+endfunction
+
+function! s:finish(pull)
+ let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen'))
+ if new_frozen
+ let s = new_frozen > 1 ? 's' : ''
+ call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s))
+ endif
+ call append(3, '- Finishing ... ') | 4
+ redraw
+ call plug#helptags()
+ call plug#end()
+ call setline(4, getline(4) . 'Done!')
+ redraw
+ let msgs = []
+ if !empty(s:update.errors)
+ call add(msgs, "Press 'R' to retry.")
+ endif
+ if a:pull && len(s:update.new) < len(filter(getline(5, '$'),
+ \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'"))
+ call add(msgs, "Press 'D' to see the updated changes.")
+ endif
+ echo join(msgs, ' ')
+ call s:finish_bindings()
+endfunction
+
+function! s:retry()
+ if empty(s:update.errors)
+ return
+ endif
+ echo
+ call s:update_impl(s:update.pull, s:update.force,
+ \ extend(copy(s:update.errors), [s:update.threads]))
+endfunction
+
+function! s:is_managed(name)
+ return has_key(g:plugs[a:name], 'uri')
+endfunction
+
+function! s:names(...)
+ return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)'))
+endfunction
+
+function! s:check_ruby()
+ silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'")
+ if !exists('g:plug_ruby')
+ redraw!
+ return s:warn('echom', 'Warning: Ruby interface is broken')
+ endif
+ let ruby_version = split(g:plug_ruby, '\.')
+ unlet g:plug_ruby
+ return s:version_requirement(ruby_version, [1, 8, 7])
+endfunction
+
+function! s:update_impl(pull, force, args) abort
+ let sync = index(a:args, '--sync') >= 0 || has('vim_starting')
+ let args = filter(copy(a:args), 'v:val != "--sync"')
+ let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
+ \ remove(args, -1) : get(g:, 'plug_threads', 16)
+
+ let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
+ let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
+ \ filter(managed, 'index(args, v:key) >= 0')
+
+ if empty(todo)
+ return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install'))
+ endif
+
+ if !s:is_win && s:git_version_requirement(2, 3)
+ let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : ''
+ let $GIT_TERMINAL_PROMPT = 0
+ for plug in values(todo)
+ let plug.uri = substitute(plug.uri,
+ \ '^https://git::@github\.com', 'https://github.com', '')
+ endfor
+ endif
+
+ if !isdirectory(g:plug_home)
+ try
+ call mkdir(g:plug_home, 'p')
+ catch
+ return s:err(printf('Invalid plug directory: %s. '.
+ \ 'Try to call plug#begin with a valid directory', g:plug_home))
+ endtry
+ endif
+
+ if has('nvim') && !exists('*jobwait') && threads > 1
+ call s:warn('echom', '[vim-plug] Update Neovim for parallel installer')
+ endif
+
+ let use_job = s:nvim || s:vim8
+ let python = (has('python') || has('python3')) && !use_job
+ let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby()
+
+ let s:update = {
+ \ 'start': reltime(),
+ \ 'all': todo,
+ \ 'todo': copy(todo),
+ \ 'errors': [],
+ \ 'pull': a:pull,
+ \ 'force': a:force,
+ \ 'new': {},
+ \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1,
+ \ 'bar': '',
+ \ 'fin': 0
+ \ }
+
+ call s:prepare(1)
+ call append(0, ['', ''])
+ normal! 2G
+ silent! redraw
+
+ let s:clone_opt = get(g:, 'plug_shallow', 1) ?
+ \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : ''
+
+ if has('win32unix') || has('wsl')
+ let s:clone_opt .= ' -c core.eol=lf -c core.autocrlf=input'
+ endif
+
+ let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : ''
+
+ " Python version requirement (>= 2.7)
+ if python && !has('python3') && !ruby && !use_job && s:update.threads > 1
+ redir => pyv
+ silent python import platform; print platform.python_version()
+ redir END
+ let python = s:version_requirement(
+ \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6])
+ endif
+
+ if (python || ruby) && s:update.threads > 1
+ try
+ let imd = &imd
+ if s:mac_gui
+ set noimd
+ endif
+ if ruby
+ call s:update_ruby()
+ else
+ call s:update_python()
+ endif
+ catch
+ let lines = getline(4, '$')
+ let printed = {}
+ silent! 4,$d _
+ for line in lines
+ let name = s:extract_name(line, '.', '')
+ if empty(name) || !has_key(printed, name)
+ call append('$', line)
+ if !empty(name)
+ let printed[name] = 1
+ if line[0] == 'x' && index(s:update.errors, name) < 0
+ call add(s:update.errors, name)
+ end
+ endif
+ endif
+ endfor
+ finally
+ let &imd = imd
+ call s:update_finish()
+ endtry
+ else
+ call s:update_vim()
+ while use_job && sync
+ sleep 100m
+ if s:update.fin
+ break
+ endif
+ endwhile
+ endif
+endfunction
+
+function! s:log4(name, msg)
+ call setline(4, printf('- %s (%s)', a:msg, a:name))
+ redraw
+endfunction
+
+function! s:update_finish()
+ if exists('s:git_terminal_prompt')
+ let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt
+ endif
+ if s:switch_in()
+ call append(3, '- Updating ...') | 4
+ for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))'))
+ let [pos, _] = s:logpos(name)
+ if !pos
+ continue
+ endif
+ if has_key(spec, 'commit')
+ call s:log4(name, 'Checking out '.spec.commit)
+ let out = s:checkout(spec)
+ elseif has_key(spec, 'tag')
+ let tag = spec.tag
+ if tag =~ '\*'
+ let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir))
+ if !v:shell_error && !empty(tags)
+ let tag = tags[0]
+ call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag))
+ call append(3, '')
+ endif
+ endif
+ call s:log4(name, 'Checking out '.tag)
+ let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
+ else
+ let branch = get(spec, 'branch', 'master')
+ call s:log4(name, 'Merging origin/'.s:esc(branch))
+ let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1'
+ \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir)
+ endif
+ if !v:shell_error && filereadable(spec.dir.'/.gitmodules') &&
+ \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
+ call s:log4(name, 'Updating submodules. This may take a while.')
+ let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
+ endif
+ let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
+ if v:shell_error
+ call add(s:update.errors, name)
+ call s:regress_bar()
+ silent execute pos 'd _'
+ call append(4, msg) | 4
+ elseif !empty(out)
+ call setline(pos, msg[0])
+ endif
+ redraw
+ endfor
+ silent 4 d _
+ try
+ call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")'))
+ catch
+ call s:warn('echom', v:exception)
+ call s:warn('echo', '')
+ return
+ endtry
+ call s:finish(s:update.pull)
+ call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.')
+ call s:switch_out('normal! gg')
+ endif
+endfunction
+
+function! s:job_abort()
+ if (!s:nvim && !s:vim8) || !exists('s:jobs')
+ return
+ endif
+
+ for [name, j] in items(s:jobs)
+ if s:nvim
+ silent! call jobstop(j.jobid)
+ elseif s:vim8
+ silent! call job_stop(j.jobid)
+ endif
+ if j.new
+ call s:rm_rf(g:plugs[name].dir)
+ endif
+ endfor
+ let s:jobs = {}
+endfunction
+
+function! s:last_non_empty_line(lines)
+ let len = len(a:lines)
+ for idx in range(len)
+ let line = a:lines[len-idx-1]
+ if !empty(line)
+ return line
+ endif
+ endfor
+ return ''
+endfunction
+
+function! s:job_out_cb(self, data) abort
+ let self = a:self
+ let data = remove(self.lines, -1) . a:data
+ let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]')
+ call extend(self.lines, lines)
+ " To reduce the number of buffer updates
+ let self.tick = get(self, 'tick', -1) + 1
+ if !self.running || self.tick % len(s:jobs) == 0
+ let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-')
+ let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines)
+ call s:log(bullet, self.name, result)
+ endif
+endfunction
+
+function! s:job_exit_cb(self, data) abort
+ let a:self.running = 0
+ let a:self.error = a:data != 0
+ call s:reap(a:self.name)
+ call s:tick()
+endfunction
+
+function! s:job_cb(fn, job, ch, data)
+ if !s:plug_window_exists() " plug window closed
+ return s:job_abort()
+ endif
+ call call(a:fn, [a:job, a:data])
+endfunction
+
+function! s:nvim_cb(job_id, data, event) dict abort
+ return a:event == 'stdout' ?
+ \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) :
+ \ s:job_cb('s:job_exit_cb', self, 0, a:data)
+endfunction
+
+function! s:spawn(name, cmd, opts)
+ let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''],
+ \ 'new': get(a:opts, 'new', 0) }
+ let s:jobs[a:name] = job
+ let cmd = has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir, 0) : a:cmd
+ let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd]
+
+ if s:nvim
+ call extend(job, {
+ \ 'on_stdout': function('s:nvim_cb'),
+ \ 'on_exit': function('s:nvim_cb'),
+ \ })
+ let jid = s:plug_call('jobstart', argv, job)
+ if jid > 0
+ let job.jobid = jid
+ else
+ let job.running = 0
+ let job.error = 1
+ let job.lines = [jid < 0 ? argv[0].' is not executable' :
+ \ 'Invalid arguments (or job table is full)']
+ endif
+ elseif s:vim8
+ let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
+ \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]),
+ \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]),
+ \ 'out_mode': 'raw'
+ \})
+ if job_status(jid) == 'run'
+ let job.jobid = jid
+ else
+ let job.running = 0
+ let job.error = 1
+ let job.lines = ['Failed to start job']
+ endif
+ else
+ let job.lines = s:lines(call('s:system', [cmd]))
+ let job.error = v:shell_error != 0
+ let job.running = 0
+ endif
+endfunction
+
+function! s:reap(name)
+ let job = s:jobs[a:name]
+ if job.error
+ call add(s:update.errors, a:name)
+ elseif get(job, 'new', 0)
+ let s:update.new[a:name] = 1
+ endif
+ let s:update.bar .= job.error ? 'x' : '='
+
+ let bullet = job.error ? 'x' : '-'
+ let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines)
+ call s:log(bullet, a:name, empty(result) ? 'OK' : result)
+ call s:bar()
+
+ call remove(s:jobs, a:name)
+endfunction
+
+function! s:bar()
+ if s:switch_in()
+ let total = len(s:update.all)
+ call setline(1, (s:update.pull ? 'Updating' : 'Installing').
+ \ ' plugins ('.len(s:update.bar).'/'.total.')')
+ call s:progress_bar(2, s:update.bar, total)
+ call s:switch_out()
+ endif
+endfunction
+
+function! s:logpos(name)
+ let max = line('$')
+ for i in range(4, max > 4 ? max : 4)
+ if getline(i) =~# '^[-+x*] '.a:name.':'
+ for j in range(i + 1, max > 5 ? max : 5)
+ if getline(j) !~ '^ '
+ return [i, j - 1]
+ endif
+ endfor
+ return [i, i]
+ endif
+ endfor
+ return [0, 0]
+endfunction
+
+function! s:log(bullet, name, lines)
+ if s:switch_in()
+ let [b, e] = s:logpos(a:name)
+ if b > 0
+ silent execute printf('%d,%d d _', b, e)
+ if b > winheight('.')
+ let b = 4
+ endif
+ else
+ let b = 4
+ endif
+ " FIXME For some reason, nomodifiable is set after :d in vim8
+ setlocal modifiable
+ call append(b - 1, s:format_message(a:bullet, a:name, a:lines))
+ call s:switch_out()
+ endif
+endfunction
+
+function! s:update_vim()
+ let s:jobs = {}
+
+ call s:bar()
+ call s:tick()
+endfunction
+
+function! s:tick()
+ let pull = s:update.pull
+ let prog = s:progress_opt(s:nvim || s:vim8)
+while 1 " Without TCO, Vim stack is bound to explode
+ if empty(s:update.todo)
+ if empty(s:jobs) && !s:update.fin
+ call s:update_finish()
+ let s:update.fin = 1
+ endif
+ return
+ endif
+
+ let name = keys(s:update.todo)[0]
+ let spec = remove(s:update.todo, name)
+ let new = empty(globpath(spec.dir, '.git', 1))
+
+ call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
+ redraw
+
+ let has_tag = has_key(spec, 'tag')
+ if !new
+ let [error, _] = s:git_validate(spec, 0)
+ if empty(error)
+ if pull
+ let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : ''
+ call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir })
+ else
+ let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
+ endif
+ else
+ let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 }
+ endif
+ else
+ call s:spawn(name,
+ \ printf('git clone %s %s %s %s 2>&1',
+ \ has_tag ? '' : s:clone_opt,
+ \ prog,
+ \ plug#shellescape(spec.uri, {'script': 0}),
+ \ plug#shellescape(s:trim(spec.dir), {'script': 0})), { 'new': 1 })
+ endif
+
+ if !s:jobs[name].running
+ call s:reap(name)
+ endif
+ if len(s:jobs) >= s:update.threads
+ break
+ endif
+endwhile
+endfunction
+
+function! s:update_python()
+let py_exe = has('python') ? 'python' : 'python3'
+execute py_exe "<< EOF"
+import datetime
+import functools
+import os
+try:
+ import queue
+except ImportError:
+ import Queue as queue
+import random
+import re
+import shutil
+import signal
+import subprocess
+import tempfile
+import threading as thr
+import time
+import traceback
+import vim
+
+G_NVIM = vim.eval("has('nvim')") == '1'
+G_PULL = vim.eval('s:update.pull') == '1'
+G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
+G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
+G_CLONE_OPT = vim.eval('s:clone_opt')
+G_PROGRESS = vim.eval('s:progress_opt(1)')
+G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
+G_STOP = thr.Event()
+G_IS_WIN = vim.eval('s:is_win') == '1'
+
+class PlugError(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+class CmdTimedOut(PlugError):
+ pass
+class CmdFailed(PlugError):
+ pass
+class InvalidURI(PlugError):
+ pass
+class Action(object):
+ INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-']
+
+class Buffer(object):
+ def __init__(self, lock, num_plugs, is_pull):
+ self.bar = ''
+ self.event = 'Updating' if is_pull else 'Installing'
+ self.lock = lock
+ self.maxy = int(vim.eval('winheight(".")'))
+ self.num_plugs = num_plugs
+
+ def __where(self, name):
+ """ Find first line with name in current buffer. Return line num. """
+ found, lnum = False, 0
+ matcher = re.compile('^[-+x*] {0}:'.format(name))
+ for line in vim.current.buffer:
+ if matcher.search(line) is not None:
+ found = True
+ break
+ lnum += 1
+
+ if not found:
+ lnum = -1
+ return lnum
+
+ def header(self):
+ curbuf = vim.current.buffer
+ curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs)
+
+ num_spaces = self.num_plugs - len(self.bar)
+ curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ')
+
+ with self.lock:
+ vim.command('normal! 2G')
+ vim.command('redraw')
+
+ def write(self, action, name, lines):
+ first, rest = lines[0], lines[1:]
+ msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)]
+ msg.extend([' ' + line for line in rest])
+
+ try:
+ if action == Action.ERROR:
+ self.bar += 'x'
+ vim.command("call add(s:update.errors, '{0}')".format(name))
+ elif action == Action.DONE:
+ self.bar += '='
+
+ curbuf = vim.current.buffer
+ lnum = self.__where(name)
+ if lnum != -1: # Found matching line num
+ del curbuf[lnum]
+ if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]):
+ lnum = 3
+ else:
+ lnum = 3
+ curbuf.append(msg, lnum)
+
+ self.header()
+ except vim.error:
+ pass
+
+class Command(object):
+ CD = 'cd /d' if G_IS_WIN else 'cd'
+
+ def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None):
+ self.cmd = cmd
+ if cmd_dir:
+ self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd)
+ self.timeout = timeout
+ self.callback = cb if cb else (lambda msg: None)
+ self.clean = clean if clean else (lambda: None)
+ self.proc = None
+
+ @property
+ def alive(self):
+ """ Returns true only if command still running. """
+ return self.proc and self.proc.poll() is None
+
+ def execute(self, ntries=3):
+ """ Execute the command with ntries if CmdTimedOut.
+ Returns the output of the command if no Exception.
+ """
+ attempt, finished, limit = 0, False, self.timeout
+
+ while not finished:
+ try:
+ attempt += 1
+ result = self.try_command()
+ finished = True
+ return result
+ except CmdTimedOut:
+ if attempt != ntries:
+ self.notify_retry()
+ self.timeout += limit
+ else:
+ raise
+
+ def notify_retry(self):
+ """ Retry required for command, notify user. """
+ for count in range(3, 0, -1):
+ if G_STOP.is_set():
+ raise KeyboardInterrupt
+ msg = 'Timeout. Will retry in {0} second{1} ...'.format(
+ count, 's' if count != 1 else '')
+ self.callback([msg])
+ time.sleep(1)
+ self.callback(['Retrying ...'])
+
+ def try_command(self):
+ """ Execute a cmd & poll for callback. Returns list of output.
+ Raises CmdFailed -> return code for Popen isn't 0
+ Raises CmdTimedOut -> command exceeded timeout without new output
+ """
+ first_line = True
+
+ try:
+ tfile = tempfile.NamedTemporaryFile(mode='w+b')
+ preexec_fn = not G_IS_WIN and os.setsid or None
+ self.proc = subprocess.Popen(self.cmd, stdout=tfile,
+ stderr=subprocess.STDOUT,
+ stdin=subprocess.PIPE, shell=True,
+ preexec_fn=preexec_fn)
+ thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,))
+ thrd.start()
+
+ thread_not_started = True
+ while thread_not_started:
+ try:
+ thrd.join(0.1)
+ thread_not_started = False
+ except RuntimeError:
+ pass
+
+ while self.alive:
+ if G_STOP.is_set():
+ raise KeyboardInterrupt
+
+ if first_line or random.random() < G_LOG_PROB:
+ first_line = False
+ line = '' if G_IS_WIN else nonblock_read(tfile.name)
+ if line:
+ self.callback([line])
+
+ time_diff = time.time() - os.path.getmtime(tfile.name)
+ if time_diff > self.timeout:
+ raise CmdTimedOut(['Timeout!'])
+
+ thrd.join(0.5)
+
+ tfile.seek(0)
+ result = [line.decode('utf-8', 'replace').rstrip() for line in tfile]
+
+ if self.proc.returncode != 0:
+ raise CmdFailed([''] + result)
+
+ return result
+ except:
+ self.terminate()
+ raise
+
+ def terminate(self):
+ """ Terminate process and cleanup. """
+ if self.alive:
+ if G_IS_WIN:
+ os.kill(self.proc.pid, signal.SIGINT)
+ else:
+ os.killpg(self.proc.pid, signal.SIGTERM)
+ self.clean()
+
+class Plugin(object):
+ def __init__(self, name, args, buf_q, lock):
+ self.name = name
+ self.args = args
+ self.buf_q = buf_q
+ self.lock = lock
+ self.tag = args.get('tag', 0)
+
+ def manage(self):
+ try:
+ if os.path.exists(self.args['dir']):
+ self.update()
+ else:
+ self.install()
+ with self.lock:
+ thread_vim_command("let s:update.new['{0}'] = 1".format(self.name))
+ except PlugError as exc:
+ self.write(Action.ERROR, self.name, exc.msg)
+ except KeyboardInterrupt:
+ G_STOP.set()
+ self.write(Action.ERROR, self.name, ['Interrupted!'])
+ except:
+ # Any exception except those above print stack trace
+ msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip())
+ self.write(Action.ERROR, self.name, msg.split('\n'))
+ raise
+
+ def install(self):
+ target = self.args['dir']
+ if target[-1] == '\\':
+ target = target[0:-1]
+
+ def clean(target):
+ def _clean():
+ try:
+ shutil.rmtree(target)
+ except OSError:
+ pass
+ return _clean
+
+ self.write(Action.INSTALL, self.name, ['Installing ...'])
+ callback = functools.partial(self.write, Action.INSTALL, self.name)
+ cmd = 'git clone {0} {1} {2} {3} 2>&1'.format(
+ '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'],
+ esc(target))
+ com = Command(cmd, None, G_TIMEOUT, callback, clean(target))
+ result = com.execute(G_RETRIES)
+ self.write(Action.DONE, self.name, result[-1:])
+
+ def repo_uri(self):
+ cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url'
+ command = Command(cmd, self.args['dir'], G_TIMEOUT,)
+ result = command.execute(G_RETRIES)
+ return result[-1]
+
+ def update(self):
+ actual_uri = self.repo_uri()
+ expect_uri = self.args['uri']
+ regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$')
+ ma = regex.match(actual_uri)
+ mb = regex.match(expect_uri)
+ if ma is None or mb is None or ma.groups() != mb.groups():
+ msg = ['',
+ 'Invalid URI: {0}'.format(actual_uri),
+ 'Expected {0}'.format(expect_uri),
+ 'PlugClean required.']
+ raise InvalidURI(msg)
+
+ if G_PULL:
+ self.write(Action.UPDATE, self.name, ['Updating ...'])
+ callback = functools.partial(self.write, Action.UPDATE, self.name)
+ fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else ''
+ cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS)
+ com = Command(cmd, self.args['dir'], G_TIMEOUT, callback)
+ result = com.execute(G_RETRIES)
+ self.write(Action.DONE, self.name, result[-1:])
+ else:
+ self.write(Action.DONE, self.name, ['Already installed'])
+
+ def write(self, action, name, msg):
+ self.buf_q.put((action, name, msg))
+
+class PlugThread(thr.Thread):
+ def __init__(self, tname, args):
+ super(PlugThread, self).__init__()
+ self.tname = tname
+ self.args = args
+
+ def run(self):
+ thr.current_thread().name = self.tname
+ buf_q, work_q, lock = self.args
+
+ try:
+ while not G_STOP.is_set():
+ name, args = work_q.get_nowait()
+ plug = Plugin(name, args, buf_q, lock)
+ plug.manage()
+ work_q.task_done()
+ except queue.Empty:
+ pass
+
+class RefreshThread(thr.Thread):
+ def __init__(self, lock):
+ super(RefreshThread, self).__init__()
+ self.lock = lock
+ self.running = True
+
+ def run(self):
+ while self.running:
+ with self.lock:
+ thread_vim_command('noautocmd normal! a')
+ time.sleep(0.33)
+
+ def stop(self):
+ self.running = False
+
+if G_NVIM:
+ def thread_vim_command(cmd):
+ vim.session.threadsafe_call(lambda: vim.command(cmd))
+else:
+ def thread_vim_command(cmd):
+ vim.command(cmd)
+
+def esc(name):
+ return '"' + name.replace('"', '\"') + '"'
+
+def nonblock_read(fname):
+ """ Read a file with nonblock flag. Return the last line. """
+ fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK)
+ buf = os.read(fread, 100000).decode('utf-8', 'replace')
+ os.close(fread)
+
+ line = buf.rstrip('\r\n')
+ left = max(line.rfind('\r'), line.rfind('\n'))
+ if left != -1:
+ left += 1
+ line = line[left:]
+
+ return line
+
+def main():
+ thr.current_thread().name = 'main'
+ nthreads = int(vim.eval('s:update.threads'))
+ plugs = vim.eval('s:update.todo')
+ mac_gui = vim.eval('s:mac_gui') == '1'
+
+ lock = thr.Lock()
+ buf = Buffer(lock, len(plugs), G_PULL)
+ buf_q, work_q = queue.Queue(), queue.Queue()
+ for work in plugs.items():
+ work_q.put(work)
+
+ start_cnt = thr.active_count()
+ for num in range(nthreads):
+ tname = 'PlugT-{0:02}'.format(num)
+ thread = PlugThread(tname, (buf_q, work_q, lock))
+ thread.start()
+ if mac_gui:
+ rthread = RefreshThread(lock)
+ rthread.start()
+
+ while not buf_q.empty() or thr.active_count() != start_cnt:
+ try:
+ action, name, msg = buf_q.get(True, 0.25)
+ buf.write(action, name, ['OK'] if not msg else msg)
+ buf_q.task_done()
+ except queue.Empty:
+ pass
+ except KeyboardInterrupt:
+ G_STOP.set()
+
+ if mac_gui:
+ rthread.stop()
+ rthread.join()
+
+main()
+EOF
+endfunction
+
+function! s:update_ruby()
+ ruby << EOF
+ module PlugStream
+ SEP = ["\r", "\n", nil]
+ def get_line
+ buffer = ''
+ loop do
+ char = readchar rescue return
+ if SEP.include? char.chr
+ buffer << $/
+ break
+ else
+ buffer << char
+ end
+ end
+ buffer
+ end
+ end unless defined?(PlugStream)
+
+ def esc arg
+ %["#{arg.gsub('"', '\"')}"]
+ end
+
+ def killall pid
+ pids = [pid]
+ if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
+ pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil }
+ else
+ unless `which pgrep 2> /dev/null`.empty?
+ children = pids
+ until children.empty?
+ children = children.map { |pid|
+ `pgrep -P #{pid}`.lines.map { |l| l.chomp }
+ }.flatten
+ pids += children
+ end
+ end
+ pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
+ end
+ end
+
+ def compare_git_uri a, b
+ regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$}
+ regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1)
+ end
+
+ require 'thread'
+ require 'fileutils'
+ require 'timeout'
+ running = true
+ iswin = VIM::evaluate('s:is_win').to_i == 1
+ pull = VIM::evaluate('s:update.pull').to_i == 1
+ base = VIM::evaluate('g:plug_home')
+ all = VIM::evaluate('s:update.todo')
+ limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
+ tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
+ nthr = VIM::evaluate('s:update.threads').to_i
+ maxy = VIM::evaluate('winheight(".")').to_i
+ vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/
+ cd = iswin ? 'cd /d' : 'cd'
+ tot = VIM::evaluate('len(s:update.todo)') || 0
+ bar = ''
+ skip = 'Already installed'
+ mtx = Mutex.new
+ take1 = proc { mtx.synchronize { running && all.shift } }
+ logh = proc {
+ cnt = bar.length
+ $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
+ $curbuf[2] = '[' + bar.ljust(tot) + ']'
+ VIM::command('normal! 2G')
+ VIM::command('redraw')
+ }
+ where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
+ log = proc { |name, result, type|
+ mtx.synchronize do
+ ing = ![true, false].include?(type)
+ bar += type ? '=' : 'x' unless ing
+ b = case type
+ when :install then '+' when :update then '*'
+ when true, nil then '-' else
+ VIM::command("call add(s:update.errors, '#{name}')")
+ 'x'
+ end
+ result =
+ if type || type.nil?
+ ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"]
+ elsif result =~ /^Interrupted|^Timeout/
+ ["#{b} #{name}: #{result}"]
+ else
+ ["#{b} #{name}"] + result.lines.map { |l| " " << l }
+ end
+ if lnum = where.call(name)
+ $curbuf.delete lnum
+ lnum = 4 if ing && lnum > maxy
+ end
+ result.each_with_index do |line, offset|
+ $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
+ end
+ logh.call
+ end
+ }
+ bt = proc { |cmd, name, type, cleanup|
+ tried = timeout = 0
+ begin
+ tried += 1
+ timeout += limit
+ fd = nil
+ data = ''
+ if iswin
+ Timeout::timeout(timeout) do
+ tmp = VIM::evaluate('tempname()')
+ system("(#{cmd}) > #{tmp}")
+ data = File.read(tmp).chomp
+ File.unlink tmp rescue nil
+ end
+ else
+ fd = IO.popen(cmd).extend(PlugStream)
+ first_line = true
+ log_prob = 1.0 / nthr
+ while line = Timeout::timeout(timeout) { fd.get_line }
+ data << line
+ log.call name, line.chomp, type if name && (first_line || rand < log_prob)
+ first_line = false
+ end
+ fd.close
+ end
+ [$? == 0, data.chomp]
+ rescue Timeout::Error, Interrupt => e
+ if fd && !fd.closed?
+ killall fd.pid
+ fd.close
+ end
+ cleanup.call if cleanup
+ if e.is_a?(Timeout::Error) && tried < tries
+ 3.downto(1) do |countdown|
+ s = countdown > 1 ? 's' : ''
+ log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
+ sleep 1
+ end
+ log.call name, 'Retrying ...', type
+ retry
+ end
+ [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
+ end
+ }
+ main = Thread.current
+ threads = []
+ watcher = Thread.new {
+ if vim7
+ while VIM::evaluate('getchar(1)')
+ sleep 0.1
+ end
+ else
+ require 'io/console' # >= Ruby 1.9
+ nil until IO.console.getch == 3.chr
+ end
+ mtx.synchronize do
+ running = false
+ threads.each { |t| t.raise Interrupt } unless vim7
+ end
+ threads.each { |t| t.join rescue nil }
+ main.kill
+ }
+ refresh = Thread.new {
+ while true
+ mtx.synchronize do
+ break unless running
+ VIM::command('noautocmd normal! a')
+ end
+ sleep 0.2
+ end
+ } if VIM::evaluate('s:mac_gui') == 1
+
+ clone_opt = VIM::evaluate('s:clone_opt')
+ progress = VIM::evaluate('s:progress_opt(1)')
+ nthr.times do
+ mtx.synchronize do
+ threads << Thread.new {
+ while pair = take1.call
+ name = pair.first
+ dir, uri, tag = pair.last.values_at *%w[dir uri tag]
+ exists = File.directory? dir
+ ok, result =
+ if exists
+ chdir = "#{cd} #{iswin ? dir : esc(dir)}"
+ ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil
+ current_uri = data.lines.to_a.last
+ if !ret
+ if data =~ /^Interrupted|^Timeout/
+ [false, data]
+ else
+ [false, [data.chomp, "PlugClean required."].join($/)]
+ end
+ elsif !compare_git_uri(current_uri, uri)
+ [false, ["Invalid URI: #{current_uri}",
+ "Expected: #{uri}",
+ "PlugClean required."].join($/)]
+ else
+ if pull
+ log.call name, 'Updating ...', :update
+ fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : ''
+ bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil
+ else
+ [true, skip]
+ end
+ end
+ else
+ d = esc dir.sub(%r{[\\/]+$}, '')
+ log.call name, 'Installing ...', :install
+ bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc {
+ FileUtils.rm_rf dir
+ }
+ end
+ mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok
+ log.call name, result, ok
+ end
+ } if running
+ end
+ end
+ threads.each { |t| t.join rescue nil }
+ logh.call
+ refresh.kill if refresh
+ watcher.kill
+EOF
+endfunction
+
+function! s:shellesc_cmd(arg, script)
+ let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g')
+ return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g')
+endfunction
+
+function! s:shellesc_ps1(arg)
+ return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'"
+endfunction
+
+function! s:shellesc_sh(arg)
+ return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'"
+endfunction
+
+function! plug#shellescape(arg, ...)
+ let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
+ let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
+ let script = get(opts, 'script', 1)
+ if shell =~# 'cmd\.exe'
+ return s:shellesc_cmd(a:arg, script)
+ elseif shell =~# 'powershell\.exe' || shell =~# 'pwsh$'
+ return s:shellesc_ps1(a:arg)
+ endif
+ return s:shellesc_sh(a:arg)
+endfunction
+
+function! s:glob_dir(path)
+ return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
+endfunction
+
+function! s:progress_bar(line, bar, total)
+ call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
+endfunction
+
+function! s:compare_git_uri(a, b)
+ " See `git help clone'
+ " https:// [user@] github.com[:port] / junegunn/vim-plug [.git]
+ " [git@] github.com[:port] : junegunn/vim-plug [.git]
+ " file:// / junegunn/vim-plug [/]
+ " / junegunn/vim-plug [/]
+ let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$'
+ let ma = matchlist(a:a, pat)
+ let mb = matchlist(a:b, pat)
+ return ma[1:2] ==# mb[1:2]
+endfunction
+
+function! s:format_message(bullet, name, message)
+ if a:bullet != 'x'
+ return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))]
+ else
+ let lines = map(s:lines(a:message), '" ".v:val')
+ return extend([printf('x %s:', a:name)], lines)
+ endif
+endfunction
+
+function! s:with_cd(cmd, dir, ...)
+ let script = a:0 > 0 ? a:1 : 1
+ return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd)
+endfunction
+
+function! s:system(cmd, ...)
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd
+ if s:is_win
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ return system(cmd)
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ call delete(batchfile)
+ endif
+ endtry
+endfunction
+
+function! s:system_chomp(...)
+ let ret = call('s:system', a:000)
+ return v:shell_error ? '' : substitute(ret, '\n$', '', '')
+endfunction
+
+function! s:git_validate(spec, check_branch)
+ let err = ''
+ if isdirectory(a:spec.dir)
+ let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir))
+ let remote = result[-1]
+ if v:shell_error
+ let err = join([remote, 'PlugClean required.'], "\n")
+ elseif !s:compare_git_uri(remote, a:spec.uri)
+ let err = join(['Invalid URI: '.remote,
+ \ 'Expected: '.a:spec.uri,
+ \ 'PlugClean required.'], "\n")
+ elseif a:check_branch && has_key(a:spec, 'commit')
+ let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir))
+ let sha = result[-1]
+ if v:shell_error
+ let err = join(add(result, 'PlugClean required.'), "\n")
+ elseif !s:hash_match(sha, a:spec.commit)
+ let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
+ \ a:spec.commit[:6], sha[:6]),
+ \ 'PlugUpdate required.'], "\n")
+ endif
+ elseif a:check_branch
+ let branch = result[0]
+ " Check tag
+ if has_key(a:spec, 'tag')
+ let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
+ if a:spec.tag !=# tag && a:spec.tag !~ '\*'
+ let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
+ \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
+ endif
+ " Check branch
+ elseif a:spec.branch !=# branch
+ let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
+ \ branch, a:spec.branch)
+ endif
+ if empty(err)
+ let [ahead, behind] = split(s:lastline(s:system(printf(
+ \ 'git rev-list --count --left-right HEAD...origin/%s',
+ \ a:spec.branch), a:spec.dir)), '\t')
+ if !v:shell_error && ahead
+ if behind
+ " Only mention PlugClean if diverged, otherwise it's likely to be
+ " pushable (and probably not that messed up).
+ let err = printf(
+ \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
+ \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind)
+ else
+ let err = printf("Ahead of origin/%s by %d commit(s).\n"
+ \ .'Cannot update until local changes are pushed.',
+ \ a:spec.branch, ahead)
+ endif
+ endif
+ endif
+ endif
+ else
+ let err = 'Not found'
+ endif
+ return [err, err =~# 'PlugClean']
+endfunction
+
+function! s:rm_rf(dir)
+ if isdirectory(a:dir)
+ call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . plug#shellescape(a:dir))
+ endif
+endfunction
+
+function! s:clean(force)
+ call s:prepare()
+ call append(0, 'Searching for invalid plugins in '.g:plug_home)
+ call append(1, '')
+
+ " List of valid directories
+ let dirs = []
+ let errs = {}
+ let [cnt, total] = [0, len(g:plugs)]
+ for [name, spec] in items(g:plugs)
+ if !s:is_managed(name)
+ call add(dirs, spec.dir)
+ else
+ let [err, clean] = s:git_validate(spec, 1)
+ if clean
+ let errs[spec.dir] = s:lines(err)[0]
+ else
+ call add(dirs, spec.dir)
+ endif
+ endif
+ let cnt += 1
+ call s:progress_bar(2, repeat('=', cnt), total)
+ normal! 2G
+ redraw
+ endfor
+
+ let allowed = {}
+ for dir in dirs
+ let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1
+ let allowed[dir] = 1
+ for child in s:glob_dir(dir)
+ let allowed[child] = 1
+ endfor
+ endfor
+
+ let todo = []
+ let found = sort(s:glob_dir(g:plug_home))
+ while !empty(found)
+ let f = remove(found, 0)
+ if !has_key(allowed, f) && isdirectory(f)
+ call add(todo, f)
+ call append(line('$'), '- ' . f)
+ if has_key(errs, f)
+ call append(line('$'), ' ' . errs[f])
+ endif
+ let found = filter(found, 'stridx(v:val, f) != 0')
+ end
+ endwhile
+
+ 4
+ redraw
+ if empty(todo)
+ call append(line('$'), 'Already clean.')
+ else
+ let s:clean_count = 0
+ call append(3, ['Directories to delete:', ''])
+ redraw!
+ if a:force || s:ask_no_interrupt('Delete all directories?')
+ call s:delete([6, line('$')], 1)
+ else
+ call setline(4, 'Cancelled.')
+ nnoremap <silent> <buffer> d :set opfunc=<sid>delete_op<cr>g@
+ nmap <silent> <buffer> dd d_
+ xnoremap <silent> <buffer> d :<c-u>call <sid>delete_op(visualmode(), 1)<cr>
+ echo 'Delete the lines (d{motion}) to delete the corresponding directories'
+ endif
+ endif
+ 4
+ setlocal nomodifiable
+endfunction
+
+function! s:delete_op(type, ...)
+ call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0)
+endfunction
+
+function! s:delete(range, force)
+ let [l1, l2] = a:range
+ let force = a:force
+ while l1 <= l2
+ let line = getline(l1)
+ if line =~ '^- ' && isdirectory(line[2:])
+ execute l1
+ redraw!
+ let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
+ let force = force || answer > 1
+ if answer
+ call s:rm_rf(line[2:])
+ setlocal modifiable
+ call setline(l1, '~'.line[1:])
+ let s:clean_count += 1
+ call setline(4, printf('Removed %d directories.', s:clean_count))
+ setlocal nomodifiable
+ endif
+ endif
+ let l1 += 1
+ endwhile
+endfunction
+
+function! s:upgrade()
+ echo 'Downloading the latest version of vim-plug'
+ redraw
+ let tmp = s:plug_tempname()
+ let new = tmp . '/plug.vim'
+
+ try
+ let out = s:system(printf('git clone --depth 1 %s %s', plug#shellescape(s:plug_src), plug#shellescape(tmp)))
+ if v:shell_error
+ return s:err('Error upgrading vim-plug: '. out)
+ endif
+
+ if readfile(s:me) ==# readfile(new)
+ echo 'vim-plug is already up-to-date'
+ return 0
+ else
+ call rename(s:me, s:me . '.old')
+ call rename(new, s:me)
+ unlet g:loaded_plug
+ echo 'vim-plug has been upgraded'
+ return 1
+ endif
+ finally
+ silent! call s:rm_rf(tmp)
+ endtry
+endfunction
+
+function! s:upgrade_specs()
+ for spec in values(g:plugs)
+ let spec.frozen = get(spec, 'frozen', 0)
+ endfor
+endfunction
+
+function! s:status()
+ call s:prepare()
+ call append(0, 'Checking plugins')
+ call append(1, '')
+
+ let ecnt = 0
+ let unloaded = 0
+ let [cnt, total] = [0, len(g:plugs)]
+ for [name, spec] in items(g:plugs)
+ let is_dir = isdirectory(spec.dir)
+ if has_key(spec, 'uri')
+ if is_dir
+ let [err, _] = s:git_validate(spec, 1)
+ let [valid, msg] = [empty(err), empty(err) ? 'OK' : err]
+ else
+ let [valid, msg] = [0, 'Not found. Try PlugInstall.']
+ endif
+ else
+ if is_dir
+ let [valid, msg] = [1, 'OK']
+ else
+ let [valid, msg] = [0, 'Not found.']
+ endif
+ endif
+ let cnt += 1
+ let ecnt += !valid
+ " `s:loaded` entry can be missing if PlugUpgraded
+ if is_dir && get(s:loaded, name, -1) == 0
+ let unloaded = 1
+ let msg .= ' (not loaded)'
+ endif
+ call s:progress_bar(2, repeat('=', cnt), total)
+ call append(3, s:format_message(valid ? '-' : 'x', name, msg))
+ normal! 2G
+ redraw
+ endfor
+ call setline(1, 'Finished. '.ecnt.' error(s).')
+ normal! gg
+ setlocal nomodifiable
+ if unloaded
+ echo "Press 'L' on each line to load plugin, or 'U' to update"
+ nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
+ xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
+ end
+endfunction
+
+function! s:extract_name(str, prefix, suffix)
+ return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$')
+endfunction
+
+function! s:status_load(lnum)
+ let line = getline(a:lnum)
+ let name = s:extract_name(line, '-', '(not loaded)')
+ if !empty(name)
+ call plug#load(name)
+ setlocal modifiable
+ call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
+ setlocal nomodifiable
+ endif
+endfunction
+
+function! s:status_update() range
+ let lines = getline(a:firstline, a:lastline)
+ let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)')
+ if !empty(names)
+ echo
+ execute 'PlugUpdate' join(names)
+ endif
+endfunction
+
+function! s:is_preview_window_open()
+ silent! wincmd P
+ if &previewwindow
+ wincmd p
+ return 1
+ endif
+endfunction
+
+function! s:find_name(lnum)
+ for lnum in reverse(range(1, a:lnum))
+ let line = getline(lnum)
+ if empty(line)
+ return ''
+ endif
+ let name = s:extract_name(line, '-', '')
+ if !empty(name)
+ return name
+ endif
+ endfor
+ return ''
+endfunction
+
+function! s:preview_commit()
+ if b:plug_preview < 0
+ let b:plug_preview = !s:is_preview_window_open()
+ endif
+
+ let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}')
+ if empty(sha)
+ return
+ endif
+
+ let name = s:find_name(line('.'))
+ if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
+ return
+ endif
+
+ if exists('g:plug_pwindow') && !s:is_preview_window_open()
+ execute g:plug_pwindow
+ execute 'e' sha
+ else
+ execute 'pedit' sha
+ wincmd P
+ endif
+ setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha
+ if s:is_win
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ execute 'silent %!' cmd
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ call delete(batchfile)
+ endif
+ endtry
+ setlocal nomodifiable
+ nnoremap <silent> <buffer> q :q<cr>
+ wincmd p
+endfunction
+
+function! s:section(flags)
+ call search('\(^[x-] \)\@<=[^:]\+:', a:flags)
+endfunction
+
+function! s:format_git_log(line)
+ let indent = ' '
+ let tokens = split(a:line, nr2char(1))
+ if len(tokens) != 5
+ return indent.substitute(a:line, '\s*$', '', '')
+ endif
+ let [graph, sha, refs, subject, date] = tokens
+ let tag = matchstr(refs, 'tag: [^,)]\+')
+ let tag = empty(tag) ? ' ' : ' ('.tag.') '
+ return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date)
+endfunction
+
+function! s:append_ul(lnum, text)
+ call append(a:lnum, ['', a:text, repeat('-', len(a:text))])
+endfunction
+
+function! s:diff()
+ call s:prepare()
+ call append(0, ['Collecting changes ...', ''])
+ let cnts = [0, 0]
+ let bar = ''
+ let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)')
+ call s:progress_bar(2, bar, len(total))
+ for origin in [1, 0]
+ let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))'))))
+ if empty(plugs)
+ continue
+ endif
+ call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
+ for [k, v] in plugs
+ let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..'
+ let cmd = 'git log --graph --color=never '
+ \ . (s:git_version_requirement(2, 10, 0) ? '--no-show-signature ' : '')
+ \ . join(map(['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range], 'plug#shellescape(v:val)'))
+ if has_key(v, 'rtp')
+ let cmd .= ' -- '.plug#shellescape(v.rtp)
+ endif
+ let diff = s:system_chomp(cmd, v.dir)
+ if !empty(diff)
+ let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
+ call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
+ let cnts[origin] += 1
+ endif
+ let bar .= '='
+ call s:progress_bar(2, bar, len(total))
+ normal! 2G
+ redraw
+ endfor
+ if !cnts[origin]
+ call append(5, ['', 'N/A'])
+ endif
+ endfor
+ call setline(1, printf('%d plugin(s) updated.', cnts[0])
+ \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : ''))
+
+ if cnts[0] || cnts[1]
+ nnoremap <silent> <buffer> <plug>(plug-preview) :silent! call <SID>preview_commit()<cr>
+ if empty(maparg("\<cr>", 'n'))
+ nmap <buffer> <cr> <plug>(plug-preview)
+ endif
+ if empty(maparg('o', 'n'))
+ nmap <buffer> o <plug>(plug-preview)
+ endif
+ endif
+ if cnts[0]
+ nnoremap <silent> <buffer> X :call <SID>revert()<cr>
+ echo "Press 'X' on each block to revert the update"
+ endif
+ normal! gg
+ setlocal nomodifiable
+endfunction
+
+function! s:revert()
+ if search('^Pending updates', 'bnW')
+ return
+ endif
+
+ let name = s:find_name(line('.'))
+ if empty(name) || !has_key(g:plugs, name) ||
+ \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y'
+ return
+ endif
+
+ call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir)
+ setlocal modifiable
+ normal! "_dap
+ setlocal nomodifiable
+ echo 'Reverted'
+endfunction
+
+function! s:snapshot(force, ...) abort
+ call s:prepare()
+ setf vim
+ call append(0, ['" Generated by vim-plug',
+ \ '" '.strftime("%c"),
+ \ '" :source this file in vim to restore the snapshot',
+ \ '" or execute: vim -S snapshot.vim',
+ \ '', '', 'PlugUpdate!'])
+ 1
+ let anchor = line('$') - 3
+ let names = sort(keys(filter(copy(g:plugs),
+ \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
+ for name in reverse(names)
+ let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir)
+ if !empty(sha)
+ call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
+ redraw
+ endif
+ endfor
+
+ if a:0 > 0
+ let fn = s:plug_expand(a:1)
+ if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?'))
+ return
+ endif
+ call writefile(getline(1, '$'), fn)
+ echo 'Saved as '.a:1
+ silent execute 'e' s:esc(fn)
+ setf vim
+ endif
+endfunction
+
+function! s:split_rtp()
+ return split(&rtp, '\\\@<!,')
+endfunction
+
+let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, ''))
+let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, ''))
+
+if exists('g:plugs')
+ let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
+ call s:upgrade_specs()
+ call s:define_commands()
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/.config/nvim/coc-settings.json b/.config/nvim/coc-settings.json
new file mode 100644
index 0000000..1a58556
--- /dev/null
+++ b/.config/nvim/coc-settings.json
@@ -0,0 +1,3 @@
+{
+ "clangd.path": "/home/loek/.config/coc/extensions/coc-clangd-data/install/10.0.0/clangd_10.0.0/bin/clangd"
+}
diff --git a/.config/nvim/ftplugin/tex.vim b/.config/nvim/ftplugin/tex.vim
new file mode 100644
index 0000000..1c78d4b
--- /dev/null
+++ b/.config/nvim/ftplugin/tex.vim
@@ -0,0 +1,4 @@
+setlocal cc=86
+setlocal textwidth=85
+noremap \b c\begin{<C-R>"}<CR>\end{<C-R>"}
+
diff --git a/.config/nvim/init.vim b/.config/nvim/init.vim
new file mode 100644
index 0000000..bd53196
--- /dev/null
+++ b/.config/nvim/init.vim
@@ -0,0 +1,246 @@
+let mapleader = " "
+set number relativenumber
+set tabstop=4
+set wrap
+set ai
+set shiftwidth=4
+set termguicolors
+set shortmess=I
+set splitbelow splitright
+set noshowmode
+set ignorecase
+set smartcase
+set timeoutlen=500
+set conceallevel=0
+set mouse=a
+set linebreak " set wrap but don't wrap inside words
+set viminfo+='1000,n/home/loek/.local/nvim/viminfo
+let g:sneak#label = 1
+let g:which_key_map = {}
+let g:airline_powerline_fonts = 1
+let g:minimap_highlight='Visual'
+hi! link CocFloating SneakScope
+cabbrev help tab help
+
+if ! filereadable(expand('~/.config/nvim/autoload/plug.vim'))
+ echo "Downloading junegunn/vim-plug to manage plugins..."
+ silent !mkdir -p ~/.config/nvim/autoload/
+ silent !curl "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim" > ~/.config/nvim/autoload/plug.vim
+ autocmd VimEnter * PlugInstall
+endif
+
+" plugged
+call plug#begin('~/.config/nvim/plugged')
+" quality of life
+Plug 'jiangmiao/auto-pairs'
+Plug 'tpope/vim-surround'
+Plug 'Chiel92/vim-autoformat'
+Plug 'itchyny/lightline.vim'
+Plug 'terryma/vim-multiple-cursors'
+" Plug 'ap/vim-css-color' " color name highlighter
+Plug 'vim-scripts/colorizer' " better highlighter?
+Plug 'aurieh/discord.nvim', { 'do': ':UpdateRemotePlugins'}
+Plug 'AndrewRadev/tagalong.vim'
+Plug 'tpope/vim-commentary'
+Plug 'liuchengxu/vim-which-key'
+Plug 'justinmk/vim-sneak'
+Plug 'jbgutierrez/vim-better-comments'
+Plug 'junegunn/goyo.vim'
+Plug 'othree/eregex.vim'
+Plug 'psliwka/vim-smoothie'
+
+" language plugins
+Plug 'lervag/vimtex'
+Plug 'pangloss/vim-javascript'
+Plug 'hail2u/vim-css3-syntax'
+Plug 'octol/vim-cpp-enhanced-highlight'
+Plug 'vim-scripts/c.vim'
+
+" 'vim=ide'
+Plug 'neoclide/coc.nvim', {'branch': 'release'}
+Plug 'ryanoasis/vim-devicons' "Icons for filetypes
+Plug 'junegunn/fzf.vim'
+Plug '/usr/local/opt/fzf'
+
+" themes
+Plug 'arzg/vim-colors-xcode'
+Plug 'sainnhe/sonokai'
+Plug 'bluz71/vim-nightfly-guicolors'
+Plug 'Mcmartelle/vim-monokai-bold'
+Plug 'ntk148v/vim-horizon'
+Plug 'NLKNguyen/papercolor-theme'
+Plug 'scheakur/vim-scheakur'
+Plug 'mkarmona/materialbox'
+Plug 'morhetz/gruvbox'
+call plug#end()
+
+" keybinds
+tnoremap <Esc> <C-\><C-n>
+
+nnoremap tn :tabnext<CR>
+nnoremap tp :tabprevious<CR>
+nnoremap tt :tabnew<CR>
+
+nmap <CR> o<Esc>
+
+nnoremap <A-j> :m +1<CR>
+nnoremap <A-k> :m -2<CR>
+
+" leader keybindings
+nnoremap <silent> <leader> :WhichKey '<space>'<CR>
+
+map <leader>p "+p
+map <leader>y "+y
+let g:which_key_map.p = 'x11-paste'
+let g:which_key_map.y = 'x11-yank'
+
+map <leader>h :noh<cr>
+let g:which_key_map.h = 'no-highlighting'
+
+map <leader>b :Autoformat<cr>
+let g:which_key_map.b = 'format-file'
+
+map <leader>w /\s\+$<CR>
+let g:which_key_map.w = 'trailing-whitespace'
+
+map <leader>dv :!opout <c-r>%<cr><cr>
+map <leader>dc :w! <bar> !compiler <c-r>%<cr>
+let g:which_key_map.d = {
+ \ 'name': '+document',
+ \ 'v': 'view',
+ \ 'c': 'compile'
+ \ }
+
+map <leader>ts :sp term://zsh<cr>i
+map <leader>tv :vsp term://zsh<cr>i
+map <leader>tt :tabnew term://zsh<cr>i
+let g:which_key_map.t = {
+ \ 'name': '+term',
+ \ 's': 'split',
+ \ 'v': 'vsplit',
+ \ 't': 'tab'
+ \ }
+
+map <leader>.v :tabnew ~/.config/nvim/init.vim<cr>
+
+map <leader>.c :tabnew ~/.config/picom.conf<cr>
+map <leader>.z :tabnew ~/.zshrc<cr>
+map <leader>.i :tabnew ~/.config/i3/config<cr>
+map <leader>.p :tabnew ~/.config/polybar/config.ini<cr>
+let g:which_key_map['.'] = {
+ \ 'name': '+config',
+ \ 'v': 'vim',
+ \ 'z': 'zsh',
+ \ 'i': 'i3',
+ \ 'c': 'picom',
+ \ 'p': 'polybar'
+ \ }
+
+colorscheme xcodedark
+let g:lightline = {
+ \ 'colorscheme': 'pywal',
+ \ 'separator': { 'left': '', 'right': '' },
+ \ 'mode_map': {
+ \ 'n' : 'NORM',
+ \ 'i' : 'INS',
+ \ 'R' : 'REP',
+ \ 'v' : 'VIS',
+ \ 'V' : 'V-L',
+ \ "\<C-v>": 'V-B',
+ \ 'c' : 'CMD',
+ \ 's' : 'SEL',
+ \ 'S' : 'S-L',
+ \ "\<C-s>": 'S-B',
+ \ 't': 'TERM'
+ \ },
+ \ 'active': {
+ \ 'left': [
+ \ [ 'mode', 'paste' ],
+ \ [ 'readonly', 'filename', 'modified']
+ \ ],
+ \ 'right': [
+ \ [ 'lineinfo' ],
+ \ [ 'filetype' ]
+ \ ]
+ \ },
+ \ }
+source $HOME/.config/nvim/pywal/pywal.vim
+
+" vimtex config
+let g:tex_flavor = 'latex'
+let g:vimtex_compiler_latexmk = {
+ \ 'backend' : 'nvim',
+ \ 'background' : 1,
+ \ 'build_dir' : '',
+ \ 'callback' : 1,
+ \ 'continuous' : 1,
+ \ 'executable' : 'latexmk',
+ \ 'engine' : 'xelatex',
+ \ 'hooks' : [],
+ \ 'options' : [
+ \ '-xelatex',
+ \ '-file-line-error',
+ \ '-synctex=1',
+ \ '-interaction=nonstopmode',
+ \ ],
+ \}
+
+" auto start compilation (!not tested!)
+augroup vimtex_config
+ autocmd User VimtexEventInitPost silent VimtexCompile
+augroup END
+" TeX quotes
+autocmd FileType tex let b:surround_{char2nr("q")} = "`\r'"
+autocmd FileType tex let b:surround_{char2nr('Q')} = "``\r''"
+
+" coc.vim <tab> completion and <cr> stuffs
+inoremap <silent><expr> <TAB>
+ \ pumvisible() ? "\<C-n>" :
+ \ <SID>check_back_space() ? "\<TAB>" :
+ \ coc#refresh()
+inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>"
+
+function! s:check_back_space() abort
+ let col = col('.') - 1
+ return !col || getline('.')[col - 1] =~# '\s'
+endfunction
+
+if has('patch8.1.1068')
+ " Use `complete_info` if your (Neo)Vim version supports it.
+ inoremap <expr> <cr> complete_info()["selected"] != "-1" ? "\<C-y>" : "\<C-g>u\<CR>"
+else
+ imap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"
+endif
+
+" coc code navigation
+map <silent> <leader>cd <Plug>(coc-definition)
+map <silent> <leader>cy <Plug>(coc-type-definition)
+map <silent> <leader>ci <Plug>(coc-implementation)
+map <silent> <leader>cr <Plug>(coc-references)
+map <leader>cn <Plug>(coc-rename)
+let g:which_key_map.c = {
+ \ 'name': '+coc',
+ \ 'd': 'definition',
+ \ 'y': 'type-definition',
+ \ 'i': 'implementation',
+ \ 'r': 'references',
+ \ 'n': 'rename'
+ \ }
+
+" use <c-space>for trigger completion
+inoremap <silent><expr> <c-space> coc#refresh()
+
+" auto-comment uit
+autocmd FileType * setlocal formatoptions-=c formatoptions-=r formatoptions-=o
+
+" which key register dict
+call which_key#register('<Space>', "g:which_key_map")
+
+" no line numbers in terminal
+augroup TerminalStuff
+ " Clear old autocommands
+ au!
+ autocmd TermOpen * setlocal nonumber norelativenumber
+augroup END
+
+
diff --git a/.config/nvim/links.sh b/.config/nvim/links.sh
new file mode 100644
index 0000000..0bdeb1b
--- /dev/null
+++ b/.config/nvim/links.sh
@@ -0,0 +1,9 @@
+# https://simplyian.com/2014/04/28/how-to-have-language-specific-settings-in-vim/
+# https://vimawesome.com/
+# https://github.com/LukeSmithxyz/voidrice/blob/master/.config/nvim/init.vim
+# https://github.com/neoclide/coc.nvim
+# https://github.com/jiangmiao/auto-pairs
+# https://github.com/ryanoasis/vim-devicons
+# https://github.com/prettier/vim-prettier
+# https://github.com/DarthOstrich/dotfiles/blob/master/init.vim
+# https://github.com/junegunn/vim-plug/wiki/tips#automatic-installation \ No newline at end of file
diff --git a/.config/nvim/pywal/base.vim b/.config/nvim/pywal/base.vim
new file mode 100644
index 0000000..c25eede
--- /dev/null
+++ b/.config/nvim/pywal/base.vim
@@ -0,0 +1,19 @@
+let s:p = {'normal': {}, 'inactive': {}, 'insert': {}, 'replace': {}, 'visual': {}, 'tabline': {}}
+let s:p.normal.left = [ [ s:color6readable, s:color6 ], [ s:color2readable, s:color2 ] ]
+let s:p.normal.right = [ [ s:color1readable, s:color1 ], [ s:color2readable, s:color2 ] ]
+let s:p.inactive.right = [ [ s:color1readable, s:color1 ], [ s:color2readable, s:color2 ] ]
+let s:p.inactive.left = [ [ s:color6readable, s:color6 ], [ s:color2readable, s:color2 ] ]
+let s:p.insert.left = [ [ s:color5readable, s:color5 ], [ s:color2readable, s:color2 ] ]
+let s:p.replace.left = [ [ s:color4readable, s:color4 ], [ s:color2readable, s:color2 ] ]
+let s:p.visual.left = [ [ s:color3readable, s:color3 ], [ s:color2readable, s:color2 ] ]
+let s:p.normal.middle = [ [ s:none, s:none ] ]
+let s:p.inactive.middle = [ [ s:none, s:none ] ]
+let s:p.tabline.left = [ [ s:fg, s:bg ] ]
+let s:p.tabline.tabsel = [ [ s:fg, s:bg5 ] ]
+let s:p.tabline.middle = [ [ s:none, s:none ] ]
+let s:p.tabline.right = copy(s:p.normal.right)
+let s:p.normal.error = [ [ s:test, s:test ] ]
+let s:p.normal.warning = [ [ s:test, s:test ] ]
+
+let g:lightline#colorscheme#pywal#palette = lightline#colorscheme#flatten(s:p)
+
diff --git a/.config/nvim/pywal/colors.vim b/.config/nvim/pywal/colors.vim
new file mode 100644
index 0000000..36673b5
--- /dev/null
+++ b/.config/nvim/pywal/colors.vim
@@ -0,0 +1,46 @@
+hi! Normal guibg=#141415 ctermbg=NONE
+hi! EndOfBuffer guibg=#141415 ctermbg=NONE
+hi! CocInfoFloat guibg=#28282A ctermbg=NONE
+
+let s:bg = [ '#141415', 'NONE' ]
+let s:fg = [ '#c4c4c4', 'NONE' ]
+let s:bg1 = [ '#181819', 'NONE' ]
+let s:bg2 = [ '#1C1C1D', 'NONE' ]
+let s:bg3 = [ '#202022', 'NONE' ]
+let s:bg4 = [ '#242426', 'NONE' ]
+let s:bg5 = [ '#28282A', 'NONE' ]
+let s:color0 = [ '#141415', 'NONE' ]
+let s:color1 = [ '#292b37', 'NONE' ]
+let s:color2 = [ '#3c2a27', 'NONE' ]
+let s:color3 = [ '#3b3033', 'NONE' ]
+let s:color4 = [ '#3f3330', 'NONE' ]
+let s:color5 = [ '#4b4240', 'NONE' ]
+let s:color6 = [ '#66615a', 'NONE' ]
+let s:color7 = [ '#c4c4c4', 'NONE' ]
+let s:color8 = [ '#4e4e4f', 'NONE' ]
+let s:color9 = [ '#292b37', 'NONE' ]
+let s:color10 = [ '#3c2a27', 'NONE' ]
+let s:color11 = [ '#3b3033', 'NONE' ]
+let s:color12 = [ '#3f3330', 'NONE' ]
+let s:color13 = [ '#4b4240', 'NONE' ]
+let s:color14 = [ '#66615a', 'NONE' ]
+let s:color15 = [ '#c4c4c4', 'NONE' ]
+let s:color0readable = [ '#c4c4c4', 'NONE' ]
+let s:color1readable = [ '#c4c4c4', 'NONE' ]
+let s:color2readable = [ '#c4c4c4', 'NONE' ]
+let s:color3readable = [ '#c4c4c4', 'NONE' ]
+let s:color4readable = [ '#c4c4c4', 'NONE' ]
+let s:color5readable = [ '#c4c4c4', 'NONE' ]
+let s:color6readable = [ '#c4c4c4', 'NONE' ]
+let s:color7readable = [ '#141415', 'NONE' ]
+let s:color8readable = [ '#c4c4c4', 'NONE' ]
+let s:color9readable = [ '#c4c4c4', 'NONE' ]
+let s:color10readable = [ '#c4c4c4', 'NONE' ]
+let s:color11readable = [ '#c4c4c4', 'NONE' ]
+let s:color12readable = [ '#c4c4c4', 'NONE' ]
+let s:color13readable = [ '#c4c4c4', 'NONE' ]
+let s:color14readable = [ '#c4c4c4', 'NONE' ]
+let s:color15readable = [ '#141415', 'NONE' ]
+let s:none = [ 'NONE', 'NONE' ]
+let s:test = [ '#ff00ff', 'NONE' ]
+
diff --git a/.config/nvim/pywal/pywal.vim b/.config/nvim/pywal/pywal.vim
new file mode 100644
index 0000000..ba4c44e
--- /dev/null
+++ b/.config/nvim/pywal/pywal.vim
@@ -0,0 +1,65 @@
+hi! Normal guibg=#141415 ctermbg=NONE
+hi! EndOfBuffer guibg=#141415 ctermbg=NONE
+hi! CocInfoFloat guibg=#28282A ctermbg=NONE
+
+let s:bg = [ '#141415', 'NONE' ]
+let s:fg = [ '#c4c4c4', 'NONE' ]
+let s:bg1 = [ '#181819', 'NONE' ]
+let s:bg2 = [ '#1C1C1D', 'NONE' ]
+let s:bg3 = [ '#202022', 'NONE' ]
+let s:bg4 = [ '#242426', 'NONE' ]
+let s:bg5 = [ '#28282A', 'NONE' ]
+let s:color0 = [ '#141415', 'NONE' ]
+let s:color1 = [ '#292b37', 'NONE' ]
+let s:color2 = [ '#3c2a27', 'NONE' ]
+let s:color3 = [ '#3b3033', 'NONE' ]
+let s:color4 = [ '#3f3330', 'NONE' ]
+let s:color5 = [ '#4b4240', 'NONE' ]
+let s:color6 = [ '#66615a', 'NONE' ]
+let s:color7 = [ '#c4c4c4', 'NONE' ]
+let s:color8 = [ '#4e4e4f', 'NONE' ]
+let s:color9 = [ '#292b37', 'NONE' ]
+let s:color10 = [ '#3c2a27', 'NONE' ]
+let s:color11 = [ '#3b3033', 'NONE' ]
+let s:color12 = [ '#3f3330', 'NONE' ]
+let s:color13 = [ '#4b4240', 'NONE' ]
+let s:color14 = [ '#66615a', 'NONE' ]
+let s:color15 = [ '#c4c4c4', 'NONE' ]
+let s:color0readable = [ '#c4c4c4', 'NONE' ]
+let s:color1readable = [ '#c4c4c4', 'NONE' ]
+let s:color2readable = [ '#c4c4c4', 'NONE' ]
+let s:color3readable = [ '#c4c4c4', 'NONE' ]
+let s:color4readable = [ '#c4c4c4', 'NONE' ]
+let s:color5readable = [ '#c4c4c4', 'NONE' ]
+let s:color6readable = [ '#c4c4c4', 'NONE' ]
+let s:color7readable = [ '#141415', 'NONE' ]
+let s:color8readable = [ '#c4c4c4', 'NONE' ]
+let s:color9readable = [ '#c4c4c4', 'NONE' ]
+let s:color10readable = [ '#c4c4c4', 'NONE' ]
+let s:color11readable = [ '#c4c4c4', 'NONE' ]
+let s:color12readable = [ '#c4c4c4', 'NONE' ]
+let s:color13readable = [ '#c4c4c4', 'NONE' ]
+let s:color14readable = [ '#c4c4c4', 'NONE' ]
+let s:color15readable = [ '#141415', 'NONE' ]
+let s:none = [ 'NONE', 'NONE' ]
+let s:test = [ '#ff00ff', 'NONE' ]
+
+let s:p = {'normal': {}, 'inactive': {}, 'insert': {}, 'replace': {}, 'visual': {}, 'tabline': {}}
+let s:p.normal.left = [ [ s:color6readable, s:color6 ], [ s:color2readable, s:color2 ] ]
+let s:p.normal.right = [ [ s:color1readable, s:color1 ], [ s:color2readable, s:color2 ] ]
+let s:p.inactive.right = [ [ s:color1readable, s:color1 ], [ s:color2readable, s:color2 ] ]
+let s:p.inactive.left = [ [ s:color6readable, s:color6 ], [ s:color2readable, s:color2 ] ]
+let s:p.insert.left = [ [ s:color5readable, s:color5 ], [ s:color2readable, s:color2 ] ]
+let s:p.replace.left = [ [ s:color4readable, s:color4 ], [ s:color2readable, s:color2 ] ]
+let s:p.visual.left = [ [ s:color3readable, s:color3 ], [ s:color2readable, s:color2 ] ]
+let s:p.normal.middle = [ [ s:none, s:none ] ]
+let s:p.inactive.middle = [ [ s:none, s:none ] ]
+let s:p.tabline.left = [ [ s:fg, s:bg ] ]
+let s:p.tabline.tabsel = [ [ s:fg, s:bg5 ] ]
+let s:p.tabline.middle = [ [ s:none, s:none ] ]
+let s:p.tabline.right = copy(s:p.normal.right)
+let s:p.normal.error = [ [ s:test, s:test ] ]
+let s:p.normal.warning = [ [ s:test, s:test ] ]
+
+let g:lightline#colorscheme#pywal#palette = lightline#colorscheme#flatten(s:p)
+
diff --git a/.config/picom.conf b/.config/picom.conf
new file mode 100644
index 0000000..67afeaa
--- /dev/null
+++ b/.config/picom.conf
@@ -0,0 +1,70 @@
+backend = "glx";
+
+fading = false;
+fade-delta = 10;
+fade-in-step = 0.13;
+fade-out-step = 0.1;
+shadow = true;
+shadow-radius = 48;
+shadow-opacity = 0.3;
+shadow-offset-y = -48;
+shadow-offset-x = -48;
+
+blur-kern = "3x3box";
+blur-method = "kawase";
+blur-strength = 12;
+
+blur-background-exclude = [
+ "class_g = 'Polybar'",
+ "class_g = 'firefox' && !I3_FLOATING_WINDOW@:c"
+]
+
+shadow-exclude = [
+ "!I3_FLOATING_WINDOW@:c && _NET_WM_WINDOW_TYPE@:32a *= '_NET_WM_WINDOW_TYPE_NORMAL'",
+ "class_g = 'firefox' && !I3_FLOATING_WINDOW@:c"
+]
+
+opacity-rule = [
+ "80:class_g = 'Spotify'",
+ "90:class_g = 'Zathura'"
+]
+
+wintypes: {
+ dock = {
+ shadow = false;
+ corner-radius = 2;
+ opacity = 0.999;
+ };
+
+ desktop = {
+ shadow = false;
+ };
+
+ tooltip = {
+ fade = true;
+ shadow = false;
+ opacity = 1;
+ };
+
+ menu = {
+ fade = false;
+ opacity = 0.8;
+ };
+};
+
+# combo
+# desktop
+# dialog
+# dnd
+# dock
+# dropdown_menu
+# menu
+# normal
+# notify
+# popup_menu
+# splash
+# toolbar
+# tooltip
+# unknown
+# utility
+
diff --git a/.config/polybar/config.ini b/.config/polybar/config.ini
new file mode 100644
index 0000000..a24f161
--- /dev/null
+++ b/.config/polybar/config.ini
@@ -0,0 +1,358 @@
+[barsettings]
+padding = 1.9
+
+[global/wm]
+margin-bottom = 50
+margin-top = 0
+
+[bar/main]
+
+; tray-position = right
+; tray-padding = ${barsettings.padding}
+; tray-background = ${color.bg}
+; tray-margin = 0
+
+monitor =
+
+monitor-fallback =
+
+monitor-strict = false
+
+override-redirect = false
+
+bottom = false
+
+fixed-center = true
+
+width = 100%
+height = 16
+
+offset-x = 0
+offset-y = 0
+
+background = ${color.alpha}
+
+foreground = ${color.fg}
+
+radius-top = 0.0
+radius-bottom = 0.0
+
+padding = 0
+
+module-margin-left = 0
+module-margin-right = 0
+
+font-0 = "Scientifica:weight=regular:size=4;2"
+font-1 = "Weather Icons:antialias=false:size=6;2"
+font-2 = "Siji:size=8;2"
+font-3 = "Material Design Icons:antialias=false:size=8;2"
+font-4 = "Fira Code:size=10;2"
+
+modules-center = onstart time weather date alsa
+
+separator =
+
+spacing = 0
+
+dim-value = 1.0
+
+wm-name =
+
+locale =
+
+wm-restack = i3
+
+enable-ipc = true
+
+click-left =
+click-middle =
+click-right =
+scroll-up =
+scroll-down =
+double-click-left =
+double-click-middle =
+double-click-right =
+
+cursor-click =
+cursor-scroll =
+
+[settings]
+throttle-output = 5
+throttle-output-for = 10
+
+throttle-input-for = 30
+
+screenchange-reload = false
+
+compositing-background = source
+compositing-foreground = over
+compositing-overline = over
+compositing-underline = over
+compositing-border = over
+
+format-foreground =
+format-background =
+format-underline =
+format-overline =
+format-spacing =
+format-padding =
+format-margin =
+format-offset =
+
+pseudo-transparency = false
+
+[color]
+
+bg = #141415
+fg = #c4c4c4
+fg-alt = #141415
+
+alpha = #00000000
+
+shade1 = #3F3330
+shade2 = #4C423E
+shade3 = #59514C
+shade4 = #66615A
+
+shade-fg = #c4c4c4
+
+[module/alsa]
+type = internal/alsa
+
+format-volume = <ramp-volume> <label-volume>
+format-volume-background = ${color.shade4}
+format-volume-foreground = ${color.shade-fg}
+format-volume-padding = ${barsettings.padding}
+
+label-volume = %percentage%%
+
+format-muted-prefix = " "
+format-muted-background = ${color.shade-fg}
+format-muted-foreground = ${color.shade4}
+format-muted-padding = ${barsettings.padding}
+label-muted = "Muted"
+
+ramp-volume-0 = 
+ramp-volume-1 = 
+ramp-volume-2 = 
+
+ramp-headphones-0 = 
+ramp-headphones-1 = 
+
+[module/battery]
+type = internal/battery
+
+full-at = 99
+battery = BAT1
+adapter = ACAD
+poll-interval = 2
+time-format = %H:%M
+
+format-charging = <animation-charging> <label-charging>
+format-charging-background = ${color.shade3}
+format-charging-foreground = ${color.fg-alt}
+format-charging-padding = ${barsettings.padding}
+
+format-discharging = <ramp-capacity> <label-discharging>
+format-discharging-background = ${color.shade3}
+format-discharging-foreground = ${color.fg-alt}
+format-discharging-padding = ${barsettings.padding}
+
+format-full = <label-full>
+format-full-background = ${color.shade3}
+format-full-foreground = ${color.fg-alt}
+format-full-padding = ${barsettings.padding}
+
+label-charging = %percentage%%
+label-discharging = %percentage%%
+label-full = 100% Charged
+
+ramp-capacity-0 = 
+ramp-capacity-1 = 
+ramp-capacity-2 = 
+ramp-capacity-3 = 
+ramp-capacity-4 = 
+ramp-capacity-5 = 
+ramp-capacity-6 = 
+ramp-capacity-7 = 
+ramp-capacity-8 = 
+ramp-capacity-9 = 
+
+animation-charging-0 = 
+animation-charging-1 = 
+animation-charging-2 = 
+animation-charging-3 = 
+
+animation-charging-framerate = 750
+
+[module/cpu]
+type = internal/cpu
+
+interval = 1
+
+format = <label>
+format-prefix = 󰍛
+format-background = ${color.bg}
+format-foreground = ${color.shade2}
+format-padding = ${barsettings.padding}
+
+label = " %percentage%%"
+
+[module/date]
+type = internal/date
+interval = 1.0
+time =  %A, %B %d
+format = <label>
+format-background = ${color.shade3}
+format-foreground = ${color.shade-fg}
+format-padding = ${barsettings.padding}
+
+label = %time%
+
+[module/time]
+type = internal/date
+interval = 1.0
+time =  %H:%M
+format = <label>
+format-background = ${color.shade1}
+format-foreground = ${color.shade-fg}
+format-padding = ${barsettings.padding}
+
+label = %time%
+
+[module/memory]
+type = internal/memory
+
+interval = 3
+
+format = <label>
+format-prefix = 󰘚
+format-background = ${color.bg}
+format-foreground = ${color.shade1}
+format-padding = ${barsettings.padding}
+
+label = " %mb_used%"
+
+[module/packages]
+type = custom/script
+exec = echo "󰏗 `pacman -Q | wc -l`"
+format-padding = ${barsettings.padding}
+interval = 60
+format-background = ${color.bg}
+format-foreground = ${color.shade3}
+
+[module/player-mpris-simple]
+type = custom/script
+exec = ~/.config/polybar/player-mpris-simple.sh
+interval = 3
+format-background = ${color.bg}
+format-foreground = ${color.fg}
+format-padding = ${barsettings.padding}
+click-middle = playerctl previous &
+click-right = playerctl next &
+click-left = playerctl play-pause &
+
+[module/weather]
+type = custom/script
+exec = ~/.config/polybar-forecast/target/release/polybar-forecast
+interval = 120
+exec-if = ping openweathermap.org -c 1
+label-font = 2
+format-background = ${color.shade2}
+format-foreground = ${color.shade-fg}
+format-padding = ${barsettings.padding}
+
+[module/whoami]
+type = custom/script
+exec = echo "$(whoami)@$(hostname)"
+interval = 9999999
+label-font = 2
+format-background = ${color.shade1}
+format-foreground = ${color.fg}
+format-padding = ${barsettings.padding}
+
+[module/intip]
+type = custom/script
+exec = echo "$(ifconfig | grep -Po --color=never '(?<=inet addr:)(192\.\d+.\d+\.\d+)')"
+interval = 9999999
+label-font = 2
+format-background = ${color.shade3}
+format-foreground = ${color.fg-alt}
+format-padding = ${barsettings.padding}
+[module/extip]
+type = custom/script
+exec = echo "$(curl -s ifconfig.me)"
+interval = 9999999
+label-font = 2
+format-background = ${color.shade3}
+format-foreground = ${color.fg-alt}
+format-padding = ${barsettings.padding}
+
+[module/network]
+type = internal/network
+interface = wlan0
+
+interval = 1.0
+accumulate-stats = true
+unknown-as-up = true
+
+format-connected = <ramp-signal> <label-connected>
+format-connected-background = ${color.shade2}
+format-connected-foreground = ${color.fg-alt}
+format-connected-padding = ${barsettings.padding}
+
+format-disconnected = <label-disconnected>
+format-disconnected-background = ${color.shade2}
+format-disconnected-foreground = ${color.fg-alt}
+format-disconnected-padding = ${barsettings.padding}
+
+label-connected = %essid%
+label-disconnected =  Disconnected
+
+ramp-signal-0 = 
+ramp-signal-1 = 
+ramp-signal-2 = 
+ramp-signal-3 = 
+ramp-signal-4 = 
+
+; [module/i3]
+; type = internal/xworkspaces
+
+; pin-workspaces = true
+
+; enable-click = true
+; enable-scroll = true
+
+; format = <label-state>
+; format-padding = 0
+
+; label-monitor = %name%
+
+; label-active = %name%
+; label-active-foreground = ${color.bg}
+; label-active-background = ${color.shade5}
+
+; label-occupied = %icon%
+; label-occupied-underline = ${color.bg}
+
+; label-urgent = %icon%
+; label-urgent-foreground = ${color.fg}
+; label-urgent-background = ${color.bg}
+
+; label-empty = %name%
+; label-empty-background = ${color.bg}
+; label-empty-foreground = ${color.fg}
+
+; label-active-padding = ${barsettings.padding}
+; label-urgent-padding = ${barsettings.padding}
+; label-occupied-padding = ${barsettings.padding}
+; label-empty-padding = ${barsettings.padding}
+
+[module/onstart]
+type = custom/script
+exec = sh ~/.config/polybar/onstart.sh > /dev/null
+interval = 999999999
+format-background = ${color.alpha}
+format-foreground = #ff00ff
+format-padding = 0
+
diff --git a/.config/polybar/onstart.sh b/.config/polybar/onstart.sh
new file mode 100644
index 0000000..033cec0
--- /dev/null
+++ b/.config/polybar/onstart.sh
@@ -0,0 +1,3 @@
+# Tray below bar :(
+sleep 1
+xdo raise -n Polybar -n tray
diff --git a/.config/polybar/player-mpris-simple.sh b/.config/polybar/player-mpris-simple.sh
new file mode 100755
index 0000000..938ac21
--- /dev/null
+++ b/.config/polybar/player-mpris-simple.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+player_status=$(playerctl status 2> /dev/null)
+
+if [ "$player_status" = "Playing" ] || [ "$player_status" = "Paused" ]; then
+ echo "$(playerctl metadata artist) - $(playerctl metadata title)" | awk -v len=60 '{ if (length($0) > len) print substr($0, 1, len-3) "..."; else print; }'
+else
+ echo ""
+fi
diff --git a/.config/rofi/config b/.config/rofi/config
new file mode 100644
index 0000000..9aa0c48
--- /dev/null
+++ b/.config/rofi/config
@@ -0,0 +1,17 @@
+rofi.show-icons: false
+rofi.modi: drun,run,window,ssh
+rofi.lines: 7
+rofi.line-padding: 10
+rofi.matching: fuzzy
+rofi.bw: 0
+rofi.padding: 0
+rofi.separator-style: none
+rofi.hide-scrollbar: true
+rofi.line-margin: 0
+rofi.font: sans-serif 10
+rofi.theme: /home/loek/.config/rofi/pywal.rasi
+rofi.kb-row-up: Up,Control+k,Shift+Tab,Shift+ISO_Left_Tab
+rofi.kb-row-down: Down,Control+j
+rofi.kb-accept-entry: Control+m,Return,KP_Enter
+rofi.kb-remove-to-eol: Control+Shift+e
+
diff --git a/.config/rofi/pywal.rasi b/.config/rofi/pywal.rasi
new file mode 100644
index 0000000..7297da0
--- /dev/null
+++ b/.config/rofi/pywal.rasi
@@ -0,0 +1,40 @@
+* {
+ background-color: #14141533;
+ text-color: #c4c4c4;
+ spacing: 0;
+ width: 720px;
+}
+
+inputbar {
+ border: 0 0 1px 0;
+ children: [prompt, entry];
+}
+
+prompt {
+ padding: 16px;
+}
+
+textbox {
+ background-color: #141415;
+ border-color: #141415;
+ padding: 8px 16px;
+}
+
+entry {
+ padding: 16px;
+}
+
+listview {
+ cycle: false;
+ margin: 0 0 -1px 0;
+ scrollbar: false;
+}
+
+element {
+ padding: 16px;
+}
+
+element selected {
+ background-color: #66615a;
+ text-color: #c4c4c4;
+} \ No newline at end of file
diff --git a/.config/spicetify/Themes/Loekaars/README.md b/.config/spicetify/Themes/Loekaars/README.md
new file mode 100644
index 0000000..c96cdc3
--- /dev/null
+++ b/.config/spicetify/Themes/Loekaars/README.md
@@ -0,0 +1,11 @@
+# Black
+
+## Screenshots
+
+![Screenshot](https://i.imgur.com/HjK3Gab.png)
+
+## Downloads and Info
+
+[color.ini](https://raw.githubusercontent.com/lollilol/spicetify-themes/master/Black/color.ini) [user.css](https://raw.githubusercontent.com/lollilol/spicetify-themes/master/Black/user.css)
+
+Inspired by [Dark](https://github.com/morpheusthewhite/spicetify-themes/tree/master/Dark)
diff --git a/.config/spicetify/Themes/Loekaars/color.ini b/.config/spicetify/Themes/Loekaars/color.ini
new file mode 100644
index 0000000..92b942e
--- /dev/null
+++ b/.config/spicetify/Themes/Loekaars/color.ini
@@ -0,0 +1,18 @@
+[Base]
+Base=object Object]
+main_fg=c4c4c4
+secondary_fg=B0B0B0
+main_bg=141415
+sidebar_and_player_bg=141415
+cover_overlay_and_shadow=141415
+indicator_fg_and_button_bg=66615a
+pressing_fg=66615a
+slider_bg=1F2024
+sidebar_indicator_and_hover_button_bg=66615a
+scrollbar_fg_and_selected_row_bg=1D1E21
+pressing_button_fg=c4c4c4
+pressing_button_bg=222227
+selected_button=66615a
+miscellaneous_bg=1D1E21
+miscellaneous_hover_bg=66615a
+preserve_1=c4c4c4
diff --git a/.config/spicetify/Themes/Loekaars/user.css b/.config/spicetify/Themes/Loekaars/user.css
new file mode 100644
index 0000000..6639898
--- /dev/null
+++ b/.config/spicetify/Themes/Loekaars/user.css
@@ -0,0 +1,630 @@
+:root {
+ --bar-height: 90px;
+}
+.sidebar {
+--left-sidebar-item-height: 29px !important;
+}
+.resizer-right {
+ background-color: #000;
+}
+
+body {
+ font-family: 'Product Sans';
+}
+
+/* Hide Ad banner */
+#view-leaderboard-ad {
+ display: none;
+}
+
+/*Round corner cover image*/
+.card-image,
+.card-placeholder-wrapper,
+.card-image-content-wrapper,
+.Card:not(.Card--artist) .Card__image,
+.Card:not(.Card--artist) .Card__image-wrapper {
+ border-radius: 10px !important;
+ overflow: hidden !important
+}
+
+/*Hide some annoying elements like profile name and pic, upgrade button and device connect bar at bottom, new playlist button*/
+.profile.content-top-bar__profile-link,
+.upgrade-button,
+.view-player .remote-playback-bar,
+.LeftSidebarNewPlaylistButton__button {
+ display: none !important;
+}
+
+
+/*Exclude these elements from draggable property because it stops them from clickable*/
+.profile-items-container,
+.profile {
+ -webkit-app-region: no-drag !important;
+}
+
+/*Thinner scrollbar*/
+::-webkit-scrollbar {
+ height: 6px !important;
+ width: 6px !important;
+ background-color: transparent;
+}
+
+/*Round corner scrollbar*/
+::-webkit-scrollbar-thumb {
+ border-radius: 3px !important;
+}
+
+/*Hide top and bottom buttons of scrollbar */
+/*who uses those, lol*/
+::-webkit-scrollbar-button {
+ display: none !important;
+}
+
+/*Hide cover image overlay*/
+.card-overlay {
+ visibility: hidden !important;
+}
+
+.card-image-hit-area .card-button-add,
+.card-image-hit-area .card-button-play,
+.card-image-hit-area .card-button-more,
+.Card__image-hit-area .card-button-add,
+.Card__image-hit-area .card-button-play,
+.Card__image-hit-area .card-button-more,
+.Card__image-hit-area .Card__play-button,
+.Card__image-hit-area .Card__add-button,
+.Card__image-hit-area .Card__more-button,
+.Card__image-hit-area .Card__overlay {
+ transition-property: all !important;
+ transition-duration: 0.3s !important;
+ transition-timing-function: cubic-bezier(.3,0,0,1) !important;
+ opacity: 1 !important;
+}
+
+.glue-page-header__content .glue-page-header__image-inner {
+ border-radius: 10px;
+ box-shadow: unset !important;
+}
+
+.glue-page-header__full-description-overlay {
+ box-shadow: unset !important;
+}
+
+.card-placeholder-wrapper {
+ background: transparent !important;
+}
+
+/*Spice up search input background*/
+.SearchInput {
+ color: var(--modspotify_main_fg);
+}
+.SearchInput__input {
+ color: var(--modspotify_secondary_fg);
+ background-color: var(--modspotify_slider_bg) !important;
+ border-radius: 4px !important;
+ padding-left: 34px;
+}
+
+.sidebar:hover .sidebar-navbar.sidebar-scroll-element {
+ opacity: 0.4!important;
+}
+
+.view-player .player-controls-container,
+.view-player .player-controls-container .controls {
+ overflow: visible !important;
+}
+
+.view-player .player-controls-container .controls .button-play{
+ height:50px !important;
+ border-radius:50px !important;
+ background-color: var(--modspotify_slider_bg) !important;
+ box-shadow:0 0 0 0 !important;
+ width:50px !important;
+ overflow: visible !important;
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2) !important;
+ transition:none 0.3s cubic-bezier(.3,0,.7,1);
+}
+
+.view-player .player-controls-container .controls .button-play:before{
+ font-size:18px !important;
+ padding-left: 16px !important;
+ padding-top: 9px !important;
+}
+
+.view-player .player-controls-container .controls .button-play:after {
+ box-shadow: unset !important;
+}
+
+.view-player .player-controls-container {
+ position: absolute !important;
+ width: 100% !important;
+}
+
+.view-player .player-controls-container .controls {
+ width: 100% !important;
+ height: 100% !important;
+ align-items: center !important;
+ margin-top : 0px !important;
+}
+
+/* Add round corner for Gerne and Mood cards */
+.gc-image-container,
+.gc-image {
+ border-radius: 10px !important;
+}
+
+/*
+Collage of 3 album covers is usually seen in Browse and Chart.
+*/
+.card-puff__image-wrapper,
+.card-puff__info-container,
+.card-puff__card-image {
+ border-radius: 10px !important;
+}
+
+.card-puff__image-wrapper {
+ overflow: visible;
+}
+
+.card-puff__card-image {
+ box-shadow: 5px 0 30px rgba(0,0,0,0.7);
+ overflow: visible;
+}
+
+.card-puff__title-container {
+ background-color: transparent !important;
+}
+
+.card-puff.pressed .card-puff__image-wrapper,
+.card-puff.pressed .card-puff__info-container {
+ opacity: 0.7 !important;
+}
+
+.card-puff__title {
+ padding: 5px 10px 5px 10px !important;
+ background-color: var(--modspotify_main_bg) !important;
+ border-radius: 4px;
+ border: 2px solid var(--modspotify_main_fg);
+}
+
+/*
+We use round corner on cover so they look weird in original
+form, so I move last cover to the right 20px and first one to the left 20px
+*/
+.card-puff__card-image:nth-child(1) {
+ right: 20px;
+ box-shadow: 0 0 0 0 !important;
+}
+
+.card-puff__card-image:nth-child(3) {
+ left: 20px;
+}
+
+.grid-overlay-label {
+ top: 140px !important;
+}
+
+/**/
+.glue-page-header__background-color {
+ background-image: none !important;
+ background: var(--modspotify_main_bg);
+}
+
+/* .glue-page-header__sticky {
+ padding-top: 60px !important;
+} */
+
+/*
+Remove those title, cringy description and
+meaningless followers number
+*/
+
+.carousel .card-info-subtitle-description,
+.carousel .card-info-subtitle-metadata,
+.carousel .card:not(.card-type-station).card-info-title,
+.carousel .card.card-type-playlist.image-loaded .Card__info-subtitle-description,
+.carousel .card.card-type-playlist.image-loaded .Card__info-subtitle-metadata {
+ display: none !important;
+}
+
+
+/*
+In top of Browse usually has bunch of Playlist or Album cards,
+and they has .carousel as a wrapper and it hides anything that
+overflows from its zone, aka our shadow and lifting animation.
+*/
+.carousel {
+ overflow: visible !important;
+}
+
+/*
+Button with text Play
+*/
+.button.button-green,
+.GlueButton.GlueButton--style-green {
+ color: var(--modspotify_main_fg) !important;
+}
+
+/*
+Change text color in playlist
+*/
+.tl-explicit .label,
+.tl-premium .label,
+.tl-cell:not(.tl-number),
+.tl-cell a:link,
+.tl-highlight {
+ color: var(--modspotify_secondary_fg);
+}
+
+.card-type-album .card-info-title,
+.card-type-track .card-info-title,
+.card-type-collection-album .card-info-title,
+.card-type-episode .card-info-title {
+ font-size: 15px;
+ font-weight: 900 !important;
+ text-align: center !important;
+ width: 100% !important;
+}
+
+.card-type-album .card-info-subtitle-links,
+.card-type-track .card-info-subtitle-links,
+.card-type-collection-album .card-info-subtitle-links,
+.card-type-episode .card-info-subtitle-links {
+ text-align: center !important;
+ width: 100% !important;
+}
+
+.tracklist-station-container::after {
+ background: transparent !important;
+}
+
+.GlueHeader__background-overlay {
+ background: var(--modspotify_main_bg) !important;
+}
+
+/* Move navigation buttons and search field to the right and down */
+.browser-navigation-top-bar {
+ margin-left: 40px !important;
+ margin-top: 15px !important;
+}
+
+.SearchInput__input,
+.SearchInput__searchIcon,
+.SearchInput__clearButton {
+ margin-top: 15px !important;
+}
+
+.content-top-bar__profile-menu-button {
+ margin-top: 15px !important;
+}
+
+.body-container--windows:not(.with-buddy-list):not(.messagebar) .content-top-bar__profile {
+ margin-right: 110px !important;
+ margin-top: -5px;
+}
+
+/* Spice up Fullscreen mode */
+#view-player .album-art .album-art__image {
+ border-radius: 30px !important;
+ box-shadow: 0 10px 70px rgba(var(--modspotify_rgb_cover_overlay_and_shadow),.5) !important;
+}
+
+#view-player .album-art .album-art__image .card-image-content-wrapper,
+#view-player .album-art .album-art__image .card-image-content-wrapper .card-image {
+ border-radius: 30px !important;
+}
+
+#video-player .album-art__foreground {
+ flex-direction: row;
+ text-align: left;
+}
+
+#video-player .album-art__background {
+ background-color: initial;
+}
+
+#video-player .album-art__track-details {
+ padding-left: 50px;
+ line-height: initial;
+}
+
+#video-player .album-art__track-title {
+ font-size: 84px;
+ margin-top: 0;
+ line-height: initial;
+}
+
+#video-player .album-art__artist-name {
+ font-size: 54px;
+ margin-top: 0;
+ line-height: initial;
+}
+
+/* Daily mixes */
+.carousel .card-info-wrapper.card-info-with-description.card-info-with-metadata {
+ height: 50px !important;
+}
+
+/* Remove section divider */
+.section-divider {
+ border-bottom: 0 !important;
+}
+
+/* Adjust Position of border active tab in Nav bar at top
+and add little glowing effect
+*/
+.nav.navbar-nav {
+ overflow: hidden !important;
+}
+
+.nav.navbar-nav a {
+ overflow: visible !important;
+}
+
+.nav.navbar-nav a::after {
+ bottom: 0px !important;
+ width: 100% !important;
+}
+
+.nav.navbar-nav a:focus:not(.button):active::after{
+ background-color: var(--modspotify_pressing_fg) !important;
+}
+
+/* Notification bar */
+#content-wrapper #view-message-bar {
+ position: absolute !important;
+ width: calc(100% - 160px) !important;
+ margin-left: 80px !important;
+ border-radius: 0 0 10px 10px !important;
+}
+
+/* Small cover Big cover mechanism */
+.now-playing.cover-size-transition.active.image-expanded #now-playing-image-small {
+ display: none;
+}
+
+.now-playing.cover-size-transition.active.image-expanded .cover-image-link-wrapper {
+ flex: 0 1 10px;
+}
+
+#view-now-playing a.image {
+ overflow: visible !important;
+}
+
+/* Profile arrow in top left */
+.content-top-bar__profile-menu-button .dropdown {
+ position: fixed !important;
+ top: 10px !important;
+ -webkit-app-region: no-drag !important;
+}
+
+/* [WINDOWS] Change Profile menu horizontal position */
+body.body-container--windows .content-top-bar__profile-menu-button .dropdown {
+ right: 190px !important;
+}
+
+body:not(.body-container--windows) .content-top-bar__profile-menu-button .dropdown {
+ right: 20px !important;
+}
+
+/* Small tooltip */
+#tooltip {
+ box-shadow: 0 0 10px rgba(0,0,0,0.2) !important;
+ border-radius: 5px !important;
+ border: 2px solid var(--modspotify_main_fg);
+ padding: 10px 10px;
+}
+
+.tooltip-arrow-top, .tooltip-arrow-bottom {
+ display: none !important;
+}
+
+.lyrics-lines-container,
+.message-container {
+ color: var(--modspotify_main_fg) !important;
+}
+
+/* Home page */
+.GlueCarousel__grid-wrapper::-webkit-scrollbar-thumb {
+ display: none;
+}
+
+.GlueCard__info-wrapper,
+.Card__info-wrapper {
+ margin-bottom: 30px;
+}
+
+.card-horizontal-interior-wrapper .card-info- {
+ text-align: start !important;
+}
+
+.tl-row.selected:hover .tl-cell {
+ background: var(--modspotify_scrollbar_fg_and_selected_row_bg) !important;
+}
+
+.GlueTableRow--is-selected {
+ background-color: var(--modspotify_scrollbar_fg_and_selected_row_bg) !important;
+}
+
+.tracklist-podcast .tl-progress .row-progress__bar {
+ background-color: var(--modspotify_main_fg);
+}
+
+.Header__background-color{
+ background-color: var(--modspotify_main_bg) !important;
+}
+
+.Button--style-green,
+.button.button-green, .button.button-white {
+ /* border-radius: 4px; */
+ color: var(--modspotify_main_fg) !important;
+}
+
+.glue-page-header:not(.glue-page-header--album):not(.glue-page-header--playlist):not(.glue-page-header--artist):not(.glue-page-header--dailymix):not(.glue-page-header--user):not(.glue-page-header--show)
+ .glue-page-header__content-inner .glue-page-header__title-text,
+.HomeHeader .Header__content-inner .Header__title-text-inner,
+.MadeForYouHeader .Header__content-inner .Header__title-text-inner,
+.RecentlyPlayedPage__header .Header__content-inner .Header__title-text-inner {
+ background-color: var(--modspotify_main_bg);
+ /* padding: 5px 20px;
+ border: 5px solid var(--modspotify_main_fg) !important;
+ border-radius: 6px;
+ box-shadow: 0 4px 12px 0 rgba(var(--modspotify_rgb_cover_overlay_and_shadow),.2); */
+ text-transform: uppercase;
+}
+
+.glue-page-header.glue-page-header--album .glue-page-header__content-inner .glue-page-header__title,
+.glue-page-header.glue-page-header--artist .glue-page-header__content-inner .glue-page-header__title,
+.glue-page-header.glue-page-header--dailymix .glue-page-header__content-inner .glue-page-header__title,
+.glue-page-header.glue-page-header--playlist .glue-page-header__content-inner .glue-page-header__title {
+ margin-top: 10px;
+}
+
+span.glue-page-header__title-text {
+ color: var(--modspotify_main_fg);
+}
+
+.glue-page-header .glue-page-header__content-inner .glue-page-header__button {
+ margin-top: 40px;
+}
+
+.glue-page-header__content-inner,
+.glue-page-header__data,
+.glue-page-header__title,
+.Header__content-inner,
+.Header__data,
+.Header__title,
+.Header__title-text,
+.Header__title-text-inner {
+ overflow: visible !important;
+}
+
+/*Force player bar to has fixed height*/
+.view-player {
+ height: var(--bar-height) !important;
+ border-top: 0;
+}
+
+.view-player .now-playing {
+ overflow: unset;
+}
+
+.view-player .cover-image-container {
+ position: fixed !important;
+ left: 0;
+ bottom: 0;
+}
+
+.view-player .now-playing .cover-image-link,
+.view-player .now-playing .cover-image-link figure {
+ width: var(--bar-height);
+ height: var(--bar-height);
+}
+
+#now-playing-image-small .cover-image {
+ width: var(--bar-height);
+ height: var(--bar-height);
+}
+
+.view-player .now-playing .cover-image-link-wrapper {
+ flex: 0 1 calc(var(--bar-height) + 8px);
+}
+
+.text-container {
+ z-index: 3;
+}
+
+.view-player .now-playing-container .button-add {
+ color: var(--modspotify_main_fg) !important;
+}
+/*
+.progress-container .progress-bar,
+.progress-container .inner {
+ top: 0 !important;
+ margin-top: 0 !important;
+ height: 5px;
+}
+
+.progress-container .progress-bar-wrapper {
+ top: 0 !important;
+ height: 5px;
+}
+*/
+.progress-container {
+ /*position: fixed !important;*/
+ width: 55% !important;
+ /*bottom: var(--bar-height) !important;*/
+ /*margin : 0 !important;*/
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.glue-page-header__p2s-details,
+.glue-page-header__p2s-followers {
+ display: none;
+}
+
+.context-menu {
+ border: none !important;
+ box-shadow: 0px 3px 30px -5px #0008 !important;
+ border-radius: 7px !important;
+ overflow: hidden;
+}
+
+#menu-wrapper ::-webkit-scrollbar {
+ display: none;
+}
+
+.resizer-right {
+ background-color: var(--modspotify_main_bg);
+}
+
+.main-view-wrapper {
+ overflow: unset;
+}
+
+.Button--style-icon-stroke:after,
+.Button--style-icon-stroke:hover:after,
+.Button--style-icon-stroke,
+.glue-page-header__button .button-icon-with-stroke,
+.glue-page-header__button .button-icon-with-stroke::after {
+ box-shadow: unset;
+}
+
+.glue-page-header.glue-page-header--artist .glue-page-header__label {
+ padding-top: 10px;
+}
+
+.glue-page-header.glue-page-header--artist.has-custom-image .glue-page-header__label {
+ padding-top: 8px;
+}
+
+.glue-page-header__content .glue-page-header__label {
+ margin-left: -2px;
+ z-index: 2;
+}
+
+/* .glue-page-header__label span,
+.Header__label span {
+ background-color: var(--modspotify_main_fg);
+ color: var(--modspotify_main_bg);
+ padding: 2px 10px;
+} */
+
+.glue-page-header__label .header-verified-check {
+ background-color: transparent;
+}
+
+body.remotebar .view-player .player-bar-wrapper {
+ height: 100%;
+}
+
+
+.Header__image-inner {
+ box-shadow: unset;
+}
+
+.Button:not(.Button--style-stroke) * ,
+.button:not(.button-with-stroke) * {
+ color: var(--modspotify_main_bg) !important;
+} \ No newline at end of file
diff --git a/.config/spicetify/config.ini b/.config/spicetify/config.ini
new file mode 100644
index 0000000..c554077
--- /dev/null
+++ b/.config/spicetify/config.ini
@@ -0,0 +1,36 @@
+[AdditionalOptions]
+made_for_you_hub = 0
+song_page = 0
+extensions =
+fastUser_switching = 0
+lyric_force_no_sync = 0
+new_feedback_ui = 0
+search_in_sidebar = 0
+custom_apps =
+home = 0
+radio = 0
+tastebuds = 0
+visualization_high_framerate = 0
+experimental_features = 0
+lyric_always_show = 0
+minimal_ui = 0
+
+[Setting]
+replace_colors = 1
+overwrite_assets = 0
+spotify_path = /opt/spotify
+prefs_path = /home/loek/.config/spotify/prefs
+current_theme = Loekaars
+color_scheme =
+inject_css = 1
+
+[Preprocesses]
+remove_rtl_rule = 1
+expose_apis = 1
+disable_sentry = 1
+disable_ui_logging = 1
+
+; DO NOT CHANGE!
+[Backup]
+version = 1.1.10.546.ge08ef575
+
diff --git a/.config/startpage/index.html b/.config/startpage/index.html
new file mode 100644
index 0000000..2de34cd
--- /dev/null
+++ b/.config/startpage/index.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <meta http-equiv='X-UA-Compatible' content='IE=edge'>
+ <title>New Tab</title>
+ <meta name='viewport' content='width=device-width, initial-scale=1'>
+ <link rel='stylesheet' type='text/css' media='screen' href='style.css'>
+ <script src="jquery.min.js"></script>
+ <script src="script.js" defer></script>
+</head>
+<body>
+ <div class="center">
+ <h1 class="time">
+ </h1>
+
+ <div class="term">
+ <div class="columns">
+ <div class="column">
+ <h3 style="color: var(--pywal-readable-color2)">school</h3>
+ <a href="https://www.montiplaza.nl/">montiplaza</a>
+ <a href="https://somtoday.nl/">somtoday</a>
+ <a href="https://calendar.google.com">google calendar</a>
+ <a href="https://client.etesync.com/pim">etesync calendar</a>
+ </div>
+
+ <div class="column">
+ <h3 style="color: var(--pywal-readable-color3)">social</h3>
+ <a href="https://www.reddit.com/new">reddit</a>
+ <a href="https://www.twitter.com/">twitter</a>
+ <a href="https://www.twitch.com/directory/following">twitch</a>
+ <a href="https://www.youtube.com/">youtube</a>
+ </div>
+
+ <div class="column">
+ <h3 style="color: var(--pywal-readable-color4)">nerd shit</h3>
+ <a href="https://github.com/lonkaars">github</a>
+ <a href="https://10fastfingers.com/typing-test/dutch">typing test</a>
+ <a href="https://frikandelbroodje:3000/">private git</a>
+ </div>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/.config/startpage/jquery.min.js b/.config/startpage/jquery.min.js
new file mode 100644
index 0000000..b061403
--- /dev/null
+++ b/.config/startpage/jquery.min.js
@@ -0,0 +1,2 @@
+/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
diff --git a/.config/startpage/pywal.css b/.config/startpage/pywal.css
new file mode 100644
index 0000000..de2f510
--- /dev/null
+++ b/.config/startpage/pywal.css
@@ -0,0 +1,63 @@
+:root {
+ --pywal-shade0: #18191C;
+ --pywal-shade1: #1D1E21;
+ --pywal-shade2: #1F2024;
+ --pywal-shade3: #222227;
+ --pywal-shade4: #1F202A;
+
+ --pywal-bg0: #141415;
+ --pywal-bg1: #181819;
+ --pywal-bg2: #1C1C1D;
+ --pywal-bg3: #202022;
+
+ --pywal-color0: #141415;
+ --pywal-color1: #292b37;
+ --pywal-color2: #3c2a27;
+ --pywal-color3: #3b3033;
+ --pywal-color4: #3f3330;
+ --pywal-color5: #4b4240;
+ --pywal-color6: #66615a;
+ --pywal-color7: #c4c4c4;
+ --pywal-color8: #4e4e4f;
+ --pywal-color9: #292b37;
+ --pywal-color10: #3c2a27;
+ --pywal-color11: #3b3033;
+ --pywal-color12: #3f3330;
+ --pywal-color13: #4b4240;
+ --pywal-color14: #66615a;
+ --pywal-color15: #c4c4c4;
+
+ --pywal-readable-color0: #6B6B70;
+ --pywal-readable-color1: #585B71;
+ --pywal-readable-color2: #745550;
+ --pywal-readable-color3: #67565A;
+ --pywal-readable-color4: #665450;
+ --pywal-readable-color5: #4B4240;
+ --pywal-readable-color6: #66615A;
+ --pywal-readable-color7: #C4C4C4;
+ --pywal-readable-color8: #4E4E4F;
+ --pywal-readable-color9: #585B71;
+ --pywal-readable-color10: #745550;
+ --pywal-readable-color11: #67565A;
+ --pywal-readable-color12: #665450;
+ --pywal-readable-color13: #4B4240;
+ --pywal-readable-color14: #66615A;
+ --pywal-readable-color15: #C4C4C4;
+
+ --pywal-readableOn-color0: #c4c4c4;
+ --pywal-readableOn-color1: #c4c4c4;
+ --pywal-readableOn-color2: #c4c4c4;
+ --pywal-readableOn-color3: #c4c4c4;
+ --pywal-readableOn-color4: #c4c4c4;
+ --pywal-readableOn-color5: #c4c4c4;
+ --pywal-readableOn-color6: #c4c4c4;
+ --pywal-readableOn-color7: #141415;
+ --pywal-readableOn-color8: #c4c4c4;
+ --pywal-readableOn-color9: #c4c4c4;
+ --pywal-readableOn-color10: #c4c4c4;
+ --pywal-readableOn-color11: #c4c4c4;
+ --pywal-readableOn-color12: #c4c4c4;
+ --pywal-readableOn-color13: #c4c4c4;
+ --pywal-readableOn-color14: #c4c4c4;
+ --pywal-readableOn-color15: #141415;
+} \ No newline at end of file
diff --git a/.config/startpage/script.js b/.config/startpage/script.js
new file mode 100644
index 0000000..c735e78
--- /dev/null
+++ b/.config/startpage/script.js
@@ -0,0 +1,7 @@
+// Time
+$('.time').text(new Date().toLocaleTimeString('nl').substr(0, 5))
+setInterval(() => {
+ $('.time').text(new Date().toLocaleTimeString('nl').substr(0, 5))
+}, 500);
+
+
diff --git a/.config/startpage/style.css b/.config/startpage/style.css
new file mode 100644
index 0000000..f6eea2d
--- /dev/null
+++ b/.config/startpage/style.css
@@ -0,0 +1,91 @@
+@import url(file:///home/loek/.config/startpage/pywal.css);
+
+html, body {
+ margin: 0;
+ padding: 0;
+ background: var(--pywal-color0);
+ color: var(--pywal-color15);
+ font-family: "JetBrainsMono NF";
+}
+
+* {
+ cursor: normal;
+ user-select: none;
+}
+
+h1 {
+ margin: 0;
+}
+
+.center {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ white-space: nowrap;
+}
+
+.term {
+ width: 500px;
+ padding: 16px;
+ position: relative;
+}
+
+.term::before {
+ content: '';
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-color: var(--pywal-color8);
+ opacity: 0.2;
+ z-index: -2;
+ border-radius: 8px;
+}
+
+h3 {
+ font-size: 14px;
+ font-weight: 800;
+ margin: 0 0 8px 0;
+}
+
+.columns {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+}
+
+.columns .column {
+ display: inline-block;
+}
+
+.columns .column * {
+ display: block;
+}
+
+a {
+ font-size: 14px;
+ font-weight: normal;
+ text-decoration: none;
+ color: var(--pywal-color15);
+ position: relative;
+ left: 0;
+ transition: left .3s;
+}
+
+a::before {
+ content: '➜';
+ position: absolute;
+ left: -30px;
+ opacity: 0;
+ transition: left .3s, opacity .3s;
+}
+
+a:hover {
+ left: 15px;
+}
+
+a:hover::before {
+ left: -15px;
+ opacity: 1;
+}
diff --git a/.config/user-dirs.dirs b/.config/user-dirs.dirs
new file mode 100644
index 0000000..f7c6dec
--- /dev/null
+++ b/.config/user-dirs.dirs
@@ -0,0 +1,15 @@
+# This file is written by xdg-user-dirs-update
+# If you want to change or add directories, just edit the line you're
+# interested in. All local changes will be retained on the next run.
+# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
+# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
+# absolute path. No other format is supported.
+#
+XDG_DOWNLOAD_DIR="$HOME/dl"
+XDG_DOCUMENTS_DIR="$HOME/docs"
+XDG_MUSIC_DIR="$HOME/music"
+XDG_DESKTOP_DIR="$HOME/"
+XDG_TEMPLATES_DIR="$HOME/"
+XDG_PUBLICSHARE_DIR="$HOME/"
+XDG_PICTURES_DIR="$HOME/"
+XDG_VIDEOS_DIR="$HOME/"
diff --git a/.config/user-dirs.locale b/.config/user-dirs.locale
new file mode 100644
index 0000000..3e0b419
--- /dev/null
+++ b/.config/user-dirs.locale
@@ -0,0 +1 @@
+en_US \ No newline at end of file
diff --git a/.config/zathura/zathurarc b/.config/zathura/zathurarc
new file mode 100644
index 0000000..2e94927
--- /dev/null
+++ b/.config/zathura/zathurarc
@@ -0,0 +1,13 @@
+set scroll-page-aware "true"
+set smooth-scroll "true"
+set scroll-full-overlap 0.01
+set scroll-step 100
+set font "Fira Code 10"
+
+# recolor (dark mode)
+map <C-i> recolor
+set recolor "true"
+set recolor-lightcolor "#141415"
+set recolor-darkcolor "#c4c4c4"
+set recolor-reverse-video "true"
+set recolor-keephue "true"
diff --git a/.local/share/bin/brave b/.local/share/bin/brave
new file mode 100755
index 0000000..c645f54
--- /dev/null
+++ b/.local/share/bin/brave
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
+BRAVE_USER_FLAGS_FILE="$XDG_CONFIG_HOME/brave-flags.conf"
+if [[ -f $BRAVE_USER_FLAGS_FILE ]]; then
+ USER_FLAGS="$(cat $BRAVE_USER_FLAGS_FILE | sed 's/#.*//')"
+fi
+
+/usr/bin/brave $@ $USER_FLAGS
diff --git a/.local/share/bin/loadwall b/.local/share/bin/loadwall
new file mode 100755
index 0000000..e419667
--- /dev/null
+++ b/.local/share/bin/loadwall
@@ -0,0 +1,5 @@
+#!/bin/node
+var args = process.argv;
+var image = args.find(a => a.match(/\.(png|jpeg|jpg)/g));
+var wal = require(__dirname + "/pywal/jswal.js");
+wal(["--bg-fill", image]);
diff --git a/.local/share/bin/m3uprefix b/.local/share/bin/m3uprefix
new file mode 100755
index 0000000..c5ab206
--- /dev/null
+++ b/.local/share/bin/m3uprefix
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+fullfile=$1
+
+filename=$(basename -- "$fullfile")
+filename="${filename%.*}"
+
+nvim "+%s/^[^#]/$filename\/&/g" "+wq" "$fullfile"
+
diff --git a/.local/share/konsole/Loekaars.profile b/.local/share/konsole/Loekaars.profile
new file mode 100644
index 0000000..ce1eadd
--- /dev/null
+++ b/.local/share/konsole/Loekaars.profile
@@ -0,0 +1,35 @@
+[Appearance]
+AntiAliasFonts=true
+BoldIntense=true
+ColorScheme=colors-konsole
+Font=JetBrainsMono Nerd Font,10,-1,5,57,0,0,0,0,0,Medium
+LineSpacing=0
+UseFontLineChararacters=true
+
+[Cursor Options]
+CustomCursorColor=213,216,218
+UseCustomCursorColor=true
+
+[General]
+DimWhenInactive=false
+LocalTabTitleFormat=%n
+Name=Loekaars
+Parent=FALLBACK/
+ShowTerminalSizeHint=false
+TerminalCenter=false
+TerminalColumns=150
+TerminalMargin=0
+TerminalRows=44
+
+[Interaction Options]
+AutoCopySelectedText=true
+CopyTextAsHTML=false
+MiddleClickPasteMode=1
+OpenLinksByDirectClickEnabled=true
+UnderlineLinksEnabled=true
+
+[Scrolling]
+ScrollBarPosition=2
+
+[Terminal Features]
+UrlHintsModifiers=268435456
diff --git a/.local/share/konsole/colors-konsole.colorscheme b/.local/share/konsole/colors-konsole.colorscheme
new file mode 120000
index 0000000..76c0d26
--- /dev/null
+++ b/.local/share/konsole/colors-konsole.colorscheme
@@ -0,0 +1 @@
+/home/loek/.cache/wal/colors-konsole.colorscheme \ No newline at end of file
diff --git a/.xprofile b/.xprofile
new file mode 100755
index 0000000..1cdf34a
--- /dev/null
+++ b/.xprofile
@@ -0,0 +1,9 @@
+export _JAVA_AWT_WM_NONREPARENTING=1
+export TZ='Europe/Amsterdam'
+
+/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 &
+exec xrdb /home/loek/.config/X11/Xresources &
+autocutsel &
+
+i3
+
diff --git a/.zshrc b/.zshrc
new file mode 100644
index 0000000..d6c21e5
--- /dev/null
+++ b/.zshrc
@@ -0,0 +1,117 @@
+# xdg to be safe :)
+XDG_CONFIG_HOME="$HOME"/.config
+XDG_CACHE_HOME="$HOME"/.cache
+XDG_DATA_HOME="$HOME"/.local/share
+
+ZSH_THEME="agnoster"
+
+export ZSH="/home/loek/.local/share/oh-my-zsh"
+export ADOTDIR="$XDG_CONFIG_HOME"/antigen
+
+source ~/.config/antigen.zsh
+antigen use oh-my-zsh
+antigen bundle git
+antigen bundle zsh-users/zsh-syntax-highlighting
+antigen bundle zsh-users/zsh-autosuggestions
+antigen theme agnoster
+antigen bundle arzzen/calc.plugin.zsh
+antigen apply
+
+source $ZSH/oh-my-zsh.sh
+
+# scripts
+export PATH=~/.local/share/bin:$PATH
+export PATH=~/.local/go/bin:$PATH
+
+# restic
+export RESTIC_PASSWORD_FILE=/mnt/g/resticpass
+export RESTIC_REPOSITORY=/mnt/g
+
+# environment variables
+export EDITOR='nvim'
+export FZF_DEFAULT_COMMAND='find .'
+export XCURSOR_PATH=${XCURSOR_PATH}:~/.local/share/icons
+export GOPATH=$HOME/.local/go
+export ETESYNC_URL="https://superesc:9443"
+
+# js-beautify
+export jsbeautify_end_with_newline=true
+export jsbeautify_brace_style="preserve-inline"
+export jsbeautify_indent_with_tabs=true
+
+# node certificate settings
+export NODE_EXTRA_CA_CERTS=/usr/share/ca-certificates/bitwarden.crt
+
+# NVM
+export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
+[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
+
+# mkcd
+function mkcd {
+ mkdir "$1" && cd "$1"
+}
+
+# clone
+function clone {
+ git clone https://github.com/$1 && cd "${1#*/}"
+}
+
+# gitignore
+function gi() { curl -sL https://www.toptal.com/developers/gitignore/api/$@ ;}
+
+# ueberzug
+source "`ueberzug library`"
+
+# aliases
+alias chrome="chromium"
+alias l="live-server"
+alias r='ranger --choosedir=$HOME/.cache/rangerdir; LASTDIR=`cat $HOME/.cache/rangerdir`; cd "$LASTDIR"'
+alias wacom="xsetwacom set $(xsetwacom list devices | grep stylus | grep -oP --color=never "(?<=id\:\s)([0-9]*)") area 0 0 15200 4275"
+alias reload="sudo systemctl restart lightdm"
+alias rwall="node ~/.local/share/bin/pywal/wall.js"
+alias patchmc="node /home/loek/.local/share/bin/patch_minecraft_profiles.js"
+alias cls="clear"
+alias lowerpolybar="xdo lower -n Polybar -n tray && xdo lower -N Polybar"
+alias timesync="sudo ntpd -qg && sudo hwclock --systohc"
+alias v="nvim"
+alias vim="nvim"
+alias copy="xclip -selection c"
+alias sdl="spotifydl"
+alias sctl="systemctl"
+alias ncp="ncmpcpp"
+alias xsiv="i3-swallow sxiv"
+alias scrcpy="i3-swallow scrcpy"
+alias dnd="dragon-drag-and-drop"
+alias adda="git add -A"
+alias first="git commit -m \"first commit\""
+
+# cursors
+alias cursorbl="printf '\033[1 q'"
+alias cursorul="printf '\033[4 q'"
+alias cursorbar="printf '\033[6 q'"
+
+# dircolors
+eval "`dircolors ~/.config/dircolors`"
+
+# cleanup
+rm -rf ~/.Wurst\ encryption ~/.xsession-errors ~/.xsession-errors.old ~/.zcompdump* ~/.zshrc.zwc ~/.lesshst ~/.lyrics ~/.android ~/.zcalc_history .git-credentials .node_repl_history .python_history
+export ANDROID_SDK_HOME="$XDG_CONFIG_HOME"/android
+export ADB_VENDOR_KEY="$XDG_CONFIG_HOME"/android
+export CARGO_HOME="$XDG_DATA_HOME"/cargo
+alias nvidia-settings=nvidia-settings --config="$XDG_CONFIG_HOME"/nvidia/settings
+export gnome_user_dir="$HOME"/.config/gnome/apps
+export GNUPGHOME="$XDG_DATA_HOME"/gnupg
+alias gpg2="gpg2 --homedir $XDG_DATA_HOME/gnupg"
+export GRADLE_USER_HOME="$XDG_DATA_HOME"/gradle
+export _JAVA_OPTIONS=-Djava.util.prefs.userRoot="$XDG_CONFIG_HOME"/java
+export NPM_CONFIG_USERCONFIG=$XDG_CONFIG_HOME/npm/npmrc
+export NVM_DIR="$XDG_DATA_HOME"/nvm
+export PARALLEL_HOME="$XDG_CONFIG_HOME"/parallel
+export PYLINTHOME="$XDG_CACHE_HOME"/pylint
+export STACK_ROOT="$XDG_DATA_HOME"/stack
+export WINEPREFIX="$XDG_DATA_HOME"/wineprefixes/default
+export HISTFILE="$XDG_DATA_HOME"/zsh/history
+export CUDA_CACHE_PATH="$XDG_CACHE_HOME"/nv
+alias gpg2="gpg2 --homedir $XDG_DATA_HOME/gnupg"
+export CONAN_USER_HOME="$XDG_CONFIG_HOME/conan"
+
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..fdb474d
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,24 @@
+# dottydots
+
+these are dotfiles
+
+- discord
+- spotify
+- zsh
+- nvim
+- i3
+- polybar
+- zathura
+- chromium
+- brave
+- rofi
+- konsole
+
+also includes jswal, my shitty inefficient wrapper for wal that adds readable colors and more things to theme and such
+
+# screenshots
+
+![](1.png)
+![](2.png)
+![](3.png)
+