aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.Xresources22
-rw-r--r--.bashrc10
-rw-r--r--.config/Trolltech.conf17
-rw-r--r--.config/awesome/charitable/.gitignore7
-rw-r--r--.config/awesome/charitable/README.md124
-rw-r--r--.config/awesome/charitable/init.lua160
-rw-r--r--.config/awesome/rc.lua863
-rw-r--r--.config/awesome/theme.lua26
-rw-r--r--.config/awesome/todo.lua47
-rw-r--r--.config/dolphinrc14
-rw-r--r--.config/environment.d/00-path.conf7
-rw-r--r--.config/firefox/chrome/userChrome.css5
-rw-r--r--.config/firefox/user.js36
-rw-r--r--.config/fuzzel/fuzzel.ini20
-rw-r--r--.config/gammastep/config.ini8
-rw-r--r--.config/git/authorized_keys0
-rw-r--r--.config/git/config61
-rw-r--r--.config/git/ignore8
-rw-r--r--.config/gtk-3.0/bookmarks3
-rw-r--r--.config/gtk-3.0/colors.css84
-rw-r--r--.config/gtk-3.0/gtk.css1
-rw-r--r--.config/gtk-3.0/settings.ini23
-rw-r--r--.config/gtk-4.0/colors.css84
-rw-r--r--.config/gtk-4.0/gtk.css1
-rw-r--r--.config/gtk-4.0/settings.ini9
-rwxr-xr-x.config/herbstluftwm/autostart70
-rwxr-xr-x.config/herbstluftwm/panel-bot.sh86
-rwxr-xr-x.config/herbstluftwm/panel-top.sh194
-rw-r--r--.config/hypr/hyprland.conf143
-rw-r--r--.config/kcminputrc8
-rw-r--r--.config/kfontinstuirc3
-rw-r--r--.config/mpv/mpv.conf1
-rw-r--r--.config/ntfy/client.yml1
-rw-r--r--.config/plasma-localerc8
-rw-r--r--.config/plasmarc0
-rw-r--r--.config/powerdevilrc2
-rw-r--r--.config/powermanagementprofilesrc60
-rw-r--r--.config/qutebrowser/autoconfig.yml83
-rw-r--r--.config/qutebrowser/bookmarks/urls1
-rw-r--r--.config/qutebrowser/config.py3
-rw-r--r--.config/qutebrowser/greasemonkey/4chan-X.user.js27983
-rw-r--r--.config/qutebrowser/plugins/redirect.py41
-rw-r--r--.config/qutebrowser/quickmarks2
-rw-r--r--.config/shell/env12
-rw-r--r--.config/shell/functions.d/venv48
-rw-r--r--.config/shell/sources49
m---------.config/shell/vendor/bash/feature-auto-completion0
m---------.config/shell/vendor/zsh/feature-syntax-highlighting0
-rw-r--r--.config/swayidle/config6
-rw-r--r--.config/swaylock/config2
-rw-r--r--.config/sxhkd/sxhkdrc4
-rw-r--r--.config/systemd/user/dapp-.service.d/00-ExecSearchPath.conf4
-rw-r--r--.config/systemd/user/dapp-.service.d/00-WorkingDirectory.conf2
-rw-r--r--.config/systemd/user/dapp-bg-.service.d/00-Restart.conf2
-rw-r--r--.config/systemd/user/dapp-bg-chwp.service12
-rw-r--r--.config/systemd/user/dapp-bg-chwp.service.d/00-Restart.conf2
-rw-r--r--.config/systemd/user/dapp-bg-chwp.timer10
-rw-r--r--.config/systemd/user/dapp-bg-dunst.service11
-rw-r--r--.config/systemd/user/dapp-bg-ntfy@.service12
-rw-r--r--.config/systemd/user/dapp-bg-redshift.service11
-rw-r--r--.config/systemd/user/dapp-bg-sxhkd.service11
-rw-r--r--.config/systemd/user/dapp-bg-xcompmgr.service11
-rw-r--r--.config/systemd/user/dapp-fg-.service.d/00-Restart.conf2
-rw-r--r--.config/systemd/user/dapp-fg-chromium.service11
-rw-r--r--.config/systemd/user/dapp-fg-firefox.service11
-rw-r--r--.config/systemd/user/dapp-fg-gajim.service11
-rw-r--r--.config/systemd/user/dapp-fg-irc.service11
-rw-r--r--.config/systemd/user/dapp-fg-keepassxc.service11
-rw-r--r--.config/systemd/user/dapp-fg-networkmanager.service11
-rw-r--r--.config/systemd/user/dapp-fg-nextcloud.service11
-rw-r--r--.config/systemd/user/wsys-.service.d/00-Target.conf6
-rw-r--r--.config/systemd/user/wsys-xorg.service20
-rw-r--r--.config/systemd/user/wsys-xorg.socket5
-rw-r--r--.config/systemd/user/wsys-xorg.target3
-rw-r--r--.config/systemd/user/wsys.target2
-rw-r--r--.config/systemd/user/xss-lock.service12
-rw-r--r--.config/waybar/config-hyprland.json24
-rw-r--r--.config/waybar/config.json94
-rw-r--r--.config/waybar/style.css140
-rw-r--r--.gitmodules63
l---------.local/bin/awesome1
-rwxr-xr-x.local/bin/bl28
-rwxr-xr-x.local/bin/count (renamed from .local/bin/notify-send)38
-rwxr-xr-x.local/bin/dmenu22
-rwxr-xr-x.local/bin/downloadgemist42
-rwxr-xr-x.local/bin/get-focussed-monitor43
l---------.local/bin/herbstluftwm1
-rwxr-xr-x.local/bin/hyprland-switch-tag161
-rwxr-xr-x.local/bin/import-pape19
-rwxr-xr-x.local/bin/ipass48
l---------.local/bin/irc1
-rwxr-xr-x.local/bin/krokify30
-rwxr-xr-x.local/bin/kubectl-hosts6
-rwxr-xr-x.local/bin/kubectl-rageclean-pod17
-rwxr-xr-x.local/bin/kubectl-secret76
-rwxr-xr-x.local/bin/media-import53
-rwxr-xr-x.local/bin/open3
-rwxr-xr-x.local/bin/pkgsrc221
-rwxr-xr-x.local/bin/updot63
-rwxr-xr-x.local/bin/vol2
-rwxr-xr-x.local/bin/x14
l---------.local/bin/xdg-open1
-rw-r--r--.local/etc/wrapper.d/awesome.sh6
-rw-r--r--.local/etc/wrapper.d/firefox.rc10
-rw-r--r--.local/etc/wrapper.d/firefox.sh14
-rw-r--r--.local/etc/wrapper.d/herbstluftwm.sh6
-rw-r--r--.local/etc/wrapper.d/irc.sh10
-rw-r--r--.local/etc/wrapper.d/jellyfin.sh (renamed from .local/etc/wrapper.d/jellyfin.rc)0
-rw-r--r--.local/etc/wrapper.d/mpv.rc16
-rw-r--r--.local/etc/wrapper.d/mpv.sh31
-rw-r--r--.local/etc/wrapper.d/signal.sh (renamed from .local/etc/wrapper.d/signal.rc)0
-rw-r--r--.local/etc/x/autostart9
-rw-r--r--.local/etc/x/xinit.d/awesome.rc6
-rw-r--r--.local/etc/x/xinit.d/kde.rc13
-rw-r--r--.local/etc/x/xinitrc44
-rw-r--r--.local/share/applications/firefox.desktop5
-rwxr-xr-x.local/share/wrapper.sh12
-rw-r--r--.ssh/authorized_keys23
-rw-r--r--.ssh/config40
-rw-r--r--.ssh/config.d/mintlab-lab19
-rw-r--r--.ssh/config.d/tyilnet39
-rw-r--r--.ssh/config.d/xxllnc (renamed from .ssh/config.d/mintlab)4
-rw-r--r--.ssh/config.d/xxllnc-zgw (renamed from .ssh/config.d/mintlab-prd)0
-rw-r--r--.ssh/id.d/root@bast-ed25519.pub1
-rw-r--r--.ssh/id.d/root@gaeru-ed25519.pub2
-rw-r--r--.ssh/id.d/root@krohxe-ed25519.pub1
-rw-r--r--.ssh/id.d/root@mieshu-ed25519.pub1
-rw-r--r--.ssh/id.d/root@nouki-ed25519.pub1
-rw-r--r--.ssh/id.d/root@oolah-ed25519.pub1
-rw-r--r--.ssh/id.d/root@plarabe-ed25519.pub1
-rw-r--r--.ssh/id.d/tyil@anoia-ed25519.pub2
-rw-r--r--.ssh/id.d/tyil@bast-ed25519.pub5
-rw-r--r--.ssh/id.d/tyil@gaeru-ed25519.pub1
-rw-r--r--.ssh/id.d/tyil@ludifah-ed25519.pub1
-rw-r--r--.ssh/id.d/tyil@mieshu-ed25519.pub1
-rw-r--r--.ssh/id.d/tyil@nouki-ed25519.pub1
-rw-r--r--.ssh/id.d/tyil@oolah-ed25519.pub1
-rw-r--r--.ssh/id.d/tyil@plarabe-ed25519.pub1
-rw-r--r--.ssh/id.d/xxllnc@edephas-ed25519.pub1
-rw-r--r--.ssh/id.d/xxllnc@ivdea-ed25519.pub1
m---------.vim/bundle/feature-camelcasemotion0
m---------.vim/bundle/feature-rainbow-parenthesis0
m---------.vim/bundle/syntax-blade0
m---------.vim/bundle/syntax-go0
m---------.vim/bundle/syntax-hcl0
m---------.vim/bundle/syntax-helm0
m---------.vim/bundle/syntax-ledger0
m---------.vim/bundle/syntax-markdown0
m---------.vim/bundle/syntax-mustache0
m---------.vim/bundle/syntax-raku0
m---------.vim/bundle/syntax-terraform0
m---------.vim/bundle/syntax-todo0
m---------.vim/bundle/syntax-toml0
m---------.vim/bundle/theme-codedark0
m---------.vim/bundle/theme-colorsbox0
m---------.vim/bundle/theme-jellybeans0
m---------.vim/bundle/theme-molokai0
-rw-r--r--.vim/ftplugin/php.vim3
-rw-r--r--.vim/ftplugin/yaml.vim49
-rw-r--r--.vim/plugin/k8s-edit-secret.vim60
-rw-r--r--.vim/vimrc3
-rw-r--r--.weechat/trigger.conf162
m---------.weechat/vendor/protocol-slack0
-rw-r--r--.weechat/weechat.conf614
-rw-r--r--.zshrc11
-rw-r--r--README.md7
166 files changed, 31422 insertions, 1693 deletions
diff --git a/.Xresources b/.Xresources
new file mode 100644
index 0000000..893516a
--- /dev/null
+++ b/.Xresources
@@ -0,0 +1,22 @@
+*.background: #000000
+*.foreground: #eeeeee
+
+*.color0: #696969
+*.color1: #ca674a
+*.color2: #96a967
+*.color3: #d3a94a
+*.color4: #5778c1
+*.color5: #9c35ac
+*.color6: #6eb5f3
+*.color7: #a9a9a9
+
+*.color8: #666666
+*.color9: #d54e53
+*.color10: #b9ca4a
+*.color11: #e7c547
+*.color12: #7aa6da
+*.color13: #c397d8
+*.color14: #70c0b1
+*.color15: #eaeaea
+
+URxvt*font: xft:Liberation Mono:size=14
diff --git a/.bashrc b/.bashrc
index 54c0aa0..511ae17 100644
--- a/.bashrc
+++ b/.bashrc
@@ -72,6 +72,12 @@ __precmd()
PS1+="\001$SHELLC_F_LIGHTGRAY\002:\001$SHELLC_RESET_COLOR_F\002"
PS1+="\001$SHELLC_F_BLUE\002\w\001$SHELLC_RESET_COLOR_F\002"
+ # Add venv indicator
+ if [[ -n "$PYTHON_VENV_DIR" ]]
+ then
+ PS1+="\001$SHELLC_F_CYAN\002#$(awk -F/ '{ print $(NF-1) }' <<< "$PYTHON_VENV_DIR")\001$SHELLC_RESET_COLOR_F\002"
+ fi
+
# Add git status
if git rev-parse --is-inside-work-tree &> /dev/null
then
@@ -98,11 +104,13 @@ __precmd()
fi
# Add the exit code of the previous command
- PS1+=" "
+ PS1+=" "
if [[ $previous_exit -ne 0 ]]
then
PS1+="\001$SHELLC_F_RED\002"
+ else
+ PS1+="\001$SHELLC_F_LIGHTGRAY\002"
fi
PS1+="$(printf "%03i" "$previous_exit")\001$SHELLC_RESET_COLOR_F\002"
diff --git a/.config/Trolltech.conf b/.config/Trolltech.conf
new file mode 100644
index 0000000..35cbeb6
--- /dev/null
+++ b/.config/Trolltech.conf
@@ -0,0 +1,17 @@
+[qt]
+GUIEffects=none
+KDE\contrast=7
+KWinPalette\activeBackground=#2f343f
+KWinPalette\activeBlend=#2f343f
+KWinPalette\activeForeground=#d3dae3
+KWinPalette\activeTitleBtnBg=#2f343f
+KWinPalette\frame=#2f343f
+KWinPalette\inactiveBackground=#2f343f
+KWinPalette\inactiveBlend=#2f343f
+KWinPalette\inactiveForeground=#666a73
+KWinPalette\inactiveFrame=#2f343f
+KWinPalette\inactiveTitleBtnBg=#2f343f
+Palette\active=#d3dae3, #414857, #4a5263, #3e4553, #17191f, #282d36, #d3dae3, #ffffff, #d3dae3, #404552, #2f343f, #111216, #5294e2, #d3dae3, #1d99f3, #9b59b6, #404552, #000000, #353945, #d3dae3, #d3dae3
+Palette\disabled=#646973, #3e4553, #485061, #3c4351, #16181d, #272b34, #6e737f, #ffffff, #6f7582, #3d424e, #2d323c, #101115, #2d323c, #646973, #315e84, #5b4870, #3d424e, #000000, #353945, #d3dae3, #d3dae3
+Palette\inactive=#d3dae3, #414857, #4a5263, #3e4553, #17191f, #282d36, #d3dae3, #ffffff, #d3dae3, #404552, #2f343f, #111216, #2b4768, #d3dae3, #1d99f3, #9b59b6, #404552, #000000, #353945, #d3dae3, #d3dae3
+font="Open Sans,10,-1,5,50,0,0,0,0,0"
diff --git a/.config/awesome/charitable/.gitignore b/.config/awesome/charitable/.gitignore
new file mode 100644
index 0000000..edf2840
--- /dev/null
+++ b/.config/awesome/charitable/.gitignore
@@ -0,0 +1,7 @@
+.svn/*
+*~
+*.bak
+.vimrc*
+*.swp
+.idea
+*.iml
diff --git a/.config/awesome/charitable/README.md b/.config/awesome/charitable/README.md
new file mode 100644
index 0000000..da4a924
--- /dev/null
+++ b/.config/awesome/charitable/README.md
@@ -0,0 +1,124 @@
+# Charitable
+
+Shared tags library for multiple monitors using AwesomeWM.
+Sourced from https://github.com/frioux/charitable
+
+## Usage
+
+```lua
+local charitable = require("charitable")
+
+-- Create tags and taglist
+local taglist_buttons = gears.table.join(
+ awful.button({ }, 1, function(t) charitable.select_tag(t, awful.screen.focused()) end),
+
+ awful.button({ }, 3, function(t) charitable.toggle_tag(t, awful.screen.focused()) end)
+)
+
+local tags = charitable.create_tags(
+ { "1", "2", "3", "4", "5", "6", "7", "8", "9" },
+ {
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ }
+)
+
+awful.screen.connect_for_each_screen(function(s)
+ -- Show an unselected tag when a screen is connected
+ for i = 1, #tags do
+ if not tags[i].selected then
+ tags[i].screen = s
+ tags[i]:view_only()
+ break
+ end
+ end
+
+ s.mytaglist = awful.widget.taglist({
+ screen = s,
+ filter = awful.widget.taglist.filter.all,
+ buttons = taglist_buttons,
+ source = function(screen, args) return tags end,
+ })
+
+ -- ...
+end
+
+-- Keys setup
+for i = 1, 9 do
+ globalkeys = gears.table.join(globalkeys,
+ -- View tag only.
+ awful.key({ modkey }, "#" .. i + 9,
+ function () charitable.select_tag(tags[i], awful.screen.focused()) end,
+ {description = "view tag #"..i, group = "tag"}),
+ -- Toggle tag display.
+ awful.key({ modkey, "Control" }, "#" .. i + 9,
+ function () charitable.toggle_tag(tags[i], awful.screen.focused()) end,
+ {description = "toggle tag #" .. i, group = "tag"}),
+ -- Move client to tag.
+ awful.key({ modkey, "Shift" }, "#" .. i + 9,
+ function ()
+ if client.focus then
+ local tag = tags[i]
+ if tag then
+ client.focus:move_to_tag(tag)
+ end
+ end
+ end,
+ {description = "move focused client to tag #"..i, group = "tag"}),
+ -- Toggle tag on focused client.
+ awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
+ function ()
+ if client.focus then
+ local tag = tags[i]
+ if tag then
+ client.focus:toggle_tag(tag)
+ end
+ end
+ end,
+ {description = "toggle focused client on tag #" .. i, group = "tag"})
+ )
+end
+
+-- ensure that removing screens doesn't kill tags
+tag.connect_signal("request::screen", function(t)
+ t.selected = false
+ for s in screen do
+ if s ~= t.screen then
+ t.screen = s
+ return
+ end
+ end
+end)
+
+-- work around bugs in awesome 4.0 through 4.3+
+-- see https://github.com/awesomeWM/awesome/issues/2780
+awful.tag.history.restore = function() end
+```
+
+## Description
+
+Typical AwesomeWM configuration implies that tags are per screen. This library
+allows AwesomeWM to instead have tags that are shared across all connected
+screens. This is how XMonad works by default.
+
+## Quick Reference
+
+ * `charitable.create_tags` replaces [`awful.tag.new`](https://awesomewm.org/doc/api/classes/tag.html#awful.tag.new)
+ * `charitable.select_tag` replaces [`t:view_only()`](https://awesomewm.org/doc/api/classes/tag.html#tag:view_only)
+ * `charitable.toggle_tag` replaces [`awful.tag.viewtoggle`](https://awesomewm.org/doc/api/classes/tag.html#awful.tag.viewtoggle)
+ * `charitable.tag_move` is new
+ * `charitable.swap_screen` is new
+
+## History
+
+This was [originally](https://github.com/lammermann) implemented by Benjamin
+Kober, and later [forked and updated](https://github.com/XLegion/sharetags) by
+Alexey Solodkiy. This version has been updated to support AwesomeWM 4.x, and
+have a better name.
diff --git a/.config/awesome/charitable/init.lua b/.config/awesome/charitable/init.lua
new file mode 100644
index 0000000..8c0cc9b
--- /dev/null
+++ b/.config/awesome/charitable/init.lua
@@ -0,0 +1,160 @@
+-- @module sharetags
+-- functions to share tags on multiple screens
+local sharetags = {}
+
+--{{{ Grab environment we need
+local capi = { screen = screen, client = client }
+
+local pairs = pairs
+local ipairs = ipairs
+local tag = require("awful.tag")
+local awful = require("awful")
+
+--}}}
+
+
+--{{{ Functions
+
+--{{{ create_tags: create a table of tags and bind them to screens
+-- @param names : list to label the tags
+-- @param layouts : list of layouts for the tags
+-- @return table of tag objects
+function sharetags.create_tags(names, layouts)
+ local tags = {}
+ local count = #names
+ if capi.screen.count() >= #names then
+ count = capi.screen.count() + 1
+ end
+
+ for tagnumber = 1, count do
+ tags[tagnumber] = tag.add(names[tagnumber], {})
+ tags[tagnumber].number = tagnumber
+
+ awful.layout.set(layouts[tagnumber], tags[tagnumber])
+ end
+ return tags
+end
+--}}}
+
+--{{{ sharetags.tag_move: move a tag to a screen
+-- @param t : the tag object to move
+-- @param screen_target : the screen object to move to
+function sharetags.tag_move(t, target_screen)
+ local ts = t or tag.selected()
+
+ if not target_screen then return end
+
+ local current_screen = ts.screen
+
+ if current_screen and target_screen ~= current_screen then
+ -- switch for tag
+ local mynumber = ts.number
+
+ -- sort tags
+ local index = #target_screen.tags + 1
+ for i, screen_tag in pairs(target_screen.tags) do
+ local number = screen_tag.number
+ if number ~= nil and mynumber < number then
+ index = i
+ break
+ end
+ end
+
+ -- save curren_screen tags
+ local selected_tags = current_screen.selected_tags
+
+ ts.screen = target_screen
+ ts.index = index
+
+ -- restore curren_screen tag
+ tag.viewmore(selected_tags, current_screen)
+
+
+ -- switch for all clients on tag
+ if #ts:clients() > 0 then
+ for _, c in ipairs(ts:clients()) do
+ if not c.sticky then
+ c.screen = target_screen
+ c:tags({ ts })
+
+ -- Fix maximized client if display sizes not equal
+ local is_maximized = c.maximized
+ if is_maximized then
+ c.maximized = false
+ c.maximized = true
+ end
+ else
+ awful.client.toggletag(ts, c)
+ end
+ end
+ end
+ end
+end
+--}}}
+
+
+-- Open tag on screen
+function sharetags.select_tag(t, target_screen)
+ local prev_focus = capi.client.focus;
+ local tag_screen = t.screen
+ local is_tag_select = t.selected;
+ local is_tag_moved = target_screen ~= tag_screen
+
+
+ if t.selected and target_screen ~= tag_screen and #tag_screen.selected_tags == 1 then
+ sharetags.swap_screen(tag_screen, target_screen)
+ else
+ sharetags.tag_move(t, target_screen)
+ t:view_only()
+ end
+
+
+ -- If there was a moving tag then the focus on the window is lost. Checking
+ -- if this is the same tag and thus restore focus on the window
+ if is_tag_moved and is_tag_select and #t:clients() > 0 and prev_focus then
+ capi.client.focus = prev_focus
+ end
+end
+
+-- Toggle tag on screen
+function sharetags.toggle_tag(t, screen)
+ if t.screen ~= screen then
+ sharetags.tag_move(t, screen)
+ if not t.selected then
+ tag.viewtoggle(t)
+ end
+ else
+ tag.viewtoggle(t)
+ end
+end
+
+-- Swap all selected tags between two screens
+function sharetags.swap_screen(s1, s2)
+ if #s1.selected_tags ~= 1 or #s2.selected_tags ~= 1 then
+ print("can't swap multiple selected tags yet")
+ end
+
+ local t1 = s1.selected_tag
+ local t2 = s2.selected_tag
+
+ -- hide both tags in scratch space
+ t1:swap(t2)
+
+ -- Set selected in both screens to the correct count
+ if #s1.selected_tags ~= 1 then
+ for _, t in ipairs(s1.selected_tags) do
+ t.selected = false
+ end
+ end
+
+ if #s2.selected_tags ~= 1 then
+ for _, t in ipairs(s2.selected_tags) do
+ t.selected = false
+ end
+ end
+
+ t1.selected = true
+ t2.selected = true
+end
+
+return sharetags
diff --git a/.config/awesome/rc.lua b/.config/awesome/rc.lua
index 06e5ce2..045ed80 100644
--- a/.config/awesome/rc.lua
+++ b/.config/awesome/rc.lua
@@ -1,28 +1,33 @@
+-- If LuaRocks is installed, make sure that packages installed through it are
+-- found (e.g. lgi). If LuaRocks is not installed, do nothing.
+pcall(require, "luarocks.loader")
+
-- Standard awesome library
local gears = require("gears")
local awful = require("awful")
require("awful.autofocus")
--- Widget and layout library
local wibox = require("wibox")
--- Theme handling library
local beautiful = require("beautiful")
--- Notification library
local naughty = require("naughty")
local menubar = require("menubar")
-local hotkeys_popup = require("awful.hotkeys_popup").widget
--- Enable hotkeys help widget for VIM and other apps
--- when client with a matching name is opened:
+local hotkeys_popup = require("awful.hotkeys_popup")
require("awful.hotkeys_popup.keys")
-local todo = require("todo")
+-- Custom libraries
+local charitable = require("charitable")
+local widget_battery = require("awesome-wm-widgets.batteryarc-widget.batteryarc")
+local widget_brightness = require("awesome-wm-widgets.brightness-widget.brightness")
+local widget_volume = require("awesome-wm-widgets.pactl-widget.volume")
--- Error handling
+-- {{{ Error handling
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
if awesome.startup_errors then
- naughty.notify({ preset = naughty.config.presets.critical,
- title = "Oops, there were errors during startup!",
- text = awesome.startup_errors })
+ naughty.notify({
+ preset = naughty.config.presets.critical,
+ title = "Oops, there were errors during startup!",
+ text = awesome.startup_errors,
+ })
end
-- Handle runtime errors after startup
@@ -41,205 +46,183 @@ do
end
-- }}}
--- Variable definitions
--- Load theme
+-- {{{ Variable definitions
beautiful.init(awful.util.getdir("config") .. "/theme.lua")
--- This is used later as the default terminal and editor to run.
-terminal = "termite"
-editor = os.getenv("EDITOR") or "vim"
-editor_cmd = terminal .. " -e " .. editor
-
-- Default modkey.
--- Usually, Mod4 is the key with a logo between Control and Alt.
--- If you do not like this or do not have such a key,
--- I suggest you to remap Mod4 to another key using xmodmap or other tools.
--- However, you can use another modifier like Mod1, but it may interact with others.
modkey = "Mod4"
-- Table of layouts to cover with awful.layout.inc, order matters.
awful.layout.layouts = {
awful.layout.suit.tile,
- awful.layout.suit.tile.left,
- awful.layout.suit.tile.bottom,
- awful.layout.suit.tile.top,
- awful.layout.suit.fair,
- awful.layout.suit.fair.horizontal,
- awful.layout.suit.spiral,
- awful.layout.suit.spiral.dwindle,
awful.layout.suit.max,
- -- awful.layout.suit.max.fullscreen,
awful.layout.suit.magnifier,
+ awful.layout.suit.floating,
+ -- awful.layout.suit.max.fullscreen,
+ -- awful.layout.suit.tile.left,
+ -- awful.layout.suit.tile.bottom,
+ -- awful.layout.suit.tile.top,
+ -- awful.layout.suit.fair,
+ -- awful.layout.suit.fair.horizontal,
+ -- awful.layout.suit.spiral,
+ -- awful.layout.suit.spiral.dwindle,
-- awful.layout.suit.corner.nw,
- -- awful.layout.suit.floating,
-- awful.layout.suit.corner.ne,
-- awful.layout.suit.corner.sw,
-- awful.layout.suit.corner.se,
}
-- }}}
--- Helper functions
-local function client_menu_toggle_fn()
- local instance = nil
-
- return function ()
- if instance and instance.wibox.visible then
- instance:hide()
- instance = nil
- else
- instance = awful.menu.clients({ theme = { width = 250 } })
- end
- end
-end
--- }}}
-
--- Menu
--- Create a launcher widget and a main menu
-myawesomemenu = {
- { "hotkeys", function() return false, hotkeys_popup.show_help end},
- { "manual", terminal .. " -e man awesome" },
- { "edit config", editor_cmd .. " " .. awesome.conffile },
- { "restart", awesome.restart },
- { "quit", function() awesome.quit() end}
-}
-
-mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon },
- { "open terminal", terminal }
- }
- })
-
-mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
- menu = mymainmenu })
-
--- Menubar configuration
-menubar.utils.terminal = terminal -- Set the terminal for applications that require it
--- }}}
-
--- Keyboard map indicator and switcher
-mykeyboardlayout = awful.widget.keyboardlayout()
-
--- Wibar
--- Create a textclock widget
-mytextclock = wibox.widget.textclock()
-
--- Add the GTD functionality
-todo({}):attach(mytextclock)
-
+-- {{{ Wibar
-- Create a wibox for each screen and add it
local taglist_buttons = gears.table.join(
- awful.button({ }, 1, function(t) t:view_only() end),
- awful.button({ modkey }, 1, function(t)
- if client.focus then
- client.focus:move_to_tag(t)
- end
- end),
- awful.button({ }, 3, awful.tag.viewtoggle),
- awful.button({ modkey }, 3, function(t)
- if client.focus then
- client.focus:toggle_tag(t)
- end
- end),
- awful.button({ }, 4, function(t) awful.tag.viewprev(t.screen) end),
- awful.button({ }, 5, function(t) awful.tag.viewnext(t.screen) end)
- )
+ awful.button({ }, 1, function(t) charitable.select_tag(t, awful.screen.focused()) end),
+ awful.button({ }, 3, function(t) charitable.toggle_tag(t, awful.screen.focused()) end)
+)
local tasklist_buttons = gears.table.join(
awful.button({ }, 1, function (c)
if c == client.focus then
c.minimized = true
else
- -- Without this, the following
- -- :isvisible() makes no sense
- c.minimized = false
- if not c:isvisible() and c.first_tag then
- c.first_tag:view_only()
- end
- -- This will also un-minimize
- -- the client, if needed
- client.focus = c
- c:raise()
+ c:emit_signal(
+ "request::activate",
+ "tasklist",
+ {raise = true}
+ )
end
end),
- awful.button({ }, 3, client_menu_toggle_fn()),
+ awful.button({ }, 3, function()
+ awful.menu.client_list({ theme = { width = 250 } })
+ end),
awful.button({ }, 4, function ()
awful.client.focus.byidx(-1)
end),
awful.button({ }, 5, function ()
- awful.client.focus.byidx(1)
+ awful.client.focus.byidx( 1)
end))
-awful.tag.add("work", { screen = screen.primary, layout = awful.layout.suit.tile })
-awful.tag.add("social", { screen = screen.primary, layout = awful.layout.suit.max })
-awful.tag.add("web", { screen = screen.primary, layout = awful.layout.suit.max })
-awful.tag.add("email", { screen = screen.primary, layout = awful.layout.suit.magnifier })
-awful.tag.add("fun", { screen = screen.primary, layout = awful.layout.suit.max })
-awful.tag.add("media", { screen = screen.primary, layout = awful.layout.suit.max })
-awful.tag.add("vms", { screen = screen.primary, layout = awful.layout.suit.tile })
-awful.tag.add("scratch", { screen = screen.primary, layout = awful.layout.suit.tile })
-
-sharedtaglist = screen.primary.tags
+local tags = charitable.create_tags(
+ { "work", "social", "www", "email", "fun", "media", "vms", "scratch" },
+ {
+ awful.layout.layouts[1],
+ awful.layout.layouts[1],
+ awful.layout.layouts[2],
+ awful.layout.layouts[3],
+ awful.layout.layouts[2],
+ awful.layout.layouts[2],
+ awful.layout.layouts[2],
+ awful.layout.layouts[4],
+ }
+)
awful.screen.connect_for_each_screen(function(s)
- -- Create an imagebox widget which will contain an icon indicating which layout we're using.
- -- We need one layoutbox per screen.
- s.mylayoutbox = awful.widget.layoutbox(s)
- s.mylayoutbox:buttons(gears.table.join(
- awful.button({ }, 1, function () awful.layout.inc( 1) end),
- awful.button({ }, 3, function () awful.layout.inc(-1) end),
- awful.button({ }, 4, function () awful.layout.inc(-1) end),
- awful.button({ }, 5, function () awful.layout.inc( 1) end)))
-
- -- Create a taglist widget
- s.mytaglist = awful.widget.taglist(s, awful.widget.taglist.filter.all, taglist_buttons)
-
- -- Create a tasklist widget
- s.mytasklist = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, tasklist_buttons)
-
- -- Create the wibar for status information
- s.mywibox_info = awful.wibar({ position = "top", screen = s })
-
- -- Add widgets to the wibox
- s.mywibox_info:setup {
- layout = wibox.layout.align.horizontal,
- { -- Left widgets
- layout = wibox.layout.fixed.horizontal,
- mylauncher,
- s.mytaglist,
- },
- {
- layout = wibox.layout.fixed.horizontal
- },
- { -- Right widgets
- layout = wibox.layout.fixed.horizontal,
- -- mykeyboardlayout,
- wibox.widget.systray(),
- mytextclock,
- s.mylayoutbox,
- },
- }
-
- -- Create a wibar for the task list
- s.mywibox_tasks = awful.wibar({ position = "bottom", screen = s })
-
- s.mywibox_tasks:setup {
- layout = wibox.layout.align.horizontal,
- { layout = wibox.layout.fixed.horizontal, },
- s.mytasklist,
- { layout = wibox.layout.fixed.horizontal, },
- }
-
+ -- Show an unselected tag when a screen is connected
+ for i = 1, #tags do
+ if not tags[i].selected then
+ tags[i].screen = s
+ tags[i]:view_only()
+ break
+ end
+ end
+
+ s.widgets = {}
+ s.bars = {}
+
+ -- Create an imagebox widget which will contain an icon indicating which layout we're using.
+ -- We need one layoutbox per screen.
+ s.widgets.layout = awful.widget.layoutbox(s)
+ s.widgets.layout:buttons(gears.table.join(
+ awful.button({ }, 1, function () awful.layout.inc( 1) end),
+ awful.button({ }, 3, function () awful.layout.inc(-1) end),
+ awful.button({ }, 4, function () awful.layout.inc( 1) end),
+ awful.button({ }, 5, function () awful.layout.inc(-1) end))
+ )
+
+ -- Create widgets
+ s.widgets.battery = widget_battery({
+ show_current_level = false,
+ warning_msg_position = top_right,
+ })
+ s.widgets.brightness = widget_brightness({
+ program = "brightnessctl",
+ tooltip = true,
+ })
+ s.widgets.clock = wibox.widget {
+ widget = wibox.widget.textclock,
+ format = "%F %H:%M",
+ }
+ s.widgets.systray = wibox.widget.systray()
+ s.widgets.tags = awful.widget.taglist {
+ screen = s,
+ filter = awful.widget.taglist.filter.all,
+ buttons = taglist_buttons,
+ source = function(screen, args) return tags end,
+ }
+ s.widgets.tasklist = awful.widget.tasklist {
+ screen = s,
+ filter = awful.widget.tasklist.filter.currenttags,
+ buttons = tasklist_buttons,
+ }
+ s.widgets.volume = widget_volume({
+ tooltip = true,
+ widget_type = "arc",
+ })
+
+ -- Create the taskbar
+ s.bars.bottom = awful.wibar({
+ position = "bottom",
+ screen = s,
+ bg = beautiful.bg_normal .. "cc",
+ })
+
+ -- Add widgets to the wibox
+ s.bars.bottom:setup {
+ layout = wibox.layout.align.horizontal,
+ s.widgets.tags,
+ s.widgets.tasklist,
+ s.widgets.layout,
+ }
+
+ -- Create the statbar
+ s.bars.top = awful.wibar({
+ position = "top",
+ screen = s,
+ bg = beautiful.bg_normal .. "cc",
+ })
+
+ s.bars.top:setup {
+ layout = wibox.layout.align.horizontal,
+ expand = "none",
+ {
+ layout = wibox.layout.fixed.horizontal,
+ s.widgets.layout,
+ },
+ {
+ layout = wibox.layout.fixed.horizontal,
+ s.widgets.clock,
+ },
+ {
+ layout = wibox.layout.fixed.horizontal,
+ spacing = 4,
+ s.widgets.systray,
+ s.widgets.brightness,
+ s.widgets.volume,
+ s.widgets.battery,
+ },
+ }
end)
-- }}}
--- Mouse bindings
+-- {{{ Mouse bindings
root.buttons(gears.table.join(
awful.button({ }, 3, function () mymainmenu:toggle() end),
- awful.button({ }, 4, awful.tag.viewprev),
- awful.button({ }, 5, awful.tag.viewnext)
+ awful.button({ }, 4, awful.tag.viewnext),
+ awful.button({ }, 5, awful.tag.viewprev)
))
-- }}}
--- Key bindings
+-- {{{ Key bindings
globalkeys = gears.table.join(
awful.key({ modkey, }, "s", hotkeys_popup.show_help,
{description="show help", group="awesome"}),
@@ -250,6 +233,18 @@ globalkeys = gears.table.join(
awful.key({ modkey, }, "Escape", awful.tag.history.restore,
{description = "go back", group = "tag"}),
+ awful.key({ modkey, }, "Down",
+ function ()
+ awful.client.focus.byidx( 1)
+ end,
+ {description = "focus next by index", group = "client"}
+ ),
+ awful.key({ modkey, }, "Up",
+ function ()
+ awful.client.focus.byidx(-1)
+ end,
+ {description = "focus previous by index", group = "client"}
+ ),
awful.key({ modkey, }, "j",
function ()
awful.client.focus.byidx( 1)
@@ -265,7 +260,15 @@ globalkeys = gears.table.join(
awful.key({ modkey, }, "w", function () mymainmenu:show() end,
{description = "show main menu", group = "awesome"}),
+
-- Layout manipulation
+ awful.key({ modkey, "Shift" }, "Down", function () awful.client.swap.byidx( 1) end,
+ {description = "swap with next client by index", group = "client"}),
+ awful.key({ modkey, "Shift" }, "Up", function () awful.client.swap.byidx( -1) end,
+ {description = "swap with previous client by index", group = "client"}),
+ awful.key({ modkey, "Control" }, "Right", function () awful.screen.focus_bydirection("right") end,
+ {description = "focus the next screen", group = "screen"}),
+ awful.key({ modkey, "Control" }, "Left", function () awful.screen.focus_bydirection("left") end,
awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end,
{description = "swap with next client by index", group = "client"}),
awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end,
@@ -274,8 +277,8 @@ globalkeys = gears.table.join(
{description = "focus the next screen", group = "screen"}),
awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_bydirection("left") end,
{description = "focus the previous screen", group = "screen"}),
- --awful.key({ modkey, }, "u", awful.client.urgent.jumpto,
- -- {description = "jump to urgent client", group = "client"}),
+ awful.key({ modkey, }, "u", awful.client.urgent.jumpto,
+ {description = "jump to urgent client", group = "client"}),
awful.key({ modkey, }, "Tab",
function ()
awful.client.focus.history.previous()
@@ -285,13 +288,24 @@ globalkeys = gears.table.join(
end,
{description = "go back", group = "client"}),
- -- Standard program
- awful.key({ modkey, }, "Return", function () awful.spawn(terminal) end,
- {description = "open a terminal", group = "launcher"}),
- awful.key({ modkey, "Shift" }, "r", awesome.restart,
- {description = "reload awesome", group = "awesome"}),
- awful.key({ modkey, "Shift" }, "x", awesome.quit,
- {description = "quit awesome", group = "awesome"}),
+ -- Standard program
+ awful.key(
+ { modkey, "Shift" }, "r", awesome.restart,
+ {description = "reload awesome", group = "awesome"}
+ ),
+ awful.key(
+ { modkey, "Shift" }, "x", awesome.quit,
+ {description = "quit awesome", group = "awesome"}
+ ),
+
+ awful.key(
+ { modkey, }, "p", function () awful.spawn("ipass") end,
+ { description = "Select password", group = "awesome" }
+ ),
+ awful.key(
+ { modkey, }, "o", function () awful.spawn("ipass-otp") end,
+ { description = "Select password", group = "awesome" }
+ ),
awful.key({ modkey, }, "l", function () awful.tag.incmwfact( 0.05) end,
{description = "increase master width factor", group = "layout"}),
@@ -315,8 +329,9 @@ globalkeys = gears.table.join(
local c = awful.client.restore()
-- Focus restored client
if c then
- client.focus = c
- c:raise()
+ c:emit_signal(
+ "request::activate", "key.unminimize", {raise = true}
+ )
end
end,
{description = "restore minimized", group = "client"})
@@ -333,7 +348,7 @@ clientkeys = gears.table.join(
{description = "close", group = "client"}),
awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle ,
{description = "toggle floating", group = "client"}),
- awful.key({ modkey, "Shift" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
+ awful.key({ modkey, "Shift" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
{description = "move to master", group = "client"}),
awful.key({ modkey, }, "o", function (c) c:move_to_screen() end,
{description = "move to screen", group = "client"}),
@@ -366,247 +381,295 @@ clientkeys = gears.table.join(
{description = "(un)maximize horizontally", group = "client"})
)
--- Bind all key numbers to tags.
--- Be careful: we use keycodes to make it work on any keyboard layout.
--- This should map on the top row of your keyboard, usually 1 to 9.
-for i = 1, 9 do
- globalkeys = gears.table.join(globalkeys,
- -- View tag only.
- awful.key({ modkey }, "#" .. i + 10,
- function ()
- local screen = awful.screen.focused()
- local tag = sharedtaglist[i]
- if tag then
- awful.tag.setscreen(screen, tag)
- tag:view_only()
- end
- end,
- {description = "view tag #"..i, group = "tag"}),
- -- Toggle tag display.
- awful.key({ modkey, "Control" }, "#" .. i + 10,
- function ()
- local tag = sharedtaglist[i]
- if tag then
- awful.tag.viewtoggle(tag)
- end
- end,
- {description = "toggle tag #" .. i, group = "tag"}),
- -- Move client to tag.
- awful.key({ modkey, "Shift" }, "#" .. i + 10,
- function ()
- if client.focus then
- local tag = sharedtaglist[i]
- if tag then
- client.focus:move_to_tag(tag)
- end
- end
- end,
- {description = "move focused client to tag #"..i, group = "tag"}),
- -- Toggle tag on focused client.
- awful.key({ modkey, "Control", "Shift" }, "#" .. i + 10,
- function ()
- if client.focus then
- local tag = sharedtaglist[i]
- if tag then
- client.focus:toggle_tag(tag)
- end
- end
- end,
- {description = "toggle focused client on tag #" .. i, group = "tag"})
- )
+-- Bind numbers row
+for i = 1, 8 do
+ globalkeys = gears.table.join(globalkeys,
+ -- View tag only.
+ awful.key(
+ { modkey },
+ "#" .. i + 10,
+ function () charitable.select_tag(tags[i], awful.screen.focused()) end,
+ {description = "view tag #"..i, group = "tag"}
+ ),
+
+ -- Toggle tag display.
+ awful.key(
+ { modkey, "Control" },
+ "#" .. i + 10,
+ function () charitable.toggle_tag(tags[i], awful.screen.focused()) end,
+ {description = "toggle tag #" .. i, group = "tag"}
+ ),
+ -- Move client to tag.
+
+ awful.key(
+ { modkey, "Shift" },
+ "#" .. i + 10,
+ function ()
+ if client.focus then
+ local tag = tags[i]
+ if tag then
+ client.focus:move_to_tag(tag)
+ end
+ end
+ end,
+ {description = "move focused client to tag #"..i, group = "tag"}
+ ),
+
+ -- Toggle tag on focused client.
+ awful.key(
+ { modkey, "Control", "Shift" },
+ "#" .. i + 10,
+ function ()
+ if client.focus then
+ local tag = tags[i]
+ if tag then
+ client.focus:toggle_tag(tag)
+ end
+ end
+ end,
+ {description = "toggle focused client on tag #" .. i, group = "tag"}
+ )
+ )
end
clientbuttons = gears.table.join(
- awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
- awful.button({ modkey }, 1, awful.mouse.client.move),
- awful.button({ modkey }, 3, awful.mouse.client.resize))
+ awful.button({ }, 1, function (c)
+ c:emit_signal("request::activate", "mouse_click", {raise = true})
+ end),
+ awful.button({ modkey }, 1, function (c)
+ c:emit_signal("request::activate", "mouse_click", {raise = true})
+ awful.mouse.client.move(c)
+ end),
+ awful.button({ modkey }, 3, function (c)
+ c:emit_signal("request::activate", "mouse_click", {raise = true})
+ awful.mouse.client.resize(c)
+ end)
+)
-- Set keys
root.keys(globalkeys)
-- }}}
--- Rules
+-- {{{ Rules
-- Rules to apply to new clients (through the "manage" signal).
awful.rules.rules = {
- -- All clients will match this rule.
- {
- rule = { },
- properties = {
- border_width = beautiful.border_width,
- border_color = beautiful.border_normal,
- focus = awful.client.focus.filter,
- raise = true,
- keys = clientkeys,
- buttons = clientbuttons,
- screen = awful.screen.preferred,
- placement = awful.placement.no_overlap+awful.placement.no_offscreen
- }
- },
-
- -- Floating clients.
-
- {
- rule_any = {
- instance = {
- "DTA", -- Firefox addon DownThemAll.
- "copyq", -- Includes session name in class.
- },
- class = {
- "Arandr",
- "Gpick",
- "Kruler",
- "MessageWin", -- kalarm.
- "Sxiv",
- "Wpa_gui",
- "pinentry",
- "veromix",
- "xtightvncviewer"
- },
- name = {
- "Event Tester", -- xev.
- },
- role = {
- "AlarmWindow", -- Thunderbird's calendar.
- "pop-up", -- e.g. Google Chrome's (detached) Developer Tools.
- }
- },
- properties = {
- floating = true
- }
- },
-
- -- Add titlebars to normal clients and dialogs
-
- -- {
- -- rule_any = {
- -- type = {
- -- "normal",
- -- "dialog"
- -- }
- -- },
- -- properties = {
- -- titlebars_enabled = true
- -- }
- -- },
-
- -- Application-specific rules
-
- -- www-client/chromium
- {
- rule_any = {
- class = {
- "chromium-browser-chromium",
- "Chromium-browser-chromium",
- }
- },
- properties = {
- tag = sharedtaglist[3]
- },
- },
-
- -- www-client/firefox
- {
- rule_any = {
- class = {
- "Navigator",
- "Firefox Developer Edition"
- }
- },
- properties = {
- tag = sharedtaglist[3]
- },
- },
-
- -- net-im/signal-desktop
- {
- rule_any = {
- class = {
- "Signal",
- }
- },
- properties = {
- tag = sharedtaglist[2]
- },
- },
-
- -- media-video/mpv
- {
- rule_any = {
- class = {
- "gl",
- "mpv",
- }
- },
- properties = {
- tag = sharedtaglist[6]
- },
- },
-
- { properties = { tag = sharedtaglist[4] }, rule_any = { class = { "claws-mail", "Claws-mail" } } },
- { properties = { tag = sharedtaglist[5] }, rule_any = { name = { "Blizzard Battle.net" } } },
- { properties = { tag = sharedtaglist[5] }, rule_any = { name = { "World of Warcraft" } } },
- { properties = { tag = sharedtaglist[5] }, rule_any = { name = { "Guild Wars" } } },
-
- -- Set Firefox to always map on the tag named "2" on screen 1.
- -- { rule = { class = "Firefox" },
- -- properties = { screen = 1, tag = "2" } },
-
- -- Jellyfin
- {
- rule = {
- class = "jellyfin",
- -- class = {,
- -- "Chromium-browser-chromium",
- -- },
- },
- properties = {
- tag = sharedtaglist[6],
- maximized = true,
- },
- },
-
- -- World of Warcraft
- {
- rule_any = {
- class = {
- "wow.exe",
- },
- },
- properties = {
- tag = sharedtaglist[5],
- fullscreen = true,
+ { -- All clients will match this rule.
+ rule = { },
+ properties = {
+ border_width = beautiful.border_width,
+ border_color = beautiful.border_normal,
+ focus = awful.client.focus.filter,
+ raise = true,
+ keys = clientkeys,
+ buttons = clientbuttons,
+ screen = awful.screen.preferred,
+ placement = awful.placement.no_overlap+awful.placement.no_offscreen,
+ },
+ },
+
+ -- KDE Rules
+
+ { -- Remove the default desktop
+ rule_any = {
+ name = { "^Desktop " }
+ },
+ properties = {
+ hidden = true,
+ },
+ },
+ { -- Make the KDE shell play nicely
+ rule_any = {
+ class = {
+ "plasmashell",
+ },
+ },
+ properties = {
+ floating = true,
+ border_width = 0,
+ focus = false,
+ },
+ },
+ { -- Center KDE applets
+ rule_any = {
+ class = {
+ "krunner",
+ },
+ type = {
+ "_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY",
+ },
+ },
+ properties = {
+ floating = true,
+ border_width = 0,
+ placement = awful.placement.centered,
+ },
+ },
+
+ { -- Remove borders from windows where it doesn't fit (housestyles ought to be banned, tho)
+ rule_any = {
+ class = {
+ "albert",
+ "nextcloud",
+ }
+ },
+ properties = {
+ border_width = 0,
+ },
+ },
+
+ { -- Tag rules for social
+ rule_any = {
+ class = {
+ "irc",
+ },
+ },
+ properties = {
+ tag = "social",
+ },
+ },
+
+ { -- Tag rules for www
+ rule_any = {
+ class = {
+ "chromium",
+ "firefox",
+ "firefox-esr",
+ "qutebrowser",
+ },
+ },
+ properties = {
+ tag = "www",
+ },
+ },
+
+ { -- Tag rules for email
+ rule_any = {
+ class = {
+ "evolution",
+ "thunderbird",
+ "kmail",
+ },
+ },
+ properties = {
+ tag = "email",
+ },
+ },
+
+ { -- Tag rules for fun
+ rule_any = {
+ class = {
+ "wow.exe",
+ "battle.net.exe",
+ "tsmapplication.exe",
+ "lutris",
+ "pathofexile.exe",
+ "mumble"
+ },
+ },
+ properties = {
+ tag = "fun",
+ },
+ },
+
+ { -- Tag rules for media
+ rule_any = {
+ class = {
+ "mpv",
+ }
+ },
+ properties = {
+ tag = "media",
+ },
+ },
+
+ { -- Wine clients need to behave
+ rule_any = {
+ class = {
+ "wow.exe",
+ "pathofexile.exe",
+ },
+ },
+ properties = {
+ floating = false,
+ fullscreen = false,
+ },
+ },
+
+ -- Floating clients.
+ { rule_any = {
+ instance = {
+ "DTA", -- Firefox addon DownThemAll.
+ "copyq", -- Includes session name in class.
+ "pinentry",
+ },
+ class = {
+ "Arandr",
+ "Blueman-manager",
+ "Gpick",
+ "Kruler",
+ "MessageWin", -- kalarm.
+ "Sxiv",
+ "Tor Browser", -- Needs a fixed window size to avoid fingerprinting by screen size.
+ "Wpa_gui",
+ "veromix",
+ "xtightvncviewer"},
+
+ -- Note that the name property shown in xprop might be set slightly after creation of the client
+ -- and the name shown there might not match defined rules here.
+ name = {
+ "Event Tester", -- xev.
+ },
+ role = {
+ "AlarmWindow", -- Thunderbird's calendar.
+ "ConfigManager", -- Thunderbird's about:config.
+ "pop-up", -- e.g. Google Chrome's (detached) Developer Tools.
+ }
+ }, properties = { floating = true }},
+
+ -- Add titlebars to normal clients and dialogs
+ { rule_any = {type = { "normal", "dialog" }
+ }, properties = { titlebars_enabled = false }
},
- }
}
-- }}}
--- Signals
+-- {{{ Signals
-- Signal function to execute when a new client appears.
client.connect_signal("manage", function (c)
-- Set the windows at the slave,
-- i.e. put it at the end of others instead of setting it master.
-- if not awesome.startup then awful.client.setslave(c) end
- if awesome.startup and
- not c.size_hints.user_position
+ if awesome.startup
+ and not c.size_hints.user_position
and not c.size_hints.program_position then
-- Prevent clients from being unreachable after screen count changes.
awful.placement.no_offscreen(c)
end
end)
+-- ensure that removing screens doesn't kill tags
+tag.connect_signal("request::screen", function(t)
+ t.selected = false
+
+ for s in screen do
+ if s ~= t.screen then
+ t.screen = s
+ return
+ end
+ end
+end)
+
-- Add a titlebar if titlebars_enabled is set to true in the rules.
client.connect_signal("request::titlebars", function(c)
-- buttons for the titlebar
local buttons = gears.table.join(
awful.button({ }, 1, function()
- client.focus = c
- c:raise()
+ c:emit_signal("request::activate", "titlebar", {raise = true})
awful.mouse.client.move(c)
end),
awful.button({ }, 3, function()
- client.focus = c
- c:raise()
+ c:emit_signal("request::activate", "titlebar", {raise = true})
awful.mouse.client.resize(c)
end)
)
@@ -639,12 +702,28 @@ end)
-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
- if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
- and awful.client.focus.filter(c) then
- client.focus = c
- end
+ c:emit_signal("request::activate", "mouse_enter", {raise = false})
end)
-client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
-client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
+client.connect_signal("focus", function(c)
+ c.border_color = beautiful.border_focus
+
+-- -- Mouse follows focus
+-- if mouse.object_under_pointer() ~= c then
+-- local geometry = c:geometry()
+--
+-- mouse.coords({
+-- x = geometry.x + geometry.width/2,
+-- y = geometry.y + geometry.height/2,
+-- }, true)
+-- end
+end)
+
+client.connect_signal("unfocus", function(c)
+ c.border_color = beautiful.border_normal
+end)
-- }}}
+
+awful.util.spawn("systemctl --user import-environment DISPLAY")
+awful.util.spawn("systemctl --user import-environment XAUTHORITY")
+awful.util.spawn("systemctl --user start awesome-session.target")
diff --git a/.config/awesome/theme.lua b/.config/awesome/theme.lua
index 3ee3de6..25c9167 100644
--- a/.config/awesome/theme.lua
+++ b/.config/awesome/theme.lua
@@ -12,8 +12,8 @@ local theme = {}
theme.font = "Liberation Mono 9"
-theme.bg_normal = "#393939"
-theme.bg_focus = "#5778c1"
+theme.bg_normal = "#252a35"
+theme.bg_focus = "#5294e2"
theme.bg_urgent = "#ca674a"
theme.bg_minimize = "#a9a9a9"
theme.bg_systray = theme.bg_normal
@@ -23,12 +23,20 @@ theme.fg_focus = theme.fg_normal
theme.fg_urgent = theme.fg_normal
theme.fg_minimize = theme.fg_normal
-theme.useless_gap = dpi(0)
-theme.border_width = dpi(1)
-theme.border_normal = "#000000"
-theme.border_focus = "#5778c1"
+theme.border_width = dpi(2)
+theme.border_normal = "#4b5165"
+theme.border_focus = "#5294e2"
theme.border_marked = "#91231c"
+theme.taglist_spacing = 0
+theme.taglist_bg_occupied = "#4b5165"
+
+theme.tasklist_bg_normal = "#4b5165"
+theme.tasklist_spacing = 0
+
+theme.useless_gap = dpi(4)
+theme.maximized_honor_padding = true
+
-- There are other variable sets
-- overriding the default one when
-- defined, the sets are:
@@ -56,6 +64,12 @@ theme.taglist_squares_unsel = theme_assets.taglist_squares_unsel(
-- notification_[bg|fg]
-- notification_[width|height|margin]
-- notification_[border_color|border_width|shape|opacity]
+theme.notification_border_color = "#4b5165"
+theme.notification_border_width = dpi(2)
+theme.notification_icon_size = dpi(64)
+theme.notification_margin = dpi(4)
+theme.notification_max_height = dpi(72)
+theme.notification_max_width = dpi(640)
-- Variables set for theming the menu:
-- menu_[bg|fg]_[normal|focus]
diff --git a/.config/awesome/todo.lua b/.config/awesome/todo.lua
deleted file mode 100644
index 83cf2d8..0000000
--- a/.config/awesome/todo.lua
+++ /dev/null
@@ -1,47 +0,0 @@
-local capi = {
- mouse = mouse,
- screen = screen,
-}
-local naughty = require("naughty")
-
-------------------------------------------
--- todo.txt popup widget
-------------------------------------------
-
-local todo = {}
-
-function todo:new(args)
- return setmetatable({}, {__index = self})
-end
-
-function todo:show()
- local f = assert(io.open("/home/tyil/documents/todo.txt", "rb"))
- local content = f:read("*all"):gsub("%s+$", "")
- f:close()
-
- self.notification = naughty.notify({
- title = "Todo",
- text = content,
- timeout = 0,
- hover_timeout = 0.5,
- screen = capi.mouse.screen,
- position = self.position,
- })
-end
-
-function todo:hide()
- if self.notification then
- naughty.destroy(self.notification)
- self.notification = nil
- self.num_lines = 0
- end
-end
-
-function todo:attach(widget)
- widget:connect_signal('mouse::enter', function() self:show() end)
- widget:connect_signal('mouse::leave', function() self:hide() end)
-end
-
-return setmetatable(todo, {
- __call = todo.new,
-})
diff --git a/.config/dolphinrc b/.config/dolphinrc
new file mode 100644
index 0000000..860b0e7
--- /dev/null
+++ b/.config/dolphinrc
@@ -0,0 +1,14 @@
+[$Version]
+update_info=dolphin_detailsmodesettings.upd:rename-leading-padding
+
+[General]
+Version=202
+ViewPropsTimestamp=2023,4,25,18,58,48.288
+
+[KFileDialog Settings]
+Places Icons Auto-resize=false
+Places Icons Static Size=22
+
+[MainWindow]
+MenuBar=Disabled
+ToolBarsMovable=Disabled
diff --git a/.config/environment.d/00-path.conf b/.config/environment.d/00-path.conf
new file mode 100644
index 0000000..dd56eec
--- /dev/null
+++ b/.config/environment.d/00-path.conf
@@ -0,0 +1,7 @@
+# Local paths
+PATH=/home/tyil/.local/bin:/home/tyil/.config/shell/wrappers.d
+
+# Default paths
+PATH=$PATH:/usr/local/sbin:/user/local/bin
+PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
+PATH=$PATH:/opt/bin
diff --git a/.config/firefox/chrome/userChrome.css b/.config/firefox/chrome/userChrome.css
index e2ab027..d9da526 100644
--- a/.config/firefox/chrome/userChrome.css
+++ b/.config/firefox/chrome/userChrome.css
@@ -16,3 +16,8 @@
tab-item.unread .label-content {
font-style: italic;
}
+
+/* Hide the title/tab bar */
+#TabsToolbar {
+ display: none;
+}
diff --git a/.config/firefox/user.js b/.config/firefox/user.js
index 08d62a7..c39b87c 100644
--- a/.config/firefox/user.js
+++ b/.config/firefox/user.js
@@ -1,15 +1,7 @@
// Settings changed after going through properties
-user_pref("app.shield.optoutstudies.enabled", false);
user_pref("browser.ctrlTab.recentlyUsedOrder", false);
user_pref("browser.download.dir", "/home/tyil/downloads/firefox");
user_pref("browser.download.folderList", 2);
-user_pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", false);
-user_pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", false);
-user_pref("browser.newtabpage.activity-stream.feeds.section.highlights", false);
-user_pref("browser.newtabpage.activity-stream.feeds.snippets", false);
-user_pref("browser.newtabpage.activity-stream.feeds.topsites", false);
-user_pref("browser.newtabpage.activity-stream.showSearch", false);
-user_pref("browser.newtabpage.enabled", false);
user_pref("browser.search.hiddenOneOffs", "Google,Bing,Amazon.com,eBay,Twitter");
user_pref("browser.search.suggest.enabled", false);
user_pref("browser.startup.homepage", "https://searx.tyil.nl");
@@ -19,15 +11,29 @@ user_pref("general.smoothScroll", false);
user_pref("media.autoplay.default", 5); // Disable autoplay for both sound and video
user_pref("privacy.donottrackheader.enabled", true);
-// Unload browser tabs when low on memory
-user_pref("browser.tabs.unloadOnLowMemory", true);
-
-// Make Firefox load userChrome.css again...
-user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
-
-// Disable whatever trash Firefox devs are pushing these days
+// Disable anti-features
+user_pref("app.shield.optoutstudies.enabled", false);
+user_pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", false);
+user_pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", false);
+user_pref("browser.newtabpage.activity-stream.feeds.section.highlights", false);
+user_pref("browser.newtabpage.activity-stream.feeds.snippets", false);
+user_pref("browser.newtabpage.activity-stream.feeds.topsites", false);
+user_pref("browser.newtabpage.activity-stream.showSearch", false);
+user_pref("browser.newtabpage.enabled", false);
user_pref("extensions.pocket.enabled", false);
user_pref("media.videocontrols.picture-in-picture.enabled", false);
user_pref("media.videocontrols.picture-in-picture.video-toggle.enabled", false);
+user_pref("network.captive-portal-service.enabled", false);
user_pref("network.trr.mode", 5); // DoH
user_pref("pdfjs.disabled", true);
+
+// Unload browser tabs when low on memory
+user_pref("browser.tabs.unloadOnLowMemory", true);
+
+// Make fullscreen stick to window size
+user_pref("full-screen-api.ignore-widgets", true);
+
+// Undo horrible UI decisions made by the incompetent
+user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true); // Enable userChrome.css
+user_pref("widget.non-native-theme.scrollbar.size.override", 16); // Set the scrollbar width
+user_pref("widget.gtk.overlay-scrollbars.enabled", false); // Use normal scrollbars
diff --git a/.config/fuzzel/fuzzel.ini b/.config/fuzzel/fuzzel.ini
new file mode 100644
index 0000000..1c374a7
--- /dev/null
+++ b/.config/fuzzel/fuzzel.ini
@@ -0,0 +1,20 @@
+[main]
+font = monospace:size=10
+lines = 15
+width = 80
+horizontal-pad = 8
+vertical-pad = 8
+
+[colors]
+background = 252a35ff
+selection = 5294e2ff
+text = eeeeeeff
+selection-text = eeeeeeff
+
+[border]
+radius = 4
+
+[key-bindings]
+execute-or-next = none
+delete-prev-word = Control+BackSpace Control+w
+next = Down Control+n Tab
diff --git a/.config/gammastep/config.ini b/.config/gammastep/config.ini
new file mode 100644
index 0000000..2933117
--- /dev/null
+++ b/.config/gammastep/config.ini
@@ -0,0 +1,8 @@
+[general]
+temp-day=6500
+temp-night=3000
+location-provider=manual
+
+[manual]
+lat=51.8100
+lon=4.6736
diff --git a/.config/git/authorized_keys b/.config/git/authorized_keys
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.config/git/authorized_keys
diff --git a/.config/git/config b/.config/git/config
index 63ea101..212776b 100644
--- a/.config/git/config
+++ b/.config/git/config
@@ -1,26 +1,27 @@
[alias]
- a = add
- b = branch
- bc = branch-cleanup
- c = commit
- ca = commit --amend --reset-author
- co = checkout
- cob = checkout -b
- ct = tag -s
- d = diff
- fo = fetch origin
- l = log --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%C(6)%ad %C(3)%h%Creset %s %C(7)by %C(2)%ae%C(7):%C(5)%G?'
- lg = log --graph --abbrev-commit --decorate --date=relative --all
+ a = add
+ b = branch
+ bc = branch-cleanup
+ c = commit
+ ca = commit --amend --reset-author
+ co = checkout
+ cob = checkout -b
+ ct = tag -s
+ d = diff
+ fo = fetch origin
+ l = log --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%C(6)%ad %C(3)%h%Creset %s %C(7)by %C(2)%ae%C(7):%C(5)%G?'
+ lg = log --graph --abbrev-commit --decorate --date=relative --all
mirror-add = ! git remote set-url --add --push origin
- p = push
- pa = push-all
- prev = ! git show "HEAD:$1"
- r = rebase --rebase-merges -i
- ra = commit --amend --no-edit -a
- rc = rebase --continue
- s = status -s
- t = tag --sort=-v:refname
- tr = for-each-ref --sort=taggerdate --format '%(tag)' refs/tags
+ p = push
+ pa = push-all
+ prev = ! git show "HEAD:$1"
+ r = rebase --rebase-merges -i
+ ra = commit --amend --no-edit -a
+ rc = rebase --continue
+ s = status -s
+ squash-all = "!f(){ git reset $(git commit-tree HEAD^{tree} \"$@\");};f"
+ t = tag --sort=-v:refname
+ tr = for-each-ref --sort=taggerdate --format '%(tag)' refs/tags
[commit]
gpgsign = true
@@ -39,6 +40,12 @@
[difftool]
prompt = false
+[gpg]
+ format = ssh
+
+[gpg "ssh"]
+ allowedSignersFile = ~/.config/git/authorized_keys
+
[init]
defaultBranch = master
@@ -53,7 +60,13 @@
default = current
followTags = true
+[safe]
+ directory = *
+
+[tag]
+ gpgsign = true
+
[user]
- email = p.spek@tyil.nl
- name = Patrick Spek
- signingkey = 1660F6A2DFA75347322A4DC07A6AC285E2D98827
+ email = p.spek@tyil.nl
+ name = Patrick Spek
+ signingKey = ~/.config/git/signingkey # Symlink to the SSH key to be used
diff --git a/.config/git/ignore b/.config/git/ignore
index b84abd4..b283dac 100644
--- a/.config/git/ignore
+++ b/.config/git/ignore
@@ -1,8 +1,12 @@
#
-# Misc
+# Language specific cruft
#
-# Perl 6 precompilation files
+# Python
+.venv
+__pycache__
+
+# Raku
.precomp
#
diff --git a/.config/gtk-3.0/bookmarks b/.config/gtk-3.0/bookmarks
index b5a4bef..2d395c7 100644
--- a/.config/gtk-3.0/bookmarks
+++ b/.config/gtk-3.0/bookmarks
@@ -3,3 +3,6 @@ file:///home/tyil/documents
file:///home/tyil/downloads
file:///home/tyil/pictures
file:///home/tyil/projects
+file:///home/tyil/documents/
+file:///home/tyil/mail/
+file:///home/tyil/pictures/
diff --git a/.config/gtk-3.0/colors.css b/.config/gtk-3.0/colors.css
new file mode 100644
index 0000000..bed2595
--- /dev/null
+++ b/.config/gtk-3.0/colors.css
@@ -0,0 +1,84 @@
+@define-color borders_breeze #585e68;
+@define-color content_view_bg_breeze #404552;
+@define-color error_color_backdrop_breeze #da4453;
+@define-color error_color_breeze #da4453;
+@define-color error_color_insensitive_backdrop_breeze #70414e;
+@define-color error_color_insensitive_breeze #70414e;
+@define-color insensitive_base_color_breeze #3d424e;
+@define-color insensitive_base_fg_color_breeze #6e737f;
+@define-color insensitive_bg_color_breeze #2d323c;
+@define-color insensitive_borders_breeze #3a3f4a;
+@define-color insensitive_fg_color_breeze #646973;
+@define-color insensitive_selected_bg_color_breeze #2d323c;
+@define-color insensitive_selected_fg_color_breeze #646973;
+@define-color insensitive_unfocused_bg_color_breeze #2d323c;
+@define-color insensitive_unfocused_fg_color_breeze #646973;
+@define-color insensitive_unfocused_selected_bg_color_breeze #2d323c;
+@define-color insensitive_unfocused_selected_fg_color_breeze #646973;
+@define-color link_color_breeze #1d99f3;
+@define-color link_visited_color_breeze #9b59b6;
+@define-color success_color_backdrop_breeze #27ae60;
+@define-color success_color_breeze #27ae60;
+@define-color success_color_insensitive_backdrop_breeze #356553;
+@define-color success_color_insensitive_breeze #356553;
+@define-color theme_base_color_breeze #404552;
+@define-color theme_bg_color_breeze #2f343f;
+@define-color theme_button_background_backdrop_breeze #414857;
+@define-color theme_button_background_backdrop_insensitive_breeze #3e4553;
+@define-color theme_button_background_insensitive_breeze #3e4553;
+@define-color theme_button_background_normal_breeze #414857;
+@define-color theme_button_decoration_focus_backdrop_breeze #5294e2;
+@define-color theme_button_decoration_focus_backdrop_insensitive_breeze #445e81;
+@define-color theme_button_decoration_focus_breeze #5294e2;
+@define-color theme_button_decoration_focus_insensitive_breeze #445e81;
+@define-color theme_button_decoration_hover_backdrop_breeze #5294e2;
+@define-color theme_button_decoration_hover_backdrop_insensitive_breeze #445e81;
+@define-color theme_button_decoration_hover_breeze #5294e2;
+@define-color theme_button_decoration_hover_insensitive_breeze #445e81;
+@define-color theme_button_foreground_active_backdrop_breeze #d3dae3;
+@define-color theme_button_foreground_active_backdrop_insensitive_breeze #646973;
+@define-color theme_button_foreground_active_breeze #d3dae3;
+@define-color theme_button_foreground_active_insensitive_breeze #646973;
+@define-color theme_button_foreground_backdrop_breeze #d3dae3;
+@define-color theme_button_foreground_backdrop_insensitive_breeze #6f7582;
+@define-color theme_button_foreground_insensitive_breeze #6f7582;
+@define-color theme_button_foreground_normal_breeze #d3dae3;
+@define-color theme_fg_color_breeze #d3dae3;
+@define-color theme_header_background_backdrop_breeze #2f343f;
+@define-color theme_header_background_breeze #2f343f;
+@define-color theme_header_background_light_breeze #2f343f;
+@define-color theme_header_foreground_backdrop_breeze #d3dae3;
+@define-color theme_header_foreground_breeze #d3dae3;
+@define-color theme_header_foreground_insensitive_backdrop_breeze #d3dae3;
+@define-color theme_header_foreground_insensitive_breeze #d3dae3;
+@define-color theme_hovering_selected_bg_color_breeze #5294e2;
+@define-color theme_selected_bg_color_breeze #5294e2;
+@define-color theme_selected_fg_color_breeze #d3dae3;
+@define-color theme_text_color_breeze #d3dae3;
+@define-color theme_titlebar_background_backdrop_breeze #2f343f;
+@define-color theme_titlebar_background_breeze #2f343f;
+@define-color theme_titlebar_background_light_breeze #2f343f;
+@define-color theme_titlebar_foreground_backdrop_breeze #666a73;
+@define-color theme_titlebar_foreground_breeze #d3dae3;
+@define-color theme_titlebar_foreground_insensitive_backdrop_breeze #666a73;
+@define-color theme_titlebar_foreground_insensitive_breeze #666a73;
+@define-color theme_unfocused_base_color_breeze #404552;
+@define-color theme_unfocused_bg_color_breeze #2f343f;
+@define-color theme_unfocused_fg_color_breeze #d3dae3;
+@define-color theme_unfocused_selected_bg_color_alt_breeze #2b4768;
+@define-color theme_unfocused_selected_bg_color_breeze #2b4768;
+@define-color theme_unfocused_selected_fg_color_breeze #d3dae3;
+@define-color theme_unfocused_text_color_breeze #d3dae3;
+@define-color theme_unfocused_view_bg_color_breeze #3d424e;
+@define-color theme_unfocused_view_text_color_breeze #6e737f;
+@define-color theme_view_active_decoration_color_breeze #5294e2;
+@define-color theme_view_hover_decoration_color_breeze #5294e2;
+@define-color tooltip_background_breeze #353945;
+@define-color tooltip_border_breeze #5d616d;
+@define-color tooltip_text_breeze #d3dae3;
+@define-color unfocused_borders_breeze #585e68;
+@define-color unfocused_insensitive_borders_breeze #3a3f4a;
+@define-color warning_color_backdrop_breeze #f67400;
+@define-color warning_color_breeze #f67400;
+@define-color warning_color_insensitive_backdrop_breeze #7a5133;
+@define-color warning_color_insensitive_breeze #7a5133;
diff --git a/.config/gtk-3.0/gtk.css b/.config/gtk-3.0/gtk.css
new file mode 100644
index 0000000..c9763f7
--- /dev/null
+++ b/.config/gtk-3.0/gtk.css
@@ -0,0 +1 @@
+@import 'colors.css'; \ No newline at end of file
diff --git a/.config/gtk-3.0/settings.ini b/.config/gtk-3.0/settings.ini
index f150b2f..a80eccb 100644
--- a/.config/gtk-3.0/settings.ini
+++ b/.config/gtk-3.0/settings.ini
@@ -1,15 +1,20 @@
[Settings]
-gtk-theme-name=Arc-Dark
-gtk-icon-theme-name=ACYLS
-gtk-font-name=Sans 10
-gtk-cursor-theme-name=Adwaita
-gtk-cursor-theme-size=0
-gtk-toolbar-style=GTK_TOOLBAR_BOTH
-gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
-gtk-button-images=1
-gtk-menu-images=1
+gtk-application-prefer-dark-theme=true
+gtk-button-images=true
+gtk-cursor-theme-name=breeze_cursors
+gtk-cursor-theme-size=24
+gtk-decoration-layout=icon:minimize,maximize,close
+gtk-enable-animations=false
gtk-enable-event-sounds=1
gtk-enable-input-feedback-sounds=1
+gtk-font-name=Open Sans, 10
+gtk-icon-theme-name=Papirus-Dark
+gtk-menu-images=true
+gtk-modules=colorreload-gtk-module
+gtk-primary-button-warps-slider=false
+gtk-theme-name=Arc-Dark
+gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
+gtk-toolbar-style=3
gtk-xft-antialias=1
gtk-xft-hinting=1
gtk-xft-hintstyle=hintslight
diff --git a/.config/gtk-4.0/colors.css b/.config/gtk-4.0/colors.css
new file mode 100644
index 0000000..bed2595
--- /dev/null
+++ b/.config/gtk-4.0/colors.css
@@ -0,0 +1,84 @@
+@define-color borders_breeze #585e68;
+@define-color content_view_bg_breeze #404552;
+@define-color error_color_backdrop_breeze #da4453;
+@define-color error_color_breeze #da4453;
+@define-color error_color_insensitive_backdrop_breeze #70414e;
+@define-color error_color_insensitive_breeze #70414e;
+@define-color insensitive_base_color_breeze #3d424e;
+@define-color insensitive_base_fg_color_breeze #6e737f;
+@define-color insensitive_bg_color_breeze #2d323c;
+@define-color insensitive_borders_breeze #3a3f4a;
+@define-color insensitive_fg_color_breeze #646973;
+@define-color insensitive_selected_bg_color_breeze #2d323c;
+@define-color insensitive_selected_fg_color_breeze #646973;
+@define-color insensitive_unfocused_bg_color_breeze #2d323c;
+@define-color insensitive_unfocused_fg_color_breeze #646973;
+@define-color insensitive_unfocused_selected_bg_color_breeze #2d323c;
+@define-color insensitive_unfocused_selected_fg_color_breeze #646973;
+@define-color link_color_breeze #1d99f3;
+@define-color link_visited_color_breeze #9b59b6;
+@define-color success_color_backdrop_breeze #27ae60;
+@define-color success_color_breeze #27ae60;
+@define-color success_color_insensitive_backdrop_breeze #356553;
+@define-color success_color_insensitive_breeze #356553;
+@define-color theme_base_color_breeze #404552;
+@define-color theme_bg_color_breeze #2f343f;
+@define-color theme_button_background_backdrop_breeze #414857;
+@define-color theme_button_background_backdrop_insensitive_breeze #3e4553;
+@define-color theme_button_background_insensitive_breeze #3e4553;
+@define-color theme_button_background_normal_breeze #414857;
+@define-color theme_button_decoration_focus_backdrop_breeze #5294e2;
+@define-color theme_button_decoration_focus_backdrop_insensitive_breeze #445e81;
+@define-color theme_button_decoration_focus_breeze #5294e2;
+@define-color theme_button_decoration_focus_insensitive_breeze #445e81;
+@define-color theme_button_decoration_hover_backdrop_breeze #5294e2;
+@define-color theme_button_decoration_hover_backdrop_insensitive_breeze #445e81;
+@define-color theme_button_decoration_hover_breeze #5294e2;
+@define-color theme_button_decoration_hover_insensitive_breeze #445e81;
+@define-color theme_button_foreground_active_backdrop_breeze #d3dae3;
+@define-color theme_button_foreground_active_backdrop_insensitive_breeze #646973;
+@define-color theme_button_foreground_active_breeze #d3dae3;
+@define-color theme_button_foreground_active_insensitive_breeze #646973;
+@define-color theme_button_foreground_backdrop_breeze #d3dae3;
+@define-color theme_button_foreground_backdrop_insensitive_breeze #6f7582;
+@define-color theme_button_foreground_insensitive_breeze #6f7582;
+@define-color theme_button_foreground_normal_breeze #d3dae3;
+@define-color theme_fg_color_breeze #d3dae3;
+@define-color theme_header_background_backdrop_breeze #2f343f;
+@define-color theme_header_background_breeze #2f343f;
+@define-color theme_header_background_light_breeze #2f343f;
+@define-color theme_header_foreground_backdrop_breeze #d3dae3;
+@define-color theme_header_foreground_breeze #d3dae3;
+@define-color theme_header_foreground_insensitive_backdrop_breeze #d3dae3;
+@define-color theme_header_foreground_insensitive_breeze #d3dae3;
+@define-color theme_hovering_selected_bg_color_breeze #5294e2;
+@define-color theme_selected_bg_color_breeze #5294e2;
+@define-color theme_selected_fg_color_breeze #d3dae3;
+@define-color theme_text_color_breeze #d3dae3;
+@define-color theme_titlebar_background_backdrop_breeze #2f343f;
+@define-color theme_titlebar_background_breeze #2f343f;
+@define-color theme_titlebar_background_light_breeze #2f343f;
+@define-color theme_titlebar_foreground_backdrop_breeze #666a73;
+@define-color theme_titlebar_foreground_breeze #d3dae3;
+@define-color theme_titlebar_foreground_insensitive_backdrop_breeze #666a73;
+@define-color theme_titlebar_foreground_insensitive_breeze #666a73;
+@define-color theme_unfocused_base_color_breeze #404552;
+@define-color theme_unfocused_bg_color_breeze #2f343f;
+@define-color theme_unfocused_fg_color_breeze #d3dae3;
+@define-color theme_unfocused_selected_bg_color_alt_breeze #2b4768;
+@define-color theme_unfocused_selected_bg_color_breeze #2b4768;
+@define-color theme_unfocused_selected_fg_color_breeze #d3dae3;
+@define-color theme_unfocused_text_color_breeze #d3dae3;
+@define-color theme_unfocused_view_bg_color_breeze #3d424e;
+@define-color theme_unfocused_view_text_color_breeze #6e737f;
+@define-color theme_view_active_decoration_color_breeze #5294e2;
+@define-color theme_view_hover_decoration_color_breeze #5294e2;
+@define-color tooltip_background_breeze #353945;
+@define-color tooltip_border_breeze #5d616d;
+@define-color tooltip_text_breeze #d3dae3;
+@define-color unfocused_borders_breeze #585e68;
+@define-color unfocused_insensitive_borders_breeze #3a3f4a;
+@define-color warning_color_backdrop_breeze #f67400;
+@define-color warning_color_breeze #f67400;
+@define-color warning_color_insensitive_backdrop_breeze #7a5133;
+@define-color warning_color_insensitive_breeze #7a5133;
diff --git a/.config/gtk-4.0/gtk.css b/.config/gtk-4.0/gtk.css
new file mode 100644
index 0000000..c9763f7
--- /dev/null
+++ b/.config/gtk-4.0/gtk.css
@@ -0,0 +1 @@
+@import 'colors.css'; \ No newline at end of file
diff --git a/.config/gtk-4.0/settings.ini b/.config/gtk-4.0/settings.ini
new file mode 100644
index 0000000..cae2575
--- /dev/null
+++ b/.config/gtk-4.0/settings.ini
@@ -0,0 +1,9 @@
+[Settings]
+gtk-application-prefer-dark-theme=true
+gtk-cursor-theme-name=breeze_cursors
+gtk-cursor-theme-size=24
+gtk-decoration-layout=icon:minimize,maximize,close
+gtk-enable-animations=false
+gtk-font-name=Open Sans, 10
+gtk-icon-theme-name=Papirus-Dark
+gtk-primary-button-warps-slider=false
diff --git a/.config/herbstluftwm/autostart b/.config/herbstluftwm/autostart
index 5bcf1d8..df3eb3b 100755
--- a/.config/herbstluftwm/autostart
+++ b/.config/herbstluftwm/autostart
@@ -17,7 +17,7 @@ hc keyunbind --all
Mod=Mod4 # Use the super key as the main modifier
hc keybind $Mod-Shift-x quit
-hc keybind $Mod-r reload
+hc keybind $Mod-Shift-r reload
hc keybind $Mod-x close
# basic movement in tiling and floating mode
@@ -48,8 +48,8 @@ hc keybind $Mod-Alt-k resize up +$resizestep
hc keybind $Mod-Alt-l resize right +$resizestep
# tags
-tag_names=( work social web mail fun media vms 8 9 )
-tag_keys=( {2..9} 0 )
+tag_names=( work social web mail fun media vms scratch )
+tag_keys=( {2..9} )
hc rename default "${tag_names[0]}" || true
for i in "${!tag_names[@]}" ; do
@@ -88,8 +88,8 @@ hc mousebind $Mod-Button3 resize
# focus
hc set focus_follows_mouse 1
-hc keybind $Mod-Control+j cycle_monitor -1
-hc keybind $Mod-Control+k cycle_monitor +1
+hc keybind $Mod-Control+j cycle_monitor +1
+hc keybind $Mod-Control+k cycle_monitor -1
hc keybind $Mod-Tab cycle_all +1
hc keybind $Mod-Shift-Tab cycle_all -1
hc keybind $Mod-c cycle
@@ -131,18 +131,31 @@ hc set mouse_recenter_gap 0
# rules
hc unrule -F
-#hc rule class=XTerm tag=3 # move all xterms to tag 3
+
hc rule focus=on # normally focus new clients
hc rule floatplacement=smart
-#hc rule focus=off # normally do not focus new clients
-# give focus to most common terminals
-#hc rule class~'(.*[Rr]xvt.*|.*[Tt]erm|Konsole)' focus=on
+
+# Tag rules
+hc rule class='signal' tag=social
+hc rule class='mumble' tag=social
+
+hc rule class='qutebrowser' tag=www
+hc rule class='chromium' tag=www
+
+hc rule class='thunderbird' tag=mail
+
+hc rule class='mpv' tag=media
+
+# KDE rules
+hc rule class='krunner' floating=on floatplacement=center
+hc rule class='plasmashell' floating=on
+
+# Regular rules
+hc rule class='pinentry' floating=on floatplacement=center
hc rule windowtype~'_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)' floating=on
hc rule windowtype='_NET_WM_WINDOW_TYPE_DIALOG' focus=on
hc rule windowtype~'_NET_WM_WINDOW_TYPE_(NOTIFICATION|DOCK|DESKTOP)' manage=off
-hc rule class="wow.exe" tag=5 floating=on
-
hc set tree_style 'โ•พโ”‚ โ”œโ””โ•ผโ”€โ”'
# unlock, just to be sure
@@ -152,38 +165,3 @@ hc unlock
# hc set_monitors 1280x1024+0+0 1280x1024+1280+0
# or simply:
hc detect_monitors
-
-# Handle panels
-panel_top=~/.config/herbstluftwm/panel-top.sh
-[ -x "$panel_top" ] || panel_top=/etc/xdg/herbstluftwm/panel.sh
-
-panel_bot=~/.config/herbstluftwm/panel-bot.sh
-
-for monitor in $(hc list_monitors | cut -d: -f1) ; do
- # Create space for the panels to exist
- hc pad "$monitor" 21 0 21 0
-
- # Start panels
- "$panel_top" "$monitor" &
- "$panel_bot" "$monitor" &
-done
-
-# Handle tray
-if command -v stalonetray > /dev/null
-then
- tray_monitor_offset=$(hc monitor_rect 0 | awk '{ print $1 }')
- tray_monitor_width=$(hc monitor_rect 0 | awk '{ print $3 }')
- tray_date_offset=$(xftwidth "Liberation Mono:size=9" "$(date +"%F %H:%M")")
- tray_offset_x=$(( tray_monitor_offset + tray_monitor_width - tray_date_offset - 40 ))
-
- stalonetray \
- --grow-gravity E \
- --icon-size 16 \
- --parent-bg \
- --config /dev/null \
- --skip-taskbar \
- --sticky \
- --geometry "1x1+$tray_offset_x+2" \
- --kludges fix_window_pos \
- &
-fi
diff --git a/.config/herbstluftwm/panel-bot.sh b/.config/herbstluftwm/panel-bot.sh
deleted file mode 100755
index a42ea1e..0000000
--- a/.config/herbstluftwm/panel-bot.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env bash
-
-hc() { "${herbstclient_command[@]:-herbstclient}" "$@" ; }
-quote() { local q="$(printf '%q ' "$@")"; printf '%s' "${q% }" ; }
-uniq_linebuffered() { awk '$0 != l { print ; l=$0 ; fflush(); }' "$@"; }
-
-monitor=${1:-0}
-
-hc attr "monitors.$monitor" > /dev/null || { printf "Invalid monitor $monitor" && exit 1 ; }
-
-monitor_offset_x=$(hc monitor_rect "$monitor" | awk '{print $1 }')
-monitor_offset_y=$(hc monitor_rect "$monitor" | awk '{print $2 }')
-monitor_width=$(hc monitor_rect "$monitor" | awk '{ print $3 }')
-monitor_height=$(hc monitor_rect "$monitor" | awk '{ print $4 }')
-panel_height=21
-panel_color_bg="$(hc get frame_border_normal_color)"
-panel_color_fg="#efefef"
-panel_color_focus="$(hc get frame_border_active_color)"
-panel_font="Liberation Mono:size=9"
-panel_offset_x=$(( monitor_offset_x ))
-panel_offset_y=$(( monitor_offset_y + monitor_height - panel_height ))
-
-{
- hc --idle
-} 2> /dev/null | {
- while :
- do
- # Wait for an event
- IFS=$'\t' read -ra event || break
-
- # Declare clients as an array
- declare -a clients
-
- # Check which tag the monitor is on now
- monitor_tag="$(herbstclient attr "monitors.$monitor.tag")"
-
- # Collect all clients on the monitor
- for client in $(hc attr clients | grep '0x')
- do
- [[ "$(hc attr "clients.$client.tag")" != $monitor_tag ]] && continue
-
- clients+=("$client")
- done
-
- name_offset_interval=$(( monitor_width / ${#clients[@]} ))
-
- # Look up current focus window
- focus="$(hc attr clients.focus.winid)"
-
- # Loop through the clients to create a representation
- index=0
-
- for client in "${clients[@]}"
- do
-
- if [[ "$client" == "$focus." ]]
- then
- color_fg="$panel_color_focus"
- else
- color_fg="$panel_color_fg"
- fi
-
- name=" $(hc attr "clients.$client.title") "
- name_width=$(xftwidth "$panel_font" "$name")
- name_offset=$(( name_offset_interval * index ))
-
- printf "^pa(%s)^bg(%s)^fg(%s)^ca(1,%s)%s^ca()" \
- "$name_offset" \
- "$panel_color_bg" \
- "$color_fg" \
- "herbstclient jumpto '$client'" \
- "$name"
-
- index=$(( index + 1 ))
- done
-
- # Add a newline
- printf "\n"
-
- # Clean up some variables
- unset clients
- unset index
- done
-} | dzen2 -w "$monitor_width" -x "$panel_offset_x" -y "$panel_offset_y" -fn "$panel_font" -h "$panel_height" \
- -e "button3=;button4=exec:$hc_quoted use_index -1;button5=exec:$hc_quoted use_index +1" \
- -ta l -bg "$panel_color_bg" -fg "$panel_color_fg"
diff --git a/.config/herbstluftwm/panel-top.sh b/.config/herbstluftwm/panel-top.sh
deleted file mode 100755
index c0ea7e6..0000000
--- a/.config/herbstluftwm/panel-top.sh
+++ /dev/null
@@ -1,194 +0,0 @@
-#!/usr/bin/env bash
-
-quote() {
- local q="$(printf '%q ' "$@")"
- printf '%s' "${q% }"
-}
-
-hc_quoted="$(quote "${herbstclient_command[@]:-herbstclient}")"
-hc() { "${herbstclient_command[@]:-herbstclient}" "$@" ;}
-monitor=${1:-0}
-geometry=( $(hc monitor_rect "$monitor") )
-if [ -z "$geometry" ] ;then
- echo "Invalid monitor $monitor"
- exit 1
-fi
-# geometry has the format W H X Y
-x=${geometry[0]}
-y=${geometry[1]}
-panel_width=${geometry[2]}
-panel_height=21
-font="Liberation Mono:size=9"
-bgcolor=$(hc get frame_border_normal_color)
-selbg=$(hc get window_border_active_color)
-selfg='#101010'
-
-####
-# Try to find textwidth binary.
-# In e.g. Ubuntu, this is named dzen2-textwidth.
-if which xftwidth > /dev/null
-then
- textwidth=xftwidth
-elif which textwidth > /dev/null
-then
- textwidth="textwidth";
-else
- echo "This script requires the textwidth tool of the dzen2 project."
- exit 1
-fi
-
-####
-# true if we are using the svn version of dzen2
-# depending on version/distribution, this seems to have version strings like
-# "dzen-" or "dzen-x.x.x-svn"
-if dzen2 -v 2>&1 | head -n 1 | grep -q '^dzen-\([^,]*-svn\|\),'; then
- dzen2_svn="true"
-else
- dzen2_svn=""
-fi
-
-if awk -Wv 2>/dev/null | head -1 | grep -q '^mawk'; then
- # mawk needs "-W interactive" to line-buffer stdout correctly
- # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593504
- uniq_linebuffered() {
- awk -W interactive '$0 != l { print ; l=$0 ; fflush(); }' "$@"
- }
-else
- # other awk versions (e.g. gawk) issue a warning with "-W interactive", so
- # we don't want to use it there.
- uniq_linebuffered() {
- awk '$0 != l { print ; l=$0 ; fflush(); }' "$@"
- }
-fi
-
-{
- ### Event generator ###
- # based on different input data (mpc, date, hlwm hooks, ...) this generates events, formed like this:
- # <eventname>\t<data> [...]
- # e.g.
- # date ^fg(#efefef)18:33^fg(#909090), 2013-10-^fg(#efefef)29
-
- #mpc idleloop player &
- while true ; do
- # "date" output is checked once a second, but an event is only
- # generated if the output changed compared to the previous run.
- date +$'date\t^fg(#909090)%F ^fg(#efefef)%H:%M'
- sleep 1 || break
- done > >(uniq_linebuffered) &
- childpid=$!
- hc --idle
- kill $childpid
-} 2> /dev/null | {
- IFS=$'\t' read -ra tags <<< "$(hc tag_status $monitor)"
- visible=true
- date=""
- windowtitle=""
- while true ; do
-
- ### Output ###
- # This part prints dzen data based on the _previous_ data handling run,
- # and then waits for the next event to happen.
-
- separator="^bg()^fg($selbg)|"
- # draw tags
- for i in "${tags[@]}" ; do
- case ${i:0:1} in
- '#')
- echo -n "^bg($selbg)^fg($selfg)"
- ;;
- '-')
- echo -n "^bg()^fg(#9CA668)"
- ;;
- '+')
- echo -n "^bg(#9CA668)^fg(#141414)"
- ;;
- '%')
- echo -n "^bg()^fg($selbg)"
- ;;
- ':')
- echo -n "^bg()^fg(#ffffff)"
- ;;
- '!')
- echo -n "^bg(#FF0675)^fg(#141414)"
- ;;
- *)
- echo -n "^bg()^fg(#ababab)"
- ;;
- esac
- if [ ! -z "$dzen2_svn" ] ; then
- # clickable tags if using SVN dzen
- echo -n "^ca(1,$hc_quoted focus_monitor \"$monitor\" && "
- echo -n "$hc_quoted use \"${i:1}\") ${i:1} ^ca()"
- else
- # non-clickable tags if using older dzen
- echo -n " ${i:1} "
- fi
- done
- echo -n "$separator"
- echo -n "^bg()^fg() ${windowtitle//^/^^}"
- # small adjustments
- right="$separator^bg() $date"
- right_text_only=$(echo -n "$right" | sed 's.\^[^(]*([^)]*)..g')
- # get width of right aligned text.. and add some space..
- width=$($textwidth "$font" "$right_text_only ")
- echo -n "^pa($(($panel_width - $width)))$right"
- echo
-
- ### Data handling ###
- # This part handles the events generated in the event loop, and sets
- # internal variables based on them. The event and its arguments are
- # read into the array cmd, then action is taken depending on the event
- # name.
- # "Special" events (quit_panel/togglehidepanel/reload) are also handled
- # here.
-
- # wait for next event
- IFS=$'\t' read -ra cmd || break
- # find out event origin
- case "${cmd[0]}" in
- tag*)
- #echo "resetting tags" >&2
- IFS=$'\t' read -ra tags <<< "$(hc tag_status $monitor)"
- ;;
- date)
- #echo "resetting date" >&2
- date="${cmd[@]:1}"
- ;;
- quit_panel)
- exit
- ;;
- togglehidepanel)
- currentmonidx=$(hc list_monitors | sed -n '/\[FOCUS\]$/s/:.*//p')
- if [ "${cmd[1]}" -ne "$monitor" ] ; then
- continue
- fi
- if [ "${cmd[1]}" = "current" ] && [ "$currentmonidx" -ne "$monitor" ] ; then
- continue
- fi
- echo "^togglehide()"
- if $visible ; then
- visible=false
- hc pad $monitor 0
- else
- visible=true
- hc pad $monitor $panel_height
- fi
- ;;
- reload)
- exit
- ;;
- focus_changed|window_title_changed)
- windowtitle="${cmd[@]:2}"
- ;;
- #player)
- # ;;
- esac
- done
-
- ### dzen2 ###
- # After the data is gathered and processed, the output of the previous block
- # gets piped to dzen2.
-
-} | dzen2 -w $panel_width -x $x -y $y -fn "$font" -h $panel_height \
- -e "button3=;button4=exec:$hc_quoted use_index -1;button5=exec:$hc_quoted use_index +1" \
- -ta l -bg "$bgcolor" -fg '#efefef'
diff --git a/.config/hypr/hyprland.conf b/.config/hypr/hyprland.conf
new file mode 100644
index 0000000..f0ae6ff
--- /dev/null
+++ b/.config/hypr/hyprland.conf
@@ -0,0 +1,143 @@
+monitor=,preferred,auto,1
+
+exec-once=waybar -c ~/.config/waybar/config-hyprland.json
+exec-once=waybar -c ~/.config/waybar/config.json
+
+exec-once=dunst
+exec-once=ntfy sub --from-config
+exec-once=swaybg -i "$(find ~/pictures/wallpapers/1920x1080 | shuf -n 1)"
+exec-once=swayidle
+exec-once=gammastep
+# TODO: polkit agent
+# TODO: PipeWire
+
+exec-once=keepassxc
+exec-once=nextcloud
+exec-once=nm-applet
+
+workspace=1,name:work
+workspace=2,name:social
+workspace=3,name:web
+workspace=4,name:email
+workspace=5,name:media
+workspace=6,name:games
+workspace=7,name:vms
+workspace=8,name:scratch
+
+input {
+ kb_file=
+ kb_layout=
+ kb_variant=
+ kb_model=
+ kb_options=
+ kb_rules=
+
+ follow_mouse=1
+
+ touchpad {
+ natural_scroll=no
+ tap-to-click=no
+ }
+
+ sensitivity=0 # -1.0 - 1.0, 0 means no modification.
+}
+
+general {
+ main_mod=SUPER
+ layout=master
+
+ gaps_in=4
+ gaps_out=8
+ border_size=2
+ col.active_border=0xff3381da
+ col.inactive_border=0xff333333
+
+ apply_sens_to_raw=0 # whether to apply the sensitivity to raw input (e.g. used by games where you aim using your mouse)
+
+ damage_tracking=full # leave it on full unless you hate your GPU and want to make it suffer
+}
+
+decoration {
+ rounding=0
+ blur=1
+ blur_size=3 # minimum 1
+ blur_passes=1 # minimum 1
+ blur_new_optimizations=1
+}
+
+animations {
+ enabled=0
+ animation=windows,1,7,default
+ animation=border,1,10,default
+ animation=fade,1,10,default
+ animation=workspaces,1,6,default
+}
+
+dwindle {
+ pseudotile=0 # enable pseudotiling on dwindle
+}
+
+master {
+ new_is_master=true
+ new_on_top=true
+}
+
+gestures {
+ workspace_swipe=no
+}
+
+# Keybinds
+bind=SUPER,RETURN,exec,alacritty
+bind=SUPER,X,killactive,
+bind=SUPER_SHIFT,X,exit,
+bind=SUPER,E,exec,fuzzel
+bind=SUPER_SHIFT,S,exec,loginctl lock-session
+
+bindl=,XF86AudioMute,exec,~/.local/bin/vol toggle
+bindel=,XF86AudioRaiseVolume,exec,~/.local/bin/vol inc 5
+bindel=,XF86AudioLowerVolume,exec,~/.local/bin/vol dec 5
+binde=,XF86MonBrightnessUp,exec,~/.local/bin/bl inc 5
+binde=,XF86MonBrightnessDown,exec,~/.local/bin/bl dec 5
+#bind=,Print,exec,
+#bind=,XF86AudioMicMute,exec,
+
+bind=SUPER,J,layoutmsg,cyclenext
+bind=SUPER,K,layoutmsg,cycleprev
+bind=SUPER,H,splitratio,-0.05
+bind=SUPER,L,splitratio,+0.05
+bind=SUPER_SHIFT,RETURN,layoutmsg,swapwithmaster
+bind=SUPER_SHIFT,J,layoutmsg,swapnext
+bind=SUPER_SHIFT,K,layoutmsg,swapprev
+bind=SUPER_SHIFT,H,layoutmsg,addmaster
+bind=SUPER_SHIFT,L,layoutmsg,removemaster
+bind=SUPER,SPACE,layoutmsg,orientationnext
+bind=SUPER_SHIFT,SPACE,layoutmsg,orientationprev
+bind=SUPER,M,fullscreen,1
+bind=SUPER,F,fullscreen,0
+
+bind=SUPER,2,exec,~/.local/bin/hyprland-switch-tag 1
+bind=SUPER,3,exec,~/.local/bin/hyprland-switch-tag 2
+bind=SUPER,4,exec,~/.local/bin/hyprland-switch-tag 3
+bind=SUPER,5,exec,~/.local/bin/hyprland-switch-tag 4
+bind=SUPER,6,exec,~/.local/bin/hyprland-switch-tag 5
+bind=SUPER,7,exec,~/.local/bin/hyprland-switch-tag 6
+bind=SUPER,8,exec,~/.local/bin/hyprland-switch-tag 7
+bind=SUPER,9,exec,~/.local/bin/hyprland-switch-tag 8
+bind=SUPER,0,exec,~/.local/bin/hyprland-switch-tag 9
+
+bind=SUPER_SHIFT,2,movetoworkspacesilent,1
+bind=SUPER_SHIFT,3,movetoworkspacesilent,2
+bind=SUPER_SHIFT,4,movetoworkspacesilent,3
+bind=SUPER_SHIFT,5,movetoworkspacesilent,4
+bind=SUPER_SHIFT,6,movetoworkspacesilent,5
+bind=SUPER_SHIFT,7,movetoworkspacesilent,6
+bind=SUPER_SHIFT,8,movetoworkspacesilent,7
+bind=SUPER_SHIFT,9,movetoworkspacesilent,8
+bind=SUPER_SHIFT,0,movetoworkspacesilent,9
+
+bind=SUPER,mouse_down,workspace,e+1
+bind=SUPER,mouse_up,workspace,e-1
+
+# Rules
+windowrulev2=workspace 3,class:(firefox)
+windowrulev2=workspace 4,class:(thunderbird)
diff --git a/.config/kcminputrc b/.config/kcminputrc
new file mode 100644
index 0000000..0790b54
--- /dev/null
+++ b/.config/kcminputrc
@@ -0,0 +1,8 @@
+[$Version]
+update_info=delete_cursor_old_default_size.upd:DeleteCursorOldDefaultSize,kcminputrc_fix_botched_5_21_0.upd:kcminputrc_fix_botched_5_21_0_pre,kcminputrc_fix_botched_5_21_0.upd:kcminputrc_fix_botched_5_21_0,kcminputrc_repeat.upd:kcminputrc_migrate_key_repeat
+
+[Mouse]
+X11LibInputXAccelProfileFlat=false
+
+[Tmp]
+update_info=delete_cursor_old_default_size.upd:DeleteCursorOldDefaultSize
diff --git a/.config/kfontinstuirc b/.config/kfontinstuirc
new file mode 100644
index 0000000..495a101
--- /dev/null
+++ b/.config/kfontinstuirc
@@ -0,0 +1,3 @@
+[Main Settings]
+GroupSplitterSizes=160,1207
+PreviewSplitterSizes=986,220
diff --git a/.config/mpv/mpv.conf b/.config/mpv/mpv.conf
index 0e653a7..bad9e0d 100644
--- a/.config/mpv/mpv.conf
+++ b/.config/mpv/mpv.conf
@@ -1,2 +1,3 @@
hwdec=auto
+no-keepaspect-window
ytdl-format=bestvideo[ext=webm][height<=?720][fps<50]+bestaudio/best
diff --git a/.config/ntfy/client.yml b/.config/ntfy/client.yml
new file mode 100644
index 0000000..f85515e
--- /dev/null
+++ b/.config/ntfy/client.yml
@@ -0,0 +1 @@
+default-host: https://ntfy.tyil.nl
diff --git a/.config/plasma-localerc b/.config/plasma-localerc
new file mode 100644
index 0000000..34d81ed
--- /dev/null
+++ b/.config/plasma-localerc
@@ -0,0 +1,8 @@
+[Formats]
+LANG=en_US.UTF-8
+LC_ADDRESS=nl_NL.UTF-8
+LC_MEASUREMENT=nl_NL.UTF-8
+LC_MONETARY=nl_NL.UTF-8
+LC_PAGE=nl_NL.UTF-8
+LC_TELEPHONE=nl_NL.UTF-8
+LC_TIME=nl_NL.UTF-8
diff --git a/.config/plasmarc b/.config/plasmarc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.config/plasmarc
diff --git a/.config/powerdevilrc b/.config/powerdevilrc
new file mode 100644
index 0000000..9559d5c
--- /dev/null
+++ b/.config/powerdevilrc
@@ -0,0 +1,2 @@
+[BatteryManagement]
+BatteryCriticalAction=1
diff --git a/.config/powermanagementprofilesrc b/.config/powermanagementprofilesrc
new file mode 100644
index 0000000..de069e0
--- /dev/null
+++ b/.config/powermanagementprofilesrc
@@ -0,0 +1,60 @@
+[AC]
+icon=battery-charging
+
+[AC][DPMSControl]
+idleTime=300
+lockBeforeTurnOff=0
+
+[AC][DimDisplay]
+idleTime=180000
+
+[AC][HandleButtonEvents]
+lidAction=1
+powerButtonAction=16
+powerDownAction=16
+triggerLidActionWhenExternalMonitorPresent=false
+
+[Battery]
+icon=battery-060
+
+[Battery][DPMSControl]
+idleTime=300
+lockBeforeTurnOff=0
+
+[Battery][DimDisplay]
+idleTime=120000
+
+[Battery][HandleButtonEvents]
+lidAction=1
+powerButtonAction=16
+powerDownAction=16
+triggerLidActionWhenExternalMonitorPresent=false
+
+[Battery][SuspendSession]
+idleTime=600000
+suspendThenHibernate=false
+suspendType=1
+
+[LowBattery]
+icon=battery-low
+
+[LowBattery][BrightnessControl]
+value=30
+
+[LowBattery][DPMSControl]
+idleTime=120
+lockBeforeTurnOff=0
+
+[LowBattery][DimDisplay]
+idleTime=60000
+
+[LowBattery][HandleButtonEvents]
+lidAction=1
+powerButtonAction=16
+powerDownAction=16
+triggerLidActionWhenExternalMonitorPresent=false
+
+[LowBattery][SuspendSession]
+idleTime=300000
+suspendThenHibernate=false
+suspendType=1
diff --git a/.config/qutebrowser/autoconfig.yml b/.config/qutebrowser/autoconfig.yml
new file mode 100644
index 0000000..237dd53
--- /dev/null
+++ b/.config/qutebrowser/autoconfig.yml
@@ -0,0 +1,83 @@
+# If a config.py file exists, this file is ignored unless it's explicitly loaded
+# via config.load_autoconfig(). For more information, see:
+# https://github.com/qutebrowser/qutebrowser/blob/master/doc/help/configuring.asciidoc#loading-autoconfigyml
+# DO NOT edit this file by hand, qutebrowser will overwrite it.
+# Instead, create a config.py - see :help for details.
+
+config_version: 2
+settings:
+ bindings.commands:
+ global:
+ normal:
+ gM: hint links spawn mpv {hint-url}
+ gm: spawn mpv {url}
+ content.autoplay:
+ global: false
+ content.canvas_reading:
+ global: true
+ content.cookies.accept:
+ global: no-3rdparty
+ content.geolocation:
+ global: false
+ content.headers.do_not_track:
+ global: true
+ content.javascript.enabled:
+ '*://accounts.hetzner.com/*': true
+ '*://animebytes.tv/*': true
+ '*://bitspyder.net/*': true
+ '*://broadcasthe.net/*': true
+ '*://diensten.regiobank.nl/*': true
+ '*://gazellegames.net/*': true
+ '*://iptorrents.com/*': true
+ '*://jpopsuki.eu/*': true
+ '*://lidarr.arr.tyil.nl/*': true
+ '*://mijn.vandebron.nl/*': true
+ '*://mumble.voidfire.com/*': true
+ '*://portal.nstrein.ns.nl/*': true
+ '*://prowlarr.arr.tyil.nl/*': true
+ '*://rad.arr.tyil.nl/*': true
+ '*://redacted.ch/*': true
+ '*://son.arr.tyil.nl/*': true
+ '*://sonarr.arr.tyil.nl/*': true
+ '*://tv.tyil.nl/*': true
+ '*://uptime.tyil.nl/*': true
+ '*://www.aldi.nl/*': true
+ '*://www.empornium.is/*': true
+ '*://www.myanonamouse.net/*': true
+ '*://www.paypal.com/*': true
+ '*://www.torrentleech.org/*': true
+ global: false
+ https://9292.nl/*: true
+ https://app.element.io/*: true
+ https://boards.4channel.org: true
+ https://cloud.tyil.nl/*: true
+ https://fedi.tyil.nl/*: true
+ https://uptime.tyil.nl: true
+ https://www.lexa.nl: true
+ content.media.audio_capture:
+ https://app.element.io: true
+ content.media.audio_video_capture:
+ https://app.element.io: true
+ content.notifications.enabled:
+ https://fedi.tyil.nl: false
+ https://gazellegames.net: false
+ https://tv.tyil.nl: true
+ content.webgl:
+ global: false
+ downloads.location.directory:
+ global: ~/downloads/qutebrowser
+ fonts.default_family:
+ global: null
+ fonts.web.family.sans_serif:
+ global: Open Sans
+ fonts.web.family.standard:
+ global: Sans-Serif
+ tabs.position:
+ global: left
+ url.default_page:
+ global: https://searxng.tyil.nl
+ url.searchengines:
+ global:
+ DEFAULT: https://searxng.tyil.nl/search?q={}
+ url.start_pages:
+ global: https://searxng.tyil.nl
diff --git a/.config/qutebrowser/bookmarks/urls b/.config/qutebrowser/bookmarks/urls
new file mode 100644
index 0000000..8ea7dfe
--- /dev/null
+++ b/.config/qutebrowser/bookmarks/urls
@@ -0,0 +1 @@
+https://qutebrowser.org/doc/faq.html Frequently asked questions | qutebrowser
diff --git a/.config/qutebrowser/config.py b/.config/qutebrowser/config.py
new file mode 100644
index 0000000..182dcc2
--- /dev/null
+++ b/.config/qutebrowser/config.py
@@ -0,0 +1,3 @@
+config.load_autoconfig()
+
+config.source("plugins/redirect.py")
diff --git a/.config/qutebrowser/greasemonkey/4chan-X.user.js b/.config/qutebrowser/greasemonkey/4chan-X.user.js
new file mode 100644
index 0000000..7f4c3c3
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/4chan-X.user.js
@@ -0,0 +1,27983 @@
+// ==UserScript==
+// @name 4chan X
+// @version 1.14.22.1
+// @minGMVer 1.14
+// @minFFVer 26
+// @namespace 4chan-X
+// @description 4chan X is a script that adds various features to anonymous imageboards.
+// @license MIT; https://github.com/ccd0/4chan-x/blob/master/LICENSE
+// @include http://boards.4chan.org/*
+// @include https://boards.4chan.org/*
+// @include http://sys.4chan.org/*
+// @include https://sys.4chan.org/*
+// @include http://www.4chan.org/*
+// @include https://www.4chan.org/*
+// @include http://boards.4channel.org/*
+// @include https://boards.4channel.org/*
+// @include http://sys.4channel.org/*
+// @include https://sys.4channel.org/*
+// @include http://www.4channel.org/*
+// @include https://www.4channel.org/*
+// @include http://i.4cdn.org/*
+// @include https://i.4cdn.org/*
+// @include http://is.4chan.org/*
+// @include https://is.4chan.org/*
+// @include http://is2.4chan.org/*
+// @include https://is2.4chan.org/*
+// @include http://is.4channel.org/*
+// @include https://is.4channel.org/*
+// @include http://is2.4channel.org/*
+// @include https://is2.4channel.org/*
+// @include https://erischan.org/*
+// @include https://www.erischan.org/*
+// @include https://fufufu.moe/*
+// @include https://gnfos.com/*
+// @include https://himasugi.blog/*
+// @include https://www.himasugi.blog/*
+// @include https://kakashinenpo.com/*
+// @include https://www.kakashinenpo.com/*
+// @include https://kissu.moe/*
+// @include https://www.kissu.moe/*
+// @include https://lainchan.org/*
+// @include https://www.lainchan.org/*
+// @include https://merorin.com/*
+// @include https://ota-ch.com/*
+// @include https://www.ota-ch.com/*
+// @include https://ponyville.us/*
+// @include https://www.ponyville.us/*
+// @include https://smuglo.li/*
+// @include https://notso.smuglo.li/*
+// @include https://smugloli.net/*
+// @include https://smug.nepu.moe/*
+// @include https://sportschan.org/*
+// @include https://www.sportschan.org/*
+// @include https://sushigirl.us/*
+// @include https://www.sushigirl.us/*
+// @include https://tvch.moe/*
+// @exclude http://www.4chan.org/advertise
+// @exclude https://www.4chan.org/advertise
+// @exclude http://www.4chan.org/advertise?*
+// @exclude https://www.4chan.org/advertise?*
+// @exclude http://www.4chan.org/donate
+// @exclude https://www.4chan.org/donate
+// @exclude http://www.4chan.org/donate?*
+// @exclude https://www.4chan.org/donate?*
+// @exclude http://www.4channel.org/advertise
+// @exclude https://www.4channel.org/advertise
+// @exclude http://www.4channel.org/advertise?*
+// @exclude https://www.4channel.org/advertise?*
+// @exclude http://www.4channel.org/donate
+// @exclude https://www.4channel.org/donate
+// @exclude http://www.4channel.org/donate?*
+// @exclude https://www.4channel.org/donate?*
+// @connect 4chan.org
+// @connect 4channel.org
+// @connect 4cdn.org
+// @connect 4chenz.github.io
+// @connect archive.4plebs.org
+// @connect warosu.org
+// @connect desuarchive.org
+// @connect boards.fireden.net
+// @connect arch.b4k.co
+// @connect archived.moe
+// @connect thebarchive.com
+// @connect archiveofsins.com
+// @connect www.tokyochronos.net
+// @connect archive.alice.al
+// @connect api.clyp.it
+// @connect api.dailymotion.com
+// @connect api.github.com
+// @connect soundcloud.com
+// @connect api.streamable.com
+// @connect vimeo.com
+// @connect www.youtube.com
+// @connect *
+// @grant GM_getValue
+// @grant GM_setValue
+// @grant GM_deleteValue
+// @grant GM_listValues
+// @grant GM_addValueChangeListener
+// @grant GM_openInTab
+// @grant GM_xmlhttpRequest
+// @grant GM.getValue
+// @grant GM.setValue
+// @grant GM.deleteValue
+// @grant GM.listValues
+// @grant GM.openInTab
+// @grant GM.xmlHttpRequest
+// @run-at document-start
+// @updateURL https://www.4chan-x.net/builds/4chan-X.meta.js
+// @downloadURL https://www.4chan-x.net/builds/4chan-X.user.js
+// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAF5JREFUeNrtkTESABAQxPD/R6tsE2dUGYUtFJvLDKf93KevHJAjpBorAQWSBIKqFASC4G0pCAkm4GfaEvgYXl0T6HBaE97f0vmnfYHbZOMLZCx9ISdKWwjOWZSC8GYm4SUGwfYgqI4AAAAASUVORK5CYII=
+// ==/UserScript==
+
+/*
+* 4chan X
+*
+* Licensed under the MIT license.
+* https://github.com/ccd0/4chan-x/blob/master/LICENSE
+*
+* Appchan X Copyright ยฉ 2013-2016 Zixaphir <zixaphirmoxphar@gmail.com>
+* http://zixaphir.github.io/appchan-x/
+* 4chan x Copyright ยฉ 2009-2011 James Campos <james.r.campos@gmail.com>
+* https://github.com/aeosynth/4chan-x
+* 4chan x Copyright ยฉ 2012-2014 Nicolas Stepien <stepien.nicolas@gmail.com>
+* https://4chan-x.just-believe.in/
+* 4chan x Copyright ยฉ 2013-2014 Jordan Bates <saudrapsmann@gmail.com>
+* http://seaweedchan.github.io/4chan-x/
+* 4chan x Copyright ยฉ 2012-2013 ihavenoface
+* http://ihavenoface.github.io/4chan-x/
+* 4chan SS Copyright ยฉ 2011-2013 Ahodesuka
+* https://github.com/ahodesuka/4chan-Style-Script/
+*
+* 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.
+*
+* Contributors:
+* aeosynth
+* mayhemydg
+* noface
+* !K.WeEabo0o
+* blaise
+* that4chanwolf
+* desuwa
+* seaweed
+* e000
+* ahodesuka
+* Shou
+* ferongr
+* xat
+* Ongpot
+* thisisanon
+* Anonymous
+* Seiba
+* herpaderpderp
+* WakiMiko
+* btmcsweeney
+* AppleBloom
+* detharonil
+*
+* All the people who've taken the time to write bug reports.
+*
+* Thank you.
+*/
+
+/*
+* Contains data from external sources:
+*
+* src/Monitoring/ThreadUpdater/beep.wav from http://freesound.org/people/pierrecartoons1979/sounds/90112/
+* cc-by-nc-3.0
+*
+* Font Awesome by Dave Gandy (http://fontawesome.io)
+* license: http://fontawesome.io/license/
+*
+* Icons used to identify various websites are property of the respective websites.
+*/
+
+(function() {
+
+'use strict';
+
+var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, CatalogThreadNative, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, ModContact, Nav, NormalizeURL, Notice, PSA, PSAHiding, PassLink, PassMessage, Polyfill, Post, PostHiding, PostJumper, PostRedirect, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Test, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, Tinyboard, UI, Unread, UnreadIndex, Volume;
+
+var Conf, E, c, d, doc, docSet, g;
+
+Conf = Object.create(null);
+c = console;
+d = document;
+doc = d.documentElement;
+
+// Workaround for userscript managers that run script before document.documentElement is set
+docSet = function() {
+ return (doc = d.documentElement);
+};
+
+g = {
+ VERSION: '1.14.22.1',
+ NAMESPACE: '4chan X.',
+ sites: Object.create(null),
+ boards: Object.create(null)
+};
+
+E = (function() {
+ var fn, r, regex, str;
+ str = {
+ '&': '&amp;',
+ "'": '&#039;',
+ '"': '&quot;',
+ '<': '&lt;',
+ '>': '&gt;'
+ };
+ r = String.prototype.replace;
+ regex = /[&"'<>]/g;
+ fn = function(x) {
+ return str[x];
+ };
+ return function(text) {
+ return r.call(text, regex, fn);
+ };
+})();
+
+E.cat = function(templates) {
+ var html, i, len;
+ html = '';
+ for (i = 0, len = templates.length; i < len; i++) {
+ html += templates[i].innerHTML;
+ }
+ return html;
+};
+
+Config = (function() {
+ var Config;
+
+ Config = {
+ main: {
+ 'Miscellaneous': {
+ 'Redirect to HTTPS': [true, 'Redirect to the HTTPS version of 4chan.'],
+ 'JSON Index': [true, 'Replace the original board index with one supporting searching, sorting, infinite scrolling, and a catalog mode.'],
+ 'Use 4chan X Catalog': [true, 'Link to 4chan X\'s catalog instead of the native 4chan one.', 1],
+ 'Index Refresh Notifications': [false, 'Show a notice at the top of the page when the index is refreshed.', 1],
+ 'Follow Cursor': [true, 'Image Hover and Quote Preview move with the mouse cursor.'],
+ 'Open Threads in New Tab': [false, 'Make links to threads in the index / 4chan X catalog open in a new tab.'],
+ 'External Catalog': [false, 'Link to external catalog instead of the internal one.'],
+ 'Catalog Links': [false, 'Add toggle link in header menu to turn Navigation links into links to each board\'s catalog.'],
+ 'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'],
+ 'Desktop Notifications': [true, 'Enables desktop notifications across various 4chan X features.'],
+ '404 Redirect': [true, 'Redirect dead threads and images to the archives.'],
+ 'Archive Report': [true, 'Enable reporting posts to supported archives.'],
+ 'Exempt Archives from Encryption': [true, 'Permit loading content from, and warningless redirects to, HTTP-only archives from HTTPS pages.'],
+ 'Keybinds': [true, 'Bind actions to keyboard shortcuts.'],
+ 'Time Formatting': [true, 'Localize and format timestamps.'],
+ 'Relative Post Dates': [true, 'Display dates like "3 minutes ago". Tooltip shows the timestamp.'],
+ 'Relative Date Title': [true, 'Show Relative Post Date only when hovering over dates.', 1],
+ 'Comment Expansion': [true, 'Expand comments that are too long to display on the index. Not applicable with JSON Index.'],
+ 'File Info Formatting': [true, 'Reformat the file information.'],
+ 'Thread Expansion': [true, 'Add buttons to expand threads.'],
+ 'Index Navigation': [false, 'Add buttons to navigate between threads.'],
+ 'Reply Navigation': [false, 'Add buttons to navigate to top / bottom of thread.'],
+ 'Unique ID and Capcode Navigation': [false, 'Add buttons to navigate to posts having the same unique ID or capcode.'],
+ 'Custom Board Titles': [true, 'Allow editing of the board title and subtitle by ctrl/\u2318+clicking them.'],
+ 'Persistent Custom Board Titles': [false, 'Force custom board titles to be persistent, even if the board titles are updated.', 1],
+ 'Show Updated Notifications': [true, 'Show notifications when 4chan X is successfully updated.'],
+ 'Color User IDs': [true, 'Assign unique colors to user IDs on boards that use them'],
+ 'Count Posts by ID': [true, 'Display number of posts in the thread when hovering over an ID.'],
+ 'Remove Spoilers': [false, 'Remove all spoilers in text.'],
+ 'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'],
+ 'Normalize URL': [true, 'Rewrite the URL of the current page, removing slugs and excess slashes, and changing /res/ to /thread/.'],
+ 'Work around CORB Bug': [true, 'Leave this checked until your garbage browser is fixed.'],
+ 'Disable Autoplaying Sounds': [false, 'Prevent sounds on the page from autoplaying.'],
+ 'Disable Native Extension': [true, '4chan X is NOT designed to work with the native extension.'],
+ 'Enable Native Flash Embedding': [true, 'Activate the native extension\'s Flash embedding if the native extension is disabled.']
+ },
+ 'Linkification': {
+ 'Linkify': [true, 'Convert text into links where applicable.'],
+ 'Link Title': [true, 'Replace the link of a supported site with its actual title.', 1],
+ 'Cover Preview': [true, 'Show preview of supported links on hover.', 1],
+ 'Embedding': [true, 'Embed supported services. Note: Some services don\'t work on HTTPS.', 1],
+ 'Auto-embed': [false, 'Auto-embed Linkify Embeds.', 2],
+ 'Floating Embeds': [false, 'Embed content in a frame that remains in place when the page is scrolled.', 2]
+ },
+ 'Filtering': {
+ 'Anonymize': [false, 'Make everyone Anonymous.'],
+ 'Filter': [true, 'Self-moderation placebo.'],
+ 'Filtered Backlinks': [false, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.', 1],
+ 'Filter in Native Catalog': [true, 'Apply 4chan X filters in native catalog.', 1],
+ 'MD5 Quick Filter Notifications': [true, 'Show notification when quick filtering MD5s using the button or keybind.', 1],
+ 'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'],
+ 'Thread Hiding Buttons': [true, 'Add buttons to hide entire threads.'],
+ 'Reply Hiding Buttons': [true, 'Add buttons to hide single replies.'],
+ 'Stubs': [true, 'Show stubs of hidden threads / replies.']
+ },
+ 'Images and Videos': {
+ 'Image Expansion': [true, 'Expand images / videos.'],
+ 'Image Hover': [true, 'Show full image / video on mouseover.'],
+ 'Image Hover in Catalog': [true, 'Show full image / video on mouseover in 4chan X catalog.'],
+ 'Gallery': [true, 'Adds a simple and cute image gallery. Has more options in the gallery menu.'],
+ 'Fullscreen Gallery': [false, 'Open gallery in fullscreen mode.', 1],
+ 'PDF in Gallery': [false, 'Show PDF files in gallery.', 1],
+ 'Sauce': [true, 'Add sauce links to images.'],
+ 'WEBM Metadata': [true, 'Add link to fetch title metadata from webm videos.'],
+ 'Reveal Spoiler Thumbnails': [false, 'Replace spoiler thumbnails with the original image.'],
+ 'Replace GIF': [false, 'Replace gif thumbnails with the actual image.'],
+ 'Replace JPG': [false, 'Replace jpg thumbnails with the actual image.'],
+ 'Replace PNG': [false, 'Replace png thumbnails with the actual image.'],
+ 'Replace WEBM': [false, 'Replace webm, mp4, and ogv thumbnails with the actual video. Probably will degrade browser performance ;)'],
+ 'Image Prefetching': [true, 'Add a shortcut icon to the header to turn on image preloading.'],
+ 'Fappe Tyme': [true, 'Hide posts without images when header menu item is checked. *hint* *hint*'],
+ 'Werk Tyme': [true, 'Hide all post images when header menu item is checked.'],
+ 'Autoplay': [true, 'Videos begin playing immediately when opened.'],
+ 'Restart when Opened': [false, 'Restart GIFs and WebMs when you hover over or expand them.'],
+ 'Show Controls': [true, 'Show controls on videos expanded inline.'],
+ 'Click Passthrough': [false, 'Clicks on videos trigger your browser\'s default behavior. Videos can be contracted with button / dragging to the left.', 1],
+ 'Allow Sound': [true, 'Open videos with the sound unmuted.'],
+ 'Mouse Wheel Volume': [true, 'Adjust volume of videos with the mouse wheel over the thumbnail/filename/gallery.'],
+ 'Loop in New Tab': [true, 'Loop videos opened in their own tabs.'],
+ 'Volume in New Tab': [true, 'Apply 4chan X mute and volume settings to videos opened in their own tabs.']
+ },
+ 'Menu': {
+ 'Menu': [true, 'Add a drop-down menu to posts.'],
+ 'Report Link': [true, 'Add a report link to the menu.', 1],
+ 'Copy Text Link': [true, 'Add a link to copy the post\'s text.', 1],
+ 'Thread Hiding Link': [true, 'Add a link to hide entire threads.', 1],
+ 'Reply Hiding Link': [true, 'Add a link to hide single replies.', 1],
+ 'Delete Link': [true, 'Add post and image deletion links to the menu.', 1],
+ 'Archive Link': [true, 'Add an archive link to the menu.', 1],
+ 'Edit Link': [true, 'Add a link to edit the image in Tegaki, /i/\'s painting program. Requires Quick Reply.', 1],
+ 'Download Link': [false, 'Add a download with original filename link to the menu.', 1]
+ },
+ 'Monitoring': {
+ 'Thread Updater': [true, 'Fetch and insert new replies. Has more options in the header menu and the "Advanced" tab.'],
+ 'Unread Count': [true, 'Show the unread posts count in the tab title.'],
+ 'Quoted Title': [false, 'Change the page title to reflect you\'ve been quoted.', 1],
+ 'Hide Unread Count at (0)': [false, 'Hide the unread posts count in the tab title when it reaches 0.', 1],
+ 'Unread Favicon': [true, 'Show a different favicon when there are unread posts.'],
+ 'Unread Line': [true, 'Show a line to distinguish read posts from unread ones.'],
+ 'Remember Last Read Post': [true, 'Remember how far you\'ve read after you close the thread.'],
+ 'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.', 1],
+ 'Unread Line in Index': [false, 'Show a line between read and unread posts in threads in the index.', 1],
+ 'Remove Thread Excerpt': [false, 'Replace the excerpt of the thread in the tab title with the board title.'],
+ 'Thread Stats': [true, 'Display reply and image count.'],
+ 'IP Count in Stats': [true, 'Display the unique IP count in the thread stats.', 1],
+ 'Page Count in Stats': [true, 'Display the page count in the thread stats.', 1],
+ 'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'],
+ 'Thread Watcher': [true, 'Bookmark threads. Has more options in the thread watcher menu.'],
+ 'Fixed Thread Watcher': [true, 'Makes the thread watcher scroll with the page.', 1],
+ 'Persistent Thread Watcher': [false, 'The thread watcher will be visible when the page is loaded.', 1],
+ 'Mark New IPs': [false, 'Label each post from a new IP with the thread\'s current IP count.'],
+ 'Reply Pruning': [true, 'Add option in header menu to hide old replies in long threads. Activated by default in stickies.'],
+ 'Prune All Threads': [false, 'Activate Reply Pruning by default in all threads.', 1]
+ },
+ 'Posting and Captchas': {
+ 'Quick Reply': [true, 'All-in-one form to reply, create threads, automate dumping and more.'],
+ 'Persistent QR': [false, 'The Quick reply won\'t disappear after posting.', 1],
+ 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.', 2],
+ 'Open Post in New Tab': [true, 'Open new threads in a new tab, and open replies in a new tab if you\'re not already in the thread.', 1],
+ 'Remember QR Size': [false, 'Remember the size of the Quick reply.', 1],
+ 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.', 1],
+ 'Randomize Filename': [false, 'Set the filename to a random timestamp within the past year. Disabled on /f/.', 1],
+ 'Show New Thread Option in Threads': [true, 'Show the option to post a new / different thread from inside a thread.', 1],
+ 'Show Upload Progress': [true, 'Track progress of file uploads as percentage in submit button.', 1],
+ 'Cooldown': [true, 'Indicate the remaining time before posting again.', 1],
+ 'Posting Success Notifications': [true, 'Show notifications on successful post creation or file uploading.', 1],
+ 'Auto-load captcha': [false, 'Automatically load the captcha in the QR even if your post is empty.', 1],
+ 'Post on Captcha Completion': [false, 'Submit the post immediately when the captcha is completed.', 1],
+ 'Force Noscript Captcha': [false, 'Use the non-Javascript fallback captcha even if Javascript is enabled.'],
+ 'Pass Link': [false, 'Add a 4chan Pass login link to the bottom of the page.']
+ },
+ 'Quote Links': {
+ 'Quote Backlinks': [true, 'Add quote backlinks.'],
+ 'OP Backlinks': [true, 'Add backlinks to the OP.', 1],
+ 'Bottom Backlinks': [false, 'Place backlinks at the bottom of posts.', 1],
+ 'Quote Inlining': [true, 'Inline quoted post on click.'],
+ 'Inline Cross-thread Quotes Only': [false, 'Don\'t inline quote links when the posts are visible in the thread.', 1],
+ 'Quote Hash Navigation': [false, 'Include an extra link after quotes for autoscrolling to quoted posts.', 1],
+ 'Forward Hiding': [true, 'Hide original posts of inlined backlinks.', 1],
+ 'Quote Previewing': [true, 'Show quoted post on hover.'],
+ 'Quote Highlighting': [true, 'Highlight the previewed post.', 1],
+ 'Resurrect Quotes': [true, 'Link dead quotes to the archives, and support inlining/previewing of archive links like quote links.'],
+ 'Remember Your Posts': [true, 'Remember your posting history.'],
+ 'Mark Quotes of You': [true, 'Add \'(You)\' to quotes linking to your posts.', 1],
+ 'Highlight Posts Quoting You': [true, 'Highlights any posts that contain a quote to your post.', 1],
+ 'Highlight Own Posts': [true, 'Highlights own posts.', 1],
+ 'Mark OP Quotes': [true, 'Add \'(OP)\' to OP quotes.'],
+ 'Mark Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes.'],
+ 'Quote Threading': [true, 'Add option in header menu to thread conversations.']
+ }
+ },
+ imageExpansion: {
+ 'Fit width': [true, ''],
+ 'Fit height': [false, ''],
+ 'Scroll into view': [true, 'Scroll down when expanding images to bring the full image into view.'],
+ 'Expand spoilers': [true, 'Expand all images along with spoilers.'],
+ 'Expand videos': [true, 'Expand all images also expands videos.'],
+ 'Expand from here': [false, 'Expand all images only from current position to thread end.'],
+ 'Expand thread only': [false, 'In index, expand all images only within the current thread.'],
+ 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.']
+ },
+ gallery: {
+ 'Hide Thumbnails': [false],
+ 'Fit Width': [true],
+ 'Fit Height': [true],
+ 'Stretch to Fit': [false],
+ 'Scroll to Post': [true],
+ 'Slide Delay': [6.0]
+ },
+ 'Default Volume': 1.0,
+ threadWatcher: {
+ 'Current Board': [false, 'Only show watched threads from the current board.'],
+ 'Auto Update Thread Watcher': [true, 'Periodically check status of watched threads.'],
+ 'Auto Watch': [true, 'Automatically watch threads you start.'],
+ 'Auto Watch Reply': [true, 'Automatically watch threads you reply to.'],
+ 'Auto Prune': [false, 'Automatically remove dead threads.'],
+ 'Show Page': [true, 'Show what page watched threads are on.'],
+ 'Show Unread Count': [true, 'Show number of unread posts in watched threads.'],
+ 'Show Site Prefix': [true, 'When multiple sites are shown in the thread watcher, add a prefix to board names to distinguish them.'],
+ 'Require OP Quote Link': [false, 'For purposes of thread watcher highlighting, only consider posts with a quote link to the OP as replies to the OP.']
+ },
+ filter: {
+ general: '',
+ postID: "# Highlight dubs on [s4s]:\n#/(\\d)\\1$/;highlight;top:no;boards:s4s",
+ name: "# Filter any namefags:\n#/^(?!Anonymous$)/",
+ uniqueID: "# Filter a specific ID:\n#/Txhvk1Tl/",
+ tripcode: "# Filter any tripfag\n#/^!/",
+ capcode: "# Set a custom class for mods:\n#/Mod$/;highlight:mod;op:yes\n# Set a custom class for admins:\n#/Admin$/;highlight:admin;op:yes",
+ pass: "# Filter anyone using since4pass:\n#/./",
+ email: '',
+ subject: "# Filter Generals on /v/:\n#/general/i;boards:v;op:only",
+ comment: "# Filter Stallman copypasta on /g/:\n#/what you\'re refer+ing to as linux/i;boards:g\n# Filter posts with 20 or more quote links:\n#/(?:>>\\d(?:(?!>>\\d)[^])*){20}/\n# Filter posts like T H I S / H / I / S:\n#/^>?\\s?\\w\\s?(\\w)\\s?(\\w)\\s?(\\w).*$[\\s>]+\\1[\\s>]+\\2[\\s>]+\\3/im",
+ flag: '',
+ filename: '',
+ dimensions: "# Highlight potential wallpapers:\n#/1920x1080/;op:yes;highlight;top:no;boards:w,wg",
+ filesize: '',
+ MD5: ''
+ },
+ sauces: "# Known filename formats:\nhttps://www.pixiv.net/member_illust.php?mode=medium&illust_id=%$1;regexp:/^(\\d+)_p\\d+/\njavascript:void(open(\"https://www.deviantart.com/\"+%$1.replace(/_/g,\"-\")+\"/art/\"+parseInt(%$2,36)));regexp:/^\\w+_by_(\\w+)[_-]d([\\da-z]{6})\\b/\nhttps://imgur.com/%$1;regexp:/^(?![a-zA-Z][a-z]{6})(?![A-Z]{7})(?!\\d{7})([\\da-zA-Z]{7})(?: \\(\\d+\\))?\\.\\w+$/\nhttps://flickr.com/photo.gne?id=%$1;regexp:/^(\\d+)_[\\da-f]{10}(?:_\\w)*\\b/\nhttps://www.facebook.com/photo.php?fbid=%$1;regexp:/^\\d+_(\\d+)_\\d+_[no]\\b/\n\n# Reverse image search:\nhttps://www.google.com/searchbyimage?image_url=%IMG&safe=off\nhttps://yandex.com/images/search?rpt=imageview&url=%IMG\n#//tineye.com/search?url=%IMG\n#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights\n\n# Specialized reverse image search:\n//iqdb.org/?url=%IMG\nhttps://trace.moe/?auto&url=%IMG;text:wait\n#//3d.iqdb.org/?url=%IMG\n#//saucenao.com/search.php?url=%IMG\n\n# \"View Same\" in archives:\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#https://desuarchive.org/_/search/image/%sMD5/\n#https://archive.4plebs.org/_/search/image/%sMD5/\n#https://boards.fireden.net/_/search/image/%sMD5/\n#https://foolz.fireden.net/_/search/image/%sMD5/\n\n# Other tools:\n#http://exif.regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/start?url=%URL;types:gif,jpg,png\n#//www.gif-explode.com/%URL;types:gif",
+ FappeT: {
+ werk: false
+ },
+ 'Custom CSS': true,
+ Index: {
+ 'Index Mode': 'paged',
+ 'Previous Index Mode': 'paged',
+ 'Index Size': 'small',
+ 'Show Replies': [true, 'Show replies in the index, and also in the catalog if "Catalog hover expand" is checked.'],
+ 'Catalog Hover Expand': [false, 'Expand the comment and show more details when you hover over a thread in the catalog.'],
+ 'Catalog Hover Toggle': [true, 'Turn "Catalog hover expand" on and off by clicking in the catalog.'],
+ 'Pin Watched Threads': [false, 'Move watched threads to the start of the index.'],
+ 'Anchor Hidden Threads': [true, 'Move hidden threads to the end of the index.'],
+ 'Refreshed Navigation': [false, 'Refresh index when navigating through pages.']
+ },
+ Header: {
+ 'Fixed Header': true,
+ 'Header auto-hide': false,
+ 'Header auto-hide on scroll': false,
+ 'Bottom Header': false,
+ 'Centered links': false,
+ 'Header catalog links': false,
+ 'Bottom Board List': true,
+ 'Shortcut Icons': true,
+ 'Custom Board Navigation': true
+ },
+ archives: {
+ archiveLists: 'https://4chenz.github.io/archives.json/archives.json',
+ lastarchivecheck: 0,
+ archiveAutoUpdate: true
+ },
+ externalCatalogURLs: "//catalog.neet.tv/%board/;boards:4chan.org:3,a,adv,an,asp,biz,c,cgl,ck,cm,co,diy,f,fa,fit,g,gd,his,i,int,jp,k,lgbt,lit,m,mlp,mu,n,news,o,out,p,po,pol,s4s,sci,sp,tg,toy,trv,tv,v,vg,vip,vp,vr,w,wg,wsg,wsr,x",
+ boardnav: "[ toggle-all ]\n[current-index-text:\"Index\"\ncurrent-catalog-text:\"Catalog\"\ncurrent-expired-text:\"Expired\"\ncurrent-archive-text:\"Archive\"]\n[external-text:\"FAQ\",\"https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions\"]",
+ QR: {
+ 'QR.personas': "#options:\"sage\";boards:jp;always",
+ sjisPreview: false
+ },
+ jsWhitelist: 'http://s.4cdn.org\nhttps://s.4cdn.org\nhttp://www.google.com\nhttps://www.google.com\nhttps://www.gstatic.com\nhttp://cdn.mathjax.org\nhttps://cdn.mathjax.org\nhttps://cdnjs.cloudflare.com\nhttps://hcaptcha.com\nhttps://*.hcaptcha.com\n\'self\'\n\'unsafe-inline\'\n\'unsafe-eval\'',
+ captchaLanguage: '',
+ time: '%m/%d/%y(%a)%H:%M:%S',
+ timeLocale: '',
+ backlink: '>>%id',
+ pastedname: 'file',
+ fileInfo: '%l %d (%p%s, %r%g)',
+ favicon: 'ferongr',
+ usercss: "/* Board title rice */\ndiv.boardTitle {\n font-weight: 400 !important;\n}\n:root.yotsuba div.boardTitle {\n font-family: sans-serif !important;\n text-shadow: 1px 1px 1px rgba(100,0,0,0.6);\n}\n:root.yotsuba-b div.boardTitle {\n font-family: sans-serif !important;\n text-shadow: 1px 1px 1px rgba(105,10,15,0.6);\n}\n:root.photon div.boardTitle {\n font-family: sans-serif !important;\n text-shadow: 1px 1px 1px rgba(0,74,153,0.6);\n}\n:root.tomorrow div.boardTitle {\n font-family: sans-serif !important;\n text-shadow: 1px 1px 1px rgba(167,170,168,0.6);\n}\n",
+ hotkeys: {
+ 'Toggle board list': ['Ctrl+b', 'Toggle the full board list.'],
+ 'Toggle header': ['Shift+h', 'Toggle the auto-hide option of the header.'],
+ 'Open empty QR': ['q', 'Open QR without post number inserted.'],
+ 'Open QR': ['Shift+q', 'Open QR with post number inserted.'],
+ 'Open settings': ['Alt+o', 'Open Settings.'],
+ 'Close': ['Esc', 'Close dialogs or notifications.'],
+ 'Spoiler tags': ['Ctrl+s', 'Insert spoiler tags.'],
+ 'Code tags': ['Alt+c', 'Insert code tags.'],
+ 'Eqn tags': ['Alt+e', 'Insert eqn tags.'],
+ 'Math tags': ['Alt+m', 'Insert math tags.'],
+ 'SJIS tags': ['Alt+a', 'Insert SJIS tags.'],
+ 'Toggle sage': ['Alt+s', 'Toggle sage in options field.'],
+ 'Toggle Cooldown': ['Alt+Comma', 'Toggle custom cooldown timer.'],
+ 'Post from URL': ['Alt+l', 'Post from URL.'],
+ 'Add new post': ['Alt+n', 'Add new post to the QR dump list.'],
+ 'Submit QR': ['Ctrl+Enter', 'Submit post.'],
+ 'Watch': ['w', 'Watch thread.'],
+ 'Update': ['r', 'Update the thread / refresh the index.'],
+ 'Update thread watcher': ['Shift+r', 'Manually refresh thread watcher.'],
+ 'Toggle thread watcher': ['t', 'Toggle visibility of thread watcher.'],
+ 'Toggle threading': ['Shift+t', 'Toggle threading.'],
+ 'Mark thread read': ['Ctrl+0', 'Mark thread read from index (requires "Unread Line in Index").'],
+ 'Expand image': ['Shift+e', 'Expand selected image.'],
+ 'Expand images': ['e', 'Expand all images.'],
+ 'Open Gallery': ['g', 'Opens the gallery.'],
+ 'Next Gallery Image': ['Right', 'Go to the next image in gallery mode.'],
+ 'Previous Gallery Image': ['Left', 'Go to the previous image in gallery mode.'],
+ 'Advance Gallery': ['Enter', 'Go to next image or, if Autoplay is off, play video.'],
+ 'Pause': ['p', 'Pause/play videos in the gallery.'],
+ 'Slideshow': ['Ctrl+Right', 'Toggle the gallery slideshow mode.'],
+ 'Rotate image clockwise': ['Shift+Right', 'Rotate image clockwise in gallery.'],
+ 'Rotate image anticlockwise': ['Shift+Left', 'Rotate image anticlockwise in gallery.'],
+ 'Download Gallery Image': ['Shift+j', 'Download current image in gallery.'],
+ 'fappeTyme': ['f', 'Toggle Fappe Tyme.'],
+ 'werkTyme': ['Shift+w', 'Toggle Werk Tyme.'],
+ 'Front page': ['1', 'Jump to front page.'],
+ 'Open front page': ['Shift+1', 'Open front page in a new tab.'],
+ 'Next page': ['Ctrl+Right', 'Jump to the next page.'],
+ 'Previous page': ['Ctrl+Left', 'Jump to the previous page.'],
+ 'Paged mode': ['Alt+1', 'Open the index in paged mode.'],
+ 'Infinite scrolling mode': ['Alt+2', 'Open the index in infinite scrolling mode.'],
+ 'All pages mode': ['Alt+3', 'Open the index in all threads mode.'],
+ 'Open catalog': ['Shift+c', 'Open the catalog of the current board.'],
+ 'Search form': ['Ctrl+Alt+s', 'Focus the search field on the board index.'],
+ 'Cycle sort type': ['Alt+x', 'Cycle through index sort types.'],
+ 'Next thread': ['Ctrl+Down', 'See next thread.'],
+ 'Previous thread': ['Ctrl+Up', 'See previous thread.'],
+ 'Expand thread': ['Ctrl+e', 'Expand thread.'],
+ 'Open thread': ['o', 'Open thread in current tab.'],
+ 'Open thread tab': ['Shift+o', 'Open thread in new tab.'],
+ 'Next reply': ['j', 'Select next reply.'],
+ 'Previous reply': ['k', 'Select previous reply.'],
+ 'Deselect reply': ['Shift+d', 'Deselect reply.'],
+ 'Hide': ['x', 'Hide thread.'],
+ 'Quick Filter MD5': ['5', 'Add the MD5 of the selected image to the filter list.'],
+ 'Previous Post Quoting You': ['Alt+Up', 'Scroll to the previous post that quotes you.'],
+ 'Next Post Quoting You': ['Alt+Down', 'Scroll to the next post that quotes you.']
+ },
+ updater: {
+ checkbox: {
+ 'Beep': [false, 'Beep on new post to completely read thread.'],
+ 'Beep Quoting You': [false, 'Beep on new post quoting you.'],
+ 'Auto Scroll': [false, 'Scroll updated posts into view. Only enabled at bottom of page.'],
+ 'Bottom Scroll': [false, 'Always scroll to the bottom, not the first new post. Useful for event threads.'],
+ 'Scroll BG': [false, 'Auto-scroll background tabs.'],
+ 'Auto Update': [true, 'Automatically fetch new posts.'],
+ 'Optional Increase': [false, 'Increase the intervals between updates on threads without new posts.']
+ },
+ 'Interval': 5
+ },
+ customCooldown: 0,
+ customCooldownEnabled: true,
+ 'Thread Quotes': false,
+ 'Max Replies': 1000,
+ 'Autohiding Scrollbar': false,
+ position: {
+ 'embedding.position': 'top: 50px; right: 0px;',
+ 'thread-stats.position': 'bottom: 0px; right: 0px;',
+ 'updater.position': 'bottom: 0px; left: 0px;',
+ 'thread-watcher.position': 'top: 50px; left: 0px;',
+ 'qr.position': 'top: 50px; right: 0px;'
+ },
+ fourchanImageHost: 'i.4cdn.org',
+ hiddenPSAList: [{}],
+ knownBanners: '0.jpg,1.jpg,2.jpg,4.jpg,6.jpg,7.jpg,8.jpg,9.jpg,10.jpg,11.jpg,12.jpg,13.jpg,14.jpg,16.jpg,17.jpg,18.jpg,19.jpg,20.jpg,21.jpg,22.jpg,24.jpg,25.jpg,26.jpg,28.jpg,29.jpg,33.jpg,38.jpg,39.jpg,43.jpg,44.jpg,45.jpg,46.jpg,47.jpg,52.jpg,54.jpg,57.jpg,59.jpg,60.jpg,61.jpg,64.jpg,66.jpg,67.jpg,69.jpg,71.jpg,72.jpg,76.jpg,77.jpg,81.jpg,82.jpg,83.jpg,84.jpg,88.jpg,90.jpg,91.jpg,96.jpg,98.jpg,99.jpg,100.jpg,104.jpg,106.jpg,116.jpg,119.jpg,137.jpg,140.jpg,148.jpg,149.jpg,150.jpg,154.jpg,156.jpg,157.jpg,158.jpg,159.jpg,161.jpg,162.jpg,164.jpg,165.jpg,166.jpg,167.jpg,168.jpg,169.jpg,170.jpg,171.jpg,172.jpg,173.jpg,174.jpg,175.jpg,176.jpg,178.jpg,179.jpg,180.jpg,181.jpg,182.jpg,183.jpg,186.jpg,189.jpg,190.jpg,192.jpg,193.jpg,194.jpg,197.jpg,198.jpg,200.jpg,201.jpg,202.jpg,203.jpg,205.jpg,206.jpg,207.jpg,208.jpg,210.jpg,213.jpg,214.jpg,215.jpg,216.jpg,218.jpg,219.jpg,220.jpg,221.jpg,222.jpg,223.jpg,224.jpg,227.jpg,0.png,1.png,2.png,3.png,5.png,6.png,9.png,10.png,11.png,12.png,14.png,16.png,19.png,20.png,21.png,22.png,23.png,24.png,26.png,27.png,28.png,29.png,30.png,31.png,32.png,33.png,34.png,37.png,39.png,40.png,41.png,42.png,43.png,44.png,45.png,48.png,49.png,50.png,51.png,52.png,53.png,57.png,58.png,59.png,64.png,66.png,67.png,68.png,69.png,70.png,71.png,72.png,76.png,78.png,79.png,81.png,82.png,85.png,86.png,87.png,89.png,95.png,98.png,100.png,101.png,102.png,105.png,106.png,107.png,109.png,110.png,111.png,112.png,113.png,114.png,115.png,116.png,118.png,119.png,120.png,121.png,122.png,123.png,126.png,128.png,130.png,134.png,136.png,138.png,139.png,140.png,142.png,145.png,146.png,149.png,150.png,151.png,152.png,153.png,154.png,155.png,156.png,157.png,158.png,159.png,160.png,163.png,164.png,165.png,166.png,167.png,168.png,169.png,170.png,171.png,172.png,173.png,174.png,178.png,179.png,180.png,181.png,182.png,184.png,186.png,188.png,190.png,192.png,193.png,194.png,195.png,196.png,197.png,198.png,200.png,202.png,203.png,205.png,206.png,207.png,209.png,212.png,213.png,214.png,216.png,217.png,218.png,219.png,220.png,221.png,222.png,223.png,224.png,225.png,226.png,229.png,231.png,232.png,233.png,234.png,235.png,237.png,238.png,239.png,240.png,241.png,242.png,244.png,245.png,246.png,247.png,248.png,249.png,250.png,253.png,254.png,255.png,256.png,257.png,258.png,259.png,260.png,262.png,268.png,0.gif,1.gif,2.gif,3.gif,4.gif,5.gif,6.gif,7.gif,8.gif,9.gif,10.gif,12.gif,13.gif,14.gif,15.gif,16.gif,18.gif,19.gif,20.gif,21.gif,22.gif,23.gif,24.gif,28.gif,29.gif,30.gif,33.gif,34.gif,35.gif,36.gif,37.gif,39.gif,40.gif,42.gif,44.gif,45.gif,46.gif,48.gif,50.gif,52.gif,54.gif,55.gif,57.gif,58.gif,59.gif,60.gif,61.gif,63.gif,64.gif,66.gif,67.gif,68.gif,69.gif,70.gif,72.gif,73.gif,75.gif,76.gif,77.gif,78.gif,80.gif,81.gif,82.gif,83.gif,86.gif,87.gif,88.gif,92.gif,93.gif,94.gif,95.gif,96.gif,97.gif,98.gif,99.gif,100.gif,101.gif,102.gif,103.gif,104.gif,105.gif,106.gif,108.gif,109.gif,110.gif,111.gif,112.gif,113.gif,115.gif,116.gif,117.gif,118.gif,119.gif,120.gif,122.gif,123.gif,124.gif,127.gif,129.gif,130.gif,131.gif,134.gif,135.gif,136.gif,138.gif,139.gif,141.gif,144.gif,146.gif,148.gif,149.gif,153.gif,154.gif,155.gif,157.gif,158.gif,159.gif,160.gif,161.gif,162.gif,164.gif,166.gif,167.gif,168.gif,169.gif,170.gif,171.gif,172.gif,173.gif,174.gif,175.gif,176.gif,177.gif,178.gif,181.gif,182.gif,183.gif,185.gif,186.gif,187.gif,188.gif,189.gif,190.gif,191.gif,192.gif,193.gif,195.gif,196.gif,197.gif,200.gif,201.gif,202.gif,203.gif,204.gif,205.gif,206.gif,207.gif,208.gif,209.gif,210.gif,211.gif,212.gif,213.gif,214.gif,215.gif,216.gif,217.gif,219.gif,220.gif,221.gif,222.gif,224.gif,225.gif,226.gif,227.gif,228.gif,230.gif,232.gif,233.gif,234.gif,235.gif,238.gif,240.gif,241.gif,243.gif,244.gif,245.gif,246.gif,247.gif,249.gif,250.gif,251.gif,253.gif',
+ passMessageClosed: false,
+ 'Prerequest Captcha': false,
+ 'PSAseen': [[]]
+ };
+
+ return Config;
+
+}).call(this);
+
+CSS = {
+
+boards:
+"/*!\n\
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome\n\
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\n\
+ */\n\
+@font-face {\n\
+ font-family: FontAwesome;\n\
+ src: url('data:application/font-woff;base64,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') format('woff');\n\
+ font-weight: 400;\n\
+ font-style: normal;\n\
+}\n\
+.fa-glass:before {content: \"\\f000\";}\n\
+.fa-music:before {content: \"\\f001\";}\n\
+.fa-search:before {content: \"\\f002\";}\n\
+.fa-envelope-o:before {content: \"\\f003\";}\n\
+.fa-heart:before {content: \"\\f004\";}\n\
+.fa-star:before {content: \"\\f005\";}\n\
+.fa-star-o:before {content: \"\\f006\";}\n\
+.fa-user:before {content: \"\\f007\";}\n\
+.fa-film:before {content: \"\\f008\";}\n\
+.fa-th-large:before {content: \"\\f009\";}\n\
+.fa-th:before {content: \"\\f00a\";}\n\
+.fa-th-list:before {content: \"\\f00b\";}\n\
+.fa-check:before {content: \"\\f00c\";}\n\
+.fa-remove:before, .fa-close:before, .fa-times:before {content: \"\\f00d\";}\n\
+.fa-search-plus:before {content: \"\\f00e\";}\n\
+.fa-search-minus:before {content: \"\\f010\";}\n\
+.fa-power-off:before {content: \"\\f011\";}\n\
+.fa-signal:before {content: \"\\f012\";}\n\
+.fa-gear:before, .fa-cog:before {content: \"\\f013\";}\n\
+.fa-trash-o:before {content: \"\\f014\";}\n\
+.fa-home:before {content: \"\\f015\";}\n\
+.fa-file-o:before {content: \"\\f016\";}\n\
+.fa-clock-o:before {content: \"\\f017\";}\n\
+.fa-road:before {content: \"\\f018\";}\n\
+.fa-download:before {content: \"\\f019\";}\n\
+.fa-arrow-circle-o-down:before {content: \"\\f01a\";}\n\
+.fa-arrow-circle-o-up:before {content: \"\\f01b\";}\n\
+.fa-inbox:before {content: \"\\f01c\";}\n\
+.fa-play-circle-o:before {content: \"\\f01d\";}\n\
+.fa-rotate-right:before, .fa-repeat:before {content: \"\\f01e\";}\n\
+.fa-refresh:before {content: \"\\f021\";}\n\
+.fa-list-alt:before {content: \"\\f022\";}\n\
+.fa-lock:before {content: \"\\f023\";}\n\
+.fa-flag:before {content: \"\\f024\";}\n\
+.fa-headphones:before {content: \"\\f025\";}\n\
+.fa-volume-off:before {content: \"\\f026\";}\n\
+.fa-volume-down:before {content: \"\\f027\";}\n\
+.fa-volume-up:before {content: \"\\f028\";}\n\
+.fa-qrcode:before {content: \"\\f029\";}\n\
+.fa-barcode:before {content: \"\\f02a\";}\n\
+.fa-tag:before {content: \"\\f02b\";}\n\
+.fa-tags:before {content: \"\\f02c\";}\n\
+.fa-book:before {content: \"\\f02d\";}\n\
+.fa-bookmark:before {content: \"\\f02e\";}\n\
+.fa-print:before {content: \"\\f02f\";}\n\
+.fa-camera:before {content: \"\\f030\";}\n\
+.fa-font:before {content: \"\\f031\";}\n\
+.fa-bold:before {content: \"\\f032\";}\n\
+.fa-italic:before {content: \"\\f033\";}\n\
+.fa-text-height:before {content: \"\\f034\";}\n\
+.fa-text-width:before {content: \"\\f035\";}\n\
+.fa-align-left:before {content: \"\\f036\";}\n\
+.fa-align-center:before {content: \"\\f037\";}\n\
+.fa-align-right:before {content: \"\\f038\";}\n\
+.fa-align-justify:before {content: \"\\f039\";}\n\
+.fa-list:before {content: \"\\f03a\";}\n\
+.fa-dedent:before, .fa-outdent:before {content: \"\\f03b\";}\n\
+.fa-indent:before {content: \"\\f03c\";}\n\
+.fa-video-camera:before {content: \"\\f03d\";}\n\
+.fa-photo:before, .fa-image:before, .fa-picture-o:before {content: \"\\f03e\";}\n\
+.fa-pencil:before {content: \"\\f040\";}\n\
+.fa-map-marker:before {content: \"\\f041\";}\n\
+.fa-adjust:before {content: \"\\f042\";}\n\
+.fa-tint:before {content: \"\\f043\";}\n\
+.fa-edit:before, .fa-pencil-square-o:before {content: \"\\f044\";}\n\
+.fa-share-square-o:before {content: \"\\f045\";}\n\
+.fa-check-square-o:before {content: \"\\f046\";}\n\
+.fa-arrows:before {content: \"\\f047\";}\n\
+.fa-step-backward:before {content: \"\\f048\";}\n\
+.fa-fast-backward:before {content: \"\\f049\";}\n\
+.fa-backward:before {content: \"\\f04a\";}\n\
+.fa-play:before {content: \"\\f04b\";}\n\
+.fa-pause:before {content: \"\\f04c\";}\n\
+.fa-stop:before {content: \"\\f04d\";}\n\
+.fa-forward:before {content: \"\\f04e\";}\n\
+.fa-fast-forward:before {content: \"\\f050\";}\n\
+.fa-step-forward:before {content: \"\\f051\";}\n\
+.fa-eject:before {content: \"\\f052\";}\n\
+.fa-chevron-left:before {content: \"\\f053\";}\n\
+.fa-chevron-right:before {content: \"\\f054\";}\n\
+.fa-plus-circle:before {content: \"\\f055\";}\n\
+.fa-minus-circle:before {content: \"\\f056\";}\n\
+.fa-times-circle:before {content: \"\\f057\";}\n\
+.fa-check-circle:before {content: \"\\f058\";}\n\
+.fa-question-circle:before {content: \"\\f059\";}\n\
+.fa-info-circle:before {content: \"\\f05a\";}\n\
+.fa-crosshairs:before {content: \"\\f05b\";}\n\
+.fa-times-circle-o:before {content: \"\\f05c\";}\n\
+.fa-check-circle-o:before {content: \"\\f05d\";}\n\
+.fa-ban:before {content: \"\\f05e\";}\n\
+.fa-arrow-left:before {content: \"\\f060\";}\n\
+.fa-arrow-right:before {content: \"\\f061\";}\n\
+.fa-arrow-up:before {content: \"\\f062\";}\n\
+.fa-arrow-down:before {content: \"\\f063\";}\n\
+.fa-mail-forward:before, .fa-share:before {content: \"\\f064\";}\n\
+.fa-expand:before {content: \"\\f065\";}\n\
+.fa-compress:before {content: \"\\f066\";}\n\
+.fa-plus:before {content: \"\\f067\";}\n\
+.fa-minus:before {content: \"\\f068\";}\n\
+.fa-asterisk:before {content: \"\\f069\";}\n\
+.fa-exclamation-circle:before {content: \"\\f06a\";}\n\
+.fa-gift:before {content: \"\\f06b\";}\n\
+.fa-leaf:before {content: \"\\f06c\";}\n\
+.fa-fire:before {content: \"\\f06d\";}\n\
+.fa-eye:before {content: \"\\f06e\";}\n\
+.fa-eye-slash:before {content: \"\\f070\";}\n\
+.fa-warning:before, .fa-exclamation-triangle:before {content: \"\\f071\";}\n\
+.fa-plane:before {content: \"\\f072\";}\n\
+.fa-calendar:before {content: \"\\f073\";}\n\
+.fa-random:before {content: \"\\f074\";}\n\
+.fa-comment:before {content: \"\\f075\";}\n\
+.fa-magnet:before {content: \"\\f076\";}\n\
+.fa-chevron-up:before {content: \"\\f077\";}\n\
+.fa-chevron-down:before {content: \"\\f078\";}\n\
+.fa-retweet:before {content: \"\\f079\";}\n\
+.fa-shopping-cart:before {content: \"\\f07a\";}\n\
+.fa-folder:before {content: \"\\f07b\";}\n\
+.fa-folder-open:before {content: \"\\f07c\";}\n\
+.fa-arrows-v:before {content: \"\\f07d\";}\n\
+.fa-arrows-h:before {content: \"\\f07e\";}\n\
+.fa-bar-chart-o:before, .fa-bar-chart:before {content: \"\\f080\";}\n\
+.fa-twitter-square:before {content: \"\\f081\";}\n\
+.fa-facebook-square:before {content: \"\\f082\";}\n\
+.fa-camera-retro:before {content: \"\\f083\";}\n\
+.fa-key:before {content: \"\\f084\";}\n\
+.fa-gears:before, .fa-cogs:before {content: \"\\f085\";}\n\
+.fa-comments:before {content: \"\\f086\";}\n\
+.fa-thumbs-o-up:before {content: \"\\f087\";}\n\
+.fa-thumbs-o-down:before {content: \"\\f088\";}\n\
+.fa-star-half:before {content: \"\\f089\";}\n\
+.fa-heart-o:before {content: \"\\f08a\";}\n\
+.fa-sign-out:before {content: \"\\f08b\";}\n\
+.fa-linkedin-square:before {content: \"\\f08c\";}\n\
+.fa-thumb-tack:before {content: \"\\f08d\";}\n\
+.fa-external-link:before {content: \"\\f08e\";}\n\
+.fa-sign-in:before {content: \"\\f090\";}\n\
+.fa-trophy:before {content: \"\\f091\";}\n\
+.fa-github-square:before {content: \"\\f092\";}\n\
+.fa-upload:before {content: \"\\f093\";}\n\
+.fa-lemon-o:before {content: \"\\f094\";}\n\
+.fa-phone:before {content: \"\\f095\";}\n\
+.fa-square-o:before {content: \"\\f096\";}\n\
+.fa-bookmark-o:before {content: \"\\f097\";}\n\
+.fa-phone-square:before {content: \"\\f098\";}\n\
+.fa-twitter:before {content: \"\\f099\";}\n\
+.fa-facebook-f:before, .fa-facebook:before {content: \"\\f09a\";}\n\
+.fa-github:before {content: \"\\f09b\";}\n\
+.fa-unlock:before {content: \"\\f09c\";}\n\
+.fa-credit-card:before {content: \"\\f09d\";}\n\
+.fa-feed:before, .fa-rss:before {content: \"\\f09e\";}\n\
+.fa-hdd-o:before {content: \"\\f0a0\";}\n\
+.fa-bullhorn:before {content: \"\\f0a1\";}\n\
+.fa-bell:before {content: \"\\f0f3\";}\n\
+.fa-certificate:before {content: \"\\f0a3\";}\n\
+.fa-hand-o-right:before {content: \"\\f0a4\";}\n\
+.fa-hand-o-left:before {content: \"\\f0a5\";}\n\
+.fa-hand-o-up:before {content: \"\\f0a6\";}\n\
+.fa-hand-o-down:before {content: \"\\f0a7\";}\n\
+.fa-arrow-circle-left:before {content: \"\\f0a8\";}\n\
+.fa-arrow-circle-right:before {content: \"\\f0a9\";}\n\
+.fa-arrow-circle-up:before {content: \"\\f0aa\";}\n\
+.fa-arrow-circle-down:before {content: \"\\f0ab\";}\n\
+.fa-globe:before {content: \"\\f0ac\";}\n\
+.fa-wrench:before {content: \"\\f0ad\";}\n\
+.fa-tasks:before {content: \"\\f0ae\";}\n\
+.fa-filter:before {content: \"\\f0b0\";}\n\
+.fa-briefcase:before {content: \"\\f0b1\";}\n\
+.fa-arrows-alt:before {content: \"\\f0b2\";}\n\
+.fa-group:before, .fa-users:before {content: \"\\f0c0\";}\n\
+.fa-chain:before, .fa-link:before {content: \"\\f0c1\";}\n\
+.fa-cloud:before {content: \"\\f0c2\";}\n\
+.fa-flask:before {content: \"\\f0c3\";}\n\
+.fa-cut:before, .fa-scissors:before {content: \"\\f0c4\";}\n\
+.fa-copy:before, .fa-files-o:before {content: \"\\f0c5\";}\n\
+.fa-paperclip:before {content: \"\\f0c6\";}\n\
+.fa-save:before, .fa-floppy-o:before {content: \"\\f0c7\";}\n\
+.fa-square:before {content: \"\\f0c8\";}\n\
+.fa-navicon:before, .fa-reorder:before, .fa-bars:before {content: \"\\f0c9\";}\n\
+.fa-list-ul:before {content: \"\\f0ca\";}\n\
+.fa-list-ol:before {content: \"\\f0cb\";}\n\
+.fa-strikethrough:before {content: \"\\f0cc\";}\n\
+.fa-underline:before {content: \"\\f0cd\";}\n\
+.fa-table:before {content: \"\\f0ce\";}\n\
+.fa-magic:before {content: \"\\f0d0\";}\n\
+.fa-truck:before {content: \"\\f0d1\";}\n\
+.fa-pinterest:before {content: \"\\f0d2\";}\n\
+.fa-pinterest-square:before {content: \"\\f0d3\";}\n\
+.fa-google-plus-square:before {content: \"\\f0d4\";}\n\
+.fa-google-plus:before {content: \"\\f0d5\";}\n\
+.fa-money:before {content: \"\\f0d6\";}\n\
+.fa-caret-down:before {content: \"\\f0d7\";}\n\
+.fa-caret-up:before {content: \"\\f0d8\";}\n\
+.fa-caret-left:before {content: \"\\f0d9\";}\n\
+.fa-caret-right:before {content: \"\\f0da\";}\n\
+.fa-columns:before {content: \"\\f0db\";}\n\
+.fa-unsorted:before, .fa-sort:before {content: \"\\f0dc\";}\n\
+.fa-sort-down:before, .fa-sort-desc:before {content: \"\\f0dd\";}\n\
+.fa-sort-up:before, .fa-sort-asc:before {content: \"\\f0de\";}\n\
+.fa-envelope:before {content: \"\\f0e0\";}\n\
+.fa-linkedin:before {content: \"\\f0e1\";}\n\
+.fa-rotate-left:before, .fa-undo:before {content: \"\\f0e2\";}\n\
+.fa-legal:before, .fa-gavel:before {content: \"\\f0e3\";}\n\
+.fa-dashboard:before, .fa-tachometer:before {content: \"\\f0e4\";}\n\
+.fa-comment-o:before {content: \"\\f0e5\";}\n\
+.fa-comments-o:before {content: \"\\f0e6\";}\n\
+.fa-flash:before, .fa-bolt:before {content: \"\\f0e7\";}\n\
+.fa-sitemap:before {content: \"\\f0e8\";}\n\
+.fa-umbrella:before {content: \"\\f0e9\";}\n\
+.fa-paste:before, .fa-clipboard:before {content: \"\\f0ea\";}\n\
+.fa-lightbulb-o:before {content: \"\\f0eb\";}\n\
+.fa-exchange:before {content: \"\\f0ec\";}\n\
+.fa-cloud-download:before {content: \"\\f0ed\";}\n\
+.fa-cloud-upload:before {content: \"\\f0ee\";}\n\
+.fa-user-md:before {content: \"\\f0f0\";}\n\
+.fa-stethoscope:before {content: \"\\f0f1\";}\n\
+.fa-suitcase:before {content: \"\\f0f2\";}\n\
+.fa-bell-o:before {content: \"\\f0a2\";}\n\
+.fa-coffee:before {content: \"\\f0f4\";}\n\
+.fa-cutlery:before {content: \"\\f0f5\";}\n\
+.fa-file-text-o:before {content: \"\\f0f6\";}\n\
+.fa-building-o:before {content: \"\\f0f7\";}\n\
+.fa-hospital-o:before {content: \"\\f0f8\";}\n\
+.fa-ambulance:before {content: \"\\f0f9\";}\n\
+.fa-medkit:before {content: \"\\f0fa\";}\n\
+.fa-fighter-jet:before {content: \"\\f0fb\";}\n\
+.fa-beer:before {content: \"\\f0fc\";}\n\
+.fa-h-square:before {content: \"\\f0fd\";}\n\
+.fa-plus-square:before {content: \"\\f0fe\";}\n\
+.fa-angle-double-left:before {content: \"\\f100\";}\n\
+.fa-angle-double-right:before {content: \"\\f101\";}\n\
+.fa-angle-double-up:before {content: \"\\f102\";}\n\
+.fa-angle-double-down:before {content: \"\\f103\";}\n\
+.fa-angle-left:before {content: \"\\f104\";}\n\
+.fa-angle-right:before {content: \"\\f105\";}\n\
+.fa-angle-up:before {content: \"\\f106\";}\n\
+.fa-angle-down:before {content: \"\\f107\";}\n\
+.fa-desktop:before {content: \"\\f108\";}\n\
+.fa-laptop:before {content: \"\\f109\";}\n\
+.fa-tablet:before {content: \"\\f10a\";}\n\
+.fa-mobile-phone:before, .fa-mobile:before {content: \"\\f10b\";}\n\
+.fa-circle-o:before {content: \"\\f10c\";}\n\
+.fa-quote-left:before {content: \"\\f10d\";}\n\
+.fa-quote-right:before {content: \"\\f10e\";}\n\
+.fa-spinner:before {content: \"\\f110\";}\n\
+.fa-circle:before {content: \"\\f111\";}\n\
+.fa-mail-reply:before, .fa-reply:before {content: \"\\f112\";}\n\
+.fa-github-alt:before {content: \"\\f113\";}\n\
+.fa-folder-o:before {content: \"\\f114\";}\n\
+.fa-folder-open-o:before {content: \"\\f115\";}\n\
+.fa-smile-o:before {content: \"\\f118\";}\n\
+.fa-frown-o:before {content: \"\\f119\";}\n\
+.fa-meh-o:before {content: \"\\f11a\";}\n\
+.fa-gamepad:before {content: \"\\f11b\";}\n\
+.fa-keyboard-o:before {content: \"\\f11c\";}\n\
+.fa-flag-o:before {content: \"\\f11d\";}\n\
+.fa-flag-checkered:before {content: \"\\f11e\";}\n\
+.fa-terminal:before {content: \"\\f120\";}\n\
+.fa-code:before {content: \"\\f121\";}\n\
+.fa-mail-reply-all:before, .fa-reply-all:before {content: \"\\f122\";}\n\
+.fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before {content: \"\\f123\";}\n\
+.fa-location-arrow:before {content: \"\\f124\";}\n\
+.fa-crop:before {content: \"\\f125\";}\n\
+.fa-code-fork:before {content: \"\\f126\";}\n\
+.fa-unlink:before, .fa-chain-broken:before {content: \"\\f127\";}\n\
+.fa-question:before {content: \"\\f128\";}\n\
+.fa-info:before {content: \"\\f129\";}\n\
+.fa-exclamation:before {content: \"\\f12a\";}\n\
+.fa-superscript:before {content: \"\\f12b\";}\n\
+.fa-subscript:before {content: \"\\f12c\";}\n\
+.fa-eraser:before {content: \"\\f12d\";}\n\
+.fa-puzzle-piece:before {content: \"\\f12e\";}\n\
+.fa-microphone:before {content: \"\\f130\";}\n\
+.fa-microphone-slash:before {content: \"\\f131\";}\n\
+.fa-shield:before {content: \"\\f132\";}\n\
+.fa-calendar-o:before {content: \"\\f133\";}\n\
+.fa-fire-extinguisher:before {content: \"\\f134\";}\n\
+.fa-rocket:before {content: \"\\f135\";}\n\
+.fa-maxcdn:before {content: \"\\f136\";}\n\
+.fa-chevron-circle-left:before {content: \"\\f137\";}\n\
+.fa-chevron-circle-right:before {content: \"\\f138\";}\n\
+.fa-chevron-circle-up:before {content: \"\\f139\";}\n\
+.fa-chevron-circle-down:before {content: \"\\f13a\";}\n\
+.fa-html5:before {content: \"\\f13b\";}\n\
+.fa-css3:before {content: \"\\f13c\";}\n\
+.fa-anchor:before {content: \"\\f13d\";}\n\
+.fa-unlock-alt:before {content: \"\\f13e\";}\n\
+.fa-bullseye:before {content: \"\\f140\";}\n\
+.fa-ellipsis-h:before {content: \"\\f141\";}\n\
+.fa-ellipsis-v:before {content: \"\\f142\";}\n\
+.fa-rss-square:before {content: \"\\f143\";}\n\
+.fa-play-circle:before {content: \"\\f144\";}\n\
+.fa-ticket:before {content: \"\\f145\";}\n\
+.fa-minus-square:before {content: \"\\f146\";}\n\
+.fa-minus-square-o:before {content: \"\\f147\";}\n\
+.fa-level-up:before {content: \"\\f148\";}\n\
+.fa-level-down:before {content: \"\\f149\";}\n\
+.fa-check-square:before {content: \"\\f14a\";}\n\
+.fa-pencil-square:before {content: \"\\f14b\";}\n\
+.fa-external-link-square:before {content: \"\\f14c\";}\n\
+.fa-share-square:before {content: \"\\f14d\";}\n\
+.fa-compass:before {content: \"\\f14e\";}\n\
+.fa-toggle-down:before, .fa-caret-square-o-down:before {content: \"\\f150\";}\n\
+.fa-toggle-up:before, .fa-caret-square-o-up:before {content: \"\\f151\";}\n\
+.fa-toggle-right:before, .fa-caret-square-o-right:before {content: \"\\f152\";}\n\
+.fa-euro:before, .fa-eur:before {content: \"\\f153\";}\n\
+.fa-gbp:before {content: \"\\f154\";}\n\
+.fa-dollar:before, .fa-usd:before {content: \"\\f155\";}\n\
+.fa-rupee:before, .fa-inr:before {content: \"\\f156\";}\n\
+.fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before {content: \"\\f157\";}\n\
+.fa-ruble:before, .fa-rouble:before, .fa-rub:before {content: \"\\f158\";}\n\
+.fa-won:before, .fa-krw:before {content: \"\\f159\";}\n\
+.fa-bitcoin:before, .fa-btc:before {content: \"\\f15a\";}\n\
+.fa-file:before {content: \"\\f15b\";}\n\
+.fa-file-text:before {content: \"\\f15c\";}\n\
+.fa-sort-alpha-asc:before {content: \"\\f15d\";}\n\
+.fa-sort-alpha-desc:before {content: \"\\f15e\";}\n\
+.fa-sort-amount-asc:before {content: \"\\f160\";}\n\
+.fa-sort-amount-desc:before {content: \"\\f161\";}\n\
+.fa-sort-numeric-asc:before {content: \"\\f162\";}\n\
+.fa-sort-numeric-desc:before {content: \"\\f163\";}\n\
+.fa-thumbs-up:before {content: \"\\f164\";}\n\
+.fa-thumbs-down:before {content: \"\\f165\";}\n\
+.fa-youtube-square:before {content: \"\\f166\";}\n\
+.fa-youtube:before {content: \"\\f167\";}\n\
+.fa-xing:before {content: \"\\f168\";}\n\
+.fa-xing-square:before {content: \"\\f169\";}\n\
+.fa-youtube-play:before {content: \"\\f16a\";}\n\
+.fa-dropbox:before {content: \"\\f16b\";}\n\
+.fa-stack-overflow:before {content: \"\\f16c\";}\n\
+.fa-instagram:before {content: \"\\f16d\";}\n\
+.fa-flickr:before {content: \"\\f16e\";}\n\
+.fa-adn:before {content: \"\\f170\";}\n\
+.fa-bitbucket:before {content: \"\\f171\";}\n\
+.fa-bitbucket-square:before {content: \"\\f172\";}\n\
+.fa-tumblr:before {content: \"\\f173\";}\n\
+.fa-tumblr-square:before {content: \"\\f174\";}\n\
+.fa-long-arrow-down:before {content: \"\\f175\";}\n\
+.fa-long-arrow-up:before {content: \"\\f176\";}\n\
+.fa-long-arrow-left:before {content: \"\\f177\";}\n\
+.fa-long-arrow-right:before {content: \"\\f178\";}\n\
+.fa-apple:before {content: \"\\f179\";}\n\
+.fa-windows:before {content: \"\\f17a\";}\n\
+.fa-android:before {content: \"\\f17b\";}\n\
+.fa-linux:before {content: \"\\f17c\";}\n\
+.fa-dribbble:before {content: \"\\f17d\";}\n\
+.fa-skype:before {content: \"\\f17e\";}\n\
+.fa-foursquare:before {content: \"\\f180\";}\n\
+.fa-trello:before {content: \"\\f181\";}\n\
+.fa-female:before {content: \"\\f182\";}\n\
+.fa-male:before {content: \"\\f183\";}\n\
+.fa-gittip:before, .fa-gratipay:before {content: \"\\f184\";}\n\
+.fa-sun-o:before {content: \"\\f185\";}\n\
+.fa-moon-o:before {content: \"\\f186\";}\n\
+.fa-archive:before {content: \"\\f187\";}\n\
+.fa-bug:before {content: \"\\f188\";}\n\
+.fa-vk:before {content: \"\\f189\";}\n\
+.fa-weibo:before {content: \"\\f18a\";}\n\
+.fa-renren:before {content: \"\\f18b\";}\n\
+.fa-pagelines:before {content: \"\\f18c\";}\n\
+.fa-stack-exchange:before {content: \"\\f18d\";}\n\
+.fa-arrow-circle-o-right:before {content: \"\\f18e\";}\n\
+.fa-arrow-circle-o-left:before {content: \"\\f190\";}\n\
+.fa-toggle-left:before, .fa-caret-square-o-left:before {content: \"\\f191\";}\n\
+.fa-dot-circle-o:before {content: \"\\f192\";}\n\
+.fa-wheelchair:before {content: \"\\f193\";}\n\
+.fa-vimeo-square:before {content: \"\\f194\";}\n\
+.fa-turkish-lira:before, .fa-try:before {content: \"\\f195\";}\n\
+.fa-plus-square-o:before {content: \"\\f196\";}\n\
+.fa-space-shuttle:before {content: \"\\f197\";}\n\
+.fa-slack:before {content: \"\\f198\";}\n\
+.fa-envelope-square:before {content: \"\\f199\";}\n\
+.fa-wordpress:before {content: \"\\f19a\";}\n\
+.fa-openid:before {content: \"\\f19b\";}\n\
+.fa-institution:before, .fa-bank:before, .fa-university:before {content: \"\\f19c\";}\n\
+.fa-mortar-board:before, .fa-graduation-cap:before {content: \"\\f19d\";}\n\
+.fa-yahoo:before {content: \"\\f19e\";}\n\
+.fa-google:before {content: \"\\f1a0\";}\n\
+.fa-reddit:before {content: \"\\f1a1\";}\n\
+.fa-reddit-square:before {content: \"\\f1a2\";}\n\
+.fa-stumbleupon-circle:before {content: \"\\f1a3\";}\n\
+.fa-stumbleupon:before {content: \"\\f1a4\";}\n\
+.fa-delicious:before {content: \"\\f1a5\";}\n\
+.fa-digg:before {content: \"\\f1a6\";}\n\
+.fa-pied-piper-pp:before {content: \"\\f1a7\";}\n\
+.fa-pied-piper-alt:before {content: \"\\f1a8\";}\n\
+.fa-drupal:before {content: \"\\f1a9\";}\n\
+.fa-joomla:before {content: \"\\f1aa\";}\n\
+.fa-language:before {content: \"\\f1ab\";}\n\
+.fa-fax:before {content: \"\\f1ac\";}\n\
+.fa-building:before {content: \"\\f1ad\";}\n\
+.fa-child:before {content: \"\\f1ae\";}\n\
+.fa-paw:before {content: \"\\f1b0\";}\n\
+.fa-spoon:before {content: \"\\f1b1\";}\n\
+.fa-cube:before {content: \"\\f1b2\";}\n\
+.fa-cubes:before {content: \"\\f1b3\";}\n\
+.fa-behance:before {content: \"\\f1b4\";}\n\
+.fa-behance-square:before {content: \"\\f1b5\";}\n\
+.fa-steam:before {content: \"\\f1b6\";}\n\
+.fa-steam-square:before {content: \"\\f1b7\";}\n\
+.fa-recycle:before {content: \"\\f1b8\";}\n\
+.fa-automobile:before, .fa-car:before {content: \"\\f1b9\";}\n\
+.fa-cab:before, .fa-taxi:before {content: \"\\f1ba\";}\n\
+.fa-tree:before {content: \"\\f1bb\";}\n\
+.fa-spotify:before {content: \"\\f1bc\";}\n\
+.fa-deviantart:before {content: \"\\f1bd\";}\n\
+.fa-soundcloud:before {content: \"\\f1be\";}\n\
+.fa-database:before {content: \"\\f1c0\";}\n\
+.fa-file-pdf-o:before {content: \"\\f1c1\";}\n\
+.fa-file-word-o:before {content: \"\\f1c2\";}\n\
+.fa-file-excel-o:before {content: \"\\f1c3\";}\n\
+.fa-file-powerpoint-o:before {content: \"\\f1c4\";}\n\
+.fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before {content: \"\\f1c5\";}\n\
+.fa-file-zip-o:before, .fa-file-archive-o:before {content: \"\\f1c6\";}\n\
+.fa-file-sound-o:before, .fa-file-audio-o:before {content: \"\\f1c7\";}\n\
+.fa-file-movie-o:before, .fa-file-video-o:before {content: \"\\f1c8\";}\n\
+.fa-file-code-o:before {content: \"\\f1c9\";}\n\
+.fa-vine:before {content: \"\\f1ca\";}\n\
+.fa-codepen:before {content: \"\\f1cb\";}\n\
+.fa-jsfiddle:before {content: \"\\f1cc\";}\n\
+.fa-life-bouy:before, .fa-life-buoy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before {content: \"\\f1cd\";}\n\
+.fa-circle-o-notch:before {content: \"\\f1ce\";}\n\
+.fa-ra:before, .fa-resistance:before, .fa-rebel:before {content: \"\\f1d0\";}\n\
+.fa-ge:before, .fa-empire:before {content: \"\\f1d1\";}\n\
+.fa-git-square:before {content: \"\\f1d2\";}\n\
+.fa-git:before {content: \"\\f1d3\";}\n\
+.fa-y-combinator-square:before, .fa-yc-square:before, .fa-hacker-news:before {content: \"\\f1d4\";}\n\
+.fa-tencent-weibo:before {content: \"\\f1d5\";}\n\
+.fa-qq:before {content: \"\\f1d6\";}\n\
+.fa-wechat:before, .fa-weixin:before {content: \"\\f1d7\";}\n\
+.fa-send:before, .fa-paper-plane:before {content: \"\\f1d8\";}\n\
+.fa-send-o:before, .fa-paper-plane-o:before {content: \"\\f1d9\";}\n\
+.fa-history:before {content: \"\\f1da\";}\n\
+.fa-circle-thin:before {content: \"\\f1db\";}\n\
+.fa-header:before {content: \"\\f1dc\";}\n\
+.fa-paragraph:before {content: \"\\f1dd\";}\n\
+.fa-sliders:before {content: \"\\f1de\";}\n\
+.fa-share-alt:before {content: \"\\f1e0\";}\n\
+.fa-share-alt-square:before {content: \"\\f1e1\";}\n\
+.fa-bomb:before {content: \"\\f1e2\";}\n\
+.fa-soccer-ball-o:before, .fa-futbol-o:before {content: \"\\f1e3\";}\n\
+.fa-tty:before {content: \"\\f1e4\";}\n\
+.fa-binoculars:before {content: \"\\f1e5\";}\n\
+.fa-plug:before {content: \"\\f1e6\";}\n\
+.fa-slideshare:before {content: \"\\f1e7\";}\n\
+.fa-twitch:before {content: \"\\f1e8\";}\n\
+.fa-yelp:before {content: \"\\f1e9\";}\n\
+.fa-newspaper-o:before {content: \"\\f1ea\";}\n\
+.fa-wifi:before {content: \"\\f1eb\";}\n\
+.fa-calculator:before {content: \"\\f1ec\";}\n\
+.fa-paypal:before {content: \"\\f1ed\";}\n\
+.fa-google-wallet:before {content: \"\\f1ee\";}\n\
+.fa-cc-visa:before {content: \"\\f1f0\";}\n\
+.fa-cc-mastercard:before {content: \"\\f1f1\";}\n\
+.fa-cc-discover:before {content: \"\\f1f2\";}\n\
+.fa-cc-amex:before {content: \"\\f1f3\";}\n\
+.fa-cc-paypal:before {content: \"\\f1f4\";}\n\
+.fa-cc-stripe:before {content: \"\\f1f5\";}\n\
+.fa-bell-slash:before {content: \"\\f1f6\";}\n\
+.fa-bell-slash-o:before {content: \"\\f1f7\";}\n\
+.fa-trash:before {content: \"\\f1f8\";}\n\
+.fa-copyright:before {content: \"\\f1f9\";}\n\
+.fa-at:before {content: \"\\f1fa\";}\n\
+.fa-eyedropper:before {content: \"\\f1fb\";}\n\
+.fa-paint-brush:before {content: \"\\f1fc\";}\n\
+.fa-birthday-cake:before {content: \"\\f1fd\";}\n\
+.fa-area-chart:before {content: \"\\f1fe\";}\n\
+.fa-pie-chart:before {content: \"\\f200\";}\n\
+.fa-line-chart:before {content: \"\\f201\";}\n\
+.fa-lastfm:before {content: \"\\f202\";}\n\
+.fa-lastfm-square:before {content: \"\\f203\";}\n\
+.fa-toggle-off:before {content: \"\\f204\";}\n\
+.fa-toggle-on:before {content: \"\\f205\";}\n\
+.fa-bicycle:before {content: \"\\f206\";}\n\
+.fa-bus:before {content: \"\\f207\";}\n\
+.fa-ioxhost:before {content: \"\\f208\";}\n\
+.fa-angellist:before {content: \"\\f209\";}\n\
+.fa-cc:before {content: \"\\f20a\";}\n\
+.fa-shekel:before, .fa-sheqel:before, .fa-ils:before {content: \"\\f20b\";}\n\
+.fa-meanpath:before {content: \"\\f20c\";}\n\
+.fa-buysellads:before {content: \"\\f20d\";}\n\
+.fa-connectdevelop:before {content: \"\\f20e\";}\n\
+.fa-dashcube:before {content: \"\\f210\";}\n\
+.fa-forumbee:before {content: \"\\f211\";}\n\
+.fa-leanpub:before {content: \"\\f212\";}\n\
+.fa-sellsy:before {content: \"\\f213\";}\n\
+.fa-shirtsinbulk:before {content: \"\\f214\";}\n\
+.fa-simplybuilt:before {content: \"\\f215\";}\n\
+.fa-skyatlas:before {content: \"\\f216\";}\n\
+.fa-cart-plus:before {content: \"\\f217\";}\n\
+.fa-cart-arrow-down:before {content: \"\\f218\";}\n\
+.fa-diamond:before {content: \"\\f219\";}\n\
+.fa-ship:before {content: \"\\f21a\";}\n\
+.fa-user-secret:before {content: \"\\f21b\";}\n\
+.fa-motorcycle:before {content: \"\\f21c\";}\n\
+.fa-street-view:before {content: \"\\f21d\";}\n\
+.fa-heartbeat:before {content: \"\\f21e\";}\n\
+.fa-venus:before {content: \"\\f221\";}\n\
+.fa-mars:before {content: \"\\f222\";}\n\
+.fa-mercury:before {content: \"\\f223\";}\n\
+.fa-intersex:before, .fa-transgender:before {content: \"\\f224\";}\n\
+.fa-transgender-alt:before {content: \"\\f225\";}\n\
+.fa-venus-double:before {content: \"\\f226\";}\n\
+.fa-mars-double:before {content: \"\\f227\";}\n\
+.fa-venus-mars:before {content: \"\\f228\";}\n\
+.fa-mars-stroke:before {content: \"\\f229\";}\n\
+.fa-mars-stroke-v:before {content: \"\\f22a\";}\n\
+.fa-mars-stroke-h:before {content: \"\\f22b\";}\n\
+.fa-neuter:before {content: \"\\f22c\";}\n\
+.fa-genderless:before {content: \"\\f22d\";}\n\
+.fa-facebook-official:before {content: \"\\f230\";}\n\
+.fa-pinterest-p:before {content: \"\\f231\";}\n\
+.fa-whatsapp:before {content: \"\\f232\";}\n\
+.fa-server:before {content: \"\\f233\";}\n\
+.fa-user-plus:before {content: \"\\f234\";}\n\
+.fa-user-times:before {content: \"\\f235\";}\n\
+.fa-hotel:before, .fa-bed:before {content: \"\\f236\";}\n\
+.fa-viacoin:before {content: \"\\f237\";}\n\
+.fa-train:before {content: \"\\f238\";}\n\
+.fa-subway:before {content: \"\\f239\";}\n\
+.fa-medium:before {content: \"\\f23a\";}\n\
+.fa-yc:before, .fa-y-combinator:before {content: \"\\f23b\";}\n\
+.fa-optin-monster:before {content: \"\\f23c\";}\n\
+.fa-opencart:before {content: \"\\f23d\";}\n\
+.fa-expeditedssl:before {content: \"\\f23e\";}\n\
+.fa-battery-4:before, .fa-battery:before, .fa-battery-full:before {content: \"\\f240\";}\n\
+.fa-battery-3:before, .fa-battery-three-quarters:before {content: \"\\f241\";}\n\
+.fa-battery-2:before, .fa-battery-half:before {content: \"\\f242\";}\n\
+.fa-battery-1:before, .fa-battery-quarter:before {content: \"\\f243\";}\n\
+.fa-battery-0:before, .fa-battery-empty:before {content: \"\\f244\";}\n\
+.fa-mouse-pointer:before {content: \"\\f245\";}\n\
+.fa-i-cursor:before {content: \"\\f246\";}\n\
+.fa-object-group:before {content: \"\\f247\";}\n\
+.fa-object-ungroup:before {content: \"\\f248\";}\n\
+.fa-sticky-note:before {content: \"\\f249\";}\n\
+.fa-sticky-note-o:before {content: \"\\f24a\";}\n\
+.fa-cc-jcb:before {content: \"\\f24b\";}\n\
+.fa-cc-diners-club:before {content: \"\\f24c\";}\n\
+.fa-clone:before {content: \"\\f24d\";}\n\
+.fa-balance-scale:before {content: \"\\f24e\";}\n\
+.fa-hourglass-o:before {content: \"\\f250\";}\n\
+.fa-hourglass-1:before, .fa-hourglass-start:before {content: \"\\f251\";}\n\
+.fa-hourglass-2:before, .fa-hourglass-half:before {content: \"\\f252\";}\n\
+.fa-hourglass-3:before, .fa-hourglass-end:before {content: \"\\f253\";}\n\
+.fa-hourglass:before {content: \"\\f254\";}\n\
+.fa-hand-grab-o:before, .fa-hand-rock-o:before {content: \"\\f255\";}\n\
+.fa-hand-stop-o:before, .fa-hand-paper-o:before {content: \"\\f256\";}\n\
+.fa-hand-scissors-o:before {content: \"\\f257\";}\n\
+.fa-hand-lizard-o:before {content: \"\\f258\";}\n\
+.fa-hand-spock-o:before {content: \"\\f259\";}\n\
+.fa-hand-pointer-o:before {content: \"\\f25a\";}\n\
+.fa-hand-peace-o:before {content: \"\\f25b\";}\n\
+.fa-trademark:before {content: \"\\f25c\";}\n\
+.fa-registered:before {content: \"\\f25d\";}\n\
+.fa-creative-commons:before {content: \"\\f25e\";}\n\
+.fa-gg:before {content: \"\\f260\";}\n\
+.fa-gg-circle:before {content: \"\\f261\";}\n\
+.fa-tripadvisor:before {content: \"\\f262\";}\n\
+.fa-odnoklassniki:before {content: \"\\f263\";}\n\
+.fa-odnoklassniki-square:before {content: \"\\f264\";}\n\
+.fa-get-pocket:before {content: \"\\f265\";}\n\
+.fa-wikipedia-w:before {content: \"\\f266\";}\n\
+.fa-safari:before {content: \"\\f267\";}\n\
+.fa-chrome:before {content: \"\\f268\";}\n\
+.fa-firefox:before {content: \"\\f269\";}\n\
+.fa-opera:before {content: \"\\f26a\";}\n\
+.fa-internet-explorer:before {content: \"\\f26b\";}\n\
+.fa-tv:before, .fa-television:before {content: \"\\f26c\";}\n\
+.fa-contao:before {content: \"\\f26d\";}\n\
+.fa-500px:before {content: \"\\f26e\";}\n\
+.fa-amazon:before {content: \"\\f270\";}\n\
+.fa-calendar-plus-o:before {content: \"\\f271\";}\n\
+.fa-calendar-minus-o:before {content: \"\\f272\";}\n\
+.fa-calendar-times-o:before {content: \"\\f273\";}\n\
+.fa-calendar-check-o:before {content: \"\\f274\";}\n\
+.fa-industry:before {content: \"\\f275\";}\n\
+.fa-map-pin:before {content: \"\\f276\";}\n\
+.fa-map-signs:before {content: \"\\f277\";}\n\
+.fa-map-o:before {content: \"\\f278\";}\n\
+.fa-map:before {content: \"\\f279\";}\n\
+.fa-commenting:before {content: \"\\f27a\";}\n\
+.fa-commenting-o:before {content: \"\\f27b\";}\n\
+.fa-houzz:before {content: \"\\f27c\";}\n\
+.fa-vimeo:before {content: \"\\f27d\";}\n\
+.fa-black-tie:before {content: \"\\f27e\";}\n\
+.fa-fonticons:before {content: \"\\f280\";}\n\
+.fa-reddit-alien:before {content: \"\\f281\";}\n\
+.fa-edge:before {content: \"\\f282\";}\n\
+.fa-credit-card-alt:before {content: \"\\f283\";}\n\
+.fa-codiepie:before {content: \"\\f284\";}\n\
+.fa-modx:before {content: \"\\f285\";}\n\
+.fa-fort-awesome:before {content: \"\\f286\";}\n\
+.fa-usb:before {content: \"\\f287\";}\n\
+.fa-product-hunt:before {content: \"\\f288\";}\n\
+.fa-mixcloud:before {content: \"\\f289\";}\n\
+.fa-scribd:before {content: \"\\f28a\";}\n\
+.fa-pause-circle:before {content: \"\\f28b\";}\n\
+.fa-pause-circle-o:before {content: \"\\f28c\";}\n\
+.fa-stop-circle:before {content: \"\\f28d\";}\n\
+.fa-stop-circle-o:before {content: \"\\f28e\";}\n\
+.fa-shopping-bag:before {content: \"\\f290\";}\n\
+.fa-shopping-basket:before {content: \"\\f291\";}\n\
+.fa-hashtag:before {content: \"\\f292\";}\n\
+.fa-bluetooth:before {content: \"\\f293\";}\n\
+.fa-bluetooth-b:before {content: \"\\f294\";}\n\
+.fa-percent:before {content: \"\\f295\";}\n\
+.fa-gitlab:before {content: \"\\f296\";}\n\
+.fa-wpbeginner:before {content: \"\\f297\";}\n\
+.fa-wpforms:before {content: \"\\f298\";}\n\
+.fa-envira:before {content: \"\\f299\";}\n\
+.fa-universal-access:before {content: \"\\f29a\";}\n\
+.fa-wheelchair-alt:before {content: \"\\f29b\";}\n\
+.fa-question-circle-o:before {content: \"\\f29c\";}\n\
+.fa-blind:before {content: \"\\f29d\";}\n\
+.fa-audio-description:before {content: \"\\f29e\";}\n\
+.fa-volume-control-phone:before {content: \"\\f2a0\";}\n\
+.fa-braille:before {content: \"\\f2a1\";}\n\
+.fa-assistive-listening-systems:before {content: \"\\f2a2\";}\n\
+.fa-asl-interpreting:before, .fa-american-sign-language-interpreting:before {content: \"\\f2a3\";}\n\
+.fa-deafness:before, .fa-hard-of-hearing:before, .fa-deaf:before {content: \"\\f2a4\";}\n\
+.fa-glide:before {content: \"\\f2a5\";}\n\
+.fa-glide-g:before {content: \"\\f2a6\";}\n\
+.fa-signing:before, .fa-sign-language:before {content: \"\\f2a7\";}\n\
+.fa-low-vision:before {content: \"\\f2a8\";}\n\
+.fa-viadeo:before {content: \"\\f2a9\";}\n\
+.fa-viadeo-square:before {content: \"\\f2aa\";}\n\
+.fa-snapchat:before {content: \"\\f2ab\";}\n\
+.fa-snapchat-ghost:before {content: \"\\f2ac\";}\n\
+.fa-snapchat-square:before {content: \"\\f2ad\";}\n\
+.fa-pied-piper:before {content: \"\\f2ae\";}\n\
+.fa-first-order:before {content: \"\\f2b0\";}\n\
+.fa-yoast:before {content: \"\\f2b1\";}\n\
+.fa-themeisle:before {content: \"\\f2b2\";}\n\
+.fa-google-plus-circle:before, .fa-google-plus-official:before {content: \"\\f2b3\";}\n\
+.fa-fa:before, .fa-font-awesome:before {content: \"\\f2b4\";}\n\
+.fa-handshake-o:before {content: \"\\f2b5\";}\n\
+.fa-envelope-open:before {content: \"\\f2b6\";}\n\
+.fa-envelope-open-o:before {content: \"\\f2b7\";}\n\
+.fa-linode:before {content: \"\\f2b8\";}\n\
+.fa-address-book:before {content: \"\\f2b9\";}\n\
+.fa-address-book-o:before {content: \"\\f2ba\";}\n\
+.fa-vcard:before, .fa-address-card:before {content: \"\\f2bb\";}\n\
+.fa-vcard-o:before, .fa-address-card-o:before {content: \"\\f2bc\";}\n\
+.fa-user-circle:before {content: \"\\f2bd\";}\n\
+.fa-user-circle-o:before {content: \"\\f2be\";}\n\
+.fa-user-o:before {content: \"\\f2c0\";}\n\
+.fa-id-badge:before {content: \"\\f2c1\";}\n\
+.fa-drivers-license:before, .fa-id-card:before {content: \"\\f2c2\";}\n\
+.fa-drivers-license-o:before, .fa-id-card-o:before {content: \"\\f2c3\";}\n\
+.fa-quora:before {content: \"\\f2c4\";}\n\
+.fa-free-code-camp:before {content: \"\\f2c5\";}\n\
+.fa-telegram:before {content: \"\\f2c6\";}\n\
+.fa-thermometer-4:before, .fa-thermometer:before, .fa-thermometer-full:before {content: \"\\f2c7\";}\n\
+.fa-thermometer-3:before, .fa-thermometer-three-quarters:before {content: \"\\f2c8\";}\n\
+.fa-thermometer-2:before, .fa-thermometer-half:before {content: \"\\f2c9\";}\n\
+.fa-thermometer-1:before, .fa-thermometer-quarter:before {content: \"\\f2ca\";}\n\
+.fa-thermometer-0:before, .fa-thermometer-empty:before {content: \"\\f2cb\";}\n\
+.fa-shower:before {content: \"\\f2cc\";}\n\
+.fa-bathtub:before, .fa-s15:before, .fa-bath:before {content: \"\\f2cd\";}\n\
+.fa-podcast:before {content: \"\\f2ce\";}\n\
+.fa-window-maximize:before {content: \"\\f2d0\";}\n\
+.fa-window-minimize:before {content: \"\\f2d1\";}\n\
+.fa-window-restore:before {content: \"\\f2d2\";}\n\
+.fa-times-rectangle:before, .fa-window-close:before {content: \"\\f2d3\";}\n\
+.fa-times-rectangle-o:before, .fa-window-close-o:before {content: \"\\f2d4\";}\n\
+.fa-bandcamp:before {content: \"\\f2d5\";}\n\
+.fa-grav:before {content: \"\\f2d6\";}\n\
+.fa-etsy:before {content: \"\\f2d7\";}\n\
+.fa-imdb:before {content: \"\\f2d8\";}\n\
+.fa-ravelry:before {content: \"\\f2d9\";}\n\
+.fa-eercast:before {content: \"\\f2da\";}\n\
+.fa-microchip:before {content: \"\\f2db\";}\n\
+.fa-snowflake-o:before {content: \"\\f2dc\";}\n\
+.fa-superpowers:before {content: \"\\f2dd\";}\n\
+.fa-wpexplorer:before {content: \"\\f2de\";}\n\
+.fa-meetup:before {content: \"\\f2e0\";}\n\
+.fa::before {\n\
+ font-family: FontAwesome;\n\
+ font-weight: 400;\n\
+ font-style: normal;\n\
+ -webkit-font-smoothing: antialiased;\n\
+ text-decoration: inherit;\n\
+ speak: none;\n\
+ display: inline-block;\n\
+ font-size: 13px;\n\
+ visibility: visible;\n\
+}\n\
+:root:not(.shortcut-icons) #shortcuts .fa::before {\n\
+ display: none;\n\
+}\n\
+:root.shortcut-icons #shortcuts .fa::before {\n\
+ font-size: 15px !important;\n\
+ margin-top: -3px !important;\n\
+ position: relative;\n\
+ top: 1px;\n\
+}\n\
+:root.shortcut-icons #shortcuts .fa, .menu-button .fa {\n\
+ font-size: 0;\n\
+ visibility: hidden;\n\
+}\n\
+:root.shortcut-icons .shortcut.brackets-wrap::after,\n\
+:root.shortcut-icons .shortcut.brackets-wrap::before {\n\
+ display: none;\n\
+}\n\
+:root.shortcut-icons #shortcuts a .fa,\n\
+.menu-button .fa,\n\
+.hide-reply-button .fa,\n\
+.hide-thread-button .fa {\n\
+ display: inline;\n\
+}\n\
+.fa-spin::before {\n\
+ -webkit-animation:spin 2s infinite linear;\n\
+ -moz-animation:spin 2s infinite linear;\n\
+ -o-animation:spin 2s infinite linear;\n\
+ animation:spin 2s infinite linear;\n\
+}\n\
+@-moz-keyframes spin {\n\
+ 0% {-moz-transform:rotate(0deg);}\n\
+ 100% {-moz-transform:rotate(359deg);}\n\
+}\n\
+@-webkit-keyframes spin {\n\
+ 0% {-webkit-transform:rotate(0deg);}\n\
+ 100% {-webkit-transform:rotate(359deg);}\n\
+}\n\
+@keyframes spin {\n\
+ 0% {transform:rotate(0deg);}\n\
+ 100% {transform:rotate(359deg);}\n\
+}\n\
+/* General */\n\
+.dialog {\n\
+ border: 1px solid;\n\
+ display: block;\n\
+ background-color: inherit;\n\
+}\n\
+.dialog:not(#qr):not(#thread-watcher):not(#header-bar) {\n\
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .15);\n\
+}\n\
+#qr,\n\
+#thread-watcher {\n\
+ box-shadow: -1px 2px 2px rgba(0, 0, 0, 0.25);\n\
+}\n\
+.captcha-img,\n\
+.field {\n\
+ background-color: #FFF;\n\
+ border: 1px solid #CCC;\n\
+ -moz-box-sizing: border-box;\n\
+ box-sizing: border-box;\n\
+ color: #333;\n\
+ font: 13px sans-serif;\n\
+ outline: none;\n\
+ transition: color .25s, border-color .25s;\n\
+}\n\
+.field::-moz-placeholder {\n\
+ color: #AAA;\n\
+ font-size: 13px;\n\
+ opacity: 1;\n\
+}\n\
+.captch-img:hover,\n\
+.field:hover {\n\
+ border-color: #999;\n\
+}\n\
+.field:hover, .field:focus, .field.focus {\n\
+ color: #000;\n\
+}\n\
+.field[disabled] {\n\
+ background-color: #F2F2F2;\n\
+ color: #888;\n\
+}\n\
+.field::-webkit-search-decoration {\n\
+ display: none;\n\
+}\n\
+.move {\n\
+ cursor: move;\n\
+ overflow: hidden;\n\
+}\n\
+label {\n\
+ cursor: pointer;\n\
+}\n\
+a[href=\"javascript:;\"] {\n\
+ text-decoration: none;\n\
+}\n\
+.warning {\n\
+ color: red;\n\
+}\n\
+:root.sw-yotsuba #boardNavDesktop, :root.sw-yotsuba #boardNavMobile {\n\
+ display: none !important;\n\
+}\n\
+:root.hide-bottom-board-list $site$boardListBottom {\n\
+ display: none;\n\
+}\n\
+body.hasDropDownNav{\n\
+ margin-top: 5px;\n\
+}\n\
+:root:not(.keyboard-focus) a {\n\
+ outline: none;\n\
+}\n\
+.painted {\n\
+ border-radius: 3px;\n\
+ padding: 0px 2px;\n\
+}\n\
+[hidden] {\n\
+ display: none !important;\n\
+}\n\
+/* 4chan style fixes */\n\
+/* overrides 4chan CSS on div.opContainer, div.op */\n\
+:root.sw-yotsuba .opContainer, :root.sw-yotsuba .op {\n\
+ display: block;\n\
+ overflow: visible;\n\
+}\n\
+:root.sw-yotsuba .reply > .file > .fileText {\n\
+ margin: 0 20px;\n\
+}\n\
+:root.sw-yotsuba #arc-list span.quote {\n\
+ color: #789922;\n\
+}\n\
+:root.sw-yotsuba .fileText a {\n\
+ unicode-bidi: -moz-isolate;\n\
+ unicode-bidi: -webkit-isolate;\n\
+}\n\
+:root.sw-yotsuba #g-recaptcha {\n\
+ min-height: 78px;\n\
+ height: auto;\n\
+}\n\
+:root.sw-yotsuba:not(.js-enabled) #postForm {\n\
+ display: table;\n\
+}\n\
+:root.sw-yotsuba #captchaContainerAlt td:nth-child(2) {\n\
+ display: table-cell !important;\n\
+}\n\
+:root.sw-yotsuba canvas#tegaki-canvas {\n\
+ background: none;\n\
+}\n\
+/* Disable obnoxious captcha fade-in. */\n\
+:root.sw-yotsuba > body > div:last-of-type {\n\
+ transition: none !important;\n\
+}\n\
+/* Fix captcha scrolling to top of page. */\n\
+:root.sw-yotsuba > body > div[style*=\" top: -10000px;\"] {\n\
+ visibility: hidden !important;\n\
+}\n\
+/* Make long filenames wrap properly: https://github.com/ccd0/4chan-x/issues/1082 */\n\
+:root.sw-yotsuba .post > .file {\n\
+ /* currently nonstandard but may be added: https://lists.w3.org/Archives/Public/www-style/2016Mar/0352.html, https://bugzilla.mozilla.org/show_bug.cgi?id=1296042 */\n\
+ word-break: break-word;\n\
+}\n\
+:root.sw-yotsuba:not(.ua-webkit):not(.ua-blink) .fileText {\n\
+ word-wrap: break-word;\n\
+ max-width: calc(100vw - 90px);\n\
+}\n\
+:root.sw-yotsuba > body.is_catalog .thread > a > img {\n\
+ display: inline-block;\n\
+}\n\
+/* Links to NSFW boards */\n\
+:root.sw-yotsuba .nwsb {\n\
+ display: inline;\n\
+}\n\
+:root.sw-yotsuba .fileText {\n\
+ max-width: auto;\n\
+ white-space: normal;\n\
+}\n\
+/* Ads */\n\
+:root.sw-yotsuba .ad-cnt > *, :root.sw-yotsuba .adg-rects > *, :root.sw-yotsuba .bsa-cnt {\n\
+ height: auto !important;\n\
+}\n\
+:root.sw-yotsuba:not(.ads-loaded) hr.abovePostForm,\n\
+:root.sw-yotsuba:not(.ads-loaded) .adg-rects > hr,\n\
+:root.sw-yotsuba #adg-ol + hr {\n\
+ display: none;\n\
+}\n\
+:root.sw-yotsuba .adg-rects {\n\
+ margin: 0;\n\
+ font-size: 0;\n\
+}\n\
+:root.sw-yotsuba div.center[style] {\n\
+ display: none !important;\n\
+}\n\
+/* Tinyboard / vichan conflicts */\n\
+#menu > .hide-thread-link {\n\
+ width: auto;\n\
+ height: auto;\n\
+ overflow: visible;\n\
+ background-image: none;\n\
+}\n\
+#menu label.entry {\n\
+ display: block;\n\
+}\n\
+#fourchanx-settings label {\n\
+ display: inline;\n\
+}\n\
+.intro a[href=\"javascript:;\"],\n\
+#menu a {\n\
+ margin: 0;\n\
+}\n\
+.gal-buttons.gal-buttons a {\n\
+ font-size: inherit;\n\
+}\n\
+:root.sw-tinyboard.fixed.top-header:not(.autohide) .boardlist,\n\
+:root.sw-tinyboard.fixed.top-header:not(.autohide) .bar.top {\n\
+ position: static;\n\
+}\n\
+:root.sw-tinyboard.fixed.top-header:not(.autohide) div.pages.top {\n\
+ top: auto;\n\
+ bottom: 0;\n\
+}\n\
+:root.sw-tinyboard.fixed.top-header.autohide .boardlist,\n\
+:root.sw-tinyboard.fixed.top-header.autohide .bar.top {\n\
+ z-index: 3;\n\
+}\n\
+/* Tinyboard site style conflicts */\n\
+:root[data-host=\"fufufu.moe\"].fixed.top-header:not(.autohide) div.pages.top {\n\
+ top: 26px;\n\
+ bottom: auto;\n\
+}\n\
+:root[data-host=\"merorin.com\"].fixed.top-header:not(.autohide) span.settings {\n\
+ top: 26px;\n\
+}\n\
+:root[data-host=\"fufufu.moe\"]:not(.fixed) #header-bar {\n\
+ margin-top: 38px;\n\
+}\n\
+:root[data-host=\"lainchan.org\"]:not(.fixed) #header-bar {\n\
+ margin-top: 17px;\n\
+}\n\
+:root[data-host=\"smuglo.li\"]:not(.fixed) #header-bar {\n\
+ margin-top: 8px;\n\
+}\n\
+/* Anti-autoplay */\n\
+audio.controls-added {\n\
+ display: block;\n\
+ margin: auto;\n\
+ white-space: normal;\n\
+}\n\
+:root.anti-autoplay div.embed {\n\
+ position: static;\n\
+ width: auto;\n\
+ height: auto;\n\
+ text-align: center;\n\
+}\n\
+:root.anti-autoplay .autoplay-removed {\n\
+ visibility: visible !important;\n\
+ min-width: 640px;\n\
+ min-height: 360px;\n\
+}\n\
+/* fixed, z-index */\n\
+#overlay,\n\
+#qp, #ihover,\n\
+#navlinks, .fixed #header-bar,\n\
+:root.float #updater,\n\
+:root.float #thread-stats,\n\
+#qr {\n\
+ position: fixed;\n\
+}\n\
+#overlay {\n\
+ z-index: 999;\n\
+}\n\
+#qp, #ihover {\n\
+ z-index: 60;\n\
+}\n\
+#menu, .gal-buttons {\n\
+ z-index: 50;\n\
+}\n\
+#updater, #thread-stats {\n\
+ z-index: 40;\n\
+}\n\
+:root.fixed #header-bar, #notifications {\n\
+ z-index: 35;\n\
+}\n\
+#a-gallery {\n\
+ z-index: 30;\n\
+}\n\
+#navlinks {\n\
+ z-index: 25;\n\
+}\n\
+#qr {\n\
+ z-index: 20;\n\
+}\n\
+#embedding {\n\
+ z-index: 11;\n\
+}\n\
+:root.fixed-watcher #thread-watcher {\n\
+ z-index: 10;\n\
+}\n\
+:root.fixed:not(.gallery-open) #header-bar:not(:hover) {\n\
+ z-index: 8;\n\
+}\n\
+#thread-watcher {\n\
+ z-index: 5;\n\
+}\n\
+/* Header */\n\
+.fixed.top-header body {\n\
+ padding-top: 2em;\n\
+}\n\
+.fixed.bottom-header body {\n\
+ padding-bottom: 2em;\n\
+}\n\
+.fixed #header-bar {\n\
+ right: 0;\n\
+ left: 0;\n\
+ padding: 3px 4px 4px;\n\
+ font-size: 12px;\n\
+}\n\
+.fixed.top-header #header-bar {\n\
+ top: 0;\n\
+}\n\
+.fixed.bottom-header #header-bar {\n\
+ bottom: 0;\n\
+}\n\
+#header-bar {\n\
+ border-width: 0;\n\
+ transition: all .1s .05s ease-in-out;\n\
+}\n\
+:root.fixed #header-bar {\n\
+ box-shadow: -5px 1px 10px rgba(0, 0, 0, 0.20);\n\
+}\n\
+:root.centered-links #shortcuts {\n\
+ width: 300px;\n\
+ text-align: right;\n\
+}\n\
+:root.centered-links #header-bar {\n\
+ text-align: center;\n\
+}\n\
+#custom-board-list {\n\
+ font-size: 13px;\n\
+ vertical-align: middle;\n\
+}\n\
+#full-board-list {\n\
+ vertical-align: middle;\n\
+}\n\
+:root.centered-links #custom-board-list {\n\
+ position: relative;\n\
+ left: 150px;\n\
+}\n\
+.fixed.top-header #header-bar {\n\
+ border-bottom-width: 1px;\n\
+}\n\
+.fixed.bottom-header #header-bar {\n\
+ box-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\n\
+ border-top-width: 1px;\n\
+}\n\
+.fixed.bottom-header #header-bar .menu-button i {\n\
+ border-top: none;\n\
+ border-bottom: 6px solid;\n\
+}\n\
+.fixed #header-bar.autohide:not(:hover) {\n\
+ box-shadow: none;\n\
+ transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n\
+}\n\
+.fixed.top-header #header-bar.autohide:not(:hover) {\n\
+ margin-bottom: -1em;\n\
+ -webkit-transform: translateY(-100%);\n\
+ transform: translateY(-100%);\n\
+}\n\
+.fixed.bottom-header #header-bar.autohide:not(:hover) {\n\
+ -webkit-transform: translateY(100%);\n\
+ transform: translateY(100%);\n\
+}\n\
+#scroll-marker {\n\
+ left: 0;\n\
+ right: 0;\n\
+ height: 10px;\n\
+ position: absolute;\n\
+}\n\
+#header-bar:not(.autohide) #scroll-marker {\n\
+ pointer-events: none;\n\
+}\n\
+#header-bar #scroll-marker {\n\
+ display: none;\n\
+}\n\
+.fixed #header-bar #scroll-marker {\n\
+ display: block;\n\
+}\n\
+.fixed.top-header #header-bar #scroll-marker {\n\
+ top: 100%;\n\
+}\n\
+.fixed.bottom-header #header-bar #scroll-marker {\n\
+ bottom: 100%;\n\
+}\n\
+#board-list a, #shortcuts a:not(.entry) {\n\
+ text-decoration: none;\n\
+ padding: 1px;\n\
+}\n\
+#shortcuts:empty {\n\
+ display: none;\n\
+}\n\
+.brackets-wrap::before {\n\
+ content: \"\\00a0[\";\n\
+}\n\
+.brackets-wrap::after {\n\
+ content: \"]\\00a0\";\n\
+}\n\
+.dead-thread,\n\
+.disabled:not(.replies-quoting-you) {\n\
+ opacity: .45;\n\
+}\n\
+#shortcuts {\n\
+ float: right;\n\
+}\n\
+:root.autohiding-scrollbar #shortcuts {\n\
+ margin-right: 12px;\n\
+}\n\
+.shortcut {\n\
+ margin-left: 3px;\n\
+ vertical-align: middle;\n\
+}\n\
+:root.shortcut-icons .native-settings {\n\
+ font-size: 0;\n\
+ color: transparent;\n\
+ display: inline-block;\n\
+ vertical-align: top;\n\
+ height: 12px;\n\
+ width: 14px;\n\
+ background: url('//s.4cdn.org/image/favicon.ico') 0px -1px no-repeat;\n\
+}\n\
+#navbotright,\n\
+#navtopright {\n\
+ display: none;\n\
+}\n\
+#toggleMsgBtn {\n\
+ display: none !important;\n\
+}\n\
+.current,\n\
+:root.sw-yotsuba div#boardNavDesktopFoot a.current {\n\
+ font-weight: bold;\n\
+}\n\
+@media (min-width: 1300px) {\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #header-bar {\n\
+ white-space: nowrap;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-align-items: center;\n\
+ align-items: center;\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #board-list {\n\
+ -webkit-flex: auto;\n\
+ flex: auto;\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #full-board-list {\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) .hide-board-list-container {\n\
+ -webkit-flex: none;\n\
+ flex: none;\n\
+ margin-right: 5px;\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #full-board-list > .boardList {\n\
+ -webkit-flex: auto;\n\
+ flex: auto;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ width: 0px; /* XXX Fixes Edge not shrinking the board list below default size when needed */\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #full-board-list > .boardList > a,\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #full-board-list > .boardList > span:not(.space):not(.spacer) {\n\
+ -webkit-flex: none;\n\
+ flex: none;\n\
+ padding: .17em;\n\
+ margin: -.17em -.32em;\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #full-board-list > .boardList > span {\n\
+ pointer-events: none;\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #full-board-list > .boardList > span.space {\n\
+ -webkit-flex: 0 .63 .63em;\n\
+ flex: 0 .63 .63em;\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #full-board-list > .boardList > span.spacer {\n\
+ -webkit-flex: 0 .38 .38em;\n\
+ flex: 0 .38 .38em;\n\
+ }\n\
+ :root.sw-yotsuba.fixed:not(.centered-links) #shortcuts {\n\
+ float: initial;\n\
+ -webkit-flex: none;\n\
+ flex: none;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-align-items: center;\n\
+ align-items: center;\n\
+ }\n\
+}\n\
+/* 4chan X link brackets */\n\
+.brackets-wrap::before {\n\
+ content: \"[\";\n\
+}\n\
+.brackets-wrap::after {\n\
+ content: \"]\";\n\
+}\n\
+/* Notifications */\n\
+#notifications {\n\
+ position: fixed;\n\
+ top: 0;\n\
+ height: 0;\n\
+ text-align: center;\n\
+ right: 0;\n\
+ left: 0;\n\
+ visibility: visible;\n\
+}\n\
+#notifications:empty {\n\
+ display: none;\n\
+}\n\
+:root.fixed.top-header:not(.gallery-open) #header-bar #notifications,\n\
+:root.fixed.top-header #header-bar.autohide #notifications {\n\
+ position: absolute;\n\
+ top: 100%;\n\
+}\n\
+.notification {\n\
+ color: #FFF;\n\
+ font-weight: 700;\n\
+ text-shadow: 0 1px 2px rgba(0, 0, 0, .5);\n\
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .15);\n\
+ border-radius: 2px;\n\
+ margin: 1px auto;\n\
+ width: 550px;\n\
+ max-width: 100%;\n\
+ position: relative;\n\
+ transition: all .25s ease-in-out;\n\
+}\n\
+.notification.error {\n\
+ background-color: hsla(0, 100%, 38%, .9);\n\
+}\n\
+.notification.warning {\n\
+ background-color: hsla(36, 100%, 38%, .9);\n\
+}\n\
+.notification.info {\n\
+ background-color: hsla(200, 100%, 38%, .9);\n\
+}\n\
+.notification.success {\n\
+ background-color: hsla(104, 100%, 38%, .9);\n\
+}\n\
+.notification a {\n\
+ color: white;\n\
+}\n\
+.notification > .close {\n\
+ padding: 7px;\n\
+ top: 0px;\n\
+ right: 5px;\n\
+ position: absolute;\n\
+}\n\
+.notification > .fa-times::before {\n\
+ font-size: 11px !important;\n\
+}\n\
+.message {\n\
+ -moz-box-sizing: border-box;\n\
+ box-sizing: border-box;\n\
+ padding: 6px 20px;\n\
+ max-height: 200px;\n\
+ width: 100%;\n\
+ overflow: auto;\n\
+ white-space: pre-line;\n\
+}\n\
+.message a {\n\
+ text-decoration: underline;\n\
+}\n\
+:root.tainted .report-error {\n\
+ display: none;\n\
+}\n\
+/* Settings */\n\
+:root.fourchan-x body {\n\
+ -moz-box-sizing: border-box;\n\
+ box-sizing: border-box;\n\
+}\n\
+#overlay {\n\
+ background-color: rgba(0, 0, 0, .5);\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ top: 0;\n\
+ left: 0;\n\
+ height: 100%;\n\
+ width: 100%;\n\
+}\n\
+#fourchanx-settings {\n\
+ -moz-box-sizing: border-box;\n\
+ box-sizing: border-box;\n\
+ box-shadow: 0 0 15px rgba(0, 0, 0, .15);\n\
+ height: 600px;\n\
+ max-height: 100%;\n\
+ width: 900px;\n\
+ max-width: 100%;\n\
+ margin: auto;\n\
+ padding: 5px;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-flex-direction: column;\n\
+ flex-direction: column;\n\
+}\n\
+#fourchanx-settings > nav {\n\
+ padding: 2px 2px 8px;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+}\n\
+#fourchanx-settings > nav a {\n\
+ text-decoration: underline;\n\
+}\n\
+#fourchanx-settings > nav a.close {\n\
+ text-decoration: none;\n\
+ padding: 0 2px;\n\
+ margin: 0;\n\
+}\n\
+.section-container {\n\
+ -webkit-flex: 1;\n\
+ flex: 1;\n\
+ position: relative;\n\
+ overflow: auto;\n\
+ padding-right: 5px;\n\
+ overscroll-behavior: contain;\n\
+}\n\
+.sections-list {\n\
+ -webkit-flex: 1;\n\
+ flex: 1;\n\
+}\n\
+.export, .import, .reset {\n\
+ cursor: pointer;\n\
+ text-decoration: none !important;\n\
+}\n\
+.tab-selected {\n\
+ font-weight: 700;\n\
+}\n\
+.section-sauce ul,\n\
+.section-advanced ul {\n\
+ list-style: none;\n\
+ margin: 0;\n\
+}\n\
+.section-sauce ul {\n\
+ padding: 8px;\n\
+}\n\
+.section-advanced ul {\n\
+ padding: 0px;\n\
+}\n\
+.section-sauce li,\n\
+.section-advanced li {\n\
+ padding-left: 4px;\n\
+}\n\
+.section-main ul {\n\
+ margin: 0;\n\
+ padding: 0 0 0 16px;\n\
+}\n\
+.section-main li {\n\
+ white-space: pre-line;\n\
+ list-style: disc;\n\
+}\n\
+.section-main li:not(:first-of-type) {\n\
+ margin-top: 4px;\n\
+}\n\
+.section-main label {\n\
+ text-decoration: underline;\n\
+}\n\
+div[data-checked=\"false\"] > .suboption-list {\n\
+ display: none;\n\
+}\n\
+.suboption-list {\n\
+ position: relative;\n\
+}\n\
+.suboption-list::before {\n\
+ content: \"\";\n\
+ display: inline-block;\n\
+ position: absolute;\n\
+ left: .7em;\n\
+ width: 0;\n\
+ height: 100%;\n\
+ border-left: 1px solid;\n\
+}\n\
+.suboption-list > div {\n\
+ position: relative;\n\
+ padding-left: 1.4em;\n\
+}\n\
+.suboption-list > div::before {\n\
+ content: \"\";\n\
+ display: inline-block;\n\
+ position: absolute;\n\
+ left: .7em;\n\
+ width: .7em;\n\
+ height: .6em;\n\
+ border-left: 1px solid;\n\
+ border-bottom: 1px solid;\n\
+}\n\
+#fourchanx-settings .section-main p {\n\
+ margin: .5em 0 0;\n\
+}\n\
+.section-filter ul {\n\
+ padding: 0;\n\
+}\n\
+.section-filter li {\n\
+ margin: 10px 40px;\n\
+ list-style: disc;\n\
+}\n\
+.section-filter textarea {\n\
+ height: 500px;\n\
+}\n\
+.section-main a, .section-filter a, .section-advanced a {\n\
+ text-decoration: underline;\n\
+}\n\
+#sauce-doc-expand:not(:checked) ~ #sauce-doc {\n\
+ max-height: 130px;\n\
+ overflow: auto;\n\
+}\n\
+#sauce-doc > label {\n\
+ float: right;\n\
+ margin: 0 5px;\n\
+}\n\
+/* XXX for OneeChan */\n\
+#sauce-doc-expand + .riceCheck {\n\
+ display: none;\n\
+}\n\
+.section-sauce textarea {\n\
+ height: 430px;\n\
+}\n\
+.section-advanced .field[name=\"boardnav\"] {\n\
+ width: 100%;\n\
+}\n\
+.section-advanced textarea {\n\
+ height: 150px;\n\
+}\n\
+.section-advanced textarea[name=\"archiveLists\"],\n\
+.section-advanced textarea[name=\"externalCatalogURLs\"],\n\
+.section-advanced textarea[name=\"knownBanners\"] {\n\
+ height: 75px;\n\
+}\n\
+.section-advanced .archive-cell {\n\
+ min-width: 160px;\n\
+ text-align: center;\n\
+}\n\
+.section-advanced #archive-board-select {\n\
+ position: absolute;\n\
+}\n\
+.section-advanced .note {\n\
+ font-size: 0.8em;\n\
+ font-style: italic;\n\
+ margin-left: 10px;\n\
+}\n\
+.section-advanced .note code {\n\
+ font-style: normal;\n\
+ font-size: 11px;\n\
+}\n\
+.favicon-preview > img {\n\
+ vertical-align: middle;\n\
+}\n\
+.favicon-preview > img:nth-of-type(3n+1) {\n\
+ margin-left: 4px;\n\
+}\n\
+.section-keybinds .field {\n\
+ font-family: monospace;\n\
+}\n\
+#fourchanx-settings fieldset {\n\
+ border: 1px solid;\n\
+ border-radius: 3px;\n\
+ padding: 0.35em 0.625em 0.75em;\n\
+ margin: 0px 2px;\n\
+}\n\
+#fourchanx-settings legend {\n\
+ font-weight: 700;\n\
+ color: inherit;\n\
+}\n\
+#fourchanx-settings textarea {\n\
+ font-family: monospace;\n\
+ width: 100%;\n\
+ resize: vertical;\n\
+}\n\
+#fourchanx-settings code {\n\
+ color: #000;\n\
+ background-color: #FFF;\n\
+ padding: 0 2px;\n\
+}\n\
+#fourchanx-settings th {\n\
+ text-align: center;\n\
+ font-weight: bold;\n\
+}\n\
+#fourchanx-settings p {\n\
+ margin: 1em 0px;\n\
+}\n\
+#fourchanx-settings table {\n\
+ margin: auto;\n\
+}\n\
+/* Index */\n\
+:root.index-loading .navLinks:not(.json-index),\n\
+:root.index-loading .board:not(.json-index),\n\
+:root.index-loading .pagelist:not(.json-index),\n\
+:root.infinite-mode .pagelist,\n\
+:root.all-pages-mode .pagelist,\n\
+:root.catalog-mode .pagelist,\n\
+:root:not(.catalog-mode) .indexlink,\n\
+:root.catalog-mode .cataloglink,\n\
+:root:not(.catalog-mode) #hidden-label,\n\
+:root:not(.catalog-mode) #index-size {\n\
+ display: none;\n\
+}\n\
+#index-search {\n\
+ padding-right: 1.5em;\n\
+ width: 100px;\n\
+ transition: color .25s, border-color .25s, width .25s;\n\
+}\n\
+#index-search:focus,\n\
+#index-search[data-searching] {\n\
+ width: 200px;\n\
+}\n\
+#index-search-clear {\n\
+ color: gray;\n\
+ display: inline-block;\n\
+ position: relative;\n\
+ left: -1em;\n\
+ width: 0;\n\
+}\n\
+/* ``::-webkit-*'' selectors break selector lists on Firefox. */\n\
+#index-search::-webkit-search-cancel-button {\n\
+ display: none;\n\
+}\n\
+#index-search:not([data-searching]) + #index-search-clear {\n\
+ display: none;\n\
+}\n\
+#index-options {\n\
+ float: right;\n\
+}\n\
+#lastlong-options {\n\
+ display: inline-block;\n\
+ vertical-align: middle;\n\
+ height: 28px;\n\
+ margin: -14px 0;\n\
+}\n\
+#lastlong-options > input {\n\
+ padding: 0;\n\
+ border: 0 !important;\n\
+ text-align: center;\n\
+ background: transparent;\n\
+ display: block;\n\
+ font-size: 12px;\n\
+ height: 12px;\n\
+ width: 30px;\n\
+ margin: 1px 0;\n\
+}\n\
+.summary {\n\
+ text-decoration: none;\n\
+}\n\
+/* Catalog */\n\
+:root.catalog-mode .board {\n\
+ text-align: center;\n\
+}\n\
+.catalog-thread {\n\
+ display: inline-block;\n\
+ -moz-box-sizing: border-box;\n\
+ box-sizing: border-box;\n\
+ border: 1px solid transparent;\n\
+ word-wrap: break-word;\n\
+ vertical-align: top;\n\
+ position: relative;\n\
+}\n\
+/* overrides 4chan CSS on div.thread */\n\
+.catalog-thread.catalog-thread {\n\
+ margin: 2px;\n\
+}\n\
+.catalog-small > .catalog-thread {\n\
+ width: 165px;\n\
+ height: 320px;\n\
+}\n\
+.catalog-large > .catalog-thread {\n\
+ width: 270px;\n\
+ height: 410px;\n\
+}\n\
+:root.catalog-hover-expand .catalog-thread:hover {\n\
+ z-index: 1;\n\
+}\n\
+.catalog-container {\n\
+ position: absolute;\n\
+ top: -4px;\n\
+ left: 0;\n\
+ right: 0;\n\
+ bottom: 0;\n\
+}\n\
+.catalog-container:not(:hover),\n\
+:root:not(.catalog-hover-expand) .catalog-container {\n\
+ overflow: hidden;\n\
+}\n\
+.catalog-post {\n\
+ position: absolute;\n\
+ top: 4px;\n\
+ left: 0;\n\
+ right: 0;\n\
+ border: 1px solid transparent;\n\
+ padding-top: 20px;\n\
+}\n\
+/* overrides inline CSS from Index.cb.hoverAdjust */\n\
+:root:not(.catalog-hover-expand) .catalog-post {\n\
+ left: 0 !important;\n\
+ right: 0 !important;\n\
+}\n\
+/* overrides 4chan CSS on div.post */\n\
+.catalog-post.catalog-post {\n\
+ margin: -21px -1px -1px;\n\
+ overflow: visible;\n\
+}\n\
+.catalog-thread.noFile > * > .catalog-post {\n\
+ margin-top: -7px;\n\
+ padding-top: 6px;\n\
+}\n\
+:root.catalog-hover-expand .catalog-container:hover > .catalog-post {\n\
+ margin-left: -61px;\n\
+ margin-right: -61px;\n\
+}\n\
+:root.catalog-hover-expand .catalog-container:hover > * > :not(.catalog-replies) {\n\
+ padding-left: 2px;\n\
+ padding-right: 2px;\n\
+}\n\
+.catalog-link {\n\
+ display: block;\n\
+ position: relative;\n\
+}\n\
+.catalog-thumb {\n\
+ border-radius: 2px;\n\
+ box-shadow: 0 0 5px rgba(0, 0, 0, .25);\n\
+ vertical-align: top;\n\
+}\n\
+.catalog-thumb.spoiler-file {\n\
+ width: 100px;\n\
+ height: 100px;\n\
+}\n\
+.catalog-thumb.deleted-file {\n\
+ width: 127px;\n\
+ height: 13px;\n\
+ padding: 20px 11px;\n\
+}\n\
+.catalog-thumb.no-file {\n\
+ width: 77px;\n\
+ height: 13px;\n\
+ padding: 20px 36px;\n\
+}\n\
+.catalog-icons > img,\n\
+.catalog-stats > .menu-button {\n\
+ width: 1em;\n\
+ height: 1em;\n\
+ margin: 0;\n\
+ vertical-align: text-top;\n\
+ padding-left: 2px;\n\
+}\n\
+.catalog-stats > .menu-button {\n\
+ font-weight: normal;\n\
+}\n\
+.catalog-stats > .menu-button > i::before {\n\
+ line-height: 11px;\n\
+}\n\
+.catalog-stats {\n\
+ font-size: 10px;\n\
+ font-weight: 700;\n\
+ padding-top: 2px;\n\
+}\n\
+.catalog-stats > [title] {\n\
+ cursor: help;\n\
+}\n\
+.catalog-post > .postMessage {\n\
+ margin: 0;\n\
+ padding-bottom: .3em;\n\
+}\n\
+.catalog-container:not(:hover) > * > .file,\n\
+.catalog-container:not(:hover) > * > .postInfo > :not(.subject),\n\
+.catalog-container:not(:hover) > * > .catalog-replies,\n\
+.catalog-container:not(:hover) .extra-linebreak,\n\
+.catalog-container:not(:hover) .abbr,\n\
+:root:not(.catalog-hover-expand) .catalog-container > * > .file,\n\
+:root:not(.catalog-hover-expand) .catalog-container > * > .postInfo > :not(.subject),\n\
+:root:not(.catalog-hover-expand) .catalog-container > * > .catalog-replies,\n\
+:root:not(.catalog-hover-expand) .catalog-container .extra-linebreak,\n\
+:root:not(.catalog-hover-expand) .catalog-container .abbr,\n\
+.catalog-thread > .catalog-container > :not(.catalog-post),\n\
+.catalog-post > .file > :not(.fileText),\n\
+.catalog-post > * > .fileText > :not(:first-child),\n\
+.catalog-post > .postInfo > :not(.subject):not(.nameBlock):not(.dateTime),\n\
+.catalog-post > .postInfo > .nameBlock > .contact-links,\n\
+.catalog-post > * > * > .posteruid,\n\
+.catalog-post > * > * > .postJumper,\n\
+:root.bottom-backlinks .catalog-post > .container,\n\
+.post:not(.catalog-post) > .catalog-link,\n\
+.post:not(.catalog-post) > .catalog-stats,\n\
+.post:not(.catalog-post) > .catalog-replies {\n\
+ display: none;\n\
+}\n\
+.catalog-post > .file {\n\
+ position: absolute;\n\
+ left: 0;\n\
+ right: 0;\n\
+ top: 0;\n\
+ min-height: 20px;\n\
+ background-color: inherit;\n\
+}\n\
+.catalog-post > * > .fileText {\n\
+ position: relative;\n\
+ padding: 2px;\n\
+ background-color: inherit;\n\
+}\n\
+.catalog-small .catalog-post > * .fileText {\n\
+ font-size: 10px;\n\
+}\n\
+.catalog-post > * > .fileText:not(:hover) {\n\
+ white-space: nowrap;\n\
+ overflow: hidden;\n\
+ text-overflow: ellipsis;\n\
+}\n\
+.catalog-post > * > .fileText:hover {\n\
+ z-index: 1;\n\
+}\n\
+/* overrides 4chan CSS on div.post div.postInfo */\n\
+.catalog-post > .postInfo.postInfo {\n\
+ width: auto;\n\
+}\n\
+.catalog-post > * > .subject {\n\
+ display: block;\n\
+}\n\
+.catalog-post > * > .dateTime {\n\
+ display: inline-block;\n\
+ font-style: italic;\n\
+}\n\
+:root.catalog-hover-expand .catalog-container:hover > * > * > .nameBlock,\n\
+:root.catalog-hover-expand .catalog-container:hover > * > * > .dateTime,\n\
+:root.catalog-hover-expand .catalog-container:hover > * > .postMessage:not(:empty) {\n\
+ padding-top: .3em;\n\
+}\n\
+.catalog-post .extra-linebreak {\n\
+ content: ''; /* makes this work in Blink/WebKit */\n\
+ display: block;\n\
+ margin-top: .3em;\n\
+}\n\
+.catalog-reply {\n\
+ text-align: left;\n\
+ white-space: nowrap;\n\
+ border-top: 1px solid transparent;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-flex-direction: row;\n\
+ flex-direction: row;\n\
+ -webkit-align-items: stretch;\n\
+ align-items: stretch;\n\
+}\n\
+.catalog-reply > * {\n\
+ padding: 3px;\n\
+ overflow: hidden;\n\
+ -webkit-flex: none;\n\
+ flex: none;\n\
+}\n\
+.catalog-reply > span {\n\
+ font-style: italic;\n\
+ font-weight: bold;\n\
+}\n\
+.catalog-reply-excerpt {\n\
+ -webkit-flex: 1 1 auto;\n\
+ flex: 1 1 auto;\n\
+}\n\
+.catalog-post .prettyprinted {\n\
+ max-width: 100%;\n\
+ -moz-box-sizing: border-box;\n\
+ box-sizing: border-box;\n\
+}\n\
+.catalog-post .MathJax_Display {\n\
+ text-align: center !important;\n\
+}\n\
+.catalog-container:not(:hover) .exif,\n\
+:root:not(.catalog-hover-expand) .catalog-container .exif {\n\
+ display: none !important;\n\
+}\n\
+.catalog-post > * > .exif {\n\
+ border-collapse: collapse;\n\
+}\n\
+:root.catalog-hover-expand .catalog-container:hover .exif[style*=\"display: block;\"] {\n\
+ display: inline-block !important;\n\
+}\n\
+.catalog-post > * > .exif,\n\
+.catalog-post > * > .exif > tbody {\n\
+ background-color: inherit;\n\
+}\n\
+.catalog-post > * > .exif,\n\
+.catalog-post > * > .exif td {\n\
+ min-width: 0;\n\
+}\n\
+.catalog-post > * > .exif td {\n\
+ padding-top: 1px;\n\
+}\n\
+:root.hats-enabled .catalog-thread::after {\n\
+ content: '';\n\
+ pointer-events: none;\n\
+ position: absolute;\n\
+ background-size: contain;\n\
+}\n\
+:root.hats-enabled .catalog-small > .catalog-thread::after {\n\
+ left: -8px;\n\
+ top: -59px;\n\
+ width: 96px;\n\
+ height: 96px;\n\
+}\n\
+:root.hats-enabled:not(.werkTyme) .catalog-small > .catalog-thread:not(.noFile)::after {\n\
+ left: calc(67px - .3px * var(--tn-w));\n\
+}\n\
+:root.hats-enabled .catalog-large > .catalog-thread::after {\n\
+ left: -15px;\n\
+ top: -98px;\n\
+ width: 160px;\n\
+ height: 160px;\n\
+}\n\
+:root.hats-enabled:not(.werkTyme) .catalog-large > .catalog-thread:not(.noFile)::after {\n\
+ left: calc(110px - .5px * var(--tn-w));\n\
+}\n\
+/* Copy Text Link's textarea element */\n\
+textarea.copy-text-element {\n\
+ height: 0;\n\
+ width: 0;\n\
+ position: absolute;\n\
+ top: -10000px;\n\
+}\n\
+/* Announcement Hiding */\n\
+:root.hide-announcement $site$psa {\n\
+ display: none;\n\
+}\n\
+.hide-announcement-button {\n\
+ opacity: 0.4;\n\
+ float: left;\n\
+}\n\
+/* Unread */\n\
+.unread-line {\n\
+ margin: 0;\n\
+ border-color: rgb(255,0,0);\n\
+}\n\
+.unread-line + br {\n\
+ display: none;\n\
+}\n\
+.unread-mark-read {\n\
+ float: right;\n\
+ clear: both;\n\
+ width: 100%;\n\
+ text-align: right;\n\
+}\n\
+:not(.unread-thread) > .unread-mark-read {\n\
+ display: none;\n\
+}\n\
+/* Thread Updater */\n\
+#updater {\n\
+ background: none;\n\
+ border: none;\n\
+ box-shadow: none;\n\
+}\n\
+#updater > .move {\n\
+ position: absolute;\n\
+ top: -5px;\n\
+ bottom: -5px;\n\
+ left: -5px;\n\
+ right: -5px;\n\
+ z-index: -1;\n\
+}\n\
+#updater > div:last-child {\n\
+ text-align: center;\n\
+}\n\
+#updater input[type=\"number\"] {\n\
+ width: 4em;\n\
+}\n\
+:root.float #updater {\n\
+ padding: 0px 3px;\n\
+}\n\
+:root:not(.float).shortcut-icons #updater {\n\
+ display: inline-block;\n\
+ min-width: 12pt;\n\
+ text-align: right;\n\
+}\n\
+.new {\n\
+ color: limegreen;\n\
+}\n\
+#update-status:not(.empty) + #update-timer:not(.empty):not(.loading) {\n\
+ margin-left: 5px;\n\
+}\n\
+#update-timer {\n\
+ cursor: pointer;\n\
+}\n\
+/* Thread Watcher */\n\
+#thread-watcher {\n\
+ position: absolute;\n\
+}\n\
+#thread-watcher {\n\
+ padding-bottom: 3px;\n\
+ padding-left: 3px;\n\
+ white-space: nowrap;\n\
+ min-width: 146px;\n\
+}\n\
+#watched-threads {\n\
+ overflow-x: hidden;\n\
+ overflow-y: auto;\n\
+}\n\
+#thread-watcher .refresh {\n\
+ padding: 0px 3px;\n\
+}\n\
+:root.fixed-watcher #thread-watcher {\n\
+ position: fixed;\n\
+}\n\
+:root.fixed-watcher #watched-threads {\n\
+ /* XXX https://code.google.com/p/chromium/issues/detail?id=168840, https://bugs.webkit.org/show_bug.cgi?id=94158 */\n\
+ max-height: 85vh;\n\
+ max-height: calc(100vh - 75px);\n\
+}\n\
+:root:not(.fixed-watcher) #watched-threads:not(:hover) {\n\
+ max-height: 210px;\n\
+ overflow-y: hidden;\n\
+}\n\
+#thread-watcher > .move {\n\
+ padding-top: 3px;\n\
+}\n\
+#watched-threads > div {\n\
+ padding-left: 3px;\n\
+ padding-right: 3px;\n\
+}\n\
+#watched-threads .watcher-link {\n\
+ max-width: 250px;\n\
+ display: -webkit-inline-flex;\n\
+ display: inline-flex;\n\
+ -webkit-flex-direction: row;\n\
+ flex-direction: row;\n\
+}\n\
+#watched-threads .watcher-page,\n\
+#watched-threads .watcher-unread {\n\
+ -webkit-flex: 0 0 auto;\n\
+ flex: 0 0 auto;\n\
+ margin-right: 2px;\n\
+}\n\
+#watched-threads .watcher-title {\n\
+ overflow: hidden;\n\
+ text-overflow: ellipsis;\n\
+ -webkit-flex: 0 1 auto;\n\
+ flex: 0 1 auto;\n\
+}\n\
+#watched-threads .watcher-title:not(:first-child) {\n\
+ margin-left: 2px;\n\
+}\n\
+.replies-quoting-you > a, #watcher-link.replies-quoting-you, .last-page > a > .watcher-page {\n\
+ color: #F00;\n\
+}\n\
+#thread-watcher a {\n\
+ text-decoration: none;\n\
+}\n\
+#thread-watcher .move > .close {\n\
+ position: absolute;\n\
+ right: 0px;\n\
+ top: 0px;\n\
+ padding: 0px 4px;\n\
+}\n\
+.watch-thread-link {\n\
+ padding-top: 18px;\n\
+ width: 18px;\n\
+ height: 0px;\n\
+ display: inline-block;\n\
+ background-repeat: no-repeat;\n\
+ opacity: 0.2;\n\
+ position: relative;\n\
+ top: 1px;\n\
+ background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n\
+}\n\
+.watch-thread-link.watched {\n\
+ opacity: 1;\n\
+}\n\
+/* Thread Stats */\n\
+#thread-stats {\n\
+ background: none;\n\
+ border: none;\n\
+ box-shadow: none;\n\
+}\n\
+:root.float #thread-stats > .move > :not(#page-count) {\n\
+ pointer-events: none;\n\
+}\n\
+:root.float #thread-stats {\n\
+ padding: 0px 3px;\n\
+}\n\
+#page-count {\n\
+ cursor: pointer;\n\
+}\n\
+/* Quote */\n\
+.hashlink::before {\n\
+ content: ' ';\n\
+ visibility: hidden;\n\
+}\n\
+.inline + .hashlink {\n\
+ display: none !important;\n\
+}\n\
+:root.resurrect-quotes .deadlink {\n\
+ text-decoration: none !important;\n\
+}\n\
+.catalog-post .qmark-ct {\n\
+ display: none;\n\
+}\n\
+.backlink.deadlink:not(.forwardlink),\n\
+.quotelink.deadlink:not(.forwardlink) {\n\
+ text-decoration: underline !important;\n\
+}\n\
+:root:not(.catalog-mode) .inlined {\n\
+ opacity: .5;\n\
+}\n\
+#qp input, .forwarded {\n\
+ display: none;\n\
+}\n\
+.quotelink.forwardlink,\n\
+.backlink.forwardlink {\n\
+ text-decoration: none;\n\
+ border-bottom: 1px dashed;\n\
+}\n\
+.filtered {\n\
+ text-decoration: underline line-through;\n\
+}\n\
+:root.hide-backlinks .backlink.filtered,\n\
+:root.hide-backlinks .backlink.filtered + .hashlink.filtered {\n\
+ display: none;\n\
+}\n\
+.postNum + .container::before {\n\
+ content: \" \";\n\
+}\n\
+:root.bottom-backlinks .container {\n\
+ display: block;\n\
+ clear: both;\n\
+ margin: 0 4px;\n\
+}\n\
+:root.bottom-backlinks .backlink {\n\
+ font-size: 90%;\n\
+}\n\
+.inline {\n\
+ border: 1px solid;\n\
+ display: table;\n\
+ margin: 2px 0;\n\
+}\n\
+.container ~ .inline {\n\
+ margin-left: 20px;\n\
+}\n\
+:root.catalog-mode .inline {\n\
+ display: none;\n\
+}\n\
+.inline .post {\n\
+ border: 0 !important;\n\
+ background-color: transparent !important;\n\
+ display: table !important;\n\
+ margin: 0 !important;\n\
+ padding: 1px 2px !important;\n\
+}\n\
+#qp > .opContainer::after {\n\
+ content: '';\n\
+ clear: both;\n\
+ display: table;\n\
+}\n\
+#qp .post {\n\
+ border: none;\n\
+ margin: 0;\n\
+ padding: 2px 2px 5px;\n\
+}\n\
+#qp img {\n\
+ max-height: 80vh;\n\
+ max-width: 50vw;\n\
+}\n\
+/* Quote Threading */\n\
+.threadContainer {\n\
+ margin-left: 20px;\n\
+ border-left: 1px solid rgba(128,128,128,.3);\n\
+}\n\
+.threadOP {\n\
+ clear: both;\n\
+}\n\
+/* File */\n\
+.fileText-original,\n\
+.fnswitch:hover > .fntrunc,\n\
+.fnswitch:not(:hover) > .fnfull,\n\
+.expanded-image > .post > .file > .fileThumb > video[data-md5],\n\
+.expanded-image > .post > .file > .fileThumb > img[data-md5] {\n\
+ display: none;\n\
+}\n\
+.full-image[data-file-i-d] {\n\
+ display: none;\n\
+ cursor: pointer;\n\
+}\n\
+.expanded-image > .post > .file > .fileThumb > .full-image {\n\
+ display: inline;\n\
+}\n\
+.expanded-image {\n\
+ clear: left;\n\
+}\n\
+.expanding {\n\
+ opacity: .5;\n\
+}\n\
+:root.fit-height .full-image {\n\
+ max-height: 100vh;\n\
+}\n\
+:root.fit-height.fixed .full-image {\n\
+ /* XXX https://code.google.com/p/chromium/issues/detail?id=168840, https://bugs.webkit.org/show_bug.cgi?id=94158 */\n\
+ max-height: 93vh;\n\
+ max-height: calc(100vh - 35px);\n\
+}\n\
+:root.fit-width .full-image {\n\
+ max-width: 100%;\n\
+}\n\
+:root.ua-gecko.fit-width .full-image {\n\
+ width: 100%;\n\
+}\n\
+.fileThumb > .warning {\n\
+ clear: both;\n\
+}\n\
+#ihover {\n\
+ pointer-events: none;\n\
+ /* XXX https://code.google.com/p/chromium/issues/detail?id=168840, https://bugs.webkit.org/show_bug.cgi?id=94158 */\n\
+ max-height: 95vh;\n\
+ max-height: calc(100vh - 25px);\n\
+ max-width: 100vw;\n\
+}\n\
+/* WEBM Metadata */\n\
+.webm-title > a::before {\n\
+ content: \"title\";\n\
+ text-decoration: underline;\n\
+}\n\
+.webm-title.loading > a::after {\n\
+ content: \"...\";\n\
+}\n\
+.webm-title.error > a:hover::before,\n\
+.webm-title.error > a:focus::before {\n\
+ content: \"error\";\n\
+ text-decoration: none;\n\
+}\n\
+.webm-title > span {\n\
+ cursor: text;\n\
+}\n\
+.webm-title.not-found > span::before {\n\
+ content: \"not found\";\n\
+}\n\
+.webm-title:not(:hover):not(:focus) > span,\n\
+.webm-title:hover > span + a,\n\
+.webm-title:focus > span + a {\n\
+ display: none;\n\
+}\n\
+/* Volume control */\n\
+input[name=\"Default Volume\"] {\n\
+ width: 4em;\n\
+ height: 1ex;\n\
+ vertical-align: middle;\n\
+ margin: 0px;\n\
+}\n\
+/* Fappe and Werk Tyme */\n\
+:root.fappeTyme $site$replyOriginal.noFile,\n\
+:root.fappeTyme $site$replyOriginal.noFile + br {\n\
+ display: none;\n\
+}\n\
+:root.werkTyme $site$thumbLink,\n\
+:root.werkTyme $site$file$thumb,\n\
+:root.werkTyme .catalog-thumb:not(.deleted-file):not(.no-file),\n\
+:root:not(.werkTyme) .werkTyme-filename {\n\
+ display: none;\n\
+}\n\
+.werkTyme-filename {\n\
+ font-weight: bold;\n\
+ font-size: 110%;\n\
+}\n\
+:root.werkTyme .catalog-link {\n\
+ box-shadow: 0 0 5px rgba(0, 0, 0, .25);\n\
+ padding: 8px;\n\
+ text-align: center;\n\
+}\n\
+:root.werkTyme .catalog-thumb {\n\
+ box-shadow: none;\n\
+ padding: 0;\n\
+ vertical-align: middle;\n\
+}\n\
+.indicator {\n\
+ background: rgba(255,0,0,0.8);\n\
+ font-weight: bold;\n\
+ display: inline-block;\n\
+ min-width: 9px;\n\
+ padding: 0px 2px;\n\
+ margin: 0 1px;\n\
+ text-align: center;\n\
+ color: white;\n\
+ border-radius: 2px;\n\
+ cursor: pointer;\n\
+}\n\
+:root:not(.fappeTyme) #shortcut-fappe,\n\
+:root:not(.werkTyme) #shortcut-werk {\n\
+ display: none;\n\
+}\n\
+/* Index/Reply Navigation */\n\
+#navlinks {\n\
+ font-size: 16px;\n\
+ top: 25px;\n\
+ right: 10px;\n\
+}\n\
+:root.catalog-mode #navlinks {\n\
+ display: none;\n\
+}\n\
+/* Highlighting */\n\
+.qphl {\n\
+ outline: 2px solid rgba(216, 94, 49, .8);\n\
+}\n\
+:root.highlight-you .quotesYou$site$highlightable$op,\n\
+:root.highlight-you .quotesYou$site$highlightable$reply {\n\
+ border-left: 3px solid rgba(221, 0, 0, .8);\n\
+}\n\
+:root.highlight-own .yourPost$site$highlightable$op,\n\
+:root.highlight-own .yourPost$site$highlightable$reply {\n\
+ border-left: 3px dashed rgba(221, 0, 0, .8);\n\
+}\n\
+.filter-highlight$site$highlightable$op,\n\
+.filter-highlight$site$highlightable$reply {\n\
+ box-shadow: inset 5px 0 rgba(221, 0, 0, .5);\n\
+}\n\
+:root.highlight-own .yourPost > $site$sideArrows,\n\
+:root.highlight-you .quotesYou > $site$sideArrows,\n\
+.filter-highlight > $site$sideArrows {\n\
+ color: rgba(221, 0, 0, .8);\n\
+}\n\
+:root.highlight-own .yourPost$site$highlightable$op::after,\n\
+:root.highlight-you .quotesYou$site$highlightable$op::after,\n\
+.filter-highlight$site$highlightable$op::after {\n\
+ content: \"\";\n\
+ display: block;\n\
+ clear: both;\n\
+}\n\
+:root:not(.werkTyme) .catalog-thread.filter-highlight .catalog-thumb,\n\
+:root.werkTyme .catalog-thread.filter-highlight:not(:hover),\n\
+:root.werkTyme:not(.catalog-hover-expand) .catalog-thread.filter-highlight,\n\
+:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post,\n\
+:root.catalog $site$catalog$thread.filter-highlight$site$highlightable$catalog {\n\
+ box-shadow: 0 0 3px 3px rgba(255, 0, 0, .5);\n\
+}\n\
+:root:not(.werkTyme) .catalog-thread.watched .catalog-thumb,\n\
+:root:root.werkTyme .catalog-thread.watched:not(:hover),\n\
+:root:root.werkTyme:not(.catalog-hover-expand) .catalog-thread.watched,\n\
+:root.werkTyme.catalog-hover-expand .catalog-thread.watched > .catalog-container:hover > .catalog-post {\n\
+ border: 2px solid rgba(255, 0, 0, .75);\n\
+}\n\
+/* Spoiler text */\n\
+:root.reveal-spoilers $site$spoiler,\n\
+:root.reveal-spoilers $site$spoiler > a {\n\
+ color: white !important;\n\
+}\n\
+:root.reveal-spoilers .removed-spoiler::before {\n\
+ content: \"[spoiler]\";\n\
+}\n\
+:root.reveal-spoilers .removed-spoiler::after {\n\
+ content: \"[/spoiler]\";\n\
+}\n\
+/* Thread & Reply Hiding */\n\
+.hide-thread-button,\n\
+.hide-reply-button {\n\
+ float: left;\n\
+ margin-right: 4px;\n\
+ padding: 2px;\n\
+}\n\
+$site$infoRoot a.hide-reply-button {\n\
+ margin-right: 6px;\n\
+ padding: 0;\n\
+}\n\
+.replacedSideArrows {\n\
+ float: left;\n\
+}\n\
+.hide-thread-button:not(:hover),\n\
+.hide-reply-button:not(:hover) {\n\
+ opacity: 0.4;\n\
+}\n\
+.threadContainer .hide-reply-button {\n\
+ margin-left: 2px !important;\n\
+ position: relative;\n\
+ left: 1px;\n\
+}\n\
+.hide-thread-button {\n\
+ margin-top: -1px;\n\
+ width: 11px;\n\
+}\n\
+.stub ~ :not(.threadDivider) {\n\
+ display: none !important;\n\
+}\n\
+.stub input {\n\
+ display: inline-block;\n\
+}\n\
+$site$thread[hidden] + hr {\n\
+ display: none;\n\
+}\n\
+:root.reply-hide $site$sideArrows {\n\
+ display: none;\n\
+}\n\
+:root.sw-yotsuba.thread-hide .party-hat {\n\
+ left: 19px;\n\
+}\n\
+/* Anonymize */\n\
+:root.anonymize $site$info$name,\n\
+:root.sw-yotsuba.anonymize .post-author:not([class*=capcode]) {\n\
+ font-size: 0;\n\
+}\n\
+:root.anonymize $site$info$tripcode,\n\
+:root.sw-yotsuba.anonymize .n-pu {\n\
+ display: none;\n\
+}\n\
+:root.anonymize $site$info$name::before,\n\
+:root.sw-yotsuba.anonymize .post-author:not([class*=capcode])::before {\n\
+ content: \"Anonymous\";\n\
+ font-size: 10pt;\n\
+}\n\
+:root.sw-yotsuba.anonymize .flashListing .name::before,\n\
+:root.sw-yotsuba.anonymize .post-last > .post-author:not([class*=capcode])::before {\n\
+ font-size: 9pt;\n\
+}\n\
+/* QR */\n\
+:root.hide-original-post-form #togglePostFormLink,\n\
+#qr.autohide:not(.focus):not(:hover):not(:active) > form,\n\
+:root.thread-view #qr:not(.show-new-thread-option) select[data-name=\"thread\"],\n\
+#file-n-submit:not(.has-file) #qr-filerm {\n\
+ display: none;\n\
+}\n\
+:root.hide-original-post-form #postForm {\n\
+ display: none !important;\n\
+}\n\
+#qr select,\n\
+#qr-filename-container > a,\n\
+.remove,\n\
+.captcha-img {\n\
+ cursor: pointer;\n\
+}\n\
+#qr {\n\
+ position: fixed;\n\
+ padding: 1px;\n\
+ border: 1px solid transparent;\n\
+ min-width: 300px;\n\
+ border-radius: 3px 3px 0 0;\n\
+}\n\
+#qr > form {\n\
+ /* XXX https://code.google.com/p/chromium/issues/detail?id=168840, https://bugs.webkit.org/show_bug.cgi?id=94158 */\n\
+ max-height: 85vh;\n\
+ max-height: calc(100vh - 75px);\n\
+ overflow-y: auto;\n\
+ overflow-x: hidden;\n\
+}\n\
+#qrtab {\n\
+ border-radius: 3px 3px 0 0;\n\
+}\n\
+#qrtab {\n\
+ margin-bottom: 1px;\n\
+}\n\
+#qr .close {\n\
+ float: right;\n\
+ padding: 0 3px;\n\
+}\n\
+.qr-link-container {\n\
+ text-align: center;\n\
+ margin: 16px 0;\n\
+}\n\
+.qr-link-container-bottom {\n\
+ width: 200px;\n\
+ position: absolute;\n\
+ left: -100px;\n\
+ margin-left: 50%;\n\
+ text-align: center;\n\
+}\n\
+.qr-link {\n\
+ border-radius: 3px;\n\
+ padding: 6px 10px 5px;\n\
+ font-weight: bold;\n\
+ vertical-align: middle;\n\
+ border-style: solid;\n\
+ border-width: 1px;\n\
+ font-size: 10pt;\n\
+}\n\
+.qr-link-container + #togglePostFormLink {\n\
+ font-size: 10pt;\n\
+ font-weight: normal;\n\
+ margin: -8px 0 3.5px;\n\
+}\n\
+.persona {\n\
+ width: 100%;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-flex-direction: row;\n\
+ flex-direction: row;\n\
+}\n\
+.persona .field {\n\
+ -webkit-flex: 1;\n\
+ flex: 1;\n\
+ width: 0;\n\
+}\n\
+#qr.forced-anon input[data-name=\"name\"]:not(.force-show),\n\
+#qr.forced-anon input[data-name=\"sub\"]:not(.force-show),\n\
+#qr.reply-to-thread input[data-name=\"sub\"]:not(.force-show),\n\
+body:not(.board_f) #qr select[name=\"filetag\"],\n\
+#qr.reply-to-thread select[name=\"filetag\"],\n\
+#qr:not(.has-sjis) #sjis-toggle,\n\
+#qr:not(.has-math) #tex-preview-button,\n\
+#qr.tex-preview .textarea > :not(#tex-preview),\n\
+#qr:not(.tex-preview) #tex-preview {\n\
+ display: none;\n\
+}\n\
+.persona button {\n\
+ -webkit-flex: 0 0 23px;\n\
+ flex: 0 0 23px;\n\
+ -webkit-align-self: stretch;\n\
+ align-self: stretch;\n\
+ border: 1px solid #BBB;\n\
+ padding: 0;\n\
+ background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat;\n\
+ color: #000;\n\
+}\n\
+#qr.sjis-preview #sjis-toggle, #qr.tex-preview #tex-preview-button {\n\
+ background: #DCDCDC;\n\
+}\n\
+#sjis-toggle, #qr.sjis-preview textarea.field {\n\
+ font-family: \"IPAMonaPGothic\",\"Mona\",\"MS PGothic\",monospace;\n\
+ font-size: 16px;\n\
+ line-height: 17px;\n\
+}\n\
+#tex-preview-button {\n\
+ font-size: 10px;\n\
+}\n\
+#tex-preview {\n\
+ white-space: pre-line;\n\
+}\n\
+#qr textarea.field {\n\
+ height: 14.8em;\n\
+ min-height: 9em;\n\
+}\n\
+#qr.has-captcha textarea.field {\n\
+ height: 9em;\n\
+}\n\
+input.field.tripped:not(:hover):not(:focus) {\n\
+ color: transparent !important;\n\
+ text-shadow: none !important;\n\
+}\n\
+#qr textarea {\n\
+ min-width: 300px;\n\
+ resize: both;\n\
+}\n\
+.field {\n\
+ -moz-box-sizing: border-box;\n\
+ box-sizing: border-box;\n\
+ margin: 0px;\n\
+ padding: 2px 4px 3px;\n\
+}\n\
+#qr label input[type=\"checkbox\"] {\n\
+ position: relative;\n\
+ top: 2px;\n\
+}\n\
+/* Recaptcha v2 */\n\
+#qr .captcha-root {\n\
+ position: relative;\n\
+}\n\
+#qr .captcha-container > div {\n\
+ margin: auto;\n\
+ width: 304px;\n\
+}\n\
+/* XXX scrollable with scroll bar hidden; prevents scroll on space press */\n\
+:root.ua-blink #qr .captcha-container > div,\n\
+:root.ua-edge #qr .captcha-container > div {\n\
+ overflow: hidden;\n\
+}\n\
+:root.ua-blink #qr .captcha-container > div > div:first-of-type,\n\
+:root.ua-edge #qr .captcha-container > div > div:first-of-type {\n\
+ overflow-y: scroll;\n\
+ overflow-x: hidden;\n\
+ padding-right: 30px;\n\
+ height: 99%;\n\
+ width: 100%;\n\
+}\n\
+#qr .captcha-counter {\n\
+ display: block;\n\
+ width: 100%;\n\
+ text-align: center;\n\
+ pointer-events: none;\n\
+}\n\
+#qr.captcha-open .captcha-counter {\n\
+ position: absolute;\n\
+ bottom: 3px;\n\
+}\n\
+#qr .captcha-counter > a {\n\
+ pointer-events: auto;\n\
+ display: inline-block; /* XXX https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8851747/ */\n\
+}\n\
+#qr:not(.captcha-open) .captcha-counter > a {\n\
+ display: block;\n\
+ width: 100%;\n\
+}\n\
+#qr.captcha-v2 #qr-captcha-iframe {\n\
+ width: 302px;\n\
+ height: 423px;\n\
+ border: 0;\n\
+ display: block;\n\
+ margin: auto;\n\
+}\n\
+.goog-bubble-content {\n\
+ max-width: 100vw;\n\
+ max-height: 100vh;\n\
+ overflow: auto;\n\
+}\n\
+.goog-bubble-content iframe {\n\
+ position: static !important;\n\
+}\n\
+/* File Input, Submit Button, Oekaki */\n\
+#file-n-submit, #qr .oekaki {\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-align-items: stretch;\n\
+ align-items: stretch;\n\
+ height: 25px;\n\
+ margin-top: 1px;\n\
+}\n\
+#file-n-submit > input, #qr-draw-button {\n\
+ background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat;\n\
+ border: 1px solid #BBB;\n\
+ border-radius: 2px;\n\
+ height: 100%;\n\
+}\n\
+#qr-file-button, #qr-draw-button {\n\
+ width: 15%;\n\
+}\n\
+#file-n-submit input[type=\"submit\"] {\n\
+ width: 25%;\n\
+}\n\
+#qr-filename-container {\n\
+ -webkit-flex: 1 1 auto;\n\
+ flex: 1 1 auto;\n\
+ width: 0;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-align-items: center;\n\
+ align-items: center;\n\
+ position: relative;\n\
+ padding: 1px;\n\
+}\n\
+input#qr-filename {\n\
+ border: none !important;\n\
+ background: none !important;\n\
+ outline: none;\n\
+}\n\
+#qr-filename,\n\
+.has-file #qr-no-file {\n\
+ display: none;\n\
+}\n\
+#qr-no-file,\n\
+.has-file #qr-filename {\n\
+ -webkit-flex: 1 1 auto;\n\
+ flex: 1 1 auto;\n\
+ width: 0px; /* XXX Fixes filename not shrinking to allow space for buttons in Edge */\n\
+ display: inline-block;\n\
+ padding: 0;\n\
+ padding-left: 3px;\n\
+ overflow: hidden;\n\
+ text-overflow: ellipsis;\n\
+ white-space: nowrap;\n\
+}\n\
+#qr-no-file {\n\
+ color: #AAA;\n\
+}\n\
+#qr .oekaki.has-file {\n\
+ display: none;\n\
+}\n\
+#qr .oekaki > label {\n\
+ -webkit-flex: 1 1 auto;\n\
+ flex: 1 1 auto;\n\
+ width: 0;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-align-items: center;\n\
+ align-items: center;\n\
+ height: 100%;\n\
+}\n\
+#qr .oekaki > label > span {\n\
+ margin: 0 3px;\n\
+}\n\
+#qr .oekaki > label > input {\n\
+ -webkit-flex: 1 1 auto;\n\
+ flex: 1 1 auto;\n\
+ width: 0;\n\
+ height: 100%;\n\
+}\n\
+#qr .oekaki-bg {\n\
+ position: relative;\n\
+ display: inline-block;\n\
+ height: 100%;\n\
+ width: 10%;\n\
+ margin-left: 3px;\n\
+}\n\
+#qr .oekaki-bg > * {\n\
+ position: absolute;\n\
+ top: 0;\n\
+ left: 0;\n\
+ margin: 0;\n\
+}\n\
+#qr .oekaki-bg > :not([name=\"oekaki-bgcolor\"]) {\n\
+ z-index: 1;\n\
+}\n\
+#qr [name=\"oekaki-bgcolor\"] {\n\
+ height: 100%;\n\
+ width: 100%;\n\
+ border: none;\n\
+ padding: 0;\n\
+}\n\
+#qr [name=\"oekaki-bg\"]:not(:checked) ~ [name=\"oekaki-bgcolor\"] {\n\
+ visibility: hidden;\n\
+}\n\
+#qr input[type=\"file\"] {\n\
+ visibility: hidden;\n\
+ position: absolute;\n\
+}\n\
+/* Spoiler Checkbox, QR Icons */\n\
+#qr-filename-container > label, #qr-filename-container > a {\n\
+ -webkit-flex: none;\n\
+ flex: none;\n\
+ margin: 0;\n\
+ margin-right: 3px;\n\
+}\n\
+#qr:not(.has-spoiler) #qr-spoiler-label,\n\
+#file-n-submit:not(.has-file) #qr-spoiler-label,\n\
+.has-file #paste-area,\n\
+.has-file #url-button,\n\
+#file-n-submit:not(.custom-cooldown) #custom-cooldown-button {\n\
+ display: none;\n\
+}\n\
+#qr-filename-container > label {\n\
+ position: relative;\n\
+}\n\
+#qr-filename-container input[type=\"checkbox\"] {\n\
+ margin: 0;\n\
+}\n\
+.checkbox-letter {\n\
+ font-size: 13px;\n\
+ font-weight: bold;\n\
+}\n\
+#qr-filename-container label:not(:hover) > input[type=\"checkbox\"]:not(:focus):not(:checked),\n\
+#qr-filename-container label:not(:hover) > input[type=\"checkbox\"]:not(:focus):not(:checked) ~ :not(.checkbox-letter),\n\
+#qr-filename-container label:hover > .checkbox-letter,\n\
+input[type=\"checkbox\"]:focus ~ .checkbox-letter,\n\
+input[type=\"checkbox\"]:checked ~ .checkbox-letter {\n\
+ /* not displayed but still focusable */\n\
+ position: absolute;\n\
+ opacity: 0;\n\
+ pointer-events: none;\n\
+}\n\
+.checkbox-letter, #paste-area, #url-button, #custom-cooldown-button, #dump-button {\n\
+ opacity: 0.6;\n\
+}\n\
+#paste-area {\n\
+ font-size: 0;\n\
+}\n\
+#paste-area:focus {\n\
+ opacity: 1;\n\
+}\n\
+#custom-cooldown-button.disabled {\n\
+ opacity: 0.27;\n\
+}\n\
+/* Thread and Flash Tag Select */\n\
+#qr select {\n\
+ background: white;\n\
+ border: 1px solid #CCC;\n\
+}\n\
+#qr select[data-name=\"thread\"] {\n\
+ float: right;\n\
+}\n\
+#qr > form > select {\n\
+ margin-top: 1px;\n\
+}\n\
+/* Dumping UI */\n\
+.dump #dump-list-container {\n\
+ display: block;\n\
+}\n\
+#dump-list-container {\n\
+ display: none;\n\
+ position: relative;\n\
+ overflow-y: hidden;\n\
+ margin-top: 1px;\n\
+}\n\
+#dump-list {\n\
+ overflow-x: auto;\n\
+ overflow-y: auto;\n\
+ white-space: nowrap;\n\
+ width: 248px;\n\
+ max-height: 248px;\n\
+ min-height: 90px;\n\
+ max-width: 100%;\n\
+ min-width: 100%;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-flex-wrap: wrap;\n\
+ flex-wrap: wrap;\n\
+}\n\
+#dump-list:hover {\n\
+ overflow-x: auto;\n\
+}\n\
+.qr-preview {\n\
+ -moz-box-sizing: border-box;\n\
+ box-sizing: border-box;\n\
+ counter-increment: thumbnails;\n\
+ cursor: move;\n\
+ display: inline-block;\n\
+ height: 90px;\n\
+ width: 90px;\n\
+ padding: 2px;\n\
+ opacity: .5;\n\
+ overflow: hidden;\n\
+ position: relative;\n\
+ text-shadow: 0 0 2px #000;\n\
+ -webkit-transition: opacity .25s ease-in-out, -webkit-transform .25s ease-in-out;\n\
+ transition: opacity .25s ease-in-out, transform .25s ease-in-out, -webkit-transform .25s ease-in-out;\n\
+ vertical-align: top;\n\
+ background-size: cover;\n\
+ -webkit-flex: none;\n\
+ flex: none;\n\
+}\n\
+.qr-preview:hover,\n\
+.qr-preview:focus {\n\
+ opacity: .9;\n\
+}\n\
+.qr-preview::before {\n\
+ content: counter(thumbnails);\n\
+ color: #fff;\n\
+ position: absolute;\n\
+ top: 3px;\n\
+ right: 3px;\n\
+ text-shadow: 0 0 3px #000, 0 0 8px #000;\n\
+}\n\
+.qr-preview#selected {\n\
+ opacity: 1;\n\
+}\n\
+.qr-preview.drag {\n\
+ box-shadow: 0 0 10px rgba(0,0,0,.5);\n\
+ -webkit-transform: scale(.8);\n\
+ transform: scale(.8);\n\
+}\n\
+.qr-preview.over {\n\
+ border-color: #fff;\n\
+ -webkit-transform: scale(1.1);\n\
+ transform: scale(1.1);\n\
+ opacity: 0.9;\n\
+ z-index: 10;\n\
+}\n\
+.qr-preview > span {\n\
+ color: #fff;\n\
+}\n\
+.remove {\n\
+ background: none;\n\
+ color: #e00;\n\
+ padding: 1px;\n\
+}\n\
+a:only-of-type > .remove {\n\
+ display: none;\n\
+}\n\
+.remove:hover::after {\n\
+ content: \" Remove\";\n\
+}\n\
+.qr-preview:not(.has-file) label,\n\
+#qr:not(.has-spoiler) .qr-preview-spoiler {\n\
+ display: none;\n\
+}\n\
+.qr-preview > label {\n\
+ background: rgba(0,0,0,.5);\n\
+ color: #fff;\n\
+ right: 0;\n\
+ bottom: 0;\n\
+ left: 0;\n\
+ position: absolute;\n\
+ text-align: center;\n\
+}\n\
+.qr-preview > label > input {\n\
+ margin: 0;\n\
+}\n\
+#add-post {\n\
+ cursor: pointer;\n\
+ font-size: 2em;\n\
+ position: absolute;\n\
+ bottom: 20px;\n\
+ right: 10px;\n\
+ -webkit-transform: translateY(-50%);\n\
+ transform: translateY(-50%);\n\
+}\n\
+.textarea {\n\
+ position: relative;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+}\n\
+#char-count {\n\
+ color: #000;\n\
+ background: hsla(0, 0%, 100%, .5);\n\
+ font-size: 8pt;\n\
+ position: absolute;\n\
+ bottom: 1px;\n\
+ right: 1px;\n\
+ pointer-events: none;\n\
+}\n\
+#char-count.warning {\n\
+ color: red;\n\
+}\n\
+/* Menu */\n\
+.menu-button:not(.fa-bars) {\n\
+ display: inline-block;\n\
+ position: relative;\n\
+ cursor: pointer;\n\
+}\n\
+#header-bar .menu-button i {\n\
+ border-top: 6px solid;\n\
+ border-right: 4px solid transparent;\n\
+ border-left: 4px solid transparent;\n\
+ display: inline-block;\n\
+ margin: 2px;\n\
+ vertical-align: middle;\n\
+}\n\
+.postInfo > .menu-button,\n\
+#thread-watcher .menu-button {\n\
+ width: 18px;\n\
+ height: 15px;\n\
+ text-align: center;\n\
+}\n\
+#menu {\n\
+ position: fixed;\n\
+ outline: none;\n\
+ font-weight: normal;\n\
+}\n\
+#menu, .submenu {\n\
+ border-radius: 3px;\n\
+ padding-top: 1px;\n\
+ padding-bottom: 3px;\n\
+}\n\
+.entry {\n\
+ cursor: pointer;\n\
+ display: block;\n\
+ outline: none;\n\
+ padding: 2px 10px;\n\
+ position: relative;\n\
+ text-decoration: none;\n\
+ white-space: nowrap;\n\
+ min-width: 70px;\n\
+ text-align: left;\n\
+ text-shadow: none;\n\
+ font-size: 10pt;\n\
+}\n\
+.left>.entry.has-submenu {\n\
+ padding-right: 17px !important;\n\
+}\n\
+.entry input[type=\"checkbox\"],\n\
+.entry input[type=\"radio\"] {\n\
+ margin: 0px;\n\
+ position: relative;\n\
+ top: 2px;\n\
+}\n\
+.entry input[type=\"number\"] {\n\
+ width: 4.5em;\n\
+}\n\
+.entry.has-shortcut-text {\n\
+ display: flex;\n\
+ justify-content: space-between;\n\
+ align-items: center;\n\
+}\n\
+.entry .shortcut-text {\n\
+ opacity: 0.5;\n\
+ font-size: 70%;\n\
+ margin-left: 5px;\n\
+}\n\
+.has-submenu::after {\n\
+ content: \"\";\n\
+ border-left: .5em solid;\n\
+ border-top: .3em solid transparent;\n\
+ border-bottom: .3em solid transparent;\n\
+ display: inline-block;\n\
+ margin: .3em;\n\
+ position: absolute;\n\
+ right: 3px;\n\
+}\n\
+.left .has-submenu::after {\n\
+ border-left: 0;\n\
+ border-right: .5em solid;\n\
+}\n\
+.submenu {\n\
+ display: none;\n\
+ position: absolute;\n\
+ left: 100%;\n\
+ top: -1px;\n\
+ margin-left: 0px;\n\
+ margin-top: -2px;\n\
+}\n\
+.focused > .submenu {\n\
+ display: block;\n\
+}\n\
+.imp-exp-result {\n\
+ position: absolute;\n\
+ text-align: center;\n\
+ margin: auto;\n\
+ right: 0px;\n\
+ left: 0px;\n\
+ width: 200px;\n\
+}\n\
+/* Custom Board Titles */\n\
+.boardTitle, .boardSubtitle {\n\
+ white-space: pre-line;\n\
+}\n\
+.boardTitle[contenteditable=\"true\"],\n\
+.boardSubtitle[contenteditable=\"true\"] {\n\
+ cursor: text !important;\n\
+}\n\
+/* Embedding */\n\
+.embedder:not(.embedded) > span {\n\
+ display: none;\n\
+}\n\
+#embedding {\n\
+ padding: 1px 4px 1px 4px;\n\
+ position: fixed;\n\
+}\n\
+#embedding.empty {\n\
+ display: none;\n\
+}\n\
+#embedding > div:first-child {\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+}\n\
+#embedding .move {\n\
+ -webkit-flex: 1;\n\
+ flex: 1;\n\
+}\n\
+#embedding .jump {\n\
+ margin: -1px 4px;\n\
+ text-decoration: none;\n\
+}\n\
+/* Gallery */\n\
+#a-gallery {\n\
+ position: fixed;\n\
+ top: 0;\n\
+ bottom: 0;\n\
+ left: 0;\n\
+ right: 0;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-flex-direction: row;\n\
+ flex-direction: row;\n\
+ background: rgba(0,0,0,0.7);\n\
+}\n\
+.gal-viewport {\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-align-items: stretch;\n\
+ align-items: stretch;\n\
+ -webkit-flex-direction: row;\n\
+ flex-direction: row;\n\
+ -webkit-flex: 1 1 auto;\n\
+ flex: 1 1 auto;\n\
+ overflow: hidden;\n\
+}\n\
+.gal-thumbnails {\n\
+ -webkit-flex: 0 0 150px;\n\
+ flex: 0 0 150px;\n\
+ overflow-y: auto;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-flex-direction: column;\n\
+ flex-direction: column;\n\
+ -webkit-align-items: stretch;\n\
+ align-items: stretch;\n\
+ text-align: center;\n\
+ background: rgba(0,0,0,.5);\n\
+ border-left: 1px solid #222;\n\
+}\n\
+.gal-hide-thumbnails .gal-thumbnails {\n\
+ display: none;\n\
+}\n\
+.gal-thumb img,\n\
+.gal-thumb video {\n\
+ max-width: 125px;\n\
+ max-height: 125px;\n\
+ height: auto;\n\
+ width: auto;\n\
+}\n\
+.gal-thumb {\n\
+ -webkit-flex: 0 0 auto;\n\
+ flex: 0 0 auto;\n\
+ padding: 3px;\n\
+ line-height: 0;\n\
+ transition: background .2s linear;\n\
+}\n\
+.gal-highlight {\n\
+ background: rgba(0, 190, 255,.8);\n\
+}\n\
+.gal-prev {\n\
+ border-right: 1px solid #222;\n\
+}\n\
+.gal-next {\n\
+ border-left: 1px solid #222;\n\
+}\n\
+.gal-prev,\n\
+.gal-next {\n\
+ -webkit-flex: 0 0 20px;\n\
+ flex: 0 0 20px;\n\
+ position: relative;\n\
+ cursor: pointer;\n\
+ opacity: 0.7;\n\
+ background-color: rgba(0, 0, 0, 0.3);\n\
+}\n\
+.gal-prev:hover,\n\
+.gal-next:hover {\n\
+ opacity: 1;\n\
+}\n\
+.gal-prev::after,\n\
+.gal-next::after {\n\
+ position: absolute;\n\
+ top: 48.6%;\n\
+ -webkit-transform: translateY(-50%);\n\
+ transform: translateY(-50%);\n\
+ display: inline-block;\n\
+ border-top: 11px solid transparent;\n\
+ border-bottom: 11px solid transparent;\n\
+ content: \"\";\n\
+}\n\
+.gal-prev::after {\n\
+ border-right: 12px solid #fff;\n\
+ right: 5px;\n\
+}\n\
+.gal-next::after {\n\
+ border-left: 12px solid #fff;\n\
+ right: 3px;\n\
+}\n\
+.gal-image {\n\
+ -webkit-flex: 1 0 auto;\n\
+ flex: 1 0 auto;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-align-items: flex-start;\n\
+ align-items: flex-start;\n\
+ -webkit-justify-content: space-around;\n\
+ justify-content: space-around;\n\
+ overflow: hidden;\n\
+ /* Flex > Non-Flex child max-width and overflow fix (Firefox only?) */\n\
+ width: 1%;\n\
+}\n\
+:root:not(.gal-fit-height):not(.gal-pdf) .gal-image {\n\
+ overflow-y: scroll !important;\n\
+}\n\
+:root:not(.gal-fit-width):not(.gal-pdf) .gal-image {\n\
+ overflow-x: scroll !important;\n\
+}\n\
+.gal-image a {\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-align-items: flex-start;\n\
+ align-items: flex-start;\n\
+ margin: auto;\n\
+ line-height: 0;\n\
+ max-width: 100%;\n\
+}\n\
+:root.gal-pdf .gal-image a {\n\
+ width: 100%;\n\
+ height: 100%;\n\
+}\n\
+.gal-image img,\n\
+.gal-image video {\n\
+ -webkit-flex: none;\n\
+ flex: none;\n\
+}\n\
+.gal-fit-width .gal-image img,\n\
+.gal-fit-width .gal-image video {\n\
+ max-width: 100%;\n\
+}\n\
+.gal-fit-height .gal-image img,\n\
+.gal-fit-height .gal-image video {\n\
+ /* XXX https://code.google.com/p/chromium/issues/detail?id=168840, https://bugs.webkit.org/show_bug.cgi?id=94158 */\n\
+ max-height: 95vh;\n\
+ max-height: calc(100vh - 25px);\n\
+}\n\
+.gal-image iframe {\n\
+ width: 100%;\n\
+ height: 100%;\n\
+}\n\
+.gal-buttons {\n\
+ font-size: 2em;\n\
+ margin-right: 3px;\n\
+ padding-left: 7px;\n\
+ padding-right: 7px;\n\
+ top: 5px;\n\
+}\n\
+:root.gal-pdf .gal-buttons {\n\
+ top: 40px;\n\
+ background: rgba(0,0,0,0.6) !important;\n\
+ border-radius: 3px;\n\
+}\n\
+.gal-buttons a {\n\
+ color: #ffffff;\n\
+ text-shadow: 0px 0px 1px #000000;\n\
+}\n\
+.gal-buttons i {\n\
+ display: inline-block;\n\
+ margin: 2px;\n\
+ position: relative;\n\
+}\n\
+.gal-start i {\n\
+ border-left: 10px solid;\n\
+ border-top: 6px solid transparent;\n\
+ border-bottom: 6px solid transparent;\n\
+ bottom: 1px;\n\
+}\n\
+.gal-stop i {\n\
+ border: 5px solid;\n\
+ bottom: 2px;\n\
+}\n\
+.gal-buttons.gal-playing > .gal-start,\n\
+.gal-buttons:not(.gal-playing) > .gal-stop {\n\
+ display: none;\n\
+}\n\
+.gal-buttons .menu-button i {\n\
+ border-top: 10px solid;\n\
+ border-right: 6px solid transparent;\n\
+ border-left: 6px solid transparent;\n\
+ bottom: 2px;\n\
+ vertical-align: baseline;\n\
+}\n\
+.gal-labels {\n\
+ position: fixed;\n\
+ bottom: 6px;\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
+ -webkit-flex-direction: column;\n\
+ flex-direction: column;\n\
+ -webkit-align-items: flex-end;\n\
+ align-items: flex-end;\n\
+}\n\
+:root:not(.show-sauce) .gal-sauce {\n\
+ display: none;\n\
+}\n\
+.gal-name,\n\
+.gal-count,\n\
+.gal-sauce {\n\
+ background: rgba(0,0,0,0.6) !important;\n\
+ border-radius: 3px;\n\
+ padding: 1px 5px 2px 5px;\n\
+ margin-top: 3px;\n\
+ color: #ffffff !important;\n\
+ text-decoration: none !important;\n\
+}\n\
+.gal-sauce a {\n\
+ color: #ffffff !important;\n\
+}\n\
+.gal-name:hover,\n\
+.gal-buttons a:hover,\n\
+.gal-sauce a:hover {\n\
+ color: rgb(95, 95, 101) !important;\n\
+}\n\
+:root.gal-pdf .gal-buttons a:hover {\n\
+ color: rgb(204, 204, 204) !important;\n\
+}\n\
+.gal-buttons,\n\
+.gal-labels {\n\
+ position: fixed;\n\
+ right: 195px;\n\
+}\n\
+.gal-hide-thumbnails .gal-buttons,\n\
+.gal-hide-thumbnails .gal-labels {\n\
+ right: 44px;\n\
+}\n\
+:root:not(.gal-fit-width):not(.gal-pdf) .gal-labels {\n\
+ bottom: 23px !important;\n\
+}\n\
+:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-buttons,\n\
+:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-labels {\n\
+ right: 178px !important;\n\
+}\n\
+:root.gal-hide-thumbnails.gal-fit-height:not(.gal-pdf) .gal-buttons,\n\
+:root.gal-hide-thumbnails.gal-fit-height:not(.gal-pdf) .gal-labels {\n\
+ right: 28px !important;\n\
+}\n\
+:root.gallery-open.fixed #header-bar:not(.autohide),\n\
+:root.gallery-open.fixed #header-bar:not(.autohide) #shortcuts .fa::before {\n\
+ visibility: hidden;\n\
+}\n\
+/* Mod Contact Links */\n\
+.contact-links {\n\
+ margin-left: 2px;\n\
+}\n\
+.move-note > a {\n\
+ text-decoration: underline;\n\
+}\n\
+.invisible {\n\
+ font-size: 0;\n\
+}\n\
+/* PostJumper */\n\
+.postJumper > .prev,\n\
+.postJumper > .next {\n\
+ font-size: 120%;\n\
+}\n\
+/* PSA */\n\
+.fcx-announcement {\n\
+ text-align: center;\n\
+}\n\
+.fcx-announcement a {\n\
+ text-decoration: underline;\n\
+}\n\
+/* General */\n\
+:root.yotsuba .dialog {\n\
+ background-color: #F0E0D6;\n\
+ border-color: #D9BFB7;\n\
+}\n\
+:root.yotsuba .field:focus,\n\
+:root.yotsuba .field.focus {\n\
+ border-color: #EA8;\n\
+}\n\
+/* 4chan style fixes */\n\
+:root.yotsuba.highlight-you .quotesYou$site$highlightable$reply {\n\
+ border-left: 3px solid rgba(221, 0, 0, .8) !important;\n\
+}\n\
+:root.yotsuba.highlight-own .yourPost$site$highlightable$reply {\n\
+ border-left: 3px dashed rgba(221, 0, 0, .8) !important;\n\
+}\n\
+/* Header */\n\
+:root.yotsuba #header-bar.dialog {\n\
+ background-color: rgba(240,224,214,0.98);\n\
+}\n\
+:root.yotsuba:not(.fixed) #header-bar, :root.yotsuba #notifications {\n\
+ font-size: 9pt;\n\
+}\n\
+:root.yotsuba #header-bar, :root.yotsuba #notifications {\n\
+ color: #B86;\n\
+}\n\
+:root.yotsuba #board-list a, :root.yotsuba #shortcuts a {\n\
+ color: #800000;\n\
+}\n\
+/* Settings */\n\
+:root.yotsuba #fourchanx-settings fieldset, :root.yotsuba .section-main div::before {\n\
+ border-color: #D9BFB7;\n\
+}\n\
+:root.yotsuba .suboption-list > div:last-of-type {\n\
+ background-color: #F0E0D6;\n\
+}\n\
+/* Catalog */\n\
+:root.yotsuba.catalog-hover-expand .catalog-container:hover > .post {\n\
+ background-color: #F0E0D6;\n\
+}\n\
+:root.yotsuba.werkTyme .catalog-thread:not(:hover),\n\
+:root.yotsuba.werkTyme:not(.catalog-hover-expand) .catalog-thread,\n\
+:root.yotsuba.catalog-hover-expand .catalog-container:hover > .post,\n\
+:root.yotsuba.catalog-hover-expand .catalog-container:hover .catalog-reply {\n\
+ border-color: #D9BFB7;\n\
+}\n\
+/* Quote */\n\
+:root.yotsuba .backlink.deadlink {\n\
+ color: #00E !important;\n\
+}\n\
+:root.yotsuba .inline {\n\
+ border-color: #D9BFB7;\n\
+ background-color: rgba(255, 255, 255, .14);\n\
+}\n\
+/* Fappe and Werk Tyme */\n\
+:root.yotsuba .indicator {\n\
+ color: #F0E0D6;\n\
+}\n\
+/* QR */\n\
+.yotsuba #dump-list::-webkit-scrollbar-thumb {\n\
+ background-color: #F0E0D6;\n\
+ border-color: #D9BFB7;\n\
+}\n\
+:root.yotsuba .qr-preview {\n\
+ background-color: rgba(0, 0, 0, .15);\n\
+}\n\
+:root.yotsuba .qr-link {\n\
+ border-color: rgb(225, 209, 199) rgb(225, 209, 199) rgb(210, 194, 184);\n\
+ background: linear-gradient(#FFEFE5, #F0E0D6) repeat scroll 0% 0% transparent;\n\
+}\n\
+:root.yotsuba .qr-link:hover {\n\
+ background: #F0E0D6;\n\
+}\n\
+/* Menu */\n\
+:root.yotsuba #menu {\n\
+ color: #800000;\n\
+}\n\
+:root.yotsuba .entry {\n\
+ font-size: 10pt;\n\
+}\n\
+:root.yotsuba .focused.entry {\n\
+ background: rgba(255, 255, 255, .33);\n\
+}\n\
+/* Unread */\n\
+:root.yotsuba .unread-mark-read {\n\
+ background-color: rgba(240,224,214,0.5);\n\
+}\n\
+/* Thread Watcher */\n\
+:root.yotsuba .replies-quoting-you > a, :root.yotsuba #watcher-link.replies-quoting-you, :root.yotsuba .last-page > a > .watcher-page {\n\
+ color: #F00;\n\
+}\n\
+/* Watcher Favicon */\n\
+:root.yotsuba .watch-thread-link\n\
+{\n\
+ background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n\
+}\n\
+/* General */\n\
+:root.yotsuba-b .dialog {\n\
+ background-color: #D6DAF0;\n\
+ border-color: #B7C5D9;\n\
+}\n\
+:root.yotsuba-b .field:focus,\n\
+:root.yotsuba-b .field.focus {\n\
+ border-color: #98E;\n\
+}\n\
+/* 4chan style fixes */\n\
+:root.yotsuba-b.highlight-you .quotesYou$site$highlightable$reply {\n\
+ border-left: 3px solid rgba(221, 0, 0, .8) !important;\n\
+}\n\
+:root.yotsuba-b.highlight-own .yourPost$site$highlightable$reply {\n\
+ border-left: 3px dashed rgba(221, 0, 0, .8) !important;\n\
+}\n\
+/* Header */\n\
+:root.yotsuba-b #header-bar.dialog {\n\
+ background-color: rgba(214,218,240,0.98);\n\
+}\n\
+:root.yotsuba-b:not(.fixed) #header-bar, :root.yotsuba-b #notifications {\n\
+ font-size: 9pt;\n\
+}\n\
+:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\n\
+ color: #89A;\n\
+}\n\
+:root.yotsuba-b #board-list a, :root.yotsuba-b #shortcuts a {\n\
+ color: #34345C;\n\
+}\n\
+/* Settings */\n\
+:root.yotsuba-b #fourchanx-settings fieldset, :root.yotsuba-b .section-main div::before {\n\
+ border-color: #B7C5D9;\n\
+}\n\
+:root.yotsuba-b .suboption-list > div:last-of-type {\n\
+ background-color: #D6DAF0;\n\
+}\n\
+/* Catalog */\n\
+:root.yotsuba-b.catalog-hover-expand .catalog-container:hover > .post {\n\
+ background-color: #D6DAF0;\n\
+}\n\
+:root.yotsuba-b.werkTyme .catalog-thread:not(:hover),\n\
+:root.yotsuba-b.werkTyme:not(.catalog-hover-expand) .catalog-thread,\n\
+:root.yotsuba-b.catalog-hover-expand .catalog-container:hover > .post,\n\
+:root.yotsuba-b.catalog-hover-expand .catalog-container:hover .catalog-reply {\n\
+ border-color: #B7C5D9;\n\
+}\n\
+/* Quote */\n\
+:root.yotsuba-b .backlink.deadlink {\n\
+ color: #34345C !important;\n\
+}\n\
+:root.yotsuba-b .inline {\n\
+ border-color: #B7C5D9;\n\
+ background-color: rgba(255, 255, 255, .14);\n\
+}\n\
+/* Fappe and Werk Tyme */\n\
+:root.yotsuba-b .indicator {\n\
+ color: #D6DAF0;\n\
+}\n\
+/* QR */\n\
+.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\n\
+ background-color: #D6DAF0;\n\
+ border-color: #B7C5D9;\n\
+}\n\
+:root.yotsuba-b .qr-preview {\n\
+ background-color: rgba(0, 0, 0, .15);\n\
+}\n\
+:root.yotsuba-b .qr-link {\n\
+ border-color: rgb(199, 203, 225) rgb(199, 203, 225) rgb(184, 188, 210);\n\
+ background: linear-gradient(#E5E9FF, #D6DAF0) repeat scroll 0% 0% transparent;\n\
+}\n\
+:root.yotsuba-b .qr-link:hover {\n\
+ background: #D9DDF3;\n\
+}\n\
+/* Menu */\n\
+:root.yotsuba-b #menu {\n\
+ color: #000;\n\
+}\n\
+:root.yotsuba-b .entry {\n\
+ font-size: 10pt;\n\
+}\n\
+:root.yotsuba-b .focused.entry {\n\
+ background: rgba(255, 255, 255, .33);\n\
+}\n\
+/* Unread */\n\
+:root.yotsuba-b .unread-mark-read {\n\
+ background-color: rgba(214,218,240,0.5);\n\
+}\n\
+/* Thread Watcher */\n\
+:root.yotsuba-b .replies-quoting-you > a, :root.yotsuba-b #watcher-link.replies-quoting-you {\n\
+ color: #F00;\n\
+}\n\
+/* Watcher Favicon */\n\
+:root.yotsuba-b .watch-thread-link\n\
+{\n\
+ background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n\
+}\n\
+/* General */\n\
+:root.futaba .dialog {\n\
+ background-color: #F0E0D6;\n\
+ border-color: #D9BFB7;\n\
+}\n\
+:root.futaba .field:focus,\n\
+:root.futaba .field.focus {\n\
+ border-color: #EA8;\n\
+}\n\
+/* Header */\n\
+:root.futaba #header-bar.dialog {\n\
+ background-color: rgba(240,224,214,0.98);\n\
+}\n\
+:root.futaba:not(.fixed) #header-bar, :root.futaba #notifications {\n\
+ font-size: 11pt;\n\
+}\n\
+:root.futaba #header-bar, :root.futaba #notifications {\n\
+ color: #B86;\n\
+}\n\
+:root.futaba #header-bar a, :root.futaba #notifications a {\n\
+ color: #800000;\n\
+}\n\
+/* Settings */\n\
+:root.futaba #fourchanx-settings fieldset, :root.futaba .section-main div::before {\n\
+ border-color: #D9BFB7;\n\
+}\n\
+:root.futaba .suboption-list > div:last-of-type {\n\
+ background-color: #F0E0D6;\n\
+}\n\
+/* Catalog */\n\
+:root.futaba.catalog-hover-expand .catalog-container:hover > .post {\n\
+ background-color: #F0E0D6;\n\
+}\n\
+:root.futaba.werkTyme .catalog-thread:not(:hover),\n\
+:root.futaba.werkTyme:not(.catalog-hover-expand) .catalog-thread,\n\
+:root.futaba.catalog-hover-expand .catalog-container:hover > .post,\n\
+:root.futaba.catalog-hover-expand .catalog-container:hover .catalog-reply {\n\
+ border-color: #D9BFB7;\n\
+}\n\
+/* Quote */\n\
+:root.futaba .backlink.deadlink {\n\
+ color: #00E !important;\n\
+}\n\
+:root.futaba .inline {\n\
+ border-color: #D9BFB7;\n\
+ background-color: rgba(255, 255, 255, .14);\n\
+}\n\
+/* Fappe and Werk Tyme */\n\
+:root.futaba .indicator {\n\
+ color: #F0E0D6;\n\
+}\n\
+/* Anonymize */\n\
+:root.futaba.anonymize $site$info$name::before {\n\
+ font-size: 12pt;\n\
+}\n\
+/* QR */\n\
+.futaba #dump-list::-webkit-scrollbar-thumb {\n\
+ background-color: #F0E0D6;\n\
+ border-color: #D9BFB7;\n\
+}\n\
+:root.futaba .qr-preview {\n\
+ background-color: rgba(0, 0, 0, .15);\n\
+}\n\
+:root.futaba .qr-link {\n\
+ border-color: rgb(225, 209, 199) rgb(225, 209, 199) rgb(210, 194, 184);\n\
+ background: linear-gradient(#FFEFE5, #F0E0D6) repeat scroll 0% 0% transparent;\n\
+}\n\
+:root.futaba .qr-link:hover {\n\
+ background: #F0E0D6;\n\
+}\n\
+/* Menu */\n\
+:root.futaba #menu {\n\
+ color: #800000;\n\
+}\n\
+:root.futaba .entry {\n\
+ font-size: 12pt;\n\
+}\n\
+:root.futaba .focused.entry {\n\
+ background: rgba(255, 255, 255, .33);\n\
+}\n\
+/* Unread */\n\
+:root.futaba .unread-mark-read {\n\
+ background-color: rgba(240,224,214,0.5);\n\
+}\n\
+/* Thread Watcher */\n\
+:root.futaba .replies-quoting-you > a, :root.futaba #watcher-link.replies-quoting-you, :root.futaba .last-page > a > .watcher-page {\n\
+ color: #F00;\n\
+}\n\
+/* Watcher Favicon */\n\
+:root.futaba .watch-thread-link\n\
+{\n\
+ background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(128,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n\
+}\n\
+/* General */\n\
+:root.burichan .dialog {\n\
+ background-color: #D6DAF0;\n\
+ border-color: #B7C5D9;\n\
+}\n\
+:root.burichan .field:focus,\n\
+:root.burichan .field.focus {\n\
+ border-color: #98E;\n\
+}\n\
+/* Header */\n\
+:root.burichan #header-bar.dialog {\n\
+ background-color: rgba(214,218,240,0.98);\n\
+}\n\
+:root.burichan:not(.fixed) #header-bar, :root.burichan #header-bar #notifications {\n\
+ font-size: 11pt;\n\
+}\n\
+:root.burichan #header-bar, :root.burichan #header-bar #notifications {\n\
+ color: #89A;\n\
+}\n\
+:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\n\
+ color: #34345C;\n\
+}\n\
+/* Settings */\n\
+:root.burichan #fourchanx-settings fieldset, :root.burichan .section-main div::before {\n\
+ border-color: #B7C5D9;\n\
+}\n\
+:root.burichan .suboption-list > div:last-of-type {\n\
+ background-color: #D6DAF0;\n\
+}\n\
+/* Catalog */\n\
+:root.burichan.catalog-hover-expand .catalog-container:hover > .post {\n\
+ background-color: #D6DAF0;\n\
+}\n\
+:root.burichan.werkTyme .catalog-thread:not(:hover),\n\
+:root.burichan.werkTyme:not(.catalog-hover-expand) .catalog-thread,\n\
+:root.burichan.catalog-hover-expand .catalog-container:hover > .post,\n\
+:root.burichan.catalog-hover-expand .catalog-container:hover .catalog-reply {\n\
+ border-color: #B7C5D9;\n\
+}\n\
+/* Quote */\n\
+:root.burichan .backlink.deadlink {\n\
+ color: #34345C !important;\n\
+}\n\
+:root.burichan .inline {\n\
+ border-color: #B7C5D9;\n\
+ background-color: rgba(255, 255, 255, .14);\n\
+}\n\
+/* Fappe and Werk Tyme */\n\
+:root.burichan .indicator {\n\
+ color: #D6DAF0;\n\
+}\n\
+/* Anonymize */\n\
+:root.burichan.anonymize $site$info$name::before {\n\
+ font-size: 12pt;\n\
+}\n\
+/* QR */\n\
+.burichan #dump-list::-webkit-scrollbar-thumb {\n\
+ background-color: #D6DAF0;\n\
+ border-color: #B7C5D9;\n\
+}\n\
+:root.burichan .qr-preview {\n\
+ background-color: rgba(0, 0, 0, .15);\n\
+}\n\
+:root.burichan .qr-link {\n\
+ border-color: rgb(199, 203, 225) rgb(199, 203, 225) rgb(184, 188, 210);\n\
+ background: linear-gradient(#E5E9FF, #D6DAF0) repeat scroll 0% 0% transparent;\n\
+}\n\
+:root.burichan .qr-link:hover {\n\
+ background: #D9DDF3;\n\
+}\n\
+/* Menu */\n\
+:root.burichan #menu {\n\
+ color: #000000;\n\
+}\n\
+:root.burichan .entry {\n\
+ font-size: 12pt;\n\
+}\n\
+:root.burichan .focused.entry {\n\
+ background: rgba(255, 255, 255, .33);\n\
+}\n\
+/* Unread */\n\
+:root.burichan .unread-mark-read {\n\
+ background-color: rgba(214,218,240,0.5);\n\
+}\n\
+/* Thread Watcher */\n\
+:root.burichan .replies-quoting-you > a, :root.burichan #watcher-link.replies-quoting-you, :root.burichan .last-page > a > .watcher-page {\n\
+ color: #F00;\n\
+}\n\
+/* Watcher Favicon */\n\
+:root.burichan .watch-thread-link\n\
+{\n\
+ background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n\
+}\n\
+/* General */\n\
+:root.tomorrow .dialog {\n\
+ background-color: #282A2E;\n\
+ border-color: #111;\n\
+}\n\
+/* 4chan style fixes */\n\
+:root.tomorrow #arc-list span.quote {\n\
+ color: #B5BD68;\n\
+}\n\
+:root.tomorrow.highlight-you .quotesYou$site$highlightable$reply {\n\
+ border-left: 3px solid rgba(145, 182, 214, .8) !important;\n\
+}\n\
+:root.tomorrow.highlight-own .yourPost$site$highlightable$reply {\n\
+ border-left: 3px dashed rgba(145, 182, 214, .8) !important;\n\
+}\n\
+/* Header */\n\
+:root.tomorrow #header-bar.dialog {\n\
+ background-color: rgba(40,42,46,0.9);\n\
+}\n\
+:root.tomorrow:not(.fixed) #header-bar, :root.tomorrow #notifications {\n\
+ font-size: 9pt;\n\
+}\n\
+:root.tomorrow #header-bar, :root.tomorrow #notifications {\n\
+ color: #C5C8C6;\n\
+}\n\
+:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\n\
+ color: #81A2BE;\n\
+}\n\
+:root.tomorrow.shortcut-icons .native-settings {\n\
+ background-image: url('//s.4cdn.org/image/favicon-ws.ico');\n\
+}\n\
+/* Settings */\n\
+:root.tomorrow #fourchanx-settings fieldset, :root.tomorrow .section-main div::before {\n\
+ border-color: #111;\n\
+}\n\
+:root.tomorrow .suboption-list > div:last-of-type {\n\
+ background-color: #282A2E;\n\
+}\n\
+/* Catalog */\n\
+:root.tomorrow.catalog-hover-expand .catalog-container:hover > .post {\n\
+ background-color: #282A2E;\n\
+}\n\
+:root.tomorrow.werkTyme .catalog-thread:not(:hover),\n\
+:root.tomorrow.werkTyme:not(.catalog-hover-expand) .catalog-thread,\n\
+:root.tomorrow.catalog-hover-expand .catalog-container:hover > .post,\n\
+:root.tomorrow.catalog-hover-expand .catalog-container:hover .catalog-reply {\n\
+ border-color: #111;\n\
+}\n\
+/* Quote */\n\
+:root.tomorrow .backlink.deadlink {\n\
+ color: #81A2BE !important;\n\
+}\n\
+:root.tomorrow .inline {\n\
+ border-color: #111;\n\
+ background-color: rgba(0, 0, 0, .14);\n\
+}\n\
+/* Fappe and Werk Tyme */\n\
+:root.tomorrow .indicator {\n\
+ color: #282A2E;\n\
+}\n\
+/* Highlighting */\n\
+:root.tomorrow .qphl {\n\
+ outline: 2px solid rgba(145, 182, 214, .8);\n\
+}\n\
+:root.tomorrow.highlight-you .quotesYou$site$highlightable$op,\n\
+:root.tomorrow.highlight-you .quotesYou$site$highlightable$reply {\n\
+ border-left: 3px solid rgba(145, 182, 214, .8);\n\
+}\n\
+:root.tomorrow.highlight-own .yourPost$site$highlightable$op,\n\
+:root.tomorrow.highlight-own .yourPost$site$highlightable$reply {\n\
+ border-left: 3px dashed rgba(145, 182, 214, .8);\n\
+}\n\
+:root.tomorrow .filter-highlight$site$highlightable$op,\n\
+:root.tomorrow .filter-highlight$site$highlightable$reply {\n\
+ box-shadow: inset 5px 0 rgba(145, 182, 214, .5);\n\
+}\n\
+:root.tomorrow.highlight-own .yourPost > $site$sideArrows,\n\
+:root.tomorrow.highlight-you .quotesYou > $site$sideArrows,\n\
+:root.tomorrow .filter-highlight > $site$sideArrows {\n\
+ color: rgb(155, 185, 210);\n\
+}\n\
+:root.tomorrow .catalog-thread.filter-highlight .catalog-thumb,\n\
+:root.tomorrow.werkTyme .catalog-thread.filter-highlight:not(:hover),\n\
+:root.tomorrow.werkTyme:not(.catalog-hover-expand) .catalog-thread.filter-highlight,\n\
+:root.tomorrow.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post {\n\
+ box-shadow: 0 0 3px 3px rgba(64, 192, 255, .7);\n\
+}\n\
+:root.tomorrow .catalog-thread.watched .catalog-thumb,\n\
+:root.tomorrow.werkTyme .catalog-thread.watched:not(:hover),\n\
+:root.tomorrow.werkTyme:not(.catalog-hover-expand) .catalog-thread.watched,\n\
+:root.tomorrow.werkTyme.catalog-hover-expand .catalog-thread.watched > .catalog-container:hover > .catalog-post {\n\
+ border: 2px solid rgb(64, 192, 255);\n\
+}\n\
+/* QR */\n\
+.tomorrow #dump-list::-webkit-scrollbar-thumb {\n\
+ background-color: #282A2E;\n\
+ border-color: #111;\n\
+}\n\
+:root.tomorrow .qr-preview {\n\
+ background-color: rgba(255, 255, 255, .15);\n\
+}\n\
+:root.tomorrow #qr .field {\n\
+ background-color: rgb(26, 27, 29);\n\
+ color: rgb(197,200,198);\n\
+ border-color: rgb(40, 41, 42);\n\
+}\n\
+:root.tomorrow #qr .field:focus,\n\
+:root.tomorrow #qr .field.focus {\n\
+ border-color: rgb(129, 162, 190) !important;\n\
+ background-color: rgb(30,32,36);\n\
+}\n\
+:root.tomorrow .persona button {\n\
+ background: linear-gradient(to bottom, #2E3035, #222427) no-repeat;\n\
+ color: rgb(197,200,198);\n\
+ border-color: rgb(40, 41, 42);\n\
+ outline: none;\n\
+}\n\
+:root.tomorrow .persona button::-moz-focus-inner {\n\
+ border: none;\n\
+}\n\
+:root.tomorrow .persona button:focus {\n\
+ border-color: rgb(129, 162, 190);\n\
+}\n\
+:root.tomorrow #qr.sjis-preview #sjis-toggle,\n\
+:root.tomorrow #qr.tex-preview #tex-preview-button {\n\
+ background: rgb(26, 27, 29);\n\
+}\n\
+:root.tomorrow #qr select,\n\
+:root.tomorrow #file-n-submit > input,\n\
+:root.tomorrow #qr-draw-button {\n\
+ border-color: rgb(40, 41, 42);\n\
+}\n\
+:root.tomorrow #qr-filename {\n\
+ color: rgb(197,200,198);\n\
+}\n\
+:root.tomorrow .qr-link {\n\
+ border-color: rgb(25, 27, 31) rgb(25, 27, 31) rgb(10, 12, 16);\n\
+ background: linear-gradient(#37393D, #282A2E) repeat scroll 0% 0% transparent;\n\
+}\n\
+:root.tomorrow .qr-link:hover {\n\
+ background: #282A2E;\n\
+}\n\
+/* Menu */\n\
+:root.tomorrow #menu {\n\
+ color: #C5C8C6;\n\
+}\n\
+:root.tomorrow .entry {\n\
+ font-size: 10pt;\n\
+}\n\
+:root.tomorrow .focused.entry {\n\
+ background: rgba(0, 0, 0, .33);\n\
+}\n\
+/* Unread */\n\
+:root.tomorrow .unread-line {\n\
+ border-color: rgb(197, 200, 198);\n\
+}\n\
+:root.tomorrow .unread-mark-read {\n\
+ background-color: rgba(40,42,46,0.5);\n\
+}\n\
+/* Thread Watcher */\n\
+:root.tomorrow .replies-quoting-you > a, :root.tomorrow #watcher-link.replies-quoting-you, :root.tomorrow .last-page > a > .watcher-page {\n\
+ color: #F00 !important;\n\
+}\n\
+/* Watcher Favicon */\n\
+:root.tomorrow .watch-thread-link\n\
+{\n\
+ background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(197,200,198)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n\
+}\n\
+/* General */\n\
+:root.photon .dialog {\n\
+ background-color: #DDD;\n\
+ border-color: #CCC;\n\
+}\n\
+:root.photon .field:focus,\n\
+:root.photon .field.focus {\n\
+ border-color: #EA8;\n\
+}\n\
+/* 4chan style fixes */\n\
+:root.photon #arc-list tr:nth-of-type(odd) span.quote {\n\
+ color: #C0E17A;\n\
+}\n\
+:root.photon.highlight-you .quotesYou$site$highlightable$reply {\n\
+ border-left: 3px solid rgba(221, 0, 0, .8) !important;\n\
+}\n\
+:root.photon.highlight-own .yourPost$site$highlightable$reply {\n\
+ border-left: 3px dashed rgba(221, 0, 0, .8) !important;\n\
+}\n\
+/* Header */\n\
+:root.photon #header-bar.dialog {\n\
+ background-color: rgba(221,221,221,0.98);\n\
+}\n\
+:root.photon:not(.fixed) #header-bar, :root.photon #notifications {\n\
+ font-size: 9pt;\n\
+}\n\
+:root.photon #header-bar, :root.photon #notifications {\n\
+ color: #333;\n\
+}\n\
+:root.photon #header-bar a, :root.photon #notifications a {\n\
+ color: #FF6600;\n\
+}\n\
+/* Settings */\n\
+:root.photon #fourchanx-settings fieldset, :root.photon .section-main div::before {\n\
+ border-color: #CCC;\n\
+}\n\
+:root.photon .suboption-list > div:last-of-type {\n\
+ background-color: #DDD;\n\
+}\n\
+/* Catalog */\n\
+:root.photon.catalog-hover-expand .catalog-container:hover > .post {\n\
+ background-color: #DDD;\n\
+}\n\
+:root.photon.werkTyme .catalog-thread:not(:hover),\n\
+:root.photon.werkTyme:not(.catalog-hover-expand) .catalog-thread,\n\
+:root.photon.catalog-hover-expand .catalog-container:hover > .post,\n\
+:root.photon.catalog-hover-expand .catalog-container:hover .catalog-reply {\n\
+ border-color: #CCC;\n\
+}\n\
+/* Quote */\n\
+:root.photon .backlink.deadlink {\n\
+ color: #F60 !important;\n\
+}\n\
+:root.photon .inline {\n\
+ border-color: #CCC;\n\
+ background-color: rgba(255, 255, 255, .14);\n\
+}\n\
+/* Fappe and Werk Tyme */\n\
+:root.photon .indicator {\n\
+ color: #DDD;\n\
+}\n\
+/* QR */\n\
+.photon #dump-list::-webkit-scrollbar-thumb {\n\
+ background-color: #DDD;\n\
+ border-color: #CCC;\n\
+}\n\
+:root.photon .qr-preview {\n\
+ background-color: rgba(0, 0, 0, .15);\n\
+}\n\
+:root.photon .qr-link {\n\
+ border-color: rgb(206, 206, 206) rgb(206, 206, 206) rgb(191, 191, 191);\n\
+ background: linear-gradient(#ECECEC, #DDD) repeat scroll 0% 0% transparent;\n\
+}\n\
+:root.photon .qr-link:hover {\n\
+ background: #DDDDDD;\n\
+}\n\
+/* Menu */\n\
+:root.photon #menu {\n\
+ color: #333;\n\
+}\n\
+:root.photon .entry {\n\
+ font-size: 10pt;\n\
+}\n\
+:root.photon .focused.entry {\n\
+ background: rgba(255, 255, 255, .33);\n\
+}\n\
+/* Unread */\n\
+:root.photon .unread-mark-read {\n\
+ background-color: rgba(221,221,221,0.5);\n\
+}\n\
+/* Thread Watcher */\n\
+:root.photon .replies-quoting-you > a, :root.photon #watcher-link.replies-quoting-you, :root.photon .last-page > a > .watcher-page {\n\
+ color: #00F !important;\n\
+}\n\
+/* Watcher Favicon */\n\
+:root.photon .watch-thread-link\n\
+{\n\
+ background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(51,51,51)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n\
+}\n\
+/* General */\n\
+:root.spooky .dialog {\n\
+ background-color: #171526;\n\
+ border-color: #707070;\n\
+}\n\
+:root.spooky .field:focus,\n\
+:root.spooky .field.focus {\n\
+ border-color: #98E;\n\
+}\n\
+/* 4chan style fixes */\n\
+:root.spooky #arc-list span.quote {\n\
+ color: #634C2C;\n\
+}\n\
+:root.spooky.highlight-you .quotesYou$site$highlightable$reply {\n\
+ border-left: 3px solid rgba(145, 182, 214, .8) !important;\n\
+}\n\
+:root.spooky.highlight-own .yourPost$site$highlightable$reply {\n\
+ border-left: 3px dashed rgba(145, 182, 214, .8) !important;\n\
+}\n\
+/* Header */\n\
+:root.spooky #header-bar.dialog {\n\
+ background-color: rgba(23,21,38,0.98);\n\
+}\n\
+:root.spooky:not(.fixed) #header-bar, :root.spooky #notifications {\n\
+ font-size: 9pt;\n\
+}\n\
+:root.spooky #header-bar, :root.spooky #notifications {\n\
+ color: #C49756;\n\
+}\n\
+:root.spooky #board-list a, :root.spooky #shortcuts a {\n\
+ color: #FE9600;\n\
+}\n\
+:root.spooky.shortcut-icons .native-settings {\n\
+ background-image: url('//s.4cdn.org/image/favicon-ws.ico');\n\
+}\n\
+/* Settings */\n\
+:root.spooky #fourchanx-settings fieldset, :root.spooky .section-main div::before {\n\
+ border-color: #707070;\n\
+}\n\
+:root.spooky .suboption-list > div:last-of-type {\n\
+ background-color: #171526;\n\
+}\n\
+/* Catalog */\n\
+:root.spooky.catalog-hover-expand .catalog-container:hover > .post {\n\
+ background-color: #171526;\n\
+}\n\
+:root.spooky.werkTyme .catalog-thread:not(:hover),\n\
+:root.spooky.werkTyme:not(.catalog-hover-expand) .catalog-thread,\n\
+:root.spooky.catalog-hover-expand .catalog-container:hover > .post,\n\
+:root.spooky.catalog-hover-expand .catalog-container:hover .catalog-reply {\n\
+ border-color: #707070;\n\
+}\n\
+/* Quote */\n\
+:root.spooky .backlink.deadlink {\n\
+ color: #FE9600 !important;\n\
+}\n\
+:root.spooky .inline {\n\
+ border-color: #707070;\n\
+ background-color: rgba(255, 255, 255, .14);\n\
+}\n\
+/* Fappe and Werk Tyme */\n\
+:root.spooky .indicator {\n\
+ color: #171526;\n\
+}\n\
+/* Highlighting */\n\
+:root.spooky .qphl {\n\
+ outline: 2px solid rgba(145, 182, 214, .8);\n\
+}\n\
+:root.spooky.highlight-you .quotesYou$site$highlightable$op,\n\
+:root.spooky.highlight-you .quotesYou$site$highlightable$reply {\n\
+ border-left: 3px solid rgba(145, 182, 214, .8);\n\
+}\n\
+:root.spooky.highlight-own .yourPost$site$highlightable$op,\n\
+:root.spooky.highlight-own .yourPost$site$highlightable$reply {\n\
+ border-left: 3px dashed rgba(145, 182, 214, .8);\n\
+}\n\
+:root.spooky .filter-highlight$site$highlightable$op,\n\
+:root.spooky .filter-highlight$site$highlightable$reply {\n\
+ box-shadow: inset 5px 0 rgba(145, 182, 214, .5);\n\
+}\n\
+:root.spooky.highlight-own .yourPost > $site$sideArrows,\n\
+:root.spooky.highlight-you .quotesYou > $site$sideArrows,\n\
+:root.spooky .filter-highlight > $site$sideArrows {\n\
+ color: rgb(155, 185, 210);\n\
+}\n\
+/* QR */\n\
+.spooky #dump-list::-webkit-scrollbar-thumb {\n\
+ background-color: #171526;\n\
+ border-color: #707070;\n\
+}\n\
+:root.spooky .qr-preview {\n\
+ background-color: rgba(0, 0, 0, .15);\n\
+}\n\
+:root.spooky #qr .field {\n\
+ background-color: rgb(26, 27, 29);\n\
+ color: rgb(197,200,198);\n\
+ border-color: rgb(40, 41, 42);\n\
+}\n\
+:root.spooky #qr .field:focus,\n\
+:root.spooky #qr .field.focus {\n\
+ border-color: rgb(254, 150, 0) !important;\n\
+ background-color: rgb(30,32,36);\n\
+}\n\
+:root.spooky .persona button {\n\
+ background: linear-gradient(to bottom, #2E3035, #222427) no-repeat;\n\
+ color: rgb(197,200,198);\n\
+ border-color: rgb(40, 41, 42);\n\
+ outline: none;\n\
+}\n\
+:root.spooky .persona button::-moz-focus-inner {\n\
+ border: none;\n\
+}\n\
+:root.spooky .persona button:focus {\n\
+ border-color: rgb(254, 150, 0);\n\
+}\n\
+:root.spooky #qr.sjis-preview #sjis-toggle,\n\
+:root.spooky #qr.tex-preview #tex-preview-button {\n\
+ background: rgb(26, 27, 29);\n\
+}\n\
+:root.spooky #qr select,\n\
+:root.spooky #file-n-submit > input,\n\
+:root.spooky #qr-draw-button {\n\
+ border-color: rgb(40, 41, 42);\n\
+}\n\
+:root.spooky #qr-filename {\n\
+ color: rgb(197,200,198);\n\
+}\n\
+:root.spooky .qr-link {\n\
+ border-color: rgb(8, 6, 23) rgb(8, 6, 23) rgb(0, 0, 8);\n\
+ background: linear-gradient(#262435, #171526) repeat scroll 0% 0% transparent;\n\
+}\n\
+:root.spooky .qr-link:hover {\n\
+ background: #1A1829;\n\
+}\n\
+/* Menu */\n\
+:root.spooky #menu {\n\
+ color: #FE9600;\n\
+}\n\
+:root.spooky .entry {\n\
+ font-size: 10pt;\n\
+}\n\
+:root.spooky .focused.entry {\n\
+ background: rgba(255, 255, 255, .33);\n\
+}\n\
+/* Unread */\n\
+:root.spooky .unread-line {\n\
+ border-color: rgb(197, 200, 198);\n\
+ visibility: visible;\n\
+ opacity: 1;\n\
+}\n\
+:root.spooky .unread-mark-read {\n\
+ background-color: rgba(23,21,38,0.5);\n\
+}\n\
+/* Thread Watcher */\n\
+:root.spooky .replies-quoting-you > a, :root.spooky #watcher-link.replies-quoting-you, :root.spooky .last-page > a > .watcher-page {\n\
+ color: #F00 !important;\n\
+}\n\
+/* Watcher Favicon */\n\
+:root.spooky .watch-thread-link\n\
+{\n\
+ background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(254,150,0)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n\
+}\n\
+/* Link Title Favicons */\n\
+.linkify.audio::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAitJREFUOE9jYCAWKJWwavr0KyXWb/FIbDtUFFyzJx6nVofE2Xo5nXsj0rqPNSR0nVkR2Hjmgmfd+U9Otdf+m5Vf/6+SfeU/R9ChVVgNYDRtlfJuuPA/rPfe/4QpD/6nznj0P27Kw/9unff/69Xf+69c/+C/SO7N/0z+OAxgMmmRCe++/r9i3ev/KWvf/vdY8PK/bt/9/wrNV3/IN5y/IVt1YqNg4pGTTP4HsbuA2bhZ2qvpyn+xjIObxAp3VwqlrgngLFyryVy5nhPmZJHANS2cwYexG8BmVC/pWn3hP4NZlzWuQDJI3dIiFnUUuwEsQAOcq87jNcC7fHeLUtJxHF4AGmBWeAavAWH1+1rUUk7giAWjOknllON4DXAs2NEiG4/DBQxAF/CFHfrPYI4jDFSLuJVjNrUJhB/B7gIGo1pJRt99GAZYJK7wLJ1z7Xzl4vu/7aqv/GRBj0bjqAX2qb0nJ7mXH17C4HcUxQA+hymWtSue/C5a9up/9Ozn/7Vr7v1nRY7GqMb91T3b3v6vWvPmf/S0p/9ZQk+DDLCBRSOz06Jqk+o7/21nvfqvsebDf7kZL/5zBaxphkezd+OFn7HzXvz3Wvjmv9a8N//5Ek//ZTBpVYUrMG2X5wjcdl68+uI/wa5Lr3hSNjczGFeywOVZ/bbcVGp//F9izfv/Ql03f3P4LC/HSEQquYwMFnUCDJ7dzBhyjGZNQpye89M5gpfnMvtNUyE2h4PUAQBovvT7lyNljwAAAABJRU5ErkJggg==') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.bitchute::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAadQTFRFAAAAwzw8xDs7cY6O0iws0ysrtF9f0Sws1CwsyzU1zTIy1igoyzQ01icnY7i4t0hI0S4u0ysr1Soq1ikp1ikp1Soq0ysr0S4uu0VFzjEx1Csr1ygo2Ccn2Ccn1ygo1CoqzjExzjAw1Skp2Ccn2Ccn1ikp0TAwxzY21Soq1SoqyzQ00iws1ygo1ygo0yws1Soq1Skp1igo1igo1igo1igo1igo1igo1Coq1Soq0C0t1ygo1ygo0S4unV5e1Csr1ygo1ygo1CsruUdHxzg41Skp1ygo1CsryTU10C4u1igo1ycn1ygo0i0txjo60S0t1ikp1ygo1ikp1Cws1Coq1Coq0yws0S0tyzQ00iws1Soq0ysr0i0txDs72Ccn2CUl2CYm2CQk3EFB2S8v2zw82jY24FZW3D0931FR3EBA3UND8LS04FVV7qys4V9f4WBg+erq766u9t7e7qqq2Ckp54KC9+Pj6pSU+Ojo5XNz9NHR6YqK8bu765ub5G5u9M3N6ImJ88vL5XV165eX3UVF6pWV3UhI2Soq2jU12Coq2jQ02Cgo2Sws////FaxLuAAAAF10Uk5TAAAAAAAAAAAAAAAAAAAAASJnoLy9oWolAhBz1vr72XgTGKf8/a4cCpuiDVvz9mS6xOvy9vzg6aGsPOToRAFv9fh2Awm07XgIMd765UEDOsfemVhhY00nBommbCkEI8horgAAAL5JREFUKBUFwbFKA0EUQNF7387sMq4EmzRpLSSdIBYKFv6Af2prnSYkRT4gWFgkCBJQ0EIFdcZzBCeqqh4qdk7VW2ChPusw02sAYKU7z7wEAAA2piQKFbrWSHazc1J0XWs5pdxPDykcVX+7Y9UxUsSo+s7PibqPFBRV/C5qi4i/UkrJrc7L47Bt4ZWnUaMCAE9GSrtKBQD2fR+bnAEAeOn7dUTOwApe35bDsPz0zsniQlV98IN0tJ3f6P0XAMA/kxou7OXCdnoAAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.clyp::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAwUExURTSY22ey5E2l4KbS75rM7Y3F64C/6f///8zl9nS45r/f9PL5/UGe3bPY8Vqr4v///wNjrzUAAAABYktHRA8YugDZAAAAB3RJTUUH4AINEi85AIH95AAAAE9JREFUCNdjYMAGGBWgDGYHCM2a3hkAZmi0dzSBaKaO9o5moCqmLiCjYzNQyw4QowIodQzI6E0AKcpo72gE6+Jyb1kAMehUA9RktgdYbQYAjGIVNGGXBJkAAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.dailymotion::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAnUExURQBk3ff6/trp+kKO5wZt3xx54q7P9Ozz/IS17zOG5WKh653E8sbc9/GbbcoAAABZSURBVAjXY2BAASyhDhAGc9oECMOjyAAiESEEYrBYpLWBGcwHxcvBjDDxHelghpF0yDQwY3kVgweEUeEQDWbMEepqAjO8FMsLIeYsU8o+BrbCdWboTAe4AwALXxWGjW41FwAAAABJRU5ErkJggg==') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.gfycat::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAjVBMVEWn3gCo3gSr3w2t4BSu4Bav4Ri35C+45DK45DO55DXA50rA50vB50zC6E/D6FTF6VjG6VvL62vN7G/P7XbQ7XfW74vY8JDa8ZTe8qDe8qLf86Pi9Kzj9K7k9LHp9sDp98Lq98Ps+Mr0++L5/O75/fD6/fH6/fL6/fP7/fT7/fb8/ff8/vj8/vn+/v7///91X4cfAAAAcklEQVR42o3M2xKBUACF4aVQckrIuRJK6H//x2sme4/MuPDfre9i6c/Cc3U5Dj87BuAxsXvGu6JvIIXEHRWwNHCHQNrCzkAFkbSBg4EM8i+Yw7PXBa3zRfuxVyf/Bis7nKwGKAcWxgC8prI5Sc315OlnDfzpDar2S9/oAAAAAElFTkSuQmCC') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.gist::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABblBMVEXc3NykpKTW1tbb29ugoKCdnZ0AAAACAgIEDRcKCgoMDAwODg4QIzYRDAoTExMUDwwVAg0WICsaEw8aGhoiCBklGxUmERwwKCQ7LSU7Ozs8LSZFLyNINi1JNyxJNy1KSklMOi5VR1FXV1daQTRkZGRseYZwU0F4eHh7dnR8bWV/YE6IdGiKcGCKkJaNgYeNjY2RdGOScWCUcWCZmZmhoaGkpKSoqKirfmaurq6xsbG1tbW6urq+vr7AbmzBb23CwsLGxsbHx8fHyMjJycnJysrMzMzOiYbPi4fQ0NDRoYbT09PU1NTW1tbY2NjZqIzZ2dnb29vd3d3f39/i4uLktZrk5OTl5eXm5ubn5+fo6Ojq6urs7OzttKLu7u7wuqbw8PDx8fHz8/P4+Pj5+fn7uZj8vpz9ya79ybD/tZf/upr/wZ//w6H/xKH/xaL/xrH/yqj/y7T/zqv/z7D/07D/17n/2Lv/2Lz/3L//38n/4Mk3Q/ZuAAAABnRSTlMSFcbGzc5MNKFvAAAA1klEQVQoz2NgYPZHAswMDEwRSclwkBTBxOARn4gE4j0YXBOiJNUDg7y8Ar1UlOITXBkcY73Z2Li42dg42dn4wmIdGeyjQ7nZoEA4PNqewSZKlw0O9KJsGKwjBdl4ZeWkJGQUhNjEIq0ZrMI5+D0ri7Jz8itCRAXCrRgsQ3mUy+xicrPSbfO0REItGSyCVaVL3ONSU9LcCtQUgy0YzIJ85M1LizMzCsv9xF2CzBhMAwN99TV1DI0MtDWcAgNNGUycA5CAswkDi5kDwrMOZiwMjKzGSICVEQDhZj0UQV7PewAAAABJRU5ErkJggg==') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.image::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAs5JREFUOE+lk/tvi1EYx98/xT8gW4REIpGFMEQWl2FiM9ZMZhm2xRAyOsmujFFmdFRHu0tWm87UypxStr69zPauN5e5rHVp3IYhbOvHy+wHEQlxkm+ek+d8nm9OznkeSfrfldmgJC7QyUlTymsJTfuTZ25z4HdWYwyLreYhtpgekGPw0+kKvo1Eo+IXRSIiEhkWZuc9tqnsJD9EqTUopCxjSGTpB0iueczSo1HyW8cpsExQ1DbxI2pt45j9cXpexul4FEd79RnZphAa/SD7WvuFtO6UItbU9LC+YQxNI2w0wwYT5LRAdhOU3oBTIXC9gXP3oUSGgz2vST3gYHejR0jptT1C332f8yrUEYHrz8CgxDnpm6DKCUfc0KnmXa/AEVPPwnDcD0cvetA2uYRk67Ive/lpjO7YBO1PPuF8Df3vwf4cbNE4tqdw7YVq8HYyHx6FvhE1hkMEg8HDUqvFkjT4aIjMqkqyqkswDSrcfBfH+Q561YLAZ/B+BLda6FXlU/cPv0AoEPhuoP1h4Av7Wbh9E/Py15NWWUjeSR3nZDfeN+N0DY9hG/7K1eGP3P0S5/EYRFUF/IOTBrUXHPm9fT6mr1xEwupkZqxbzLyiDJYUZ5NSnkdqdSHpxyrYdFpPgdmAsdfJwPMI/Yr65bf7tZLGGBQ7DNdJWFtIYvoOZmbuZE7OXpIKKli86zAr9p9gTVktWTVnKTI2U95uRWe3U2IJUDbVB5p6hVm5x5m9Vc/cnedZUNzC8lILaQesZBy6hEZ3maKzgvJWFzVWD9XtXvVGQbSWASFtMATVRlJIKbOTWtlJXaeXepuPM1f6MNp9GLt8mLvvYLmp0OhQ2Fwvk6m7xaqDTvY0eYWUVtcnllXfYlGpnfklVuraHHg8HjxuN+6fktUHlWWZPaZeUo/ILK0UKttBcbNbSB9GP0yLxWJJUxoZGUn80zD9C/vXQ/4NHY10h3M1zmQAAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.installgentoo::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABcVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB3dIYAAAAAAAAAAAAbGh4BBAcCBgoBBgoCBwsCCQ/QzucCCA7MyuXZ1eUBBQmTh8fo5/i9svIAAADh3vQAAAACCA0CCQ8CCQ4DDBQbGCUDChDr6vgAAAAAAAAREBIDCxK6tdfe2fTv7/cDCxIDDBQEDRUHDhgMJjXk4PZdXWdLUFoUNEYOKDgSMUMRLUBneI4eTGj08/QmW3onW3rTzvfOx/giU3IiVHMkWHdEaYJobHv3+PokWHpua6TNy9xZgZ+1quz8/foQKj0XPFInWn0nW38tZ4o6fqg8gq48grA9hrU/i7pAhrNAiLdBjLtEjr1FksNIjr5Il8pImMtKWnNqhL97odKFqti5q/q5rPq60+nCt/vLw/vPx/jV0vHY0/rc1/rg2/vh3fzn4fzu6/vx8vf19Pv19Pz49/v5+Pv8/Pv8/fr9/vv+/frziVtUAAAAT3RSTlMABQYHCAoNDhARGRobL0ZOV1xdXV5fYGBmZnB0eX2MjZSaoaGio6mqqqustLq7zubo6Ojo6evt7u/x8fLy9/f4+Pj5+vr6+vr6+/39/v7+XKgUSwAAAMhJREFUKM9jYGDg4OZmZgABKINT1dBAhBHIYFMxMBIDisjbhoZbCTExsCu5hoeY8DEwcOkEx8fY6MqpucTGB0izglVEplcU5/gmRYWBVQDNMK+s0hN3SvMyBpsBNJxXw0NfwTEjVQZqHQMHj5RfWW5mliSEC7TPzK6yJD/bXZQRzGdXcisqLy309okA2Q4Eis4peQWmstqBCdGW/CABraC45ERBBs3A6Fh/AbAKTwsHa34QZW8NVsGuLqwswQSjQICTmYMFQaEDAAF8JHLfKGswAAAAAElFTkSuQmCC') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.liveleak::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAlNJREFUGBkFwU2LVmUYAODrPu8Z5x1xSpRBXQyFoLsBE+wfiO5atJOgnf9DUPwFgtGinUgEaQsRhHYuMtpEiEWuG5iNjuOcj+c8z911xXcXL/68c3Dw1fzhg0QgEQAAEYGUKXFie9vxlSs/xk/rdavjGEkmkWSih65z4osv9GfOiK6LzEyZ2uGh4dUrmzs72ddlUUhkoiMr4PT167589Mh6c1N0nSRlqrX67dat+PDyZXRT19m5edPnt28rGFHxMcJ6d9fprS1/37tneP3aemPD1uamUydPOru3p5DdGOH0tWsu3LhhxIQJM2qEpRT/Pn3q/du3AhARSmvGTH0lplKMrVkiYpVpQaJlighzhDkzhmEA0fcWoqAfyaFW4zTlgCABxlrNmY4ylUzLsiREprFWc0T2M+ZSjKWY0AEaltZUjJixZJIpuk5pTWlNP2BYFvOyKJkCAKU1tTXHrZlqVWolUxdhxsfVSj9FmJfFMM9GdICGGa01HyMstYpMIFPJVNDPmYZSTOPoOEKHzNRlKpmWWh1j6TpLa2SKTKVWU6Z+Qolwdm/P9QcPZKa2LH69e9eIMs+WCL/cv2/98CGZPrt61am+V9APq1X89eyZ/968obVYaiXT4dGREgG+vnPHeHgYMsH2+fP+efEihtVKv7SWw/6+9/v7KYLMhIywTJPamvOXLomukyRsrNf+ePzYkpl9dJ3SWgSCSCQCfz5/7pMLF2yfO6eLiAQcHRz4/cmT+HR7O+Ob3d0fNt69+7a2BiICQCJbA0EgE5lpvbXl1OXL3/8Pfax4+6SjSukAAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.pastebin::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAB1FBMVEUAAAAAAAAAAABWYWwAAABbY3BbYm5dZnFdZXJeZnMEBAQHCAhYYGpdZnFdZnBgaHIlJyomKCooKi09QkdESU5eZGtdYmhdYmleY2lrcXdqb3Rqb3Rqb3SSmJ+SlJeWmJutr7GtrrCWm6ChpKhbW1tmZmZvb290dHR3d3d4eHh5eXl6enp8fHx+gIJ/f3+CgoKDg4OEhISFhYWHh4eKioqKjI2Li4uMjIyOjo6Pj4+QkJCRkZGSkpKUlJSVl5mWlpaYmZqZm52ampqbm5ucnJydnZ2enp6fn5+hoaGioqKkpKSkpaalpaWmp6mmp6qnqauoqKioqquoqq2qqqqrrK2srKysra6srrCsrrGurq6vr6+wsLCxsbGysrKztLa0tLS1t7m2tra3t7e4uLi5ubm6urq7u7u8vLy9vb2+vr7AwMDAwsTBwcHExcfFxcXFxsnGxsbHx8fIyMjJycnMzMzNzc3Ozs7O0NLPz8/Q0NDR0dHR09XT09PV1dXV1dbV1tfV19rW1tbX19fX19jY2tzZ2dnZ2tva2tra3N3a3N7c3Nze3t7f39/f4OHg4ODi4uLl5+jm5ubs7Ozs7e3u7u7v7+/v8PDw8PDx8fHy8vLz8/P29vYSoLMZAAAAJHRSTlMABAUGCwsNHCAiLzMzMzZEYGJwgIuOnJycnqmqq9bc3+/w8fkZ0N/uAAAA/klEQVQoU2NgYGDl5YMDdgYGBmZZ3964CYFtIR3e9Q7K/AwMHI55KfaFmcHWMy3K3MwlGRg4wz0zdYpcorRbNbL0LaWAAp3ts2umV8wo6MupTauQBgqUG03VL7W3sfZSb1erAgm02M+yzYrVCXUy6zapAQlUx/dEdyX3J3ZHVUYVywAF8o2rDNN1Go2jzGLMokAC2QbuSc42mXmaOXop9iAtCXrJ5qXWjT59Abl2ESJAAX/tSIMMiyrrqQ3T6uS5gQK6kSqpqkUermGTexQFmYACflqR+hlWZSamzQpCLEDPsSmVVDT1TJw0JUhOAMRnYOARFRMTE5cQF+ZiBPIAII5B3EVG0b4AAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.peertube::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABIFBMVEUhHyAAAABzPBnxaA3CWBEnJSYbGRptbW16enpzc3PTayWhb04hHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyApIh+0UhMfHiBWMhvsZg7zaQ0hHyAhHyAXHCHzaQ3xaA3xaA3xaA3xaA0hHyAhHyDxaA3xaA3xaA3xaA3xaA3xaA0oJickIiMdGxwUEhPxaA3xaA3xaA1sbGxwcHB3d3eFhYXxaA3xaA3xaA1zc3Nzc3Nzc3Nzc3Nzc3PxaA3xaA3xaA1zc3Nzc3NtdHjxaA3xaA1yc3STcFnvaA/yaAxzc3N4c2/FbDFzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3MhHyDxaA1zc3MAAAAfljyVAAAAXHRSTlMAAAAAAAAAAAAAAAAZkjMBHOLXYArj8p0u2VsJ1XaGL/OhKyXc1WEN2gwk2/SjKgEYiS4B/tYFGosqAdleAxzj12ML9Z8s850rJWbYeYMs1F8Koiri1V0MGZY0AYbIBFIAAAABYktHRAH/Ai3eAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4wYXFBUVX81QWQAAAKxJREFUGNNVz9UWgkAQANDBtdbu7lZsxe7ubpH//wxBPKDzNvdMAmi0Oj0QQgAYjCazBX7BStvsDqHoAzTtdLklf+Dx+vwICRAIhsKRaCyOvpAwJ6Up8pXOZHOIAFm+UCzJEQuvMhWrIFBUa/WGkodmq40Ad7q9/kDFwnA05lpYYCbT2ZykFvxQDhhmuVpvcvxaHra7vfp72KflcMSYEOB0vlyx+By+3R9PMSfe+P0enM1454kAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDYtMjRUMDM6MjE6MjEtMDc6MDDse6MAAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA2LTI0VDAzOjIxOjIxLTA3OjAwnSYbvAAAAABJRU5ErkJggg==') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.soundcloud::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABsklEQVQ4y5WTy2pUQRCGv2rbzDjJeAlIBmOyipGIIJqFEBDElwh4yULGeRFXPoEIBl/AvQ/gC2RnxCAoxijiwks852S6+3dxzslcHJCpTXVX11/Xv0097gLPgVNMJxnQNfX4zsqleWbnpoMf/oa9d988MM9MC/rp+E0a+A0dsVobMNMCOO8B6McRoABJI+A6gJmN3D2A8jgEBCEkSEMBrcrsDAzDWWn3AjgKFaDMmgRqniGFgsaDp1jrLOngDf1XT1D+A1dFc4MKAkkiCVKjjVu7g9+4Rzx4i1u6hjXbuMWr0O5QPNvCu7IaCZwEKQukLGDrm5x8uI0tr6MkiGlkiv7yLfzN+6S5i6QsIMABkEfcxhbWWYMkVAOjxvYAjc3HNHrbKI9VBQBFwF25XQKSBjqIf1YBuAurEMrczgDygD6/x2LCpFLXLUyQ+PoldphhBhYfIX09XU1+Flaukz7uYqs3SHs7cG4BmTsmkBUF9mmXEwa28BNLPaQPLepuNcbGSWQquQC2/Kdcox1FUGkcB0ykck1nA2+wTzMs8stGnP4rbWGw74EuS/GFQWfK7/wF6P4F7fzIAYkdmdEAAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.streamable::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABXFBMVEUPkPoNj/qExv0PkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoNj/oPkPoNj/oNj/qExv0PkPpruvwPkPornfoVk/opnPpnufwPkPqExv0Nj/oPkPoNj/oPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoPkPoOj/opnPsVk/oMjvoOkPoTkfo6pPsblfo3ovva7v7////v9/5Sr/whmPry+f5htvze8P7W7P5itvyl1v0imPu84P3o9P50v/zN6P73+/8lmvs8pfs+pfsKjvr9/v9EqfsNj/oom/v8/v9nufxAp/tJq/sQkPrb7v6t2f0IjPoclvr6/f9luPwUkvrp9f7h8f5ruvy/4f4kmftpuvwxoPum1v32+/8jmfpMrPvu9/7z+f9UsPs7pPv8/f/4/P9oufwalfpDqPsMj/ounvtVsPsnm/qzfQQ9AAAALXRSTlMAAAAggMzw0IYkBPb4iAamsgZ+jPwogpDO1vTYlPoulL4KivyUCiqO1PL01i67tUAWAAAAAWJLR0Q4oAel1gAAAAd0SU1FB+MGFxMuDXVcMbIAAADdSURBVBjTY2AAAmYWVjY2dg5OBgZGJiCXi4VbFwx4ePlAAlz8unAgIAgUENJFAsJMDMw8unp6+gaGRsYmpoa6IqIMYrp6ZuYWllbW5hY2toZ64gwSurp29g6OTs4urm7uHrqSDGy6nhZeet5WPr5+/gGBelJAgSCLYL+Q0LBw3YjIKKAAu250TGxcvE1CYlJySqquNAOHrl9aukVGZla2RU6uoZ4MA6esrl9evnWBYWFRMdBaOQYGXmSHyQNdyieA4CsogjzHpyQL4SqrqIJ9y8Cgpq6hqaWtogPyPgDmvSxRxBWM9AAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wNi0yNFQwMjo0NjoxMy0wNzowMCKUvXUAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDYtMjRUMDI6NDY6MTMtMDc6MDBTyQXJAAAAAElFTkSuQmCC') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.twitchtv::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAYUExURf///2RBpWRBpWRBpWRBpWRBpWRBpf///+zQyUYAAAAGdFJOUwFdZX0lTzs4r5oAAAABYktHRAcWYYjrAAAAB3RJTUUH4AINEi42iSXRNAAAAD1JREFUCNdjYEiDAAZGGIMtjQEEUBlMCWoEGci6mGEMsxQgIy0BiB3AjLS0FAYQIw0kwABipoI1AhkBQBIAFCIXxiHgq80AAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.twitter::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAEsUExURf///1Cf21Gg3FGi31Gh3VKj4FGh3lKj4VKk4lKl41Ol5FOn51Sp6VSo6FOn5lCf21Gg3FGh3VGi31Gi31Gh3lGg3FGg3FGg3FGg3FGh3lGg3FGi31Kk4lKj4FGh3lGi31Kk4lGh3lGg3FGh3lOm5FOm5VGi31Kj4VSo6FGi31Gh3VGg3FKj4FOn51Gi31So6FWr7VOl5FGi31On51Sq6lKk4lOo51Sp6VOm5FSq61Ws7VOn51Oo51Sq61Ol5FOm5FSq61Wr7VOo51On51Sr7FWs7VSp6lGg3FGh3VOm5FWr7VSp6lKj4VOm5FSo6FSr7FWs7VWs7VWr7VSq6lOo51Om5FOo51So6FOm5VOl5FSq61Ws7VSr7FSp6lSp6VWs7lWr7VKk4lSq6v///6E3MNsAAABVdFJOUwAAAAAAAAAAAAAAAAAAAB0Ii+3xnBVTJhfsMKb+qTEp9GwBF/7lLAbo0m4pLkUTdvk2Ev3+EZnOBo/3Z8ffCRzH/D0OqPxiLnvx3UI8m9n1++GwXQZNS29BAAAAAWJLR0QAiAUdSAAAAAd0SU1FB+ACDRIwBwy67tEAAADKSURBVBjTY2BAB4xogIGRH8IQEBQSFhEVE2eQkJQC8ZmkQ8PCI2Rk5RjkIxUUlRgZlVWioqNjYlXVGNQ14iI1tbR14qLj4+MTdJkZ9PQNosJCE0OjgPz4KEMWBiPjhPiEmKQokIJ4E1MmBmazhHg4MGdlYmCzsLSC8ROsmRkZmFht4Eps7ViADmOzd4DyHZ2YmYACTOzOLmATXd04mIBOd/eQ9owFCXh5c7KB/MLi4+vnHxAYFBzCwcYEEmBi5uLm4eHl42RmAnsSAMZBLgZiFUQ5AAAAAElFTkSuQmCC') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.video::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAxgDGAP8nNqN7AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gMZBjQQLEEqGwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAA5SURBVDjLY2AYaMDIwMDwn1JD/lPCZhpwL+B1wf///ykzgBhDiAoDfIYQZQAjIyP5BuDTPJqQqAQAvW0ZAMk8+EEAAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.vidlii::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACvlBMVEUCWv8HXf8AWv8AAAD///8AVP+bqP8AWv8AVO4AOqUAGkgAyf8APa0AL4QABAsASdEAVv8AUv8AUv8AVP8AWP8BWv8JXv8RYv8QYv8DW/8DXP8xdv9RiP9Af/8IXf8AKP8KXv8JXf8NYf8aaf8ATP0UZP0AVf8AT/8AT/8AVv8ATedvnPVAf/8AT/sYZvl0o/8PYf8udf8aa/8FXf8AVf8AOrRBe/Nvn/8AUv0aaPkXZ/8ATv8AKYQZYuwIXf8ca/wTZP8ASP8AED0HUNwZaf8xdPwDWv8AAAAAQMRcjvQAU/8AMZssb/Jmmf8AU/8AJXsRW+dSif8AUv8AAAAASdQtdP8ATv8AQ/8AQv8APbtKgfQud/8XZ/8TZP8FXP8AKIIcZO4wdP8AF08KU95tnv4gafhZi/Rnl/ZzofcocP8AAAAAQ8Q4efRwnvVmlvVcjvgrcfsAQsQAOK0APrwAQcUEStMLXPgDWv8AHE8APLEARdIAQ80ASeEAVf8AOJkAAAAAAAAAAAAABBMAJJIAY/9rmP+vxv90n/+buPv29/7C1P+zx/n///2Crv/7+fjs8f++z/f///3l6fX9/f/L2fj9/P5ilv9Nh/3h6f6vyf/D0vT///2lwP/Z5Pf3+P9OiP9klvr9/Puzyf+QsPX//fnW4v/k6vfv8/86ev94pfj///uRtf/y8vby9f9Fgv9EgPzt7/jj7f8mcf+eufj///x1pP/Z4fT///52pf9Uivv09fnV4v8ncf64yvj7+/6vxPX///yyyf9ynvr6+vvG1/8ocv3O2/fz9v53ofX8+/nb5v+YuPz//vy0yv8vdP3e5/fn7v/p7PX09//b5P7///6eu/9Df/zq7vjc5//I1vT//v3+/v////9+q/9Tivnn6fPy8/rW4fzI1/2qwv6YtPT8+fX39/jz9PqJrveTsvqfpuxrAAAAhXRSTlMAAAAAAAAAAAAAAAAAAAAABSlERA45nrSzP3TZ7e12Ao2LusMcrJYhFwaR/uhCwP/x5tZzBWHy+n3OvA8u17jmpwgPrOz5jAF2+3FA7PdYG8fuPQaX5jAGAV/39MCmdy/e/RGz/vj5/f/rAXj4//z13n52i5qmmFQ1lqOQaTgIBAYKEAYAKGjtAgAAAKNJREFUGBkFwT0uRGEYBtD3ZJ77uT8iGrXCAixCr7OCyRA2oCKqiUYkOgoJwhqUbMAKLEChVYhk4pxswvcWfFGVEbYtuJutqir9Ibc0uh0+V+mf5gY69yN2PzKJiTjCg8qa3uLRAJpKM9AMoL1VOi9zJ4CQ9z0jwHX+RAwAURUxAMSB/L7u35wCGlKaHrDkPGVmwhlc6FN6l1iHKxupn+djAPgHrEwa+qrzy0oAAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.vimeo::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAYFBMVEUAAAAIdZUKh6sLlLkLmr4LmsAMp88NrdYVW3MZj7Acstkrt9s1e5E7vN5EfI9JvdtKwuBijp5kpbl30eiDt8aG1uqRr7qTyNehxM+k4PCy3enB3OTg6Ovv9PXw+fz////L9U5WAAAAAXRSTlMAQObYZgAAAIFJREFUeNplz90OwiAMBWAQpAoyxclkP3je/y0H2AQXz0WT8100rRD6kNI9/cRroemQL3hXhoujZYj4OHoAmBvYGcBISwbWBvfXCrytnIDUQMkbsBpagMA7zhtQdyTFQAmIG7IkYniiZuh3XGsPqoOZkMOJOpAcLqUzNFGGu/57fwc1hgtp0mVSyQAAAABJRU5ErkJggg==') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.vine::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAHCUExURQAAAAC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+jwC+j////54tRLAAAACUdFJOUwAAAEK+9/e+QQIDAwEqzskfUZmUNHz2mrT++V1w+f5tCanNFUDwfEjtjAyyvg027Hki27QMBJzaHE/1+FkNsN0iZvv6bxyAlB589lQeyud0KB8PQO+ZBUrc+eXgcRG/3CoATe316Wxw/P6BAgBt+fp4IAwh0d4zM9q7Fm76qi605EMSrvfX/PRtAivF9IAJNMLxhA2KYlJ9AAAAAWJLR0SVCGB6gwAAAAd0SU1FB+ACDRI2MOJd7FgAAADrSURBVBjTLY9VWwJgGEPfiYWBha2YYHcHditgd3d3odjdivvBfgK727nYsyPiCrw03j6+fv6AaAMCgyAI1lElJBQSFh6hBxDJqOiY2Lh4SEKiIQlITmFqWrqRJkhGJrOA7Bzm5uUXsBBSVMySUpSVs6KyqrqmFmKuY30D0NjU3NLa1t6h9jvZ1Q30WGi19fb1KzAwyKFhYGSUY+MTkwpMTXNmFpibX+Di0rICWFldW9/A5tb2zu7ePtTrg0MeHePklPYzuDRw7uDF5RWvbwC32O0d7x8en55f4DHF6xv5/vHp6f/k6/vH+evuf1LAObptvSvrAAAAAElFTkSuQmCC') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.vocaroo::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAw9JREFUOE9jYMABuMwYmCyTJKUCGlSnFSy02TTzeOyCiQcDViX26qVz2TAyYtWmEMwuoZ3M7V40LcB79pHkc0svpvzY8jD//87nxf+3Pyn8v/ZO8v+VNyP/2mZJumI1QCWSI8232Hjumitlfw5+qPp/9l8TCt76JP//xkdx/wsXWCzjtWFkwTCkbWFe9plPk/+ga4Txz/xt/D/hkN//gMXif21a+NbyWjIwoRiy6GDT5rP/mlFsPfyp5n/NpOj/22+0gMUXXIz/H7hC/L/bFKFbPDZMrHAD5H35OPt2J9zacDv/f3V7xv9FhwrBGubsT/1//Pjx/1GJ/mD+/nfl/1v3Ovy3KRJNQbHdOlXCvOO03/+pm1P/v3v37n90hhtYw9HPtf8Xb2v937cmHswHeWPRxYj/LvkK3igGKARwicTO07118H3V/5kbi/4vPZMJtK3s/6YH2f+Pfq1B8VbjWrdnMu5s4nAD9CNFhKwz5DTUvLl419zKvAcLtG1P84BRl/b/5M/6/6f/NPzf/qzo84yj0Uus0xUU4Zor54bm9+4OfZG02OCuoAMTb9ZkC9ull1Nvrr2Z+XvRpaRfc65H/68F+jl9svEhzyLFWoccWVc+eyTHq/twydjlKRln7jX9bNMkMJnbhoFRL1xCqmKx6/yi2fYXa/c5/e846PV/5fW0/7OPx/yfcjzop34ulxdGGvDuU8mMXaX507lBuiN6ueadmQeT/p/93vf/1O+G//sP5fw/eL3o/5JLif8zVxs+Tlir9S26UyeFQQvJGBE7FvaFZ9LfN+1y+WjbItSb3GmXvXd15v8zroH/HxgE/D+aGPx/18vi/z07PeZNPRKxe/Kh0Ae8toxscCO4zBkYXArk9C1SxJUYjBkYPPIVtbbuTftz3cz//2O9wP/75iSAXdO72/dt2HL5F6YlfBW4MiJYXMiBiW3t7azHBx+V/t89N+H/8a+1//e9K/9attDp5LQjYX8SuvVL8RoAkmxa65299Erq1FnHo0qrl7t4BddriIs4MrM3rfWcFd+pGwVSAwBZ0bKP8yrZPAAAAABJRU5ErkJggg==') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+.linkify.youtube::before {\n\
+ content: \"\";\n\
+ background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\n\
+ padding-left: 18px;\n\
+}\n\
+/* XXX Moved to end of stylesheet to avoid breaking whole stylesheet in Maxthon. */\n\
+@supports (text-decoration-style: dashed) or (-moz-text-decoration-style: dashed) {\n\
+ .quotelink.forwardlink,\n\
+ .backlink.forwardlink {\n\
+ text-decoration: underline;\n\
+ -moz-text-decoration-style: dashed;\n\
+ text-decoration-style: dashed;\n\
+ border-bottom: none;\n\
+ }\n\
+}\n",
+
+report:
+"#g-recaptcha,\n\
+:root:not(.js-enabled) #captchaContainerAlt {\n\
+ height: auto;\n\
+}\n\
+#captchaContainerAlt td:nth-child(2) {\n\
+ display: table-cell !important;\n\
+}\n\
+/* Archive reports */\n\
+#archive-report {\n\
+ padding: 3px;\n\
+}\n\
+#archive-report-enabled {\n\
+ vertical-align: middle;\n\
+}\n\
+#archive-report > label {\n\
+ display: block;\n\
+}\n\
+#archive-report-reason {\n\
+ display: block;\n\
+ width: 98%;\n\
+}\n\
+.archive-report-success {\n\
+ color: green;\n\
+}\n\
+.archive-report-error {\n\
+ color: red;\n\
+}",
+
+www:
+"#captcha-cnt {\n\
+ height: auto;\n\
+}\n\
+:root:not(.js-enabled) #form {\n\
+ display: block;\n\
+}\n\
+#bd > div[style], #bd > div[style] > * {\n\
+ height: auto !important;\n\
+ margin: 0 !important;\n\
+ font-size: 0;\n\
+}\n",
+
+sub: function(css) {
+ var variables = {
+ site: g.SITE.selectors
+ };
+ return css.replace(/\$[\w\$]+/g, function(name) {
+ var words = name.slice(1).split('$');
+ var sel = variables;
+ for (var i = 0; i < words.length; i++) {
+ if (typeof sel !== 'object') return ':not(*)';
+ sel = $.getOwn(sel, words[i]);
+ }
+ if (typeof sel !== 'string') return ':not(*)';
+ return sel;
+ });
+}
+
+};
+
+$ = (function() {
+ var $,
+ slice = [].slice,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ $ = function(selector, root) {
+ if (root == null) {
+ root = d.body;
+ }
+ return root.querySelector(selector);
+ };
+
+ $.DAY = 24 * ($.HOUR = 60 * ($.MINUTE = 60 * ($.SECOND = 1000)));
+
+ $.id = function(id) {
+ return d.getElementById(id);
+ };
+
+ $.ready = function(fc) {
+ var cb;
+ if (d.readyState !== 'loading') {
+ $.queueTask(fc);
+ return;
+ }
+ cb = function() {
+ $.off(d, 'DOMContentLoaded', cb);
+ return fc();
+ };
+ return $.on(d, 'DOMContentLoaded', cb);
+ };
+
+ $.formData = function(form) {
+ var fd, key, val;
+ if (form instanceof HTMLFormElement) {
+ return new FormData(form);
+ }
+ fd = new FormData();
+ for (key in form) {
+ val = form[key];
+ if (val) {
+ if (typeof val === 'object' && 'newName' in val) {
+ fd.append(key, val, val.newName);
+ } else {
+ fd.append(key, val);
+ }
+ }
+ }
+ return fd;
+ };
+
+ $.extend = function(object, properties) {
+ var key, val;
+ for (key in properties) {
+ val = properties[key];
+ object[key] = val;
+ }
+ };
+
+ $.dict = function() {
+ return Object.create(null);
+ };
+
+ $.dict.clone = function(obj) {
+ var arr, i, j, key, map, ref, val;
+ if (typeof obj !== 'object' || obj === null) {
+ return obj;
+ } else if (obj instanceof Array) {
+ arr = [];
+ for (i = j = 0, ref = obj.length; j < ref; i = j += 1) {
+ arr.push($.dict.clone(obj[i]));
+ }
+ return arr;
+ } else {
+ map = Object.create(null);
+ for (key in obj) {
+ val = obj[key];
+ map[key] = $.dict.clone(val);
+ }
+ return map;
+ }
+ };
+
+ $.dict.json = function(str) {
+ return $.dict.clone(JSON.parse(str));
+ };
+
+ $.hasOwn = function(obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key);
+ };
+
+ $.getOwn = function(obj, key) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ return obj[key];
+ } else {
+ return void 0;
+ }
+ };
+
+ $.ajax = (function() {
+ var pageXHR;
+ if (window.wrappedJSObject && !XMLHttpRequest.wrappedJSObject) {
+ pageXHR = XPCNativeWrapper(window.wrappedJSObject.XMLHttpRequest);
+ } else {
+ pageXHR = XMLHttpRequest;
+ }
+ return function(url, options) {
+ var err, form, headers, key, onloadend, onprogress, r, ref, responseType, timeout, type, value, withCredentials;
+ if (options == null) {
+ options = {};
+ }
+ if (options.responseType == null) {
+ options.responseType = 'json';
+ }
+ options.type || (options.type = options.form && 'post' || 'get');
+ url = url.replace(/^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/');
+ onloadend = options.onloadend, timeout = options.timeout, responseType = options.responseType, withCredentials = options.withCredentials, type = options.type, onprogress = options.onprogress, form = options.form, headers = options.headers;
+ r = new pageXHR();
+ try {
+ r.open(type, url, true);
+ ref = headers || {};
+ for (key in ref) {
+ value = ref[key];
+ r.setRequestHeader(key, value);
+ }
+ $.extend(r, {
+ onloadend: onloadend,
+ timeout: timeout,
+ responseType: responseType,
+ withCredentials: withCredentials
+ });
+ $.extend(r.upload, {
+ onprogress: onprogress
+ });
+ $.on(r, 'error', function() {
+ if (!r.status) {
+ return c.warn("4chan X failed to load: " + url);
+ }
+ });
+ r.send(form);
+ } catch (error) {
+ err = error;
+ if (err.result !== 0x805e0006) {
+ throw err;
+ }
+ r.onloadend = onloadend;
+ $.queueTask($.event, 'error', null, r);
+ $.queueTask($.event, 'loadend', null, r);
+ }
+ return r;
+ };
+ })();
+
+ $.lastModified = $.dict();
+
+ $.whenModified = function(url, bucket, cb, options) {
+ var ajax, headers, params, r, ref, t, timeout, url0;
+ if (options == null) {
+ options = {};
+ }
+ timeout = options.timeout, ajax = options.ajax;
+ params = [];
+ if ($.engine === 'blink') {
+ params.push("s=" + bucket);
+ }
+ if (url.split('/')[2] === 'a.4cdn.org') {
+ params.push("t=" + (Date.now()));
+ }
+ url0 = url;
+ if (params.length) {
+ url += '?' + params.join('&');
+ }
+ headers = $.dict();
+ if ((t = (ref = $.lastModified[bucket]) != null ? ref[url0] : void 0) != null) {
+ headers['If-Modified-Since'] = t;
+ }
+ r = (ajax || $.ajax)(url, {
+ onloadend: function() {
+ var base;
+ ((base = $.lastModified)[bucket] || (base[bucket] = $.dict()))[url0] = this.getResponseHeader('Last-Modified');
+ return cb.call(this);
+ },
+ timeout: timeout,
+ headers: headers
+ });
+ return r;
+ };
+
+ (function() {
+ var reqs;
+ reqs = $.dict();
+ $.cache = function(url, cb, options) {
+ var ajax, onloadend, req;
+ if (options == null) {
+ options = {};
+ }
+ ajax = options.ajax;
+ if ((req = reqs[url])) {
+ if (req.callbacks) {
+ req.callbacks.push(cb);
+ } else {
+ $.queueTask(function() {
+ return cb.call(req, {
+ isCached: true
+ });
+ });
+ }
+ return req;
+ }
+ onloadend = function() {
+ var fn1, j, len, ref;
+ if (!this.status) {
+ delete reqs[url];
+ }
+ ref = this.callbacks;
+ fn1 = (function(_this) {
+ return function(cb) {
+ return $.queueTask(function() {
+ return cb.call(_this, {
+ isCached: false
+ });
+ });
+ };
+ })(this);
+ for (j = 0, len = ref.length; j < len; j++) {
+ cb = ref[j];
+ fn1(cb);
+ }
+ return delete this.callbacks;
+ };
+ req = (ajax || $.ajax)(url, {
+ onloadend: onloadend
+ });
+ req.callbacks = [cb];
+ return reqs[url] = req;
+ };
+ return $.cleanCache = function(testf) {
+ var url;
+ for (url in reqs) {
+ if (testf(url)) {
+ delete reqs[url];
+ }
+ }
+ };
+ })();
+
+ $.cb = {
+ checked: function() {
+ if ($.hasOwn(Conf, this.name)) {
+ $.set(this.name, this.checked);
+ return Conf[this.name] = this.checked;
+ }
+ },
+ value: function() {
+ if ($.hasOwn(Conf, this.name)) {
+ $.set(this.name, this.value.trim());
+ return Conf[this.name] = this.value;
+ }
+ }
+ };
+
+ $.asap = function(test, cb) {
+ if (test()) {
+ return cb();
+ } else {
+ return setTimeout($.asap, 25, test, cb);
+ }
+ };
+
+ $.onExists = function(root, selector, cb) {
+ var el, observer;
+ if (el = $(selector, root)) {
+ return cb(el);
+ }
+ observer = new MutationObserver(function() {
+ if (el = $(selector, root)) {
+ observer.disconnect();
+ return cb(el);
+ }
+ });
+ return observer.observe(root, {
+ childList: true,
+ subtree: true
+ });
+ };
+
+ $.addStyle = function(css, id, test) {
+ var style;
+ if (test == null) {
+ test = 'head';
+ }
+ style = $.el('style', {
+ textContent: css
+ });
+ if (id != null) {
+ style.id = id;
+ }
+ $.onExists(doc, test, function() {
+ return $.add(d.head, style);
+ });
+ return style;
+ };
+
+ $.addCSP = function(policy) {
+ var head, meta;
+ meta = $.el('meta', {
+ httpEquiv: 'Content-Security-Policy',
+ content: policy
+ });
+ if (d.head) {
+ $.add(d.head, meta);
+ return $.rm(meta);
+ } else {
+ head = $.add(doc || d, $.el('head'));
+ $.add(head, meta);
+ return $.rm(head);
+ }
+ };
+
+ $.x = function(path, root) {
+ root || (root = d.body);
+ return d.evaluate(path, root, null, 8, null).singleNodeValue;
+ };
+
+ $.X = function(path, root) {
+ root || (root = d.body);
+ return d.evaluate(path, root, null, 7, null);
+ };
+
+ $.addClass = function() {
+ var className, classNames, el, j, len;
+ el = arguments[0], classNames = 2 <= arguments.length ? slice.call(arguments, 1) : [];
+ for (j = 0, len = classNames.length; j < len; j++) {
+ className = classNames[j];
+ el.classList.add(className);
+ }
+ };
+
+ $.rmClass = function() {
+ var className, classNames, el, j, len;
+ el = arguments[0], classNames = 2 <= arguments.length ? slice.call(arguments, 1) : [];
+ for (j = 0, len = classNames.length; j < len; j++) {
+ className = classNames[j];
+ el.classList.remove(className);
+ }
+ };
+
+ $.toggleClass = function(el, className) {
+ return el.classList.toggle(className);
+ };
+
+ $.hasClass = function(el, className) {
+ return indexOf.call(el.classList, className) >= 0;
+ };
+
+ $.rm = function(el) {
+ return el != null ? el.remove() : void 0;
+ };
+
+ $.rmAll = function(root) {
+ return root.textContent = null;
+ };
+
+ $.tn = function(s) {
+ return d.createTextNode(s);
+ };
+
+ $.frag = function() {
+ return d.createDocumentFragment();
+ };
+
+ $.nodes = function(nodes) {
+ var frag, j, len, node;
+ if (!(nodes instanceof Array)) {
+ return nodes;
+ }
+ frag = $.frag();
+ for (j = 0, len = nodes.length; j < len; j++) {
+ node = nodes[j];
+ frag.appendChild(node);
+ }
+ return frag;
+ };
+
+ $.add = function(parent, el) {
+ return parent.appendChild($.nodes(el));
+ };
+
+ $.prepend = function(parent, el) {
+ return parent.insertBefore($.nodes(el), parent.firstChild);
+ };
+
+ $.after = function(root, el) {
+ return root.parentNode.insertBefore($.nodes(el), root.nextSibling);
+ };
+
+ $.before = function(root, el) {
+ return root.parentNode.insertBefore($.nodes(el), root);
+ };
+
+ $.replace = function(root, el) {
+ return root.parentNode.replaceChild($.nodes(el), root);
+ };
+
+ $.el = function(tag, properties, properties2) {
+ var el;
+ el = d.createElement(tag);
+ if (properties) {
+ $.extend(el, properties);
+ }
+ if (properties2) {
+ $.extend(el, properties2);
+ }
+ return el;
+ };
+
+ $.on = function(el, events, handler) {
+ var event, j, len, ref;
+ ref = events.split(' ');
+ for (j = 0, len = ref.length; j < len; j++) {
+ event = ref[j];
+ el.addEventListener(event, handler, false);
+ }
+ };
+
+ $.off = function(el, events, handler) {
+ var event, j, len, ref;
+ ref = events.split(' ');
+ for (j = 0, len = ref.length; j < len; j++) {
+ event = ref[j];
+ el.removeEventListener(event, handler, false);
+ }
+ };
+
+ $.one = function(el, events, handler) {
+ var cb;
+ cb = function(e) {
+ $.off(el, events, cb);
+ return handler.call(this, e);
+ };
+ return $.on(el, events, cb);
+ };
+
+ $.event = function(event, detail, root) {
+ if (root == null) {
+ root = d;
+ }
+ if ((detail != null) && typeof cloneInto === 'function') {
+ detail = cloneInto(detail, d.defaultView);
+ }
+ return root.dispatchEvent(new CustomEvent(event, {
+ bubbles: true,
+ cancelable: true,
+ detail: detail
+ }));
+ };
+
+ (function() {
+ var clone, err, ref, unsafeConstructors;
+ if (!(/PaleMoon\//.test(navigator.userAgent) && +(typeof GM_info !== "undefined" && GM_info !== null ? (ref = GM_info.version) != null ? ref.split('.')[0] : void 0 : void 0) >= 2 && typeof cloneInto === 'undefined')) {
+ return;
+ }
+ try {
+ return new CustomEvent('x', {
+ detail: {}
+ });
+ } catch (error) {
+ err = error;
+ unsafeConstructors = {
+ Object: unsafeWindow.Object,
+ Array: unsafeWindow.Array
+ };
+ clone = function(obj) {
+ var constructor, key, obj2, val;
+ if ((obj != null) && typeof obj === 'object' && (constructor = unsafeConstructors[obj.constructor.name])) {
+ obj2 = new constructor();
+ for (key in obj) {
+ val = obj[key];
+ obj2[key] = clone(val);
+ }
+ return obj2;
+ } else {
+ return obj;
+ }
+ };
+ return $.event = function(event, detail, root) {
+ if (root == null) {
+ root = d;
+ }
+ return root.dispatchEvent(new CustomEvent(event, {
+ bubbles: true,
+ cancelable: true,
+ detail: clone(detail)
+ }));
+ };
+ }
+ })();
+
+ $.modifiedClick = function(e) {
+ return e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0;
+ };
+
+ $.open = (typeof GM !== "undefined" && GM !== null ? GM.openInTab : void 0) != null ? GM.openInTab : typeof GM_openInTab !== "undefined" && GM_openInTab !== null ? GM_openInTab : function(url) {
+ return window.open(url, '_blank');
+ };
+
+ $.debounce = function(wait, fn) {
+ var args, exec, lastCall, that, timeout;
+ lastCall = 0;
+ timeout = null;
+ that = null;
+ args = null;
+ exec = function() {
+ lastCall = Date.now();
+ return fn.apply(that, args);
+ };
+ return function() {
+ args = arguments;
+ that = this;
+ if (lastCall < Date.now() - wait) {
+ return exec();
+ }
+ clearTimeout(timeout);
+ return timeout = setTimeout(exec, wait);
+ };
+ };
+
+ $.queueTask = (function() {
+ var execTask, taskChannel, taskQueue;
+ taskQueue = [];
+ execTask = function() {
+ var args, func, task;
+ task = taskQueue.shift();
+ func = task[0];
+ args = Array.prototype.slice.call(task, 1);
+ return func.apply(func, args);
+ };
+ if (window.MessageChannel) {
+ taskChannel = new MessageChannel();
+ taskChannel.port1.onmessage = execTask;
+ return function() {
+ taskQueue.push(arguments);
+ return taskChannel.port2.postMessage(null);
+ };
+ } else {
+ return function() {
+ taskQueue.push(arguments);
+ return setTimeout(execTask, 0);
+ };
+ }
+ })();
+
+ $.global = function(fn, data) {
+ var script;
+ if (doc) {
+ script = $.el('script', {
+ textContent: "(" + fn + ").call(document.currentScript.dataset);"
+ });
+ if (data) {
+ $.extend(script.dataset, data);
+ }
+ $.add(d.head || doc, script);
+ $.rm(script);
+ return script.dataset;
+ } else {
+ try {
+ fn.call(data);
+ } catch (error) {}
+ return data;
+ }
+ };
+
+ $.bytesToString = function(size) {
+ var unit;
+ unit = 0;
+ while (size >= 1024) {
+ size /= 1024;
+ unit++;
+ }
+ size = unit > 1 ? Math.round(size * 100) / 100 : Math.round(size);
+ return size + " " + ['B', 'KB', 'MB', 'GB'][unit];
+ };
+
+ $.minmax = function(value, min, max) {
+ return (value < min ? min : value > max ? max : value);
+ };
+
+ $.hasAudio = function(video) {
+ return video.mozHasAudio || !!video.webkitAudioDecodedByteCount;
+ };
+
+ $.luma = function(rgb) {
+ return rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114;
+ };
+
+ $.unescape = function(text) {
+ if (text == null) {
+ return text;
+ }
+ return text.replace(/<[^>]*>/g, '').replace(/&(amp|#039|quot|lt|gt|#44);/g, function(c) {
+ return {
+ '&amp;': '&',
+ '&#039;': "'",
+ '&quot;': '"',
+ '&lt;': '<',
+ '&gt;': '>',
+ '&#44;': ','
+ }[c];
+ });
+ };
+
+ $.isImage = function(url) {
+ return /\.(jpe?g|png|gif|bmp|webp)$/i.test(url);
+ };
+
+ $.isVideo = function(url) {
+ return /\.(webm|mp4|ogv)$/i.test(url);
+ };
+
+ $.engine = (function() {
+ if (/Edge\//.test(navigator.userAgent)) {
+ return 'edge';
+ }
+ if (/Chrome\//.test(navigator.userAgent)) {
+ return 'blink';
+ }
+ if (/WebKit\//.test(navigator.userAgent)) {
+ return 'webkit';
+ }
+ if (/Gecko\/|Goanna/.test(navigator.userAgent)) {
+ return 'gecko';
+ }
+ })();
+
+ $.platform = 'userscript';
+
+ $.hasStorage = (function() {
+ try {
+ if (localStorage.getItem(g.NAMESPACE + 'hasStorage') === 'true') {
+ return true;
+ }
+ localStorage.setItem(g.NAMESPACE + 'hasStorage', 'true');
+ return localStorage.getItem(g.NAMESPACE + 'hasStorage') === 'true';
+ } catch (error) {
+ return false;
+ }
+ })();
+
+ $.item = function(key, val) {
+ var item;
+ item = $.dict();
+ item[key] = val;
+ return item;
+ };
+
+ $.oneItemSugar = function(fn) {
+ return function(key, val, cb) {
+ if (typeof key === 'string') {
+ return fn($.item(key, val), cb);
+ } else {
+ return fn(key, val);
+ }
+ };
+ };
+
+ $.syncing = $.dict();
+
+ $.securityCheck = function(data) {
+ if (location.protocol !== 'https:') {
+ return delete data['Redirect to HTTPS'];
+ }
+ };
+
+ if (((typeof GM !== "undefined" && GM !== null ? GM.deleteValue : void 0) != null) && window.BroadcastChannel && (typeof GM_addValueChangeListener === "undefined" || GM_addValueChangeListener === null)) {
+ $.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync');
+ $.on($.syncChannel, 'message', function(e) {
+ var cb, key, ref, results, val;
+ ref = e.data;
+ results = [];
+ for (key in ref) {
+ val = ref[key];
+ if ((cb = $.syncing[key])) {
+ results.push(cb($.dict.json(JSON.stringify(val)), key));
+ }
+ }
+ return results;
+ });
+ $.sync = function(key, cb) {
+ return $.syncing[key] = cb;
+ };
+ $.forceSync = function() {};
+ $["delete"] = function(keys, cb) {
+ var key;
+ if (!(keys instanceof Array)) {
+ keys = [keys];
+ }
+ return Promise.all((function() {
+ var j, len, results;
+ results = [];
+ for (j = 0, len = keys.length; j < len; j++) {
+ key = keys[j];
+ results.push(GM.deleteValue(g.NAMESPACE + key));
+ }
+ return results;
+ })()).then(function() {
+ var items, j, key, len;
+ items = $.dict();
+ for (j = 0, len = keys.length; j < len; j++) {
+ key = keys[j];
+ items[key] = void 0;
+ }
+ $.syncChannel.postMessage(items);
+ return typeof cb === "function" ? cb() : void 0;
+ });
+ };
+ $.get = $.oneItemSugar(function(items, cb) {
+ var key, keys;
+ keys = Object.keys(items);
+ return Promise.all((function() {
+ var j, len, results;
+ results = [];
+ for (j = 0, len = keys.length; j < len; j++) {
+ key = keys[j];
+ results.push(GM.getValue(g.NAMESPACE + key));
+ }
+ return results;
+ })()).then(function(values) {
+ var i, j, len, val;
+ for (i = j = 0, len = values.length; j < len; i = ++j) {
+ val = values[i];
+ if (val) {
+ items[keys[i]] = $.dict.json(val);
+ }
+ }
+ return cb(items);
+ });
+ });
+ $.set = $.oneItemSugar(function(items, cb) {
+ var key, val;
+ $.securityCheck(items);
+ return Promise.all((function() {
+ var results;
+ results = [];
+ for (key in items) {
+ val = items[key];
+ results.push(GM.setValue(g.NAMESPACE + key, JSON.stringify(val)));
+ }
+ return results;
+ })()).then(function() {
+ $.syncChannel.postMessage(items);
+ return typeof cb === "function" ? cb() : void 0;
+ });
+ });
+ $.clear = function(cb) {
+ return GM.listValues().then(function(keys) {
+ return $["delete"](keys.map(function(key) {
+ return key.replace(g.NAMESPACE, '');
+ }), cb);
+ })["catch"](function() {
+ return $["delete"](Object.keys(Conf).concat(['previousversion', 'QR Size', 'QR.persona']), cb);
+ });
+ };
+ } else {
+ if (typeof GM_deleteValue === "undefined" || GM_deleteValue === null) {
+ $.perProtocolSettings = true;
+ }
+ if (typeof GM_deleteValue !== "undefined" && GM_deleteValue !== null) {
+ $.getValue = GM_getValue;
+ $.listValues = function() {
+ return GM_listValues();
+ };
+ } else if ($.hasStorage) {
+ $.getValue = function(key) {
+ return localStorage.getItem(key);
+ };
+ $.listValues = function() {
+ var key, results;
+ results = [];
+ for (key in localStorage) {
+ if (key.slice(0, g.NAMESPACE.length) === g.NAMESPACE) {
+ results.push(key);
+ }
+ }
+ return results;
+ };
+ } else {
+ $.getValue = function() {};
+ $.listValues = function() {
+ return [];
+ };
+ }
+ if (typeof GM_addValueChangeListener !== "undefined" && GM_addValueChangeListener !== null) {
+ $.setValue = GM_setValue;
+ $.deleteValue = GM_deleteValue;
+ } else if (typeof GM_deleteValue !== "undefined" && GM_deleteValue !== null) {
+ $.oldValue = $.dict();
+ $.setValue = function(key, val) {
+ GM_setValue(key, val);
+ if (key in $.syncing) {
+ $.oldValue[key] = val;
+ if ($.hasStorage) {
+ return localStorage.setItem(key, val);
+ }
+ }
+ };
+ $.deleteValue = function(key) {
+ GM_deleteValue(key);
+ if (key in $.syncing) {
+ delete $.oldValue[key];
+ if ($.hasStorage) {
+ return localStorage.removeItem(key);
+ }
+ }
+ };
+ if (!$.hasStorage) {
+ $.cantSync = true;
+ }
+ } else if ($.hasStorage) {
+ $.oldValue = $.dict();
+ $.setValue = function(key, val) {
+ if (key in $.syncing) {
+ $.oldValue[key] = val;
+ }
+ return localStorage.setItem(key, val);
+ };
+ $.deleteValue = function(key) {
+ if (key in $.syncing) {
+ delete $.oldValue[key];
+ }
+ return localStorage.removeItem(key);
+ };
+ } else {
+ $.setValue = function() {};
+ $.deleteValue = function() {};
+ $.cantSync = $.cantSet = true;
+ }
+ if (typeof GM_addValueChangeListener !== "undefined" && GM_addValueChangeListener !== null) {
+ $.sync = function(key, cb) {
+ return $.syncing[key] = GM_addValueChangeListener(g.NAMESPACE + key, function(key2, oldValue, newValue, remote) {
+ if (remote) {
+ if (newValue !== void 0) {
+ newValue = $.dict.json(newValue);
+ }
+ return cb(newValue, key);
+ }
+ });
+ };
+ $.forceSync = function() {};
+ } else if ((typeof GM_deleteValue !== "undefined" && GM_deleteValue !== null) || $.hasStorage) {
+ $.sync = function(key, cb) {
+ key = g.NAMESPACE + key;
+ $.syncing[key] = cb;
+ return $.oldValue[key] = $.getValue(key);
+ };
+ (function() {
+ var onChange;
+ onChange = function(arg) {
+ var cb, key, newValue;
+ key = arg.key, newValue = arg.newValue;
+ if (!(cb = $.syncing[key])) {
+ return;
+ }
+ if (newValue != null) {
+ if (newValue === $.oldValue[key]) {
+ return;
+ }
+ $.oldValue[key] = newValue;
+ return cb($.dict.json(newValue), key.slice(g.NAMESPACE.length));
+ } else {
+ if ($.oldValue[key] == null) {
+ return;
+ }
+ delete $.oldValue[key];
+ return cb(void 0, key.slice(g.NAMESPACE.length));
+ }
+ };
+ $.on(window, 'storage', onChange);
+ return $.forceSync = function(key) {
+ key = g.NAMESPACE + key;
+ return onChange({
+ key: key,
+ newValue: $.getValue(key)
+ });
+ };
+ })();
+ } else {
+ $.sync = function() {};
+ $.forceSync = function() {};
+ }
+ $["delete"] = function(keys) {
+ var j, key, len;
+ if (!(keys instanceof Array)) {
+ keys = [keys];
+ }
+ for (j = 0, len = keys.length; j < len; j++) {
+ key = keys[j];
+ $.deleteValue(g.NAMESPACE + key);
+ }
+ };
+ $.get = $.oneItemSugar(function(items, cb) {
+ return $.queueTask($.getSync, items, cb);
+ });
+ $.getSync = function(items, cb) {
+ var err, key, val2;
+ for (key in items) {
+ if ((val2 = $.getValue(g.NAMESPACE + key))) {
+ try {
+ items[key] = $.dict.json(val2);
+ } catch (error) {
+ err = error;
+ if (!/^(?:undefined)*$/.test(val2)) {
+ throw err;
+ }
+ }
+ }
+ }
+ return cb(items);
+ };
+ $.set = $.oneItemSugar(function(items, cb) {
+ $.securityCheck(items);
+ return $.queueTask(function() {
+ var key, value;
+ for (key in items) {
+ value = items[key];
+ $.setValue(g.NAMESPACE + key, JSON.stringify(value));
+ }
+ return typeof cb === "function" ? cb() : void 0;
+ });
+ });
+ $.clear = function(cb) {
+ $["delete"](Object.keys(Conf));
+ $["delete"](['previousversion', 'QR Size', 'QR.persona']);
+ try {
+ $["delete"]($.listValues().map(function(key) {
+ return key.replace(g.NAMESPACE, '');
+ }));
+ } catch (error) {}
+ return typeof cb === "function" ? cb() : void 0;
+ };
+ }
+
+ return $;
+
+}).call(this);
+
+$$ = (function() {
+ var $$,
+ slice = [].slice;
+
+ $$ = function(selector, root) {
+ if (root == null) {
+ root = d.body;
+ }
+ return slice.call(root.querySelectorAll(selector));
+ };
+
+ return $$;
+
+}).call(this);
+
+CrossOrigin = (function() {
+ var CrossOrigin, Request;
+
+ CrossOrigin = {
+ binary: function(url, cb, headers) {
+ var fallback, gmOptions;
+ if (headers == null) {
+ headers = $.dict();
+ }
+ url = url.replace(/^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/');
+ fallback = function() {
+ return $.ajax(url, {
+ headers: headers,
+ responseType: 'arraybuffer',
+ onloadend: function() {
+ if (this.status && this.response) {
+ return cb(new Uint8Array(this.response), this.getAllResponseHeaders());
+ } else {
+ return cb(null);
+ }
+ }
+ });
+ };
+ if (!(((typeof GM !== "undefined" && GM !== null ? GM.xmlHttpRequest : void 0) != null) || (typeof GM_xmlhttpRequest !== "undefined" && GM_xmlhttpRequest !== null))) {
+ fallback();
+ return;
+ }
+ gmOptions = {
+ method: "GET",
+ url: url,
+ headers: headers,
+ responseType: 'arraybuffer',
+ overrideMimeType: 'text/plain; charset=x-user-defined',
+ onload: function(xhr) {
+ var data, i, r;
+ if (xhr.response instanceof ArrayBuffer) {
+ data = new Uint8Array(xhr.response);
+ } else {
+ r = xhr.responseText;
+ data = new Uint8Array(r.length);
+ i = 0;
+ while (i < r.length) {
+ data[i] = r.charCodeAt(i);
+ i++;
+ }
+ }
+ return cb(data, xhr.responseHeaders);
+ },
+ onerror: function() {
+ return cb(null);
+ },
+ onabort: function() {
+ return cb(null);
+ }
+ };
+ try {
+ return ((typeof GM !== "undefined" && GM !== null ? GM.xmlHttpRequest : void 0) || GM_xmlhttpRequest)(gmOptions);
+ } catch (error) {
+ return fallback();
+ }
+ },
+ file: function(url, cb) {
+ return CrossOrigin.binary(url, function(data, headers) {
+ var blob, contentDisposition, contentType, match, mime, name, ref, ref1, ref2, ref3, ref4;
+ if (data == null) {
+ return cb(null);
+ }
+ name = (ref = url.match(/([^\/?#]+)\/*(?:$|[?#])/)) != null ? ref[1] : void 0;
+ contentType = (ref1 = headers.match(/Content-Type:\s*(.*)/i)) != null ? ref1[1] : void 0;
+ contentDisposition = (ref2 = headers.match(/Content-Disposition:\s*(.*)/i)) != null ? ref2[1] : void 0;
+ mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
+ match = (contentDisposition != null ? (ref3 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? ref3[1] : void 0 : void 0) || (contentType != null ? (ref4 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? ref4[1] : void 0 : void 0);
+ if (match) {
+ name = match.replace(/\\"/g, '"');
+ }
+ if (/^text\/plain;\s*charset=x-user-defined$/i.test(mime)) {
+ mime = $.getOwn(QR.typeFromExtension, name.match(/[^.]*$/)[0].toLowerCase()) || 'application/octet-stream';
+ }
+ blob = new Blob([data], {
+ type: mime
+ });
+ blob.name = name;
+ return cb(blob);
+ });
+ },
+ Request: Request = (function() {
+ function Request() {}
+
+ Request.prototype.status = 0;
+
+ Request.prototype.statusText = '';
+
+ Request.prototype.response = null;
+
+ Request.prototype.responseHeaderString = null;
+
+ Request.prototype.getResponseHeader = function(headerName) {
+ var header, i, j, key, len, ref, ref1, ref2, val;
+ if ((this.responseHeaders == null) && (this.responseHeaderString != null)) {
+ this.responseHeaders = $.dict();
+ ref = this.responseHeaderString.split('\r\n');
+ for (j = 0, len = ref.length; j < len; j++) {
+ header = ref[j];
+ if ((i = header.indexOf(':')) >= 0) {
+ key = header.slice(0, i).trim().toLowerCase();
+ val = header.slice(i + 1).trim();
+ this.responseHeaders[key] = val;
+ }
+ }
+ }
+ return (ref1 = (ref2 = this.responseHeaders) != null ? ref2[headerName.toLowerCase()] : void 0) != null ? ref1 : null;
+ };
+
+ Request.prototype.abort = function() {};
+
+ Request.prototype.onloadend = function() {};
+
+ return Request;
+
+ })(),
+ ajax: function(url, options) {
+ var gmOptions, gmReq, headers, onloadend, req, responseType, timeout;
+ if (options == null) {
+ options = {};
+ }
+ onloadend = options.onloadend, timeout = options.timeout, responseType = options.responseType, headers = options.headers;
+ if (responseType == null) {
+ responseType = 'json';
+ }
+ if (!(((typeof GM !== "undefined" && GM !== null ? GM.xmlHttpRequest : void 0) != null) || (typeof GM_xmlhttpRequest !== "undefined" && GM_xmlhttpRequest !== null))) {
+ return $.ajax(url, options);
+ }
+ req = new CrossOrigin.Request();
+ req.onloadend = onloadend;
+ gmOptions = {
+ method: 'GET',
+ url: url,
+ headers: headers,
+ timeout: timeout,
+ onload: function(xhr) {
+ var response;
+ try {
+ response = (function() {
+ switch (responseType) {
+ case 'json':
+ if (xhr.responseText) {
+ return JSON.parse(xhr.responseText);
+ } else {
+ return null;
+ }
+ break;
+ default:
+ return xhr.responseText;
+ }
+ })();
+ $.extend(req, {
+ response: response,
+ status: xhr.status,
+ statusText: xhr.statusText,
+ responseHeaderString: xhr.responseHeaders
+ });
+ } catch (error) {}
+ return req.onloadend();
+ },
+ onerror: function() {
+ return req.onloadend();
+ },
+ onabort: function() {
+ return req.onloadend();
+ },
+ ontimeout: function() {
+ return req.onloadend();
+ }
+ };
+ try {
+ gmReq = ((typeof GM !== "undefined" && GM !== null ? GM.xmlHttpRequest : void 0) || GM_xmlhttpRequest)(gmOptions);
+ } catch (error) {
+ return $.ajax(url, options);
+ }
+ if (gmReq && typeof gmReq.abort === 'function') {
+ req.abort = function() {
+ try {
+ return gmReq.abort();
+ } catch (error) {}
+ };
+ }
+ return req;
+ },
+ cache: function(url, cb) {
+ return $.cache(url, cb, {
+ ajax: CrossOrigin.ajax
+ });
+ },
+ permission: function(cb) {
+ return cb();
+ }
+ };
+
+ return CrossOrigin;
+
+}).call(this);
+
+Board = (function() {
+ var Board;
+
+ Board = (function() {
+ Board.prototype.toString = function() {
+ return this.ID;
+ };
+
+ function Board(ID) {
+ var ref;
+ this.ID = ID;
+ this.boardID = this.ID;
+ this.siteID = g.SITE.ID;
+ this.threads = new SimpleDict();
+ this.posts = new SimpleDict();
+ this.config = ((ref = BoardConfig.boards) != null ? ref[this.ID] : void 0) || {};
+ g.boards[this] = this;
+ }
+
+ Board.prototype.cooldowns = function() {
+ var c, c2, i, key, len, ref;
+ c2 = (this.config || {}).cooldowns || {};
+ c = {
+ thread: c2.threads || 0,
+ reply: c2.replies || 0,
+ image: c2.images || 0,
+ thread_global: 300
+ };
+ if (d.cookie.indexOf('pass_enabled=1') >= 0) {
+ ref = ['reply', 'image'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ key = ref[i];
+ c[key] = Math.ceil(c[key] / 2);
+ }
+ }
+ return c;
+ };
+
+ return Board;
+
+ })();
+
+ return Board;
+
+}).call(this);
+
+Callbacks = (function() {
+ var Callbacks;
+
+ Callbacks = (function() {
+ Callbacks.Post = new Callbacks('Post');
+
+ Callbacks.Thread = new Callbacks('Thread');
+
+ Callbacks.CatalogThread = new Callbacks('Catalog Thread');
+
+ Callbacks.CatalogThreadNative = new Callbacks('Catalog Thread');
+
+ function Callbacks(type) {
+ this.type = type;
+ this.keys = [];
+ }
+
+ Callbacks.prototype.push = function(arg) {
+ var cb, name;
+ name = arg.name, cb = arg.cb;
+ if (!this[name]) {
+ this.keys.push(name);
+ }
+ return this[name] = cb;
+ };
+
+ Callbacks.prototype.execute = function(node, keys, force) {
+ var err, errors, i, len, name, ref, ref1, ref2;
+ if (keys == null) {
+ keys = this.keys;
+ }
+ if (force == null) {
+ force = false;
+ }
+ if (node.callbacksExecuted && !force) {
+ return;
+ }
+ node.callbacksExecuted = true;
+ for (i = 0, len = keys.length; i < len; i++) {
+ name = keys[i];
+ try {
+ if ((ref = this[name]) != null) {
+ ref.call(node);
+ }
+ } catch (error) {
+ err = error;
+ if (!errors) {
+ errors = [];
+ }
+ errors.push({
+ message: ['"', name, '" crashed on node ', this.type, ' No.', node.ID, ' (', node.board, ').'].join(''),
+ error: err,
+ html: (ref1 = node.nodes) != null ? (ref2 = ref1.root) != null ? ref2.outerHTML : void 0 : void 0
+ });
+ }
+ }
+ if (errors) {
+ return Main.handleErrors(errors);
+ }
+ };
+
+ return Callbacks;
+
+ })();
+
+ return Callbacks;
+
+}).call(this);
+
+CatalogThread = (function() {
+ var CatalogThread;
+
+ CatalogThread = (function() {
+ CatalogThread.prototype.toString = function() {
+ return this.ID;
+ };
+
+ function CatalogThread(root, thread) {
+ var post;
+ this.thread = thread;
+ this.ID = this.thread.ID;
+ this.board = this.thread.board;
+ post = this.thread.OP.nodes.post;
+ this.nodes = {
+ root: root,
+ thumb: $('.catalog-thumb', post),
+ icons: $('.catalog-icons', post),
+ postCount: $('.post-count', post),
+ fileCount: $('.file-count', post),
+ pageCount: $('.page-count', post),
+ replies: null
+ };
+ this.thread.catalogView = this;
+ }
+
+ return CatalogThread;
+
+ })();
+
+ return CatalogThread;
+
+}).call(this);
+
+CatalogThreadNative = (function() {
+ var CatalogThreadNative;
+
+ CatalogThreadNative = (function() {
+ CatalogThreadNative.prototype.toString = function() {
+ return this.ID;
+ };
+
+ function CatalogThreadNative(root) {
+ this.nodes = {
+ root: root,
+ thumb: $(g.SITE.selectors.catalog.thumb, root)
+ };
+ this.siteID = g.SITE.ID;
+ this.boardID = this.nodes.thumb.parentNode.pathname.split(/\/+/)[1];
+ this.board = g.boards[this.boardID] || new Board(this.boardID);
+ this.ID = this.threadID = +(root.dataset.id || root.id).match(/\d*$/)[0];
+ this.thread = this.board.threads.get(this.ID) || new Thread(this.ID, this.board);
+ }
+
+ return CatalogThreadNative;
+
+ })();
+
+ return CatalogThreadNative;
+
+}).call(this);
+
+Connection = (function() {
+ var Connection,
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ Connection = (function() {
+ function Connection(target, origin, cb) {
+ this.target = target;
+ this.origin = origin;
+ this.cb = cb != null ? cb : {};
+ this.onMessage = bind(this.onMessage, this);
+ this.send = bind(this.send, this);
+ $.on(window, 'message', this.onMessage);
+ }
+
+ Connection.prototype.targetWindow = function() {
+ if (this.target instanceof window.HTMLIFrameElement) {
+ return this.target.contentWindow;
+ } else {
+ return this.target;
+ }
+ };
+
+ Connection.prototype.send = function(data) {
+ return this.targetWindow().postMessage("" + g.NAMESPACE + (JSON.stringify(data)), this.origin);
+ };
+
+ Connection.prototype.onMessage = function(e) {
+ var data, type, value;
+ if (!(e.source === this.targetWindow() && e.origin === this.origin && typeof e.data === 'string' && e.data.slice(0, g.NAMESPACE.length) === g.NAMESPACE)) {
+ return;
+ }
+ data = JSON.parse(e.data.slice(g.NAMESPACE.length));
+ for (type in data) {
+ value = data[type];
+ if ($.hasOwn(this.cb, type)) {
+ this.cb[type](value);
+ }
+ }
+ };
+
+ return Connection;
+
+ })();
+
+ return Connection;
+
+}).call(this);
+
+DataBoard = (function() {
+ var DataBoard,
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ DataBoard = (function() {
+ DataBoard.keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads', 'watcherLastModified', 'customTitles'];
+
+ function DataBoard(key1, sync, dontClean) {
+ var init;
+ this.key = key1;
+ this.onSync = bind(this.onSync, this);
+ this.initData(Conf[this.key]);
+ $.sync(this.key, this.onSync);
+ if (!dontClean) {
+ this.clean();
+ }
+ if (!sync) {
+ return;
+ }
+ init = (function(_this) {
+ return function() {
+ $.off(d, '4chanXInitFinished', init);
+ return _this.sync = sync;
+ };
+ })(this);
+ $.on(d, '4chanXInitFinished', init);
+ }
+
+ DataBoard.prototype.initData = function(data1) {
+ var base, boards, lastChecked, name, ref;
+ this.data = data1;
+ if (this.data.boards) {
+ ref = this.data, boards = ref.boards, lastChecked = ref.lastChecked;
+ this.data['4chan.org'] = {
+ boards: boards,
+ lastChecked: lastChecked
+ };
+ delete this.data.boards;
+ delete this.data.lastChecked;
+ }
+ return (base = this.data)[name = g.SITE.ID] || (base[name] = {
+ boards: $.dict()
+ });
+ };
+
+ DataBoard.prototype.changes = [];
+
+ DataBoard.prototype.save = function(change, cb) {
+ change();
+ this.changes.push(change);
+ return $.get(this.key, {
+ boards: $.dict()
+ }, (function(_this) {
+ return function(items) {
+ var i, len, needSync, ref;
+ if (!_this.changes.length) {
+ return;
+ }
+ needSync = (items[_this.key].version || 0) > (_this.data.version || 0);
+ if (needSync) {
+ _this.initData(items[_this.key]);
+ ref = _this.changes;
+ for (i = 0, len = ref.length; i < len; i++) {
+ change = ref[i];
+ change();
+ }
+ }
+ _this.changes = [];
+ _this.data.version = (_this.data.version || 0) + 1;
+ return $.set(_this.key, _this.data, function() {
+ if (needSync) {
+ if (typeof _this.sync === "function") {
+ _this.sync();
+ }
+ }
+ return typeof cb === "function" ? cb() : void 0;
+ });
+ };
+ })(this));
+ };
+
+ DataBoard.prototype.forceSync = function(cb) {
+ return $.get(this.key, {
+ boards: $.dict()
+ }, (function(_this) {
+ return function(items) {
+ var change, i, len, ref;
+ if ((items[_this.key].version || 0) > (_this.data.version || 0)) {
+ _this.initData(items[_this.key]);
+ ref = _this.changes;
+ for (i = 0, len = ref.length; i < len; i++) {
+ change = ref[i];
+ change();
+ }
+ if (typeof _this.sync === "function") {
+ _this.sync();
+ }
+ }
+ return typeof cb === "function" ? cb() : void 0;
+ };
+ })(this));
+ };
+
+ DataBoard.prototype["delete"] = function(arg, cb) {
+ var boardID, postID, siteID, threadID;
+ siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID;
+ siteID || (siteID = g.SITE.ID);
+ if (!this.data[siteID]) {
+ return;
+ }
+ return this.save((function(_this) {
+ return function() {
+ var ref;
+ if (postID) {
+ if (!((ref = _this.data[siteID].boards[boardID]) != null ? ref[threadID] : void 0)) {
+ return;
+ }
+ delete _this.data[siteID].boards[boardID][threadID][postID];
+ return _this.deleteIfEmpty({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID
+ });
+ } else if (threadID) {
+ if (!_this.data[siteID].boards[boardID]) {
+ return;
+ }
+ delete _this.data[siteID].boards[boardID][threadID];
+ return _this.deleteIfEmpty({
+ siteID: siteID,
+ boardID: boardID
+ });
+ } else {
+ return delete _this.data[siteID].boards[boardID];
+ }
+ };
+ })(this), cb);
+ };
+
+ DataBoard.prototype.deleteIfEmpty = function(arg) {
+ var boardID, siteID, threadID;
+ siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID;
+ if (!this.data[siteID]) {
+ return;
+ }
+ if (threadID) {
+ if (!Object.keys(this.data[siteID].boards[boardID][threadID]).length) {
+ delete this.data[siteID].boards[boardID][threadID];
+ return this.deleteIfEmpty({
+ siteID: siteID,
+ boardID: boardID
+ });
+ }
+ } else if (!Object.keys(this.data[siteID].boards[boardID]).length) {
+ return delete this.data[siteID].boards[boardID];
+ }
+ };
+
+ DataBoard.prototype.set = function(data, cb) {
+ return this.save((function(_this) {
+ return function() {
+ return _this.setUnsafe(data);
+ };
+ })(this), cb);
+ };
+
+ DataBoard.prototype.setUnsafe = function(arg) {
+ var base, base1, base2, base3, boardID, postID, siteID, threadID, val;
+ siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, val = arg.val;
+ siteID || (siteID = g.SITE.ID);
+ (base = this.data)[siteID] || (base[siteID] = {
+ boards: $.dict()
+ });
+ if (postID !== void 0) {
+ return ((base1 = ((base2 = this.data[siteID].boards)[boardID] || (base2[boardID] = $.dict())))[threadID] || (base1[threadID] = $.dict()))[postID] = val;
+ } else if (threadID !== void 0) {
+ return ((base3 = this.data[siteID].boards)[boardID] || (base3[boardID] = $.dict()))[threadID] = val;
+ } else {
+ return this.data[siteID].boards[boardID] = val;
+ }
+ };
+
+ DataBoard.prototype.extend = function(arg, cb) {
+ var boardID, postID, siteID, threadID, val;
+ siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, val = arg.val;
+ return this.save((function(_this) {
+ return function() {
+ var key, oldVal, subVal;
+ oldVal = _this.get({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID,
+ postID: postID,
+ defaultValue: $.dict()
+ });
+ for (key in val) {
+ subVal = val[key];
+ if (typeof subVal === 'undefined') {
+ delete oldVal[key];
+ } else {
+ oldVal[key] = subVal;
+ }
+ }
+ return _this.setUnsafe({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID,
+ postID: postID,
+ val: oldVal
+ });
+ };
+ })(this), cb);
+ };
+
+ DataBoard.prototype.setLastChecked = function(key) {
+ if (key == null) {
+ key = 'lastChecked';
+ }
+ return this.save((function(_this) {
+ return function() {
+ return _this.data[key] = Date.now();
+ };
+ })(this));
+ };
+
+ DataBoard.prototype.get = function(arg) {
+ var ID, board, boardID, defaultValue, i, len, postID, ref, siteID, thread, threadID, val;
+ siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, defaultValue = arg.defaultValue;
+ siteID || (siteID = g.SITE.ID);
+ if (board = (ref = this.data[siteID]) != null ? ref.boards[boardID] : void 0) {
+ if (threadID == null) {
+ if (postID != null) {
+ for (thread = i = 0, len = board.length; i < len; thread = ++i) {
+ ID = board[thread];
+ if (postID in thread) {
+ val = thread[postID];
+ break;
+ }
+ }
+ } else {
+ val = board;
+ }
+ } else if (thread = board[threadID]) {
+ val = postID != null ? thread[postID] : thread;
+ }
+ }
+ return val || defaultValue;
+ };
+
+ DataBoard.prototype.clean = function() {
+ var boardID, now, ref, ref1, siteID, val;
+ siteID = g.SITE.ID;
+ ref = this.data[siteID].boards;
+ for (boardID in ref) {
+ val = ref[boardID];
+ this.deleteIfEmpty({
+ siteID: siteID,
+ boardID: boardID
+ });
+ }
+ now = Date.now();
+ if (!((now - 2 * $.HOUR < (ref1 = this.data[siteID].lastChecked || 0) && ref1 <= now))) {
+ this.data[siteID].lastChecked = now;
+ for (boardID in this.data[siteID].boards) {
+ this.ajaxClean(boardID);
+ }
+ }
+ };
+
+ DataBoard.prototype.ajaxClean = function(boardID) {
+ var base, siteID, that, threadsList;
+ that = this;
+ siteID = g.SITE.ID;
+ threadsList = typeof (base = g.SITE.urls).threadsListJSON === "function" ? base.threadsListJSON({
+ siteID: siteID,
+ boardID: boardID
+ }) : void 0;
+ if (!threadsList) {
+ return;
+ }
+ return $.cache(threadsList, function() {
+ var archiveList, base1, response1;
+ if (this.status !== 200) {
+ return;
+ }
+ archiveList = typeof (base1 = g.SITE.urls).archiveListJSON === "function" ? base1.archiveListJSON({
+ siteID: siteID,
+ boardID: boardID
+ }) : void 0;
+ if (!archiveList) {
+ return that.ajaxCleanParse(boardID, this.response);
+ }
+ response1 = this.response;
+ return $.cache(archiveList, function() {
+ if (!(this.status === 200 || (!g.SITE.archivedBoardsKnown && this.status === 404))) {
+ return;
+ }
+ return that.ajaxCleanParse(boardID, response1, this.response);
+ });
+ });
+ };
+
+ DataBoard.prototype.ajaxCleanParse = function(boardID, response1, response2) {
+ var ID, board, i, j, k, len, len1, len2, page, ref, siteID, thread, threads;
+ siteID = g.SITE.ID;
+ if (!(board = this.data[siteID].boards[boardID])) {
+ return;
+ }
+ threads = $.dict();
+ if (response1) {
+ for (i = 0, len = response1.length; i < len; i++) {
+ page = response1[i];
+ ref = page.threads;
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ thread = ref[j];
+ ID = thread.no;
+ if (ID in board) {
+ threads[ID] = board[ID];
+ }
+ }
+ }
+ }
+ if (response2) {
+ for (k = 0, len2 = response2.length; k < len2; k++) {
+ ID = response2[k];
+ if (ID in board) {
+ threads[ID] = board[ID];
+ }
+ }
+ }
+ this.data[siteID].boards[boardID] = threads;
+ this.deleteIfEmpty({
+ siteID: siteID,
+ boardID: boardID
+ });
+ return $.set(this.key, this.data);
+ };
+
+ DataBoard.prototype.onSync = function(data) {
+ if (!((data.version || 0) > (this.data.version || 0))) {
+ return;
+ }
+ this.initData(data);
+ return typeof this.sync === "function" ? this.sync() : void 0;
+ };
+
+ return DataBoard;
+
+ })();
+
+ return DataBoard;
+
+}).call(this);
+
+Fetcher = (function() {
+ var Fetcher,
+ slice = [].slice;
+
+ Fetcher = (function() {
+ function Fetcher(boardID1, threadID, postID1, root, quoter) {
+ var board, post, ref, that, thread;
+ this.boardID = boardID1;
+ this.threadID = threadID;
+ this.postID = postID1;
+ this.root = root;
+ this.quoter = quoter;
+ if (post = g.posts.get(this.boardID + "." + this.postID)) {
+ this.insert(post);
+ return;
+ }
+ if ((post = (ref = Index.replyData) != null ? ref[this.boardID + "." + this.postID] : void 0) && (thread = g.threads.get(this.boardID + "." + this.threadID))) {
+ board = g.boards[this.boardID];
+ post = new Post(g.SITE.Build.postFromObject(post, this.boardID), thread, board, {
+ isFetchedQuote: true
+ });
+ Main.callbackNodes('Post', [post]);
+ this.insert(post);
+ return;
+ }
+ this.root.textContent = "Loading post No." + this.postID + "...";
+ if (this.threadID) {
+ that = this;
+ $.cache(g.SITE.urls.threadJSON({
+ boardID: this.boardID,
+ threadID: this.threadID
+ }), function(arg) {
+ var isCached;
+ isCached = arg.isCached;
+ return that.fetchedPost(this, isCached);
+ });
+ } else {
+ this.archivedPost();
+ }
+ }
+
+ Fetcher.prototype.insert = function(post) {
+ var boardID, clone, cssVersion, k, len, nodes, postID, quote, ref, ref1, ref2;
+ if (!this.root.parentNode) {
+ return;
+ }
+ this.quoter || (this.quoter = post);
+ clone = post.addClone(this.quoter.context, $.hasClass(this.root, 'dialog'));
+ Main.callbackNodes('Post', [clone]);
+ nodes = clone.nodes;
+ $.rmAll(nodes.root);
+ $.add(nodes.root, nodes.post);
+ ref = clone.nodes.quotelinks.concat(slice.call(clone.nodes.backlinks));
+ for (k = 0, len = ref.length; k < len; k++) {
+ quote = ref[k];
+ ref1 = Get.postDataFromLink(quote), boardID = ref1.boardID, postID = ref1.postID;
+ if (postID === this.quoter.ID && boardID === this.quoter.board.ID) {
+ $.addClass(quote, 'forwardlink');
+ }
+ }
+ if (clone.nodes.flag && !(Fetcher.flagCSS || (Fetcher.flagCSS = $('link[href^="//s.4cdn.org/css/flags."]')))) {
+ cssVersion = ((ref2 = $('link[href^="//s.4cdn.org/css/"]')) != null ? ref2.href.match(/\d+(?=\.css$)|$/)[0] : void 0) || Date.now();
+ Fetcher.flagCSS = $.el('link', {
+ rel: 'stylesheet',
+ href: "//s.4cdn.org/css/flags." + cssVersion + ".css"
+ });
+ $.add(d.head, Fetcher.flagCSS);
+ }
+ $.rmAll(this.root);
+ $.add(this.root, nodes.root);
+ return $.event('PostsInserted', null, this.root);
+ };
+
+ Fetcher.prototype.fetchedPost = function(req, isCached) {
+ var api, board, k, len, post, posts, status, that, thread;
+ if (post = g.posts.get(this.boardID + "." + this.postID)) {
+ this.insert(post);
+ return;
+ }
+ status = req.status;
+ if (status !== 200) {
+ if (status && this.archivedPost()) {
+ return;
+ }
+ $.addClass(this.root, 'warning');
+ this.root.textContent = status === 404 ? "Thread No." + this.threadID + " 404'd." : !status ? 'Connection Error' : "Error " + req.statusText + " (" + req.status + ").";
+ return;
+ }
+ posts = req.response.posts;
+ g.SITE.Build.spoilerRange[this.boardID] = posts[0].custom_spoiler;
+ for (k = 0, len = posts.length; k < len; k++) {
+ post = posts[k];
+ if (post.no === this.postID) {
+ break;
+ }
+ }
+ if (post.no !== this.postID) {
+ if (isCached) {
+ api = g.SITE.urls.threadJSON({
+ boardID: this.boardID,
+ threadID: this.threadID
+ });
+ $.cleanCache(function(url) {
+ return url === api;
+ });
+ that = this;
+ $.cache(api, function() {
+ return that.fetchedPost(this, false);
+ });
+ return;
+ }
+ if (this.archivedPost()) {
+ return;
+ }
+ $.addClass(this.root, 'warning');
+ this.root.textContent = "Post No." + this.postID + " was not found.";
+ return;
+ }
+ board = g.boards[this.boardID] || new Board(this.boardID);
+ thread = g.threads.get(this.boardID + "." + this.threadID) || new Thread(this.threadID, board);
+ post = new Post(g.SITE.Build.postFromObject(post, this.boardID), thread, board, {
+ isFetchedQuote: true
+ });
+ Main.callbackNodes('Post', [post]);
+ return this.insert(post);
+ };
+
+ Fetcher.prototype.archivedPost = function() {
+ var archive, encryptionOK, that, url;
+ if (!Conf['Resurrect Quotes']) {
+ return false;
+ }
+ if (!(url = Redirect.to('post', {
+ boardID: this.boardID,
+ postID: this.postID
+ }))) {
+ return false;
+ }
+ archive = Redirect.data.post[this.boardID];
+ encryptionOK = /^https:\/\//.test(url) || location.protocol === 'http:';
+ if (encryptionOK || Conf['Exempt Archives from Encryption']) {
+ that = this;
+ CrossOrigin.cache(url, function() {
+ var key, media, ref, ref1;
+ if (!encryptionOK && ((ref = this.response) != null ? ref.media : void 0)) {
+ media = this.response.media;
+ for (key in media) {
+ if (/_link$/.test(key)) {
+ if (!((ref1 = $.getOwn(media, key)) != null ? ref1.match(/^http:\/\//) : void 0)) {
+ delete media[key];
+ }
+ }
+ }
+ }
+ return that.parseArchivedPost(this.response, url, archive);
+ });
+ return true;
+ }
+ return false;
+ };
+
+ Fetcher.prototype.parseArchivedPost = function(data, url, archive) {
+ var board, comment, greentext, i, j, media_link, o, post, ref, tag, text, text2, thread, thumb_link;
+ if (post = g.posts.get(this.boardID + "." + this.postID)) {
+ this.insert(post);
+ return;
+ }
+ if (data == null) {
+ $.addClass(this.root, 'warning');
+ this.root.textContent = "Error fetching Post No." + this.postID + " from " + archive.name + ".";
+ return;
+ }
+ if (data.error) {
+ $.addClass(this.root, 'warning');
+ this.root.textContent = data.error;
+ return;
+ }
+ comment = (data.comment || '').split(/(\n|\[\/?(?:b|spoiler|code|moot|banned|fortune(?: color="#\w+")?|i|red|green|blue)\])/);
+ comment = (function() {
+ var k, len, results;
+ results = [];
+ for (i = k = 0, len = comment.length; k < len; i = ++k) {
+ text = comment[i];
+ if (i % 2 === 1) {
+ tag = this.archiveTags[text.replace(/\ .*\]/, ']')];
+ if (typeof tag === 'function') {
+ results.push(tag(text));
+ } else {
+ results.push(tag);
+ }
+ } else {
+ greentext = text[0] === '>';
+ text = text.replace(/(\[\/?[a-z]+):lit(\])/g, '$1$2');
+ text = (function() {
+ var l, len1, ref, results1;
+ ref = text.split(/(>>(?:>\/[a-z\d]+\/)?\d+)/g);
+ results1 = [];
+ for (j = l = 0, len1 = ref.length; l < len1; j = ++l) {
+ text2 = ref[j];
+ results1.push({innerHTML: ((j % 2) ? "<span class=\"deadlink\">" + E(text2) + "</span>" : E(text2))});
+ }
+ return results1;
+ })();
+ text = {innerHTML: ((greentext) ? "<span class=\"quote\">" + E.cat(text) + "</span>" : E.cat(text))};
+ results.push(text);
+ }
+ }
+ return results;
+ }).call(this);
+ comment = {innerHTML: E.cat(comment)};
+ this.threadID = +data.thread_num;
+ o = {
+ ID: this.postID,
+ threadID: this.threadID,
+ boardID: this.boardID,
+ isReply: this.postID !== this.threadID
+ };
+ o.info = {
+ subject: data.title,
+ email: data.email,
+ name: data.name || '',
+ tripcode: data.trip,
+ capcode: (function() {
+ switch (data.capcode) {
+ case 'M':
+ return 'Mod';
+ case 'A':
+ return 'Admin';
+ case 'D':
+ return 'Developer';
+ case 'V':
+ return 'Verified';
+ case 'F':
+ return 'Founder';
+ case 'G':
+ return 'Manager';
+ }
+ })(),
+ uniqueID: data.poster_hash,
+ flagCode: data.poster_country,
+ flagCodeTroll: data.troll_country_code,
+ flag: data.poster_country_name || data.troll_country_name,
+ dateUTC: data.timestamp,
+ dateText: data.fourchan_date,
+ commentHTML: comment
+ };
+ if (o.info.capcode) {
+ delete o.info.uniqueID;
+ }
+ if (data.media && !!+data.media.banned) {
+ o.fileDeleted = true;
+ } else if ((ref = data.media) != null ? ref.media_filename : void 0) {
+ thumb_link = data.media.thumb_link;
+ if ((thumb_link != null ? thumb_link[0] : void 0) === '/') {
+ thumb_link = url.split('/', 3).join('/') + thumb_link;
+ }
+ if (!Redirect.securityCheck(thumb_link)) {
+ thumb_link = '';
+ }
+ media_link = Redirect.to('file', {
+ boardID: this.boardID,
+ filename: data.media.media_orig
+ });
+ if (!Redirect.securityCheck(media_link)) {
+ media_link = '';
+ }
+ o.file = {
+ name: data.media.media_filename,
+ url: media_link || (this.boardID === 'f' ? location.protocol + "//" + (ImageHost.flashHost()) + "/" + this.boardID + "/" + (encodeURIComponent(E(data.media.media_filename))) : location.protocol + "//" + (ImageHost.host()) + "/" + this.boardID + "/" + data.media.media_orig),
+ height: data.media.media_h,
+ width: data.media.media_w,
+ MD5: data.media.media_hash,
+ size: $.bytesToString(data.media.media_size),
+ thumbURL: thumb_link || (location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.boardID + "/" + data.media.preview_orig),
+ theight: data.media.preview_h,
+ twidth: data.media.preview_w,
+ isSpoiler: data.media.spoiler === '1'
+ };
+ if (!/\.pdf$/.test(o.file.url)) {
+ o.file.dimensions = o.file.width + "x" + o.file.height;
+ }
+ if (this.boardID === 'f' && data.media.exif) {
+ o.file.tag = JSON.parse(data.media.exif).Tag;
+ }
+ }
+ o.extra = $.dict();
+ board = g.boards[this.boardID] || new Board(this.boardID);
+ thread = g.threads.get(this.boardID + "." + this.threadID) || new Thread(this.threadID, board);
+ post = new Post(g.SITE.Build.post(o), thread, board, {
+ isFetchedQuote: true
+ });
+ post.kill();
+ if (post.file) {
+ post.file.thumbURL = o.file.thumbURL;
+ }
+ Main.callbackNodes('Post', [post]);
+ return this.insert(post);
+ };
+
+ Fetcher.prototype.archiveTags = {
+ '\n': {innerHTML: "<br>"},
+ '[b]': {innerHTML: "<b>"},
+ '[/b]': {innerHTML: "</b>"},
+ '[spoiler]': {innerHTML: "<s>"},
+ '[/spoiler]': {innerHTML: "</s>"},
+ '[code]': {innerHTML: "<pre class=\"prettyprint\">"},
+ '[/code]': {innerHTML: "</pre>"},
+ '[moot]': {innerHTML: "<div style=\"padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px\">"},
+ '[/moot]': {innerHTML: "</div>"},
+ '[banned]': {innerHTML: "<strong style=\"color: red;\">"},
+ '[/banned]': {innerHTML: "</strong>"},
+ '[fortune]': function(text) {
+ return {innerHTML: "<span class=\"fortune\" style=\"color:" + E(text.match(/#\w+|$/)[0]) + "\"><b>"};
+ },
+ '[/fortune]': {innerHTML: "</b></span>"},
+ '[i]': {innerHTML: "<span class=\"mu-i\">"},
+ '[/i]': {innerHTML: "</span>"},
+ '[red]': {innerHTML: "<span class=\"mu-r\">"},
+ '[/red]': {innerHTML: "</span>"},
+ '[green]': {innerHTML: "<span class=\"mu-g\">"},
+ '[/green]': {innerHTML: "</span>"},
+ '[blue]': {innerHTML: "<span class=\"mu-b\">"},
+ '[/blue]': {innerHTML: "</span>"}
+ };
+
+ return Fetcher;
+
+ })();
+
+ return Fetcher;
+
+}).call(this);
+
+Notice = (function() {
+ var Notice,
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ Notice = (function() {
+ function Notice(type, content, timeout, onclose) {
+ this.timeout = timeout;
+ this.onclose = onclose;
+ this.close = bind(this.close, this);
+ this.add = bind(this.add, this);
+ this.el = $.el('div', {innerHTML: "<a href=\"javascript:;\" class=\"close fa fa-times\" title=\"Close\"></a><div class=\"message\"></div>"});
+ this.el.style.opacity = 0;
+ this.setType(type);
+ $.on(this.el.firstElementChild, 'click', this.close);
+ if (typeof content === 'string') {
+ content = $.tn(content);
+ }
+ $.add(this.el.lastElementChild, content);
+ $.ready(this.add);
+ }
+
+ Notice.prototype.setType = function(type) {
+ return this.el.className = "notification " + type;
+ };
+
+ Notice.prototype.add = function() {
+ if (this.closed) {
+ return;
+ }
+ if (d.hidden) {
+ $.on(d, 'visibilitychange', this.add);
+ return;
+ }
+ $.off(d, 'visibilitychange', this.add);
+ $.add(Header.noticesRoot, this.el);
+ this.el.clientHeight;
+ this.el.style.opacity = 1;
+ if (this.timeout) {
+ return setTimeout(this.close, this.timeout * $.SECOND);
+ }
+ };
+
+ Notice.prototype.close = function() {
+ this.closed = true;
+ $.off(d, 'visibilitychange', this.add);
+ $.rm(this.el);
+ return typeof this.onclose === "function" ? this.onclose() : void 0;
+ };
+
+ return Notice;
+
+ })();
+
+ return Notice;
+
+}).call(this);
+
+Post = (function() {
+ var Post,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Post = (function() {
+ Post.prototype.toString = function() {
+ return this.ID;
+ };
+
+ function Post(root, thread, board, flags) {
+ var clone, j, k, key, len, len1, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, selector;
+ this.thread = thread;
+ this.board = board;
+ if (flags == null) {
+ flags = {};
+ }
+ $.extend(this, flags);
+ this.ID = +root.id.match(/\d*$/)[0];
+ this.postID = this.ID;
+ this.threadID = this.thread.ID;
+ this.boardID = this.board.ID;
+ this.siteID = g.SITE.ID;
+ this.fullID = this.board + "." + this.ID;
+ this.context = this;
+ this.isReply = this.ID !== this.threadID;
+ root.dataset.fullID = this.fullID;
+ this.nodes = this.parseNodes(root);
+ if (!this.isReply) {
+ this.thread.OP = this;
+ ref = ['isSticky', 'isClosed', 'isArchived'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ key = ref[j];
+ if ((selector = g.SITE.selectors.icons[key])) {
+ this.thread[key] = !!$(selector, this.nodes.info);
+ }
+ }
+ if (this.thread.isArchived) {
+ this.thread.isClosed = true;
+ this.thread.kill();
+ }
+ }
+ this.info = {
+ subject: ((ref1 = this.nodes.subject) != null ? ref1.textContent : void 0) || void 0,
+ name: (ref2 = this.nodes.name) != null ? ref2.textContent : void 0,
+ email: this.nodes.email ? decodeURIComponent(this.nodes.email.href.replace(/^mailto:/, '')) : void 0,
+ tripcode: (ref3 = this.nodes.tripcode) != null ? ref3.textContent : void 0,
+ uniqueID: (ref4 = this.nodes.uniqueID) != null ? ref4.textContent : void 0,
+ capcode: (ref5 = this.nodes.capcode) != null ? ref5.textContent.replace('## ', '') : void 0,
+ pass: (ref6 = this.nodes.pass) != null ? ref6.title.match(/\d*$/)[0] : void 0,
+ flagCode: (ref7 = this.nodes.flag) != null ? (ref8 = ref7.className.match(/flag-(\w+)/)) != null ? ref8[1].toUpperCase() : void 0 : void 0,
+ flagCodeTroll: (ref9 = this.nodes.flag) != null ? (ref10 = ref9.className.match(/bfl-(\w+)/)) != null ? ref10[1].toUpperCase() : void 0 : void 0,
+ flag: (ref11 = this.nodes.flag) != null ? ref11.title : void 0,
+ date: this.nodes.date ? g.SITE.parseDate(this.nodes.date) : void 0
+ };
+ if (Conf['Anonymize']) {
+ this.info.nameBlock = 'Anonymous';
+ } else {
+ this.info.nameBlock = ((this.info.name || '') + " " + (this.info.tripcode || '')).trim();
+ }
+ if (this.info.capcode) {
+ this.info.nameBlock += " ## " + this.info.capcode;
+ }
+ if (this.info.uniqueID) {
+ this.info.nameBlock += " (ID: " + this.info.uniqueID + ")";
+ }
+ this.parseComment();
+ this.parseQuotes();
+ this.parseFiles();
+ this.isDead = false;
+ this.isHidden = false;
+ this.clones = [];
+ if (g.posts.get(this.fullID)) {
+ this.isRebuilt = true;
+ this.clones = g.posts.get(this.fullID).clones;
+ ref12 = this.clones;
+ for (k = 0, len1 = ref12.length; k < len1; k++) {
+ clone = ref12[k];
+ clone.origin = this;
+ }
+ }
+ if (!this.isFetchedQuote && this.ID > this.thread.lastPost) {
+ this.thread.lastPost = this.ID;
+ }
+ this.board.posts.push(this.ID, this);
+ this.thread.posts.push(this.ID, this);
+ g.posts.push(this.fullID, this);
+ }
+
+ Post.prototype.parseNodes = function(root) {
+ var base, info, key, nodes, post, ref, s, selector;
+ s = g.SITE.selectors;
+ post = $(s.post, root) || root;
+ info = $(s.infoRoot, post);
+ nodes = {
+ root: root,
+ bottom: this.isReply || !g.SITE.isOPContainerThread ? root : $(s.opBottom, root),
+ post: post,
+ info: info,
+ comment: $(s.comment, post),
+ quotelinks: [],
+ archivelinks: [],
+ embedlinks: []
+ };
+ ref = s.info;
+ for (key in ref) {
+ selector = ref[key];
+ nodes[key] = $(selector, info);
+ }
+ if (typeof (base = g.SITE).parseNodes === "function") {
+ base.parseNodes(this, nodes);
+ }
+ nodes.uniqueIDRoot || (nodes.uniqueIDRoot = nodes.uniqueID);
+ if ($.engine === 'edge') {
+ Object.defineProperty(nodes, 'backlinks', {
+ configurable: true,
+ enumerable: true,
+ get: function() {
+ return post.getElementsByClassName('backlink');
+ }
+ });
+ } else {
+ nodes.backlinks = post.getElementsByClassName('backlink');
+ }
+ return nodes;
+ };
+
+ Post.prototype.parseComment = function() {
+ var base, bq;
+ this.nodes.comment.normalize();
+ this.nodes.commentClean = bq = this.nodes.comment.cloneNode(true);
+ if (typeof (base = g.SITE).cleanComment === "function") {
+ base.cleanComment(bq);
+ }
+ return this.info.comment = this.nodesToText(bq);
+ };
+
+ Post.prototype.commentDisplay = function() {
+ var base, bq;
+ bq = this.nodes.commentClean.cloneNode(true);
+ if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) {
+ this.cleanSpoilers(bq);
+ }
+ if (typeof (base = g.SITE).cleanCommentDisplay === "function") {
+ base.cleanCommentDisplay(bq);
+ }
+ return this.nodesToText(bq).trim().replace(/\s+$/gm, '');
+ };
+
+ Post.prototype.commentOrig = function() {
+ var base, bq;
+ bq = this.nodes.commentClean.cloneNode(true);
+ if (typeof (base = g.SITE).insertTags === "function") {
+ base.insertTags(bq);
+ }
+ return this.nodesToText(bq);
+ };
+
+ Post.prototype.nodesToText = function(bq) {
+ var i, node, nodes, text;
+ text = "";
+ nodes = $.X('.//br|.//text()', bq);
+ i = 0;
+ while (node = nodes.snapshotItem(i++)) {
+ text += node.data || '\n';
+ }
+ return text;
+ };
+
+ Post.prototype.cleanSpoilers = function(bq) {
+ var j, len, node, spoilers;
+ spoilers = $$(g.SITE.selectors.spoiler, bq);
+ for (j = 0, len = spoilers.length; j < len; j++) {
+ node = spoilers[j];
+ $.replace(node, $.tn('[spoiler]'));
+ }
+ };
+
+ Post.prototype.parseQuotes = function() {
+ var j, len, quotelink, ref;
+ this.quotes = [];
+ ref = $$(g.SITE.selectors.quotelink, this.nodes.comment);
+ for (j = 0, len = ref.length; j < len; j++) {
+ quotelink = ref[j];
+ this.parseQuote(quotelink);
+ }
+ };
+
+ Post.prototype.parseQuote = function(quotelink) {
+ var fullID, match;
+ match = quotelink.href.match(g.SITE.regexp.quotelink);
+ if (!(match || (this.isClone && quotelink.dataset.postID))) {
+ return;
+ }
+ this.nodes.quotelinks.push(quotelink);
+ if (this.isClone) {
+ return;
+ }
+ fullID = match[1] + "." + match[3];
+ if (indexOf.call(this.quotes, fullID) < 0) {
+ return this.quotes.push(fullID);
+ }
+ };
+
+ Post.prototype.parseFiles = function() {
+ var docIndex, file, fileRoot, fileRoots, index, j, len;
+ this.files = [];
+ fileRoots = this.fileRoots();
+ index = 0;
+ for (docIndex = j = 0, len = fileRoots.length; j < len; docIndex = ++j) {
+ fileRoot = fileRoots[docIndex];
+ if ((file = this.parseFile(fileRoot))) {
+ file.index = index++;
+ file.docIndex = docIndex;
+ this.files.push(file);
+ }
+ }
+ if (this.files.length) {
+ return this.file = this.files[0];
+ }
+ };
+
+ Post.prototype.fileRoots = function() {
+ var roots;
+ if (g.SITE.selectors.multifile) {
+ roots = $$(g.SITE.selectors.multifile, this.nodes.root);
+ if (roots.length) {
+ return roots;
+ }
+ }
+ return [this.nodes.root];
+ };
+
+ Post.prototype.parseFile = function(fileRoot) {
+ var file, key, ref, ref1, selector, size, unit;
+ file = {};
+ ref = g.SITE.selectors.file;
+ for (key in ref) {
+ selector = ref[key];
+ file[key] = $(selector, fileRoot);
+ }
+ file.thumbLink = (ref1 = file.thumb) != null ? ref1.parentNode : void 0;
+ if (!(file.text && file.link)) {
+ return;
+ }
+ if (!g.SITE.parseFile(this, file)) {
+ return;
+ }
+ $.extend(file, {
+ url: file.link.href,
+ isImage: $.isImage(file.link.href),
+ isVideo: $.isVideo(file.link.href)
+ });
+ size = +file.size.match(/[\d.]+/)[0];
+ unit = ['B', 'KB', 'MB', 'GB'].indexOf(file.size.match(/\w+$/)[0]);
+ while (unit-- > 0) {
+ size *= 1024;
+ }
+ file.sizeInBytes = size;
+ return file;
+ };
+
+ Post.deadMark = $.el('span', {
+ textContent: '\u00A0(Dead)',
+ className: 'qmark-dead'
+ });
+
+ Post.prototype.kill = function(file, index) {
+ var clone, j, k, len, len1, quotelink, ref, ref1, strong;
+ if (index == null) {
+ index = 0;
+ }
+ if (file) {
+ if (this.isDead || this.files[index].isDead) {
+ return;
+ }
+ this.files[index].isDead = true;
+ $.addClass(this.nodes.root, 'deleted-file');
+ } else {
+ if (this.isDead) {
+ return;
+ }
+ this.isDead = true;
+ $.rmClass(this.nodes.root, 'deleted-file');
+ $.addClass(this.nodes.root, 'deleted-post');
+ }
+ if (!(strong = $('strong.warning', this.nodes.info))) {
+ strong = $.el('strong', {
+ className: 'warning'
+ });
+ $.after($('input', this.nodes.info), strong);
+ }
+ strong.textContent = file ? '[File deleted]' : '[Deleted]';
+ if (this.isClone) {
+ return;
+ }
+ ref = this.clones;
+ for (j = 0, len = ref.length; j < len; j++) {
+ clone = ref[j];
+ clone.kill(file, index);
+ }
+ if (file) {
+ return;
+ }
+ ref1 = Get.allQuotelinksLinkingTo(this);
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ quotelink = ref1[k];
+ if (!(!$.hasClass(quotelink, 'deadlink'))) {
+ continue;
+ }
+ $.add(quotelink, Post.deadMark.cloneNode(true));
+ $.addClass(quotelink, 'deadlink');
+ }
+ };
+
+ Post.prototype.resurrect = function() {
+ var clone, j, k, len, len1, quotelink, ref, ref1, strong;
+ this.isDead = false;
+ $.rmClass(this.nodes.root, 'deleted-post');
+ strong = $('strong.warning', this.nodes.info);
+ if (this.files.some(function(file) {
+ return file.isDead;
+ })) {
+ $.addClass(this.nodes.root, 'deleted-file');
+ strong.textContent = '[File deleted]';
+ } else {
+ $.rm(strong);
+ }
+ if (this.isClone) {
+ return;
+ }
+ ref = this.clones;
+ for (j = 0, len = ref.length; j < len; j++) {
+ clone = ref[j];
+ clone.resurrect();
+ }
+ ref1 = Get.allQuotelinksLinkingTo(this);
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ quotelink = ref1[k];
+ if (!($.hasClass(quotelink, 'deadlink'))) {
+ continue;
+ }
+ $.rm($('.qmark-dead', quotelink));
+ $.rmClass(quotelink, 'deadlink');
+ }
+ };
+
+ Post.prototype.collect = function() {
+ g.posts.rm(this.fullID);
+ this.thread.posts.rm(this);
+ return this.board.posts.rm(this);
+ };
+
+ Post.prototype.addClone = function(context, contractThumb) {
+ Callbacks.Post.execute(this);
+ return new Post.Clone(this, context, contractThumb);
+ };
+
+ Post.prototype.rmClone = function(index) {
+ var clone, j, len, ref;
+ this.clones.splice(index, 1);
+ ref = this.clones.slice(index);
+ for (j = 0, len = ref.length; j < len; j++) {
+ clone = ref[j];
+ clone.nodes.root.dataset.clone = index++;
+ }
+ };
+
+ Post.prototype.setCatalogOP = function(isCatalogOP) {
+ this.nodes.root.classList.toggle('catalog-container', isCatalogOP);
+ this.nodes.root.classList.toggle('opContainer', !isCatalogOP);
+ this.nodes.post.classList.toggle('catalog-post', isCatalogOP);
+ this.nodes.post.classList.toggle('op', !isCatalogOP);
+ return this.nodes.post.style.left = this.nodes.post.style.right = null;
+ };
+
+ return Post;
+
+ })();
+
+ return Post;
+
+}).call(this);
+
+(function() {
+ var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ hasProp = {}.hasOwnProperty,
+ slice = [].slice;
+
+ Post.Clone = (function(superClass) {
+ extend(_Class, superClass);
+
+ _Class.prototype.isClone = true;
+
+ function _Class() {
+ var that;
+ that = Object.create(Post.Clone.prototype);
+ that.construct.apply(that, arguments);
+ return that;
+ }
+
+ _Class.prototype.construct = function(origin, context, contractThumb) {
+ var base, file, fileRoot, fileRoots, i, inline, inlined, j, k, key, l, len, len1, len2, len3, len4, m, node, nodes, originFile, ref, ref1, ref2, ref3, ref4, ref5, ref6, root, selector, val;
+ this.origin = origin;
+ this.context = context;
+ ref = ['ID', 'postID', 'threadID', 'boardID', 'siteID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ key = ref[i];
+ this[key] = this.origin[key];
+ }
+ nodes = this.origin.nodes;
+ root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true);
+ (base = Post.Clone).suffix || (base.suffix = 0);
+ ref1 = [root].concat(slice.call($$('[id]', root)));
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ node = ref1[j];
+ node.id += "_" + Post.Clone.suffix;
+ }
+ Post.Clone.suffix++;
+ ref2 = $$('.inline', root);
+ for (k = 0, len2 = ref2.length; k < len2; k++) {
+ inline = ref2[k];
+ $.rm(inline);
+ }
+ ref3 = $$('.inlined', root);
+ for (l = 0, len3 = ref3.length; l < len3; l++) {
+ inlined = ref3[l];
+ $.rmClass(inlined, 'inlined');
+ }
+ this.nodes = this.parseNodes(root);
+ root.hidden = false;
+ $.rmClass(root, 'forwarded');
+ $.rmClass(this.nodes.post, 'highlight');
+ if (!this.isReply) {
+ this.setCatalogOP(false);
+ $.rm($('.catalog-link', this.nodes.post));
+ $.rm($('.catalog-stats', this.nodes.post));
+ $.rm($('.catalog-replies', this.nodes.post));
+ }
+ this.parseQuotes();
+ this.quotes = slice.call(this.origin.quotes);
+ this.files = [];
+ if (this.origin.files.length) {
+ fileRoots = this.fileRoots();
+ }
+ ref4 = this.origin.files;
+ for (m = 0, len4 = ref4.length; m < len4; m++) {
+ originFile = ref4[m];
+ file = {};
+ for (key in originFile) {
+ val = originFile[key];
+ file[key] = val;
+ }
+ fileRoot = fileRoots[file.docIndex];
+ ref5 = g.SITE.selectors.file;
+ for (key in ref5) {
+ selector = ref5[key];
+ file[key] = $(selector, fileRoot);
+ }
+ file.thumbLink = (ref6 = file.thumb) != null ? ref6.parentNode : void 0;
+ if (file.thumbLink) {
+ file.fullImage = $('.full-image', file.thumbLink);
+ }
+ file.videoControls = $('.video-controls', file.text);
+ if (file.videoThumb) {
+ file.thumb.muted = true;
+ }
+ this.files.push(file);
+ }
+ if (this.files.length) {
+ this.file = this.files[0];
+ if (this.file.thumb && contractThumb) {
+ ImageExpand.contract(this);
+ }
+ }
+ if (this.origin.isDead) {
+ this.isDead = true;
+ }
+ return root.dataset.clone = this.origin.clones.push(this) - 1;
+ };
+
+ _Class.prototype.cloneWithoutVideo = function(node) {
+ var child, clone, i, len, ref;
+ if (node.tagName === 'VIDEO' && !node.dataset.md5) {
+ return [];
+ } else if (node.nodeType === Node.ELEMENT_NODE && $('video', node)) {
+ clone = node.cloneNode(false);
+ ref = node.childNodes;
+ for (i = 0, len = ref.length; i < len; i++) {
+ child = ref[i];
+ $.add(clone, this.cloneWithoutVideo(child));
+ }
+ return clone;
+ } else {
+ return node.cloneNode(true);
+ }
+ };
+
+ return _Class;
+
+ })(Post);
+
+}).call(this);
+
+RandomAccessList = (function() {
+ var RandomAccessList;
+
+ RandomAccessList = (function() {
+ function RandomAccessList(items) {
+ var i, item, len;
+ this.length = 0;
+ if (items) {
+ for (i = 0, len = items.length; i < len; i++) {
+ item = items[i];
+ this.push(item);
+ }
+ }
+ }
+
+ RandomAccessList.prototype.push = function(data) {
+ var ID, item, last;
+ ID = data.ID;
+ ID || (ID = data.id);
+ if (this[ID]) {
+ return;
+ }
+ last = this.last;
+ this[ID] = item = {
+ prev: last,
+ next: null,
+ data: data,
+ ID: ID
+ };
+ item.prev = last;
+ this.last = last ? last.next = item : this.first = item;
+ return this.length++;
+ };
+
+ RandomAccessList.prototype.before = function(root, item) {
+ var prev;
+ if (item.next === root || item === root) {
+ return;
+ }
+ this.rmi(item);
+ prev = root.prev;
+ root.prev = item;
+ item.next = root;
+ item.prev = prev;
+ if (prev) {
+ return prev.next = item;
+ } else {
+ return this.first = item;
+ }
+ };
+
+ RandomAccessList.prototype.after = function(root, item) {
+ var next;
+ if (item.prev === root || item === root) {
+ return;
+ }
+ this.rmi(item);
+ next = root.next;
+ root.next = item;
+ item.prev = root;
+ item.next = next;
+ if (next) {
+ return next.prev = item;
+ } else {
+ return this.last = item;
+ }
+ };
+
+ RandomAccessList.prototype.prepend = function(item) {
+ var first;
+ first = this.first;
+ if (item === first || !this[item.ID]) {
+ return;
+ }
+ this.rmi(item);
+ item.next = first;
+ if (first) {
+ first.prev = item;
+ } else {
+ this.last = item;
+ }
+ this.first = item;
+ return delete item.prev;
+ };
+
+ RandomAccessList.prototype.shift = function() {
+ return this.rm(this.first.ID);
+ };
+
+ RandomAccessList.prototype.order = function() {
+ var item, order;
+ order = [item = this.first];
+ while (item = item.next) {
+ order.push(item);
+ }
+ return order;
+ };
+
+ RandomAccessList.prototype.rm = function(ID) {
+ var item;
+ item = this[ID];
+ if (!item) {
+ return;
+ }
+ delete this[ID];
+ this.length--;
+ this.rmi(item);
+ delete item.next;
+ return delete item.prev;
+ };
+
+ RandomAccessList.prototype.rmi = function(item) {
+ var next, prev;
+ prev = item.prev, next = item.next;
+ if (prev) {
+ prev.next = next;
+ } else {
+ this.first = next;
+ }
+ if (next) {
+ return next.prev = prev;
+ } else {
+ return this.last = prev;
+ }
+ };
+
+ return RandomAccessList;
+
+ })();
+
+ return RandomAccessList;
+
+}).call(this);
+
+ShimSet = (function() {
+ var ShimSet;
+
+ ShimSet = (function() {
+ function ShimSet() {
+ this.elements = $.dict();
+ this.size = 0;
+ }
+
+ ShimSet.prototype.has = function(value) {
+ return value in this.elements;
+ };
+
+ ShimSet.prototype.add = function(value) {
+ if (this.elements[value]) {
+ return;
+ }
+ this.elements[value] = true;
+ return this.size++;
+ };
+
+ ShimSet.prototype["delete"] = function(value) {
+ if (!this.elements[value]) {
+ return;
+ }
+ delete this.elements[value];
+ return this.size--;
+ };
+
+ return ShimSet;
+
+ })();
+
+ if (!('Set' in window)) {
+ window.Set = ShimSet;
+ }
+
+ return ShimSet;
+
+}).call(this);
+
+SimpleDict = (function() {
+ var SimpleDict,
+ slice = [].slice;
+
+ SimpleDict = (function() {
+ function SimpleDict() {
+ this.keys = [];
+ }
+
+ SimpleDict.prototype.push = function(key, data) {
+ key = "" + key;
+ if (!this[key]) {
+ this.keys.push(key);
+ }
+ return this[key] = data;
+ };
+
+ SimpleDict.prototype.rm = function(key) {
+ var i;
+ key = "" + key;
+ if ((i = this.keys.indexOf(key)) !== -1) {
+ this.keys.splice(i, 1);
+ return delete this[key];
+ }
+ };
+
+ SimpleDict.prototype.forEach = function(fn) {
+ var j, key, len, ref;
+ ref = slice.call(this.keys);
+ for (j = 0, len = ref.length; j < len; j++) {
+ key = ref[j];
+ fn(this[key]);
+ }
+ };
+
+ SimpleDict.prototype.get = function(key) {
+ if (key === 'keys') {
+ return void 0;
+ } else {
+ return $.getOwn(this, key);
+ }
+ };
+
+ return SimpleDict;
+
+ })();
+
+ return SimpleDict;
+
+}).call(this);
+
+Thread = (function() {
+ var Thread;
+
+ Thread = (function() {
+ Thread.prototype.toString = function() {
+ return this.ID;
+ };
+
+ function Thread(ID, board) {
+ this.board = board;
+ this.ID = +ID;
+ this.threadID = this.ID;
+ this.boardID = this.board.ID;
+ this.siteID = g.SITE.ID;
+ this.fullID = this.board + "." + this.ID;
+ this.posts = new SimpleDict();
+ this.isDead = false;
+ this.isHidden = false;
+ this.isSticky = false;
+ this.isClosed = false;
+ this.isArchived = false;
+ this.postLimit = false;
+ this.fileLimit = false;
+ this.lastPost = 0;
+ this.ipCount = void 0;
+ this.json = null;
+ this.OP = null;
+ this.catalogView = null;
+ this.nodes = {
+ root: null
+ };
+ this.board.threads.push(this.ID, this);
+ g.threads.push(this.fullID, this);
+ }
+
+ Thread.prototype.setPage = function(pageNum) {
+ var icon, info, ref, reply;
+ ref = this.OP.nodes, info = ref.info, reply = ref.reply;
+ if (!(icon = $('.page-num', info))) {
+ icon = $.el('span', {
+ className: 'page-num'
+ });
+ $.replace(reply.parentNode.previousSibling, [$.tn(' '), icon, $.tn(' ')]);
+ }
+ icon.title = "This thread is on page " + pageNum + " in the original index.";
+ icon.textContent = "[" + pageNum + "]";
+ if (this.catalogView) {
+ return this.catalogView.nodes.pageCount.textContent = pageNum;
+ }
+ };
+
+ Thread.prototype.setCount = function(type, count, reachedLimit) {
+ var el;
+ if (!this.catalogView) {
+ return;
+ }
+ el = this.catalogView.nodes[type + "Count"];
+ el.textContent = count;
+ return (reachedLimit ? $.addClass : $.rmClass)(el, 'warning');
+ };
+
+ Thread.prototype.setStatus = function(type, status) {
+ var name;
+ name = "is" + type;
+ if (this[name] === status) {
+ return;
+ }
+ this[name] = status;
+ if (!this.OP) {
+ return;
+ }
+ this.setIcon('Sticky', this.isSticky);
+ this.setIcon('Closed', this.isClosed && !this.isArchived);
+ return this.setIcon('Archived', this.isArchived);
+ };
+
+ Thread.prototype.setIcon = function(type, status) {
+ var icon, root, typeLC;
+ typeLC = type.toLowerCase();
+ icon = $("." + typeLC + "Icon", this.OP.nodes.info);
+ if (!!icon === status) {
+ return;
+ }
+ if (!status) {
+ $.rm(icon.previousSibling);
+ $.rm(icon);
+ if (this.catalogView) {
+ $.rm($("." + typeLC + "Icon", this.catalogView.nodes.icons));
+ }
+ return;
+ }
+ icon = $.el('img', {
+ src: "" + g.SITE.Build.staticPath + typeLC + g.SITE.Build.gifIcon,
+ alt: type,
+ title: type,
+ className: typeLC + "Icon retina"
+ });
+ if (g.BOARD.ID === 'f') {
+ icon.style.cssText = 'height: 18px; width: 18px;';
+ }
+ root = type !== 'Sticky' && this.isSticky ? $('.stickyIcon', this.OP.nodes.info) : $('.page-num', this.OP.nodes.info) || this.OP.nodes.quote;
+ $.after(root, [$.tn(' '), icon]);
+ if (!this.catalogView) {
+ return;
+ }
+ return (type === 'Sticky' && this.isClosed ? $.prepend : $.add)(this.catalogView.nodes.icons, icon.cloneNode());
+ };
+
+ Thread.prototype.kill = function() {
+ return this.isDead = true;
+ };
+
+ Thread.prototype.collect = function() {
+ var n;
+ n = 0;
+ this.posts.forEach(function(post) {
+ if (post.clones.length) {
+ return n++;
+ } else {
+ return post.collect();
+ }
+ });
+ if (!n) {
+ g.threads.rm(this.fullID);
+ return this.board.threads.rm(this);
+ }
+ };
+
+ return Thread;
+
+ })();
+
+ return Thread;
+
+}).call(this);
+
+SW = {};
+
+(function() {
+ var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ SW.tinyboard = {
+ isOPContainerThread: true,
+ mayLackJSON: true,
+ threadModTimeIgnoresSage: true,
+ disabledFeatures: ['Resurrect Quotes', 'Quick Reply Personas', 'Quick Reply', 'Cooldown', 'Report Link', 'Delete Link', 'Edit Link', 'Quote Inlining', 'Quote Previewing', 'Quote Backlinks', 'File Info Formatting', 'Image Expansion', 'Image Expansion (Menu)', 'Comment Expansion', 'Thread Expansion', 'Favicon', 'Quote Threading', 'Thread Updater', 'Banner', 'Flash Features', 'Reply Pruning'],
+ detect: function() {
+ var j, len, m, properties, ref, root, script;
+ ref = $$('script:not([src])', d.head);
+ for (j = 0, len = ref.length; j < len; j++) {
+ script = ref[j];
+ if ((m = script.textContent.match(/\bvar configRoot=(".*?")/))) {
+ properties = $.dict();
+ try {
+ root = JSON.parse(m[1]);
+ if (root[0] === '/') {
+ properties.root = location.origin + root;
+ } else if (/^https?:/.test(root)) {
+ properties.root = root;
+ }
+ } catch (error) {}
+ return properties;
+ }
+ }
+ return false;
+ },
+ awaitBoard: function(cb) {
+ var reactUI, s;
+ if ((reactUI = $.id('react-ui'))) {
+ s = this.selectors = Object.create(this.selectors);
+ s.boardFor = {
+ index: '.page-container'
+ };
+ s.thread = 'div[id^="thread_"]';
+ return Main.mounted(cb);
+ } else {
+ return cb();
+ }
+ },
+ urls: {
+ thread: function(arg, isArchived) {
+ var boardID, ref, siteID, threadID;
+ siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID;
+ return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/" + (isArchived ? 'archive/' : '') + "res/" + threadID + ".html";
+ },
+ post: function(arg) {
+ var postID;
+ postID = arg.postID;
+ return "#" + postID;
+ },
+ index: function(arg) {
+ var boardID, ref, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/";
+ },
+ catalog: function(arg) {
+ var boardID, ref, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/catalog.html";
+ },
+ threadJSON: function(arg, isArchived) {
+ var boardID, ref, root, siteID, threadID;
+ siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID;
+ root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0;
+ if (root) {
+ return "" + root + boardID + "/" + (isArchived ? 'archive/' : '') + "res/" + threadID + ".json";
+ } else {
+ return '';
+ }
+ },
+ archivedThreadJSON: function(thread) {
+ return SW.tinyboard.urls.threadJSON(thread, true);
+ },
+ threadsListJSON: function(arg) {
+ var boardID, ref, root, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0;
+ if (root) {
+ return "" + root + boardID + "/threads.json";
+ } else {
+ return '';
+ }
+ },
+ archiveListJSON: function(arg) {
+ var boardID, ref, root, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0;
+ if (root) {
+ return "" + root + boardID + "/archive/archive.json";
+ } else {
+ return '';
+ }
+ },
+ catalogJSON: function(arg) {
+ var boardID, ref, root, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0;
+ if (root) {
+ return "" + root + boardID + "/catalog.json";
+ } else {
+ return '';
+ }
+ },
+ file: function(arg, filename) {
+ var boardID, ref, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/" + filename;
+ },
+ thumb: function(board, filename) {
+ return SW.tinyboard.urls.file(board, filename);
+ }
+ },
+ selectors: {
+ board: 'form[name="postcontrols"]',
+ thread: 'input[name="board"] ~ div[id^="thread_"]',
+ threadDivider: 'div[id^="thread_"] > hr:last-child',
+ summary: '.omitted',
+ postContainer: 'div[id^="reply_"]:not(.hidden)',
+ opBottom: '.op',
+ replyOriginal: 'div[id^="reply_"]:not(.hidden)',
+ infoRoot: '.intro',
+ info: {
+ subject: '.subject',
+ name: '.name',
+ email: '.email',
+ tripcode: '.trip',
+ uniqueID: '.poster_id',
+ capcode: '.capcode',
+ flag: '.flag',
+ date: 'time',
+ nameBlock: 'label',
+ quote: 'a[href*="#q"]',
+ reply: 'a[href*="/res/"]:not([href*="#"])'
+ },
+ icons: {
+ isSticky: '.fa-thumb-tack',
+ isClosed: '.fa-lock'
+ },
+ file: {
+ text: '.fileinfo',
+ link: '.fileinfo > a',
+ thumb: 'a > .post-image'
+ },
+ thumbLink: '.file > a',
+ multifile: '.files > .file',
+ highlightable: {
+ op: ' > .op',
+ reply: '.reply',
+ catalog: ' > .thread'
+ },
+ comment: '.body',
+ spoiler: '.spoiler',
+ quotelink: 'a[onclick*="highlightReply("]',
+ catalog: {
+ board: '#Grid',
+ thread: '.mix',
+ thumb: '.thread-image'
+ },
+ boardList: '.boardlist',
+ boardListBottom: '.boardlist.bottom',
+ styleSheet: '#stylesheet',
+ psa: '.blotter',
+ nav: {
+ prev: '.pages > form > [value=Previous]',
+ next: '.pages > form > [value=Next]'
+ }
+ },
+ classes: {
+ highlight: 'highlighted'
+ },
+ xpath: {
+ thread: 'div[starts-with(@id,"thread_")]',
+ postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]',
+ replyContainer: 'div[starts-with(@id,"reply_")]'
+ },
+ regexp: {
+ quotelink: /\/([^\/]+)\/res\/(\d+)(?:\.\w+)?#(\d+)$/,
+ quotelinkHTML: /<a [^>]*\bhref="[^"]*\/([^\/]+)\/res\/(\d+)(?:\.\w+)?#(\d+)"/g
+ },
+ Build: {
+ parseJSON: function(data, board) {
+ var extra_file, file, i, j, len, o, ref;
+ o = SW.yotsuba.Build.parseJSON(data, board);
+ if (data.ext === 'deleted') {
+ delete o.file;
+ $.extend(o, {
+ files: [],
+ fileDeleted: true,
+ filesDeleted: [0]
+ });
+ }
+ if (data.extra_files) {
+ ref = data.extra_files;
+ for (i = j = 0, len = ref.length; j < len; i = ++j) {
+ extra_file = ref[i];
+ if (extra_file.ext === 'deleted') {
+ o.filesDeleted.push(i);
+ } else {
+ file = SW.yotsuba.Build.parseJSONFile(data, board);
+ o.files.push(file);
+ }
+ }
+ if (o.files.length) {
+ o.file = o.files[0];
+ }
+ }
+ return o;
+ },
+ parseComment: function(html) {
+ html = html.replace(/<br\b[^<]*>/gi, '\n').replace(/<[^>]*>/g, '');
+ return $.unescape(html);
+ }
+ },
+ bgColoredEl: function() {
+ return $.el('div', {
+ className: 'post reply'
+ });
+ },
+ isFileURL: function(url) {
+ return /\/src\/[^\/]+/.test(url.pathname);
+ },
+ preParsingFixes: function(board) {
+ var broken;
+ if ((broken = $('a > input[name="board"]', board))) {
+ return $.before(broken.parentNode, broken);
+ }
+ },
+ parseNodes: function(post, nodes) {
+ var m, nextSibling, node, text, uniqueID;
+ if (nodes.uniqueID) {
+ return;
+ }
+ text = '';
+ node = nodes.nameBlock.nextSibling;
+ while (node && node.nodeType === 3) {
+ text += node.textContent;
+ node = node.nextSibling;
+ }
+ if ((m = text.match(/(\s*ID:\s*)(\S+)/))) {
+ nodes.info.normalize();
+ nextSibling = nodes.nameBlock.nextSibling;
+ nextSibling = nextSibling.splitText(m[1].length);
+ nextSibling.splitText(m[2].length);
+ nodes.uniqueID = uniqueID = $.el('span', {
+ className: 'poster_id'
+ });
+ $.replace(nextSibling, uniqueID);
+ return $.add(uniqueID, nextSibling);
+ }
+ },
+ parseDate: function(node) {
+ var date, ref;
+ date = Date.parse((ref = node.getAttribute('datetime')) != null ? ref.trim() : void 0);
+ if (!isNaN(date)) {
+ return new Date(date);
+ }
+ date = Date.parse(node.textContent.trim() + ' UTC');
+ if (!isNaN(date)) {
+ return new Date(date);
+ }
+ return void 0;
+ },
+ parseFile: function(post, file) {
+ var info, infoNode, link, nameNode, ref, ref1, text, thumb;
+ text = file.text, link = file.link, thumb = file.thumb;
+ if ($.x("ancestor::" + this.xpath.postContainer + "[1]", text) !== post.nodes.root) {
+ return false;
+ }
+ if (!(infoNode = indexOf.call((ref = link.nextSibling) != null ? ref.textContent : void 0, '(') >= 0 ? link.nextSibling : link.nextElementSibling)) {
+ return false;
+ }
+ if (!(info = infoNode.textContent.match(/\((.*,\s*)?([\d.]+ ?[KMG]?B).*\)/))) {
+ return false;
+ }
+ nameNode = $('.postfilename', text);
+ $.extend(file, {
+ name: nameNode ? nameNode.title || nameNode.textContent : link.pathname.match(/[^\/]*$/)[0],
+ size: info[2],
+ dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0
+ });
+ if (thumb) {
+ $.extend(file, {
+ thumbURL: /\/static\//.test(thumb.src) && $.isImage(link.href) ? link.href : thumb.src,
+ isSpoiler: /^Spoiler/i.test(info[1] || '') || link.textContent === 'Spoiler Image'
+ });
+ }
+ return true;
+ },
+ isThumbExpanded: function(file) {
+ return $.hasClass(file.thumb.parentNode, 'expanded') || file.thumb.parentNode.dataset.expanded === 'true';
+ },
+ isLinkified: function(link) {
+ return /\bnofollow\b/.test(link.rel);
+ },
+ catalogPin: function(threadRoot) {
+ return threadRoot.dataset.sticky = 'true';
+ }
+ };
+
+}).call(this);
+
+(function() {
+ var slice = [].slice;
+
+ SW.yotsuba = {
+ isOPContainerThread: false,
+ hasIPCount: true,
+ archivedBoardsKnown: true,
+ urls: {
+ thread: function(arg) {
+ var boardID, threadID;
+ boardID = arg.boardID, threadID = arg.threadID;
+ return location.protocol + "//" + (BoardConfig.domain(boardID)) + "/" + boardID + "/thread/" + threadID;
+ },
+ post: function(arg) {
+ var postID;
+ postID = arg.postID;
+ return "#p" + postID;
+ },
+ index: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ return location.protocol + "//" + (BoardConfig.domain(boardID)) + "/" + boardID + "/";
+ },
+ catalog: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ if (boardID === 'f') {
+ return void 0;
+ } else {
+ return location.protocol + "//" + (BoardConfig.domain(boardID)) + "/" + boardID + "/catalog";
+ }
+ },
+ archive: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ if (BoardConfig.isArchived(boardID)) {
+ return location.protocol + "//" + (BoardConfig.domain(boardID)) + "/" + boardID + "/archive";
+ } else {
+ return void 0;
+ }
+ },
+ threadJSON: function(arg) {
+ var boardID, threadID;
+ boardID = arg.boardID, threadID = arg.threadID;
+ return location.protocol + "//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json";
+ },
+ threadsListJSON: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ return location.protocol + "//a.4cdn.org/" + boardID + "/threads.json";
+ },
+ archiveListJSON: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ if (BoardConfig.isArchived(boardID)) {
+ return location.protocol + "//a.4cdn.org/" + boardID + "/archive.json";
+ } else {
+ return '';
+ }
+ },
+ catalogJSON: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ return location.protocol + "//a.4cdn.org/" + boardID + "/catalog.json";
+ },
+ file: function(arg, filename) {
+ var boardID, hostname;
+ boardID = arg.boardID;
+ hostname = boardID === 'f' ? ImageHost.flashHost() : ImageHost.host();
+ return location.protocol + "//" + hostname + "/" + boardID + "/" + filename;
+ },
+ thumb: function(arg, filename) {
+ var boardID;
+ boardID = arg.boardID;
+ return location.protocol + "//" + (ImageHost.thumbHost()) + "/" + boardID + "/" + filename;
+ }
+ },
+ isPrunedByAge: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ return boardID === 'f';
+ },
+ areMD5sDeferred: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ return boardID === 'f';
+ },
+ isOnePage: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ return boardID === 'f';
+ },
+ noAudio: function(arg) {
+ var boardID;
+ boardID = arg.boardID;
+ return BoardConfig.noAudio(boardID);
+ },
+ selectors: {
+ board: '.board',
+ thread: '.thread',
+ threadDivider: '.board > hr',
+ summary: '.summary',
+ postContainer: '.postContainer',
+ replyOriginal: '.replyContainer:not([data-clone])',
+ sideArrows: 'div.sideArrows',
+ post: '.post',
+ infoRoot: '.postInfo',
+ info: {
+ subject: '.subject',
+ name: '.name',
+ email: '.useremail',
+ tripcode: '.postertrip',
+ uniqueIDRoot: '.posteruid',
+ uniqueID: '.posteruid > .hand',
+ capcode: '.capcode.hand',
+ pass: '.n-pu',
+ flag: '.flag, .bfl',
+ date: '.dateTime',
+ nameBlock: '.nameBlock',
+ quote: '.postNum > a:nth-of-type(2)',
+ reply: '.replylink'
+ },
+ icons: {
+ isSticky: '.stickyIcon',
+ isClosed: '.closedIcon',
+ isArchived: '.archivedIcon'
+ },
+ file: {
+ text: '.file > :first-child',
+ link: '.fileText > a',
+ thumb: 'a.fileThumb > [data-md5]'
+ },
+ thumbLink: 'a.fileThumb',
+ highlightable: {
+ op: '.opContainer',
+ reply: ' > .reply',
+ catalog: ''
+ },
+ comment: '.postMessage',
+ spoiler: 's',
+ quotelink: ':not(pre) > .quotelink',
+ catalog: {
+ board: '#threads',
+ thread: '.thread',
+ thumb: '.thumb'
+ },
+ boardList: '#boardNavDesktop > .boardList',
+ boardListBottom: '#boardNavDesktopFoot > .boardList',
+ styleSheet: 'link[title=switch]',
+ psa: '#globalMessage',
+ psaTop: '#globalToggle',
+ searchBox: '#search-box',
+ nav: {
+ prev: '.prev > form > [type=submit]',
+ next: '.next > form > [type=submit]'
+ }
+ },
+ classes: {
+ highlight: 'highlight'
+ },
+ xpath: {
+ thread: 'div[contains(concat(" ",@class," ")," thread ")]',
+ postContainer: 'div[contains(@class,"postContainer")]',
+ replyContainer: 'div[contains(@class,"replyContainer")]'
+ },
+ regexp: {
+ quotelink: /^https?:\/\/boards\.4chan(?:nel)?\.org\/+([^\/]+)\/+thread\/+(\d+)(?:[\/?][^#]*)?(?:#p(\d+))?$/,
+ quotelinkHTML: /<a [^>]*\bhref="(?:(?:\/\/boards\.4chan(?:nel)?\.org)?\/([^\/]+)\/thread\/)?(\d+)?(?:#p(\d+))?"/g,
+ pass: /^https?:\/\/www\.4chan(?:nel)?\.org\/+pass(?:$|[?#])/
+ },
+ bgColoredEl: function() {
+ return $.el('div', {
+ className: 'reply'
+ });
+ },
+ isThisPageLegit: function() {
+ var ref, ref1;
+ return ((ref = location.hostname) === 'boards.4chan.org' || ref === 'boards.4channel.org') && d.doctype && !$('link[href*="favicon-status.ico"]', d.head) && ((ref1 = d.title) !== '4chan - Temporarily Offline' && ref1 !== '4chan - Error' && ref1 !== '504 Gateway Time-out' && ref1 !== 'MathJax Equation Source');
+ },
+ is404: function() {
+ var ref;
+ return ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found') || (g.VIEW === 'thread' && $('.board') && !$('.opContainer'));
+ },
+ isIncomplete: function() {
+ var ref;
+ return ((ref = g.VIEW) === 'index' || ref === 'thread') && !$('.board + *');
+ },
+ isBoardlessPage: function(url) {
+ var ref;
+ return (ref = url.hostname) === 'www.4chan.org' || ref === 'www.4channel.org';
+ },
+ isAuxiliaryPage: function(url) {
+ var ref;
+ return (ref = url.hostname) !== 'boards.4chan.org' && ref !== 'boards.4channel.org';
+ },
+ isFileURL: function(url) {
+ return ImageHost.test(url.hostname);
+ },
+ initAuxiliary: function() {
+ var match, pathname;
+ switch (location.hostname) {
+ case 'www.4chan.org':
+ case 'www.4channel.org':
+ if (SW.yotsuba.regexp.pass.test(location.href)) {
+ PassMessage.init();
+ } else {
+ $.onExists(doc, 'body', function() {
+ return $.addStyle(CSS.www);
+ });
+ Captcha.replace.init();
+ }
+ break;
+ case 'sys.4chan.org':
+ case 'sys.4channel.org':
+ pathname = location.pathname.split(/\/+/);
+ if (pathname[2] === 'imgboard.php') {
+ if (/\bmode=report\b/.test(location.search)) {
+ Report.init();
+ } else if ((match = location.search.match(/\bres=(\d+)/))) {
+ $.ready(function() {
+ var ref;
+ if (Conf['404 Redirect'] && ((ref = $.id('errmsg')) != null ? ref.textContent : void 0) === 'Error: Specified thread does not exist.') {
+ return Redirect.navigate('thread', {
+ boardID: g.BOARD.ID,
+ postID: +match[1]
+ });
+ }
+ });
+ }
+ } else if (pathname[2] === 'post') {
+ PostSuccessful.init();
+ }
+ }
+ },
+ scriptData: function() {
+ var j, len, ref, script;
+ ref = $$('script:not([src])', d.head);
+ for (j = 0, len = ref.length; j < len; j++) {
+ script = ref[j];
+ if (/\bcooldowns *=/.test(script.textContent)) {
+ return script.textContent;
+ }
+ }
+ return '';
+ },
+ parseThreadMetadata: function(thread) {
+ var file, m, scriptData;
+ scriptData = this.scriptData();
+ thread.postLimit = /\bbumplimit *= *1\b/.test(scriptData);
+ thread.fileLimit = /\bimagelimit *= *1\b/.test(scriptData);
+ thread.ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : void 0;
+ if (g.BOARD.ID === 'f' && thread.OP.file) {
+ file = thread.OP.file;
+ return $.ajax(this.urls.threadJSON({
+ boardID: 'f',
+ threadID: thread.ID
+ }), {
+ timeout: $.MINUTE,
+ onloadend: function() {
+ if (this.response) {
+ return file.text.dataset.md5 = file.MD5 = this.response.posts[0].md5;
+ }
+ }
+ });
+ }
+ },
+ parseNodes: function(post, nodes) {
+ var icon, j, len, ref, results, type;
+ if (post.boardID === 'f') {
+ ref = ['Sticky', 'Closed'];
+ results = [];
+ for (j = 0, len = ref.length; j < len; j++) {
+ type = ref[j];
+ if ((icon = $("img[alt=" + type + "]", nodes.info))) {
+ results.push($.addClass(icon, (type.toLowerCase()) + "Icon", 'retina'));
+ }
+ }
+ return results;
+ }
+ },
+ parseDate: function(node) {
+ return new Date(node.dataset.utc * 1000);
+ },
+ parseFile: function(post, file) {
+ var info, link, m, ref, ref1, ref2, text, thumb;
+ text = file.text, link = file.link, thumb = file.thumb;
+ if (!(info = (ref = link.nextSibling) != null ? ref.textContent.match(/\(([\d.]+ [KMG]?B).*\)/) : void 0)) {
+ return false;
+ }
+ $.extend(file, {
+ name: text.title || link.title || link.textContent,
+ size: info[1],
+ dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0,
+ tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0,
+ MD5: text.dataset.md5
+ });
+ if (thumb) {
+ $.extend(file, {
+ thumbURL: thumb.src,
+ MD5: thumb.dataset.md5,
+ isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler')
+ });
+ if (file.isSpoiler) {
+ file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//" + (ImageHost.thumbHost()) + "/" + post.board + "/" + m[0] + "s.jpg" : void 0;
+ }
+ }
+ return true;
+ },
+ cleanComment: function(bq) {
+ var abbr, br, i, j, k, len, node, ref;
+ if ((abbr = $('.abbr', bq))) {
+ ref = $$('.abbr + br, .exif', bq);
+ for (j = 0, len = ref.length; j < len; j++) {
+ node = ref[j];
+ $.rm(node);
+ }
+ for (i = k = 0; k < 2; i = ++k) {
+ if ((br = abbr.previousSibling) && br.nodeName === 'BR') {
+ $.rm(br);
+ }
+ }
+ return $.rm(abbr);
+ }
+ },
+ cleanCommentDisplay: function(bq) {
+ var b;
+ if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) {
+ $.rm(b);
+ }
+ return $.rm($('.fortune', bq));
+ },
+ insertTags: function(bq) {
+ var j, k, len, len1, node, ref, ref1;
+ ref = $$('s, .removed-spoiler', bq);
+ for (j = 0, len = ref.length; j < len; j++) {
+ node = ref[j];
+ $.replace(node, [$.tn('[spoiler]')].concat(slice.call(node.childNodes), [$.tn('[/spoiler]')]));
+ }
+ ref1 = $$('.prettyprint', bq);
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ node = ref1[k];
+ $.replace(node, [$.tn('[code]')].concat(slice.call(node.childNodes), [$.tn('[/code]')]));
+ }
+ },
+ hasCORS: function(url) {
+ return url.split('/').slice(0, 3).join('/') === location.protocol + '//a.4cdn.org';
+ },
+ sfwBoards: function(sfw) {
+ return BoardConfig.sfwBoards(sfw);
+ },
+ uidColor: function(uid) {
+ var i, msg;
+ msg = 0;
+ i = 0;
+ while (i < 8) {
+ msg = (msg << 5) - msg + uid.charCodeAt(i++);
+ }
+ return (msg >> 8) & 0xFFFFFF;
+ },
+ isLinkified: function(link) {
+ return ImageHost.test(link.hostname);
+ },
+ testNativeExtension: function() {
+ return $.global(function() {
+ if (window.Parser.postMenuIcon) {
+ return this.enabled = 'true';
+ }
+ });
+ },
+ transformBoardList: function() {
+ var a, chr, i, items, j, len, node, nodes, ref, spacer, span;
+ nodes = [];
+ spacer = function() {
+ return $.el('span', {
+ className: 'spacer'
+ });
+ };
+ items = $.X('.//a|.//text()[not(ancestor::a)]', $(SW.yotsuba.selectors.boardList));
+ i = 0;
+ while (node = items.snapshotItem(i++)) {
+ switch (node.nodeName) {
+ case '#text':
+ ref = node.nodeValue;
+ for (j = 0, len = ref.length; j < len; j++) {
+ chr = ref[j];
+ span = $.el('span', {
+ textContent: chr
+ });
+ if (chr === ' ') {
+ span.className = 'space';
+ }
+ if (chr === ']') {
+ nodes.push(spacer());
+ }
+ nodes.push(span);
+ if (chr === '[') {
+ nodes.push(spacer());
+ }
+ }
+ break;
+ case 'A':
+ a = node.cloneNode(true);
+ nodes.push(a);
+ }
+ }
+ return nodes;
+ }
+ };
+
+}).call(this);
+
+(function() {
+ var Build,
+ slice = [].slice;
+
+ Build = {
+ staticPath: '//s.4cdn.org/image/',
+ gifIcon: window.devicePixelRatio >= 2 ? '@2x.gif' : '.gif',
+ spoilerRange: $.dict(),
+ shortFilename: function(filename) {
+ var ext;
+ ext = filename.match(/\.?[^\.]*$/)[0];
+ if (filename.length - ext.length > 30) {
+ return (filename.match(/(?:[\uD800-\uDBFF][\uDC00-\uDFFF]|[^]){0,25}/)[0]) + "(...)" + ext;
+ } else {
+ return filename;
+ }
+ },
+ spoilerThumb: function(boardID) {
+ var spoilerRange;
+ if (spoilerRange = Build.spoilerRange[boardID]) {
+ return Build.staticPath + "spoiler-" + boardID + (Math.floor(1 + spoilerRange * Math.random())) + ".png";
+ } else {
+ return Build.staticPath + "spoiler.png";
+ }
+ },
+ sameThread: function(boardID, threadID) {
+ return g.VIEW === 'thread' && g.BOARD.ID === boardID && g.THREADID === +threadID;
+ },
+ threadURL: function(boardID, threadID) {
+ if (boardID !== g.BOARD.ID) {
+ return "//" + (BoardConfig.domain(boardID)) + "/" + boardID + "/thread/" + threadID;
+ } else if (g.VIEW !== 'thread' || +threadID !== g.THREADID) {
+ return "/" + boardID + "/thread/" + threadID;
+ } else {
+ return '';
+ }
+ },
+ postURL: function(boardID, threadID, postID) {
+ return (Build.threadURL(boardID, threadID)) + "#p" + postID;
+ },
+ parseJSON: function(data, arg) {
+ var boardID, key, o, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ o = {
+ ID: data.no,
+ postID: data.no,
+ threadID: data.resto || data.no,
+ boardID: boardID,
+ siteID: siteID,
+ isReply: !!data.resto,
+ isSticky: !!data.sticky,
+ isClosed: !!data.closed,
+ isArchived: !!data.archived,
+ fileDeleted: !!data.filedeleted,
+ filesDeleted: data.filedeleted ? [0] : []
+ };
+ o.info = {
+ subject: $.unescape(data.sub),
+ email: $.unescape(data.email),
+ name: $.unescape(data.name) || '',
+ tripcode: data.trip,
+ pass: data.since4pass != null ? "" + data.since4pass : void 0,
+ uniqueID: data.id,
+ flagCode: data.country,
+ flagCodeTroll: data.board_flag,
+ flag: $.unescape(data.country_name || data.flag_name),
+ dateUTC: data.time,
+ dateText: data.now,
+ commentHTML: {
+ innerHTML: data.com || ''
+ }
+ };
+ if (data.capcode) {
+ o.info.capcode = data.capcode.replace(/_highlight$/, '').replace(/_/g, ' ').replace(/\b\w/g, function(c) {
+ return c.toUpperCase();
+ });
+ o.capcodeHighlight = /_highlight$/.test(data.capcode);
+ delete o.info.uniqueID;
+ }
+ o.files = [];
+ if (data.ext) {
+ o.file = SW.yotsuba.Build.parseJSONFile(data, {
+ siteID: siteID,
+ boardID: boardID
+ });
+ o.files.push(o.file);
+ }
+ o.extra = $.dict();
+ for (key in data) {
+ if (key[0] === 'x') {
+ o.extra[key] = data[key];
+ }
+ }
+ return o;
+ },
+ parseJSONFile: function(data, arg) {
+ var boardID, filename, o, site, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ site = g.sites[siteID];
+ filename = site.software === 'yotsuba' && boardID === 'f' ? "" + (encodeURIComponent(data.filename)) + data.ext : "" + data.tim + data.ext;
+ o = {
+ name: ($.unescape(data.filename)) + data.ext,
+ url: site.urls.file({
+ siteID: siteID,
+ boardID: boardID
+ }, filename),
+ height: data.h,
+ width: data.w,
+ MD5: data.md5,
+ size: $.bytesToString(data.fsize),
+ thumbURL: site.urls.thumb({
+ siteID: siteID,
+ boardID: boardID
+ }, data.tim + "s.jpg"),
+ theight: data.tn_h,
+ twidth: data.tn_w,
+ isSpoiler: !!data.spoiler,
+ tag: data.tag,
+ hasDownscale: !!data.m_img
+ };
+ if ((data.h != null) && !/\.pdf$/.test(o.url)) {
+ o.dimensions = o.width + "x" + o.height;
+ }
+ return o;
+ },
+ parseComment: function(html) {
+ html = html.replace(/<br\b[^<]*>/gi, '\n').replace(/\n\n<span\b[^<]* class="abbr"[^]*$/i, '').replace(/<[^>]*>/g, '');
+ return $.unescape(html);
+ },
+ parseCommentDisplay: function(html) {
+ var html2;
+ if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) {
+ while ((html2 = html.replace(/<s>(?:(?!<\/?s>).)*<\/s>/g, '[spoiler]')) !== html) {
+ html = html2;
+ }
+ }
+ html = html.replace(/^<b\b[^<]*>Rolled [^<]*<\/b>/i, '').replace(/<span\b[^<]* class="fortune"[^]*$/i, '');
+ return Build.parseComment(html).trim().replace(/\s+$/gm, '');
+ },
+ postFromObject: function(data, boardID) {
+ var o;
+ o = Build.parseJSON(data, {
+ boardID: boardID,
+ siteID: g.SITE.ID
+ });
+ return Build.post(o);
+ },
+ post: function(o) {
+ var ID, boardID, capcode, capcodeDescription, capcodeLC, capcodeLong, capcodePlural, commentHTML, container, dateText, dateUTC, email, file, fileBlock, fileThumb, fileURL, flag, flagCode, flagCodeTroll, gifIcon, href, i, len, match, name, pass, postClass, postInfo, postLink, protocol, quote, quoteLink, ref, ref1, shortFilename, staticPath, subject, threadID, tripcode, uniqueID, url, wholePost;
+ ID = o.ID, threadID = o.threadID, boardID = o.boardID, file = o.file;
+ ref = o.info, subject = ref.subject, email = ref.email, name = ref.name, tripcode = ref.tripcode, capcode = ref.capcode, pass = ref.pass, uniqueID = ref.uniqueID, flagCode = ref.flagCode, flagCodeTroll = ref.flagCodeTroll, flag = ref.flag, dateUTC = ref.dateUTC, dateText = ref.dateText, commentHTML = ref.commentHTML;
+ staticPath = Build.staticPath, gifIcon = Build.gifIcon;
+
+ /* Post Info */
+ if (capcode) {
+ capcodeLC = capcode.toLowerCase();
+ if (capcode === 'Founder') {
+ capcodePlural = 'the Founder';
+ capcodeDescription = "4chan's Founder";
+ } else if (capcode === 'Verified') {
+ capcodePlural = 'Verified Users';
+ capcodeDescription = '';
+ } else {
+ capcodeLong = $.getOwn({
+ 'Admin': 'Administrator',
+ 'Mod': 'Moderator'
+ }, capcode) || capcode;
+ capcodePlural = capcodeLong + "s";
+ capcodeDescription = "a 4chan " + capcodeLong;
+ }
+ }
+ url = Build.threadURL(boardID, threadID);
+ postLink = url + "#p" + ID;
+ quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + (+ID) + "');" : url + "#q" + ID;
+ postInfo = {innerHTML: "<div class=\"postInfo desktop\" id=\"pi" + E(ID) + "\"><input type=\"checkbox\" name=\"" + E(ID) + "\" value=\"delete\"> " + ((!o.isReply || boardID === "f" || subject) ? "<span class=\"subject\">" + E(subject || "") + "</span> " : "") + "<span class=\"nameBlock" + ((capcode) ? " capcode" + E(capcode) : "") + "\">" + ((email) ? "<a href=\"mailto:" + E(encodeURIComponent(email).replace(/%40/g, "@")) + "\" class=\"useremail\">" : "") + "<span class=\"name" + ((capcode) ? " capcode" : "") + "\">" + E(name) + "</span>" + ((tripcode) ? " <span class=\"postertrip\">" + E(tripcode) + "</span>" : "") + ((pass) ? " <span title=\"Pass user since " + E(pass) + "\" class=\"n-pu\"></span>" : "") + ((capcode) ? " <strong class=\"capcode hand id_" + E(capcodeLC) + "\" title=\"Highlight posts by " + E(capcodePlural) + "\">## " + E(capcode) + "</strong>" : "") + ((email) ? "</a>" : "") + ((boardID === "f" && !o.isReply || capcodeDescription) ? "" : " ") + ((capcodeDescription) ? " <img src=\"" + E(staticPath) + E(capcodeLC) + "icon" + E(gifIcon) + "\" alt=\"" + E(capcode) + " Icon\" title=\"This user is " + E(capcodeDescription) + ".\" class=\"identityIcon retina\">" : "") + ((uniqueID && !capcode) ? " <span class=\"posteruid id_" + E(uniqueID) + "\">(ID: <span class=\"hand\" title=\"Highlight posts by this ID\">" + E(uniqueID) + "</span>)</span>" : "") + ((flagCode) ? " <span title=\"" + E(flag) + "\" class=\"flag flag-" + E(flagCode.toLowerCase()) + "\"></span>" : "") + ((flagCodeTroll) ? " <span title=\"" + E(flag) + "\" class=\"bfl bfl-" + E(flagCodeTroll.toLowerCase()) + "\"></span>" : "") + "</span> <span class=\"dateTime\" data-utc=\"" + E(dateUTC) + "\">" + E(dateText) + "</span> <span class=\"postNum" + ((!(boardID === "f" && !o.isReply)) ? " desktop" : "") + "\"><a href=\"" + E(postLink) + "\" title=\"Link to this post\">No.</a><a href=\"" + E(quoteLink) + "\" title=\"Reply to this post\">" + E(ID) + "</a>" + ((o.isSticky) ? " <img src=\"" + E(staticPath) + "sticky" + E(gifIcon) + "\" alt=\"Sticky\" title=\"Sticky\"" + ((boardID === "f") ? " style=\"height: 18px; width: 18px;\"" : " class=\"stickyIcon retina\"") + ">" : "") + ((o.isClosed && !o.isArchived) ? " <img src=\"" + E(staticPath) + "closed" + E(gifIcon) + "\" alt=\"Closed\" title=\"Closed\"" + ((boardID === "f") ? " style=\"height: 18px; width: 18px;\"" : " class=\"closedIcon retina\"") + ">" : "") + ((o.isArchived) ? " <img src=\"" + E(staticPath) + "archived" + E(gifIcon) + "\" alt=\"Archived\" title=\"Archived\" class=\"archivedIcon retina\">" : "") + ((!o.isReply && g.VIEW === "index") ? " &nbsp; <span>[<a href=\"/" + E(boardID) + "/thread/" + E(threadID) + "\" class=\"replylink\">Reply</a>]</span>" : "") + "</span></div>"};
+
+ /* File Info */
+ if (file) {
+ protocol = /^https?:(?=\/\/i\.4cdn\.org\/)/;
+ fileURL = file.url.replace(protocol, '');
+ shortFilename = Build.shortFilename(file.name);
+ fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.thumbURL.replace(protocol, '');
+ }
+ fileBlock = {innerHTML: ((file) ? "<div class=\"file\" id=\"f" + E(ID) + "\">" + ((boardID === "f") ? "<div class=\"fileInfo\" data-md5=\"" + E(file.MD5) + "\"><span class=\"fileText\" id=\"fT" + E(ID) + "\">File: <a data-width=\"" + E(file.width) + "\" data-height=\"" + E(file.height) + "\" href=\"" + E(fileURL) + "\" target=\"_blank\">" + E(file.name) + "</a>-(" + E(file.size) + ", " + E(file.dimensions) + ((file.tag) ? ", " + E(file.tag) : "") + ")</span></div>" : "<div class=\"fileText\" id=\"fT" + E(ID) + "\"" + ((file.isSpoiler) ? " title=\"" + E(file.name) + "\"" : "") + ">File: <a" + ((file.name === shortFilename || file.isSpoiler) ? "" : " title=\"" + E(file.name) + "\"") + " href=\"" + E(fileURL) + "\" target=\"_blank\">" + ((file.isSpoiler) ? "Spoiler Image" : E(shortFilename)) + "</a> (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")</div><a class=\"fileThumb" + ((file.isSpoiler) ? " imgspoiler" : "") + "\" href=\"" + E(fileURL) + "\" target=\"_blank\"" + ((file.hasDownscale) ? " data-m" : "") + "><img src=\"" + E(fileThumb) + "\" alt=\"" + E(file.size) + "\" data-md5=\"" + E(file.MD5) + "\" style=\"height: " + E(file.isSpoiler ? 100 : file.theight) + "px; width: " + E(file.isSpoiler ? 100 : file.twidth) + "px;\"loading=\"lazy\"></a>") + "</div>" : ((o.fileDeleted) ? "<div class=\"file\" id=\"f" + E(ID) + "\"><span class=\"fileThumb\"><img src=\"" + E(staticPath) + "filedeleted-res" + E(gifIcon) + "\" alt=\"File deleted.\" class=\"fileDeletedRes retina\"></span></div>" : ""))};
+
+ /* Whole Post */
+ postClass = o.isReply ? 'reply' : 'op';
+ wholePost = {innerHTML: ((o.isReply) ? "<div class=\"sideArrows\" id=\"sa" + E(ID) + "\">&gt;&gt;</div>" : "") + "<div id=\"p" + E(ID) + "\" class=\"post " + E(postClass) + ((o.capcodeHighlight) ? " highlightPost" : "") + "\">" + ((o.isReply) ? (postInfo).innerHTML + (fileBlock).innerHTML : (fileBlock).innerHTML + (postInfo).innerHTML) + "<blockquote class=\"postMessage\" id=\"m" + E(ID) + "\">" + (commentHTML).innerHTML + "</blockquote></div>"};
+ container = $.el('div', {
+ className: "postContainer " + postClass + "Container",
+ id: "pc" + ID
+ });
+ $.extend(container, wholePost);
+ ref1 = $$('.quotelink', container);
+ for (i = 0, len = ref1.length; i < len; i++) {
+ quote = ref1[i];
+ href = quote.getAttribute('href');
+ if (href[0] === '#') {
+ if (!Build.sameThread(boardID, threadID)) {
+ quote.href = Build.threadURL(boardID, threadID) + href;
+ }
+ } else {
+ if ((match = quote.href.match(SW.yotsuba.regexp.quotelink)) && (Build.sameThread(match[1], match[2]))) {
+ quote.href = href.match(/(#[^#]*)?$/)[0] || '#';
+ }
+ }
+ }
+ return container;
+ },
+ summaryText: function(status, posts, files) {
+ var text;
+ text = '';
+ if (status) {
+ text += status + " ";
+ }
+ text += posts + " post" + (posts > 1 ? 's' : '');
+ if (+files) {
+ text += " and " + files + " image repl" + (files > 1 ? 'ies' : 'y');
+ }
+ return text += " " + (status === '-' ? 'shown' : 'omitted') + ".";
+ },
+ summary: function(boardID, threadID, posts, files) {
+ return $.el('a', {
+ className: 'summary',
+ textContent: Build.summaryText('', posts, files),
+ href: "/" + boardID + "/thread/" + threadID
+ });
+ },
+ thread: function(thread, data, withReplies) {
+ var files, posts, ref, root, summary;
+ if ((root = thread.nodes.root)) {
+ $.rmAll(root);
+ } else {
+ thread.nodes.root = root = $.el('div', {
+ className: 'thread',
+ id: "t" + data.no
+ });
+ }
+ if (Build.hat) {
+ $.add(root, Build.hat.cloneNode(false));
+ }
+ $.add(root, thread.OP.nodes.root);
+ if (data.omitted_posts || !withReplies && data.replies) {
+ ref = withReplies ? [
+ data.omitted_posts, data.images - data.last_replies.filter(function(data) {
+ return !!data.ext;
+ }).length
+ ] : [data.replies, data.images], posts = ref[0], files = ref[1];
+ summary = Build.summary(thread.board.ID, data.no, posts, files);
+ $.add(root, summary);
+ }
+ return root;
+ },
+ catalogThread: function(thread, data, pageCount) {
+ var br, container, cssText, fileCount, gifIcon, i, imgClass, len, postCount, ratio, ref, root, spoilerRange, src, staticPath, tn_h, tn_w;
+ staticPath = Build.staticPath, gifIcon = Build.gifIcon;
+ tn_w = data.tn_w, tn_h = data.tn_h;
+ if (data.spoiler && !Conf['Reveal Spoiler Thumbnails']) {
+ src = staticPath + "spoiler";
+ if (spoilerRange = Build.spoilerRange[thread.board]) {
+ src += ("-" + thread.board) + Math.floor(1 + spoilerRange * Math.random());
+ }
+ src += '.png';
+ imgClass = 'spoiler-file';
+ cssText = "--tn-w: 100; --tn-h: 100;";
+ } else if (data.filedeleted) {
+ src = staticPath + "filedeleted-res" + gifIcon;
+ imgClass = 'deleted-file';
+ } else if (thread.OP.file) {
+ src = thread.OP.file.thumbURL;
+ ratio = 250 / Math.max(tn_w, tn_h);
+ cssText = "--tn-w: " + (tn_w * ratio) + "; --tn-h: " + (tn_h * ratio) + ";";
+ } else {
+ src = staticPath + "nofile.png";
+ imgClass = 'no-file';
+ }
+ postCount = data.replies + 1;
+ fileCount = data.images + !!data.ext;
+ container = $.el('div', {innerHTML: "<a class=\"catalog-link\" href=\"/" + E(thread.board) + "/thread/" + E(thread.ID) + "\"><img src=\"" + E(src) + "\"" + ((imgClass) ? " class=\"catalog-thumb " + E(imgClass) + "\"" : " class=\"catalog-thumb\" data-width=\"" + E(data.tn_w) + "\" data-height=\"" + E(data.tn_h) + "\"") + "></a><div class=\"catalog-stats\"><span title=\"Posts / Files / Page\"><span class=\"post-count" + ((data.bumplimit) ? " warning" : "") + "\">" + E(postCount) + "</span> / <span class=\"file-count" + ((data.imagelimit) ? " warning" : "") + "\">" + E(fileCount) + "</span> / <span class=\"page-count\">" + E(pageCount) + "</span></span><span class=\"catalog-icons\">" + ((thread.isSticky) ? "<img src=\"" + E(staticPath) + "sticky" + E(gifIcon) + "\" class=\"stickyIcon\" title=\"Sticky\">" : "") + ((thread.isClosed) ? "<img src=\"" + E(staticPath) + "closed" + E(gifIcon) + "\" class=\"closedIcon\" title=\"Closed\">" : "") + "</span></div>"});
+ $.before(thread.OP.nodes.info, slice.call(container.childNodes));
+ ref = $$('br', thread.OP.nodes.comment);
+ for (i = 0, len = ref.length; i < len; i++) {
+ br = ref[i];
+ if (br.previousSibling && br.previousSibling.nodeName === 'BR') {
+ $.addClass(br, 'extra-linebreak');
+ }
+ }
+ root = $.el('div', {
+ className: 'thread catalog-thread',
+ id: "t" + thread
+ });
+ if (thread.OP.highlights) {
+ $.addClass.apply($, [root].concat(slice.call(thread.OP.highlights)));
+ }
+ if (!thread.OP.file) {
+ $.addClass(root, 'noFile');
+ }
+ root.style.cssText = cssText || '';
+ return root;
+ },
+ catalogReply: function(thread, data) {
+ var excerpt, link;
+ excerpt = '';
+ if (data.com) {
+ excerpt = Build.parseCommentDisplay(data.com).replace(/>>\d+/g, '').trim().replace(/\n+/g, ' // ');
+ }
+ if (data.ext) {
+ excerpt || (excerpt = "" + ($.unescape(data.filename)) + data.ext);
+ }
+ if (data.com) {
+ excerpt || (excerpt = $.unescape(data.com.replace(/<br\b[^<]*>/gi, ' // ')));
+ }
+ excerpt || (excerpt = '\xA0');
+ if (excerpt.length > 73) {
+ excerpt = excerpt.slice(0, 70) + "...";
+ }
+ link = Build.postURL(thread.board.ID, thread.ID, data.no);
+ return $.el('div', {
+ className: 'catalog-reply'
+ }, {innerHTML: "<span><time data-utc=\"" + E(data.time * 1000) + "\" data-abbrev=\"1\">...</time>: </span><a class=\"catalog-reply-excerpt\" href=\"" + E(link) + "\">" + E(excerpt) + "</a><a class=\"catalog-reply-preview\" href=\"" + E(link) + "\">...</a>"});
+ }
+ };
+
+ SW.yotsuba.Build = Build;
+
+}).call(this);
+
+Site = (function() {
+ var Site;
+
+ Site = {
+ defaultProperties: {
+ '4chan.org': {
+ software: 'yotsuba'
+ },
+ '4channel.org': {
+ canonical: '4chan.org'
+ },
+ '4cdn.org': {
+ canonical: '4chan.org'
+ },
+ 'notso.smuglo.li': {
+ canonical: 'smuglo.li'
+ },
+ 'smugloli.net': {
+ canonical: 'smuglo.li'
+ },
+ 'smug.nepu.moe': {
+ canonical: 'smuglo.li'
+ }
+ },
+ init: function(cb) {
+ var hostname;
+ $.extend(Conf['siteProperties'], Site.defaultProperties);
+ hostname = Site.resolve();
+ if (hostname && $.hasOwn(SW, Conf['siteProperties'][hostname].software)) {
+ this.set(hostname);
+ cb();
+ }
+ return $.onExists(doc, 'body', (function(_this) {
+ return function() {
+ var base, base1, changed, changes, key, properties, software;
+ for (software in SW) {
+ if (!((changes = typeof (base = SW[software]).detect === "function" ? base.detect() : void 0))) {
+ continue;
+ }
+ changes.software = software;
+ hostname = location.hostname.replace(/^www\./, '');
+ properties = ((base1 = Conf['siteProperties'])[hostname] || (base1[hostname] = $.dict()));
+ changed = 0;
+ for (key in changes) {
+ if (!(properties[key] !== changes[key])) {
+ continue;
+ }
+ properties[key] = changes[key];
+ changed++;
+ }
+ if (changed) {
+ $.set('siteProperties', Conf['siteProperties']);
+ }
+ if (!g.SITE) {
+ _this.set(hostname);
+ cb();
+ }
+ return;
+ }
+ };
+ })(this));
+ },
+ resolve: function(url) {
+ var canonical, hostname;
+ if (url == null) {
+ url = location;
+ }
+ hostname = url.hostname;
+ while (hostname && !$.hasOwn(Conf['siteProperties'], hostname)) {
+ hostname = hostname.replace(/^[^.]*\.?/, '');
+ }
+ if (hostname) {
+ if ((canonical = Conf['siteProperties'][hostname].canonical)) {
+ hostname = canonical;
+ }
+ }
+ return hostname;
+ },
+ parseURL: function(url) {
+ var siteID;
+ siteID = Site.resolve(url);
+ return Main.parseURL(g.sites[siteID], url);
+ },
+ set: function(hostname) {
+ var ID, properties, ref, site, software;
+ ref = Conf['siteProperties'];
+ for (ID in ref) {
+ properties = ref[ID];
+ if (properties.canonical) {
+ continue;
+ }
+ software = properties.software;
+ if (!(software && $.hasOwn(SW, software))) {
+ continue;
+ }
+ g.sites[ID] = site = Object.create(SW[software]);
+ $.extend(site, {
+ ID: ID,
+ siteID: ID,
+ properties: properties,
+ software: software
+ });
+ }
+ return g.SITE = g.sites[hostname];
+ }
+ };
+
+ return Site;
+
+}).call(this);
+
+Redirect = (function() {
+ var Redirect,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Redirect = {
+ archives: [
+ { "uid": 3, "name": "4plebs", "domain": "archive.4plebs.org", "http": true, "https": true, "software": "foolfuuka", "boards": [ "adv", "f", "hr", "mlpol", "mo", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "files": [ "adv", "f", "hr", "mlpol", "mo", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "reports": true },
+ { "uid": 10, "name": "warosu", "domain": "warosu.org", "http": false, "https": true, "software": "fuuka", "boards": [ "3", "biz", "cgl", "ck", "diy", "fa", "ic", "jp", "lit", "sci", "vr", "vt" ], "files": [ "3", "biz", "cgl", "ck", "diy", "fa", "ic", "jp", "lit", "sci", "vr", "vt" ], "search": [ "biz", "cgl", "ck", "diy", "fa", "ic", "jp", "lit", "sci", "vr", "vt" ] },
+ { "uid": 23, "name": "Desuarchive", "domain": "desuarchive.org", "http": true, "https": true, "software": "foolfuuka", "boards": [ "a", "aco", "an", "c", "cgl", "co", "d", "fit", "g", "gif", "his", "int", "k", "m", "mlp", "mu", "q", "qa", "r9k", "tg", "trash", "vr", "wsg" ], "files": [ "a", "aco", "an", "c", "cgl", "co", "d", "fit", "g", "gif", "his", "int", "k", "m", "mlp", "mu", "q", "qa", "r9k", "tg", "trash", "vr", "wsg" ], "reports": true },
+ { "uid": 24, "name": "fireden.net", "domain": "boards.fireden.net", "http": false, "https": true, "software": "foolfuuka", "boards": [ "cm", "co", "ic", "sci", "tg", "vip", "y" ], "files": [ "cm", "co", "ic", "sci", "tg", "vip", "y" ], "search": [ "cm", "co", "ic", "sci", "tg", "y" ] },
+ { "uid": 25, "name": "arch.b4k.co", "domain": "arch.b4k.co", "http": true, "https": true, "software": "foolfuuka", "boards": [ "g", "mlp", "qb", "v", "vg", "vm", "vmg", "vp", "vrpg", "vst" ], "files": [ "qb", "v", "vg", "vm", "vmg", "vp", "vrpg", "vst" ], "search": [ "qb", "v", "vg", "vm", "vmg", "vp", "vrpg", "vst" ] },
+ { "uid": 29, "name": "Archived.Moe", "domain": "archived.moe", "http": true, "https": true, "software": "foolfuuka", "boards": [ "3", "a", "aco", "adv", "an", "asp", "b", "bant", "biz", "c", "can", "cgl", "ck", "cm", "co", "cock", "con", "d", "diy", "e", "f", "fa", "fap", "fit", "fitlit", "g", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "int", "jp", "k", "lgbt", "lit", "m", "mlp", "mlpol", "mo", "mtv", "mu", "n", "news", "o", "out", "outsoc", "p", "po", "pol", "pw", "q", "qa", "qb", "qst", "r", "r9k", "s", "s4s", "sci", "soc", "sp", "spa", "t", "tg", "toy", "trash", "trv", "tv", "u", "v", "vg", "vint", "vip", "vmg", "vp", "vr", "vrpg", "vt", "w", "wg", "wsg", "wsr", "x", "xs", "y" ], "files": [ "can", "cock", "con", "fap", "fitlit", "gd", "mlpol", "mo", "mtv", "outsoc", "po", "q", "qb", "qst", "spa", "vint", "vip" ], "search": [ "aco", "adv", "an", "asp", "b", "bant", "c", "can", "cgl", "ck", "cm", "cock", "con", "d", "diy", "e", "f", "fap", "fitlit", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "lgbt", "lit", "mlpol", "mo", "mtv", "n", "news", "o", "out", "outsoc", "p", "po", "q", "qa", "qst", "r", "s", "soc", "spa", "trv", "u", "vint", "vip", "vrpg", "w", "wg", "wsg", "wsr", "x", "y" ], "reports": true },
+ { "uid": 30, "name": "TheBArchive.com", "domain": "thebarchive.com", "http": true, "https": true, "software": "foolfuuka", "boards": [ "b", "bant" ], "files": [ "b", "bant" ], "reports": true },
+ { "uid": 31, "name": "Archive Of Sins", "domain": "archiveofsins.com", "http": true, "https": true, "software": "foolfuuka", "boards": [ "h", "hc", "hm", "i", "lgbt", "r", "s", "soc", "t", "u" ], "files": [ "h", "hc", "hm", "i", "lgbt", "r", "s", "soc", "t", "u" ], "reports": true },
+ { "uid": 34, "name": "TokyoChronos", "domain": "www.tokyochronos.net", "http": false, "https": true, "software": "foolfuuka", "boards": [ "a", "c", "g", "jp", "mu", "r9k", "vp", "vrpg", "vt" ], "files": [ "a", "c", "g", "jp", "mu", "r9k", "vp", "vrpg", "vt" ], "reports": true },
+ { "uid": 36, "name": "Rozen Arcana", "domain": "archive.alice.al", "http": false, "https": true, "software": "foolfuuka", "boards": [ "bant", "c", "con", "e", "i", "n", "news", "out", "p", "pw", "qst", "toy", "vip", "vp", "vt", "w", "wg", "wsr" ], "files": [ "bant", "c", "e", "i", "n", "news", "out", "p", "pw", "qst", "toy", "vip", "vp", "vt", "w", "wg", "wsr" ], "reports": true }
+ ],
+ init: function() {
+ var now, ref;
+ this.selectArchives();
+ if (Conf['archiveAutoUpdate']) {
+ now = Date.now();
+ if (!((now - 2 * $.DAY < (ref = Conf['lastarchivecheck']) && ref <= now))) {
+ return this.update();
+ }
+ }
+ },
+ selectArchives: function() {
+ var archive, archives, boardID, boards, data, files, id, j, k, key, l, len, len1, len2, name, o, record, ref, ref1, ref2, software, type, uid;
+ o = {
+ thread: $.dict(),
+ post: $.dict(),
+ file: $.dict()
+ };
+ archives = $.dict();
+ ref = Conf['archives'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ data = ref[j];
+ ref1 = ['boards', 'files'];
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ key = ref1[k];
+ if (!(data[key] instanceof Array)) {
+ data[key] = [];
+ }
+ }
+ uid = data.uid, name = data.name, boards = data.boards, files = data.files, software = data.software;
+ if (software !== 'fuuka' && software !== 'foolfuuka') {
+ continue;
+ }
+ archives[JSON.stringify(uid != null ? uid : name)] = data;
+ for (l = 0, len2 = boards.length; l < len2; l++) {
+ boardID = boards[l];
+ if (!(boardID in o.thread)) {
+ o.thread[boardID] = data;
+ }
+ if (!(boardID in o.post || software !== 'foolfuuka')) {
+ o.post[boardID] = data;
+ }
+ if (!(boardID in o.file || indexOf.call(files, boardID) < 0)) {
+ o.file[boardID] = data;
+ }
+ }
+ }
+ ref2 = Conf['selectedArchives'];
+ for (boardID in ref2) {
+ record = ref2[boardID];
+ for (type in record) {
+ id = record[type];
+ if (!((archive = archives[JSON.stringify(id)]) && $.hasOwn(o, type))) {
+ continue;
+ }
+ boards = type === 'file' ? archive.files : archive.boards;
+ if (indexOf.call(boards, boardID) >= 0) {
+ o[type][boardID] = archive;
+ }
+ }
+ }
+ return Redirect.data = o;
+ },
+ update: function(cb) {
+ var err, fail, i, j, k, len, len1, load, nloaded, ref, ref1, response, responses, url, urls;
+ urls = [];
+ responses = [];
+ nloaded = 0;
+ ref = Conf['archiveLists'].split('\n');
+ for (j = 0, len = ref.length; j < len; j++) {
+ url = ref[j];
+ if (!(url[0] !== '#')) {
+ continue;
+ }
+ url = url.trim();
+ if (url) {
+ urls.push(url);
+ }
+ }
+ fail = function(url, action, msg) {
+ return new Notice('warning', "Error " + action + " archive data from\n" + url + "\n" + msg, 20);
+ };
+ load = function(i) {
+ return function() {
+ var response;
+ if (this.status !== 200) {
+ return fail(urls[i], 'fetching', (this.status ? "Error " + this.statusText + " (" + this.status + ")" : 'Connection Error'));
+ }
+ response = this.response;
+ if (!(response instanceof Array)) {
+ response = [response];
+ }
+ responses[i] = response;
+ nloaded++;
+ if (nloaded === urls.length) {
+ return Redirect.parse(responses, cb);
+ }
+ };
+ };
+ if (urls.length) {
+ for (i = k = 0, len1 = urls.length; k < len1; i = ++k) {
+ url = urls[i];
+ if ((ref1 = url[0]) === '[' || ref1 === '{') {
+ try {
+ response = JSON.parse(url);
+ } catch (error) {
+ err = error;
+ fail(url, 'parsing', err.message);
+ continue;
+ }
+ load(i).call({
+ status: 200,
+ response: response
+ });
+ } else {
+ CrossOrigin.ajax(url, {
+ onloadend: load(i)
+ });
+ }
+ }
+ } else {
+ Redirect.parse([], cb);
+ }
+ },
+ parse: function(responses, cb) {
+ var archiveUIDs, archives, data, items, j, k, len, len1, ref, response, uid;
+ archives = [];
+ archiveUIDs = $.dict();
+ for (j = 0, len = responses.length; j < len; j++) {
+ response = responses[j];
+ for (k = 0, len1 = response.length; k < len1; k++) {
+ data = response[k];
+ uid = JSON.stringify((ref = data.uid) != null ? ref : data.name);
+ if (uid in archiveUIDs) {
+ $.extend(archiveUIDs[uid], data);
+ } else {
+ archiveUIDs[uid] = $.dict.clone(data);
+ archives.push(data);
+ }
+ }
+ }
+ items = {
+ archives: archives,
+ lastarchivecheck: Date.now()
+ };
+ $.set(items);
+ $.extend(Conf, items);
+ Redirect.selectArchives();
+ return typeof cb === "function" ? cb() : void 0;
+ },
+ to: function(dest, data) {
+ var archive;
+ archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID];
+ if (!archive) {
+ return '';
+ }
+ return Redirect[dest](archive, data);
+ },
+ protocol: function(archive) {
+ var protocol;
+ protocol = location.protocol;
+ if (!$.getOwn(archive, protocol.slice(0, -1))) {
+ protocol = protocol === 'https:' ? 'http:' : 'https:';
+ }
+ return protocol + "//";
+ },
+ thread: function(archive, arg) {
+ var boardID, path, postID, threadID;
+ boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID;
+ path = threadID ? boardID + "/thread/" + threadID : boardID + "/post/" + postID;
+ if (archive.software === 'foolfuuka') {
+ path += '/';
+ }
+ if (threadID && postID) {
+ path += archive.software === 'foolfuuka' ? "#" + postID : "#p" + postID;
+ }
+ return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
+ },
+ post: function(archive, arg) {
+ var boardID, postID, protocol, url;
+ boardID = arg.boardID, postID = arg.postID;
+ protocol = Redirect.protocol(archive);
+ url = "" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID;
+ if (!Redirect.securityCheck(url)) {
+ return '';
+ }
+ return url;
+ },
+ file: function(archive, arg) {
+ var boardID, filename;
+ boardID = arg.boardID, filename = arg.filename;
+ if (!filename) {
+ return '';
+ }
+ if (boardID === 'f') {
+ filename = encodeURIComponent($.unescape(decodeURIComponent(filename)));
+ } else {
+ if (/[sm]\.jpg$/.test(filename)) {
+ return '';
+ }
+ }
+ return "" + (Redirect.protocol(archive)) + archive.domain + "/" + boardID + "/full_image/" + filename;
+ },
+ board: function(archive, arg) {
+ var boardID;
+ boardID = arg.boardID;
+ return "" + (Redirect.protocol(archive)) + archive.domain + "/" + boardID + "/";
+ },
+ search: function(archive, arg) {
+ var boardID, path, type, value;
+ boardID = arg.boardID, type = arg.type, value = arg.value;
+ type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type;
+ if (type === 'capcode') {
+ value = $.getOwn({
+ 'Developer': 'dev',
+ 'Verified': 'ver'
+ }, value) || value.toLowerCase();
+ } else if (type === 'image') {
+ value = value.replace(/[+\/=]/g, function(c) {
+ return {
+ '+': '-',
+ '/': '_',
+ '=': ''
+ }[c];
+ });
+ }
+ value = encodeURIComponent(value);
+ path = archive.software === 'foolfuuka' ? boardID + "/search/" + type + "/" + value + "/" : type === 'image' ? boardID + "/image/" + value : boardID + "/?task=search2&search_" + type + "=" + value;
+ return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
+ },
+ report: function(boardID) {
+ var archive, boards, domain, https, j, len, name, ref, reports, software, urls;
+ urls = [];
+ ref = Conf['archives'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ archive = ref[j];
+ software = archive.software, https = archive.https, reports = archive.reports, boards = archive.boards, name = archive.name, domain = archive.domain;
+ if (software === 'foolfuuka' && https && reports && boards instanceof Array && indexOf.call(boards, boardID) >= 0) {
+ urls.push([name, "https://" + domain + "/_/api/chan/offsite_report/"]);
+ }
+ }
+ return urls;
+ },
+ securityCheck: function(url) {
+ return /^https:\/\//.test(url) || location.protocol === 'http:' || Conf['Exempt Archives from Encryption'];
+ },
+ navigate: function(dest, data, alternative) {
+ var url;
+ if (!Redirect.data) {
+ Redirect.init();
+ }
+ url = Redirect.to(dest, data);
+ if (url && (Redirect.securityCheck(url) || confirm("Redirect to " + url + "?\n\nYour connection will not be encrypted."))) {
+ return location.replace(url);
+ } else if (alternative) {
+ return location.replace(alternative);
+ }
+ }
+ };
+
+ return Redirect;
+
+}).call(this);
+
+Anonymize = (function() {
+ var Anonymize;
+
+ Anonymize = {
+ init: function() {
+ if (!Conf['Anonymize']) {
+ return;
+ }
+ return $.addClass(doc, 'anonymize');
+ }
+ };
+
+ return Anonymize;
+
+}).call(this);
+
+Filter = (function() {
+ var Filter,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
+ slice = [].slice;
+
+ Filter = {
+ filters: $.dict(),
+ init: function() {
+ var base, base1, boards, err, excludes, file, filter, hide, hl, i, isstring, j, key, len, len1, line, mask, noti, op, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, regexp, stub, top, type, types;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'catalog') && Conf['Filter'])) {
+ return;
+ }
+ if (g.VIEW === 'catalog' && !Conf['Filter in Native Catalog']) {
+ return;
+ }
+ if (!Conf['Filtered Backlinks']) {
+ $.addClass(doc, 'hide-backlinks');
+ }
+ for (key in Config.filter) {
+ ref1 = Conf[key].split('\n');
+ for (i = 0, len = ref1.length; i < len; i++) {
+ line = ref1[i];
+ if (line[0] === '#') {
+ continue;
+ }
+ if (!(regexp = line.match(/\/(.*)\/(\w*)/))) {
+ continue;
+ }
+ filter = line.replace(regexp[0], '');
+ boards = this.parseBoards((ref2 = filter.match(/(?:^|;)\s*boards:([^;]+)/)) != null ? ref2[1] : void 0);
+ excludes = this.parseBoards((ref3 = filter.match(/(?:^|;)\s*exclude:([^;]+)/)) != null ? ref3[1] : void 0);
+ if ((isstring = (key === 'uniqueID' || key === 'MD5'))) {
+ regexp = regexp[1];
+ } else {
+ try {
+ regexp = RegExp(regexp[1], regexp[2]);
+ } catch (error) {
+ err = error;
+ new Notice('warning', [$.tn("Invalid " + key + " filter:"), $.el('br'), $.tn(line), $.el('br'), $.tn(err.message)], 60);
+ continue;
+ }
+ }
+ op = ((ref4 = filter.match(/(?:^|;)\s*op:(no|only)/)) != null ? ref4[1] : void 0) || '';
+ mask = $.getOwn({
+ 'no': 1,
+ 'only': 2
+ }, op) || 0;
+ file = ((ref5 = filter.match(/(?:^|;)\s*file:(no|only)/)) != null ? ref5[1] : void 0) || '';
+ mask = mask | ($.getOwn({
+ 'no': 4,
+ 'only': 8
+ }, file) || 0);
+ stub = (function() {
+ var ref6;
+ switch ((ref6 = filter.match(/(?:^|;)\s*stub:(yes|no)/)) != null ? ref6[1] : void 0) {
+ case 'yes':
+ return true;
+ case 'no':
+ return false;
+ default:
+ return Conf['Stubs'];
+ }
+ })();
+ noti = /(?:^|;)\s*notify/.test(filter);
+ if ((hl = /(?:^|;)\s*highlight/.test(filter))) {
+ hl = ((ref6 = filter.match(/(?:^|;)\s*highlight:([\w-]+)/)) != null ? ref6[1] : void 0) || 'filter-highlight';
+ top = ((ref7 = filter.match(/(?:^|;)\s*top:(yes|no)/)) != null ? ref7[1] : void 0) || 'yes';
+ top = top === 'yes';
+ }
+ if (key === 'general') {
+ if ((types = filter.match(/(?:^|;)\s*type:([^;]*)/))) {
+ types = types[1].split(',');
+ } else {
+ types = ['subject', 'name', 'filename', 'comment'];
+ }
+ }
+ hide = !(hl || noti);
+ filter = {
+ isstring: isstring,
+ regexp: regexp,
+ boards: boards,
+ excludes: excludes,
+ mask: mask,
+ hide: hide,
+ stub: stub,
+ hl: hl,
+ top: top,
+ noti: noti
+ };
+ if (key === 'general') {
+ for (j = 0, len1 = types.length; j < len1; j++) {
+ type = types[j];
+ ((base = this.filters)[type] || (base[type] = [])).push(filter);
+ }
+ } else {
+ ((base1 = this.filters)[key] || (base1[key] = [])).push(filter);
+ }
+ }
+ }
+ if (!Object.keys(this.filters).length) {
+ return;
+ }
+ if (g.VIEW === 'catalog') {
+ return Filter.catalog();
+ } else {
+ return Callbacks.Post.push({
+ name: 'Filter',
+ cb: this.node
+ });
+ }
+ },
+ parseBoards: function(boardsRaw) {
+ var boardID, boardID2, boards, i, j, len, len1, ref, ref1, ref2, ref3, site, siteFilter, siteID;
+ if (!boardsRaw) {
+ return false;
+ }
+ if ((boards = Filter.parseBoardsMemo[boardsRaw])) {
+ return boards;
+ }
+ boards = $.dict();
+ siteFilter = '';
+ ref = boardsRaw.split(',');
+ for (i = 0, len = ref.length; i < len; i++) {
+ boardID = ref[i];
+ if (indexOf.call(boardID, ':') >= 0) {
+ ref1 = boardID.split(':').slice(-2), siteFilter = ref1[0], boardID = ref1[1];
+ }
+ ref2 = g.sites;
+ for (siteID in ref2) {
+ site = ref2[siteID];
+ if (siteID.slice(0, siteFilter.length) === siteFilter) {
+ if (boardID === 'nsfw' || boardID === 'sfw') {
+ ref3 = (typeof site.sfwBoards === "function" ? site.sfwBoards(boardID === 'sfw') : void 0) || [];
+ for (j = 0, len1 = ref3.length; j < len1; j++) {
+ boardID2 = ref3[j];
+ boards[siteID + "/" + boardID2] = true;
+ }
+ } else {
+ boards[siteID + "/" + (encodeURIComponent(boardID))] = true;
+ }
+ }
+ }
+ }
+ Filter.parseBoardsMemo[boardsRaw] = boards;
+ return boards;
+ },
+ parseBoardsMemo: $.dict(),
+ test: function(post, hideable) {
+ var board, filter, hide, hl, i, j, key, len, len1, mask, noti, ref, ref1, ref2, site, stub, top, value;
+ if (hideable == null) {
+ hideable = true;
+ }
+ if (post.filterResults) {
+ return post.filterResults;
+ }
+ hide = false;
+ stub = true;
+ hl = void 0;
+ top = false;
+ noti = false;
+ if (QuoteYou.isYou(post)) {
+ hideable = false;
+ }
+ mask = (post.isReply ? 2 : 1);
+ mask = mask | (post.file ? 4 : 8);
+ board = post.siteID + "/" + post.boardID;
+ site = post.siteID + "/*";
+ for (key in Filter.filters) {
+ ref = Filter.values(key, post);
+ for (i = 0, len = ref.length; i < len; i++) {
+ value = ref[i];
+ ref1 = Filter.filters[key];
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ filter = ref1[j];
+ if ((filter.boards && !(filter.boards[board] || filter.boards[site])) || (filter.excludes && (filter.excludes[board] || filter.excludes[site])) || (filter.mask & mask) || (filter.isstring ? filter.regexp !== value : !filter.regexp.test(value))) {
+ continue;
+ }
+ if (filter.hide) {
+ if (hideable) {
+ hide = true;
+ stub && (stub = filter.stub);
+ }
+ } else {
+ if (!(hl && (ref2 = filter.hl, indexOf.call(hl, ref2) >= 0))) {
+ (hl || (hl = [])).push(filter.hl);
+ }
+ top || (top = filter.top);
+ if (filter.noti) {
+ noti = true;
+ }
+ }
+ }
+ }
+ }
+ if (hide) {
+ return {
+ hide: hide,
+ stub: stub
+ };
+ } else {
+ return {
+ hl: hl,
+ top: top,
+ noti: noti
+ };
+ }
+ },
+ node: function() {
+ var hide, hl, noti, ref, stub, top;
+ if (this.isClone) {
+ return;
+ }
+ ref = Filter.test(this, !this.isFetchedQuote && (this.isReply || g.VIEW === 'index')), hide = ref.hide, stub = ref.stub, hl = ref.hl, top = ref.top, noti = ref.noti;
+ if (hide) {
+ if (this.isReply) {
+ PostHiding.hide(this, stub);
+ } else {
+ ThreadHiding.hide(this.thread, stub);
+ }
+ } else {
+ if (hl) {
+ this.highlights = hl;
+ $.addClass.apply($, [this.nodes.root].concat(slice.call(hl)));
+ }
+ }
+ if (noti && Unread.posts && (this.ID > Unread.lastReadPost) && !QuoteYou.isYou(this)) {
+ return Unread.openNotification(this, ' triggered a notification filter');
+ }
+ },
+ catalog: function() {
+ var base, url;
+ if (!(url = typeof (base = g.SITE.urls).catalogJSON === "function" ? base.catalogJSON(g.BOARD) : void 0)) {
+ return;
+ }
+ Filter.catalogData = $.dict();
+ $.ajax(url, {
+ onloadend: Filter.catalogParse
+ });
+ return Callbacks.CatalogThreadNative.push({
+ name: 'Filter',
+ cb: this.catalogNode
+ });
+ },
+ catalogParse: function() {
+ var i, item, j, len, len1, page, ref, ref1, ref2;
+ if ((ref = this.status) !== 200 && ref !== 404) {
+ new Notice('warning', "Failed to fetch catalog JSON data. " + (this.status ? "Error " + this.statusText + " (" + this.status + ")" : 'Connection Error'), 1);
+ return;
+ }
+ ref1 = this.response;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ page = ref1[i];
+ ref2 = page.threads;
+ for (j = 0, len1 = ref2.length; j < len1; j++) {
+ item = ref2[j];
+ Filter.catalogData[item.no] = item;
+ }
+ }
+ g.BOARD.threads.forEach(function(thread) {
+ if (thread.catalogViewNative) {
+ return Filter.catalogNode.call(thread.catalogViewNative);
+ }
+ });
+ },
+ catalogNode: function() {
+ var base, hide, hl, ref, ref1, top;
+ if (!(this.boardID === g.BOARD.ID && Filter.catalogData[this.ID])) {
+ return;
+ }
+ if ((ref = QuoteYou.db) != null ? ref.get({
+ siteID: g.SITE.ID,
+ boardID: this.boardID,
+ threadID: this.ID,
+ postID: this.ID
+ }) : void 0) {
+ return;
+ }
+ ref1 = Filter.test(g.SITE.Build.parseJSON(Filter.catalogData[this.ID], this)), hide = ref1.hide, hl = ref1.hl, top = ref1.top;
+ if (hide) {
+ return this.nodes.root.hidden = true;
+ } else {
+ if (hl) {
+ this.highlights = hl;
+ $.addClass.apply($, [this.nodes.root].concat(slice.call(hl)));
+ }
+ if (top) {
+ $.prepend(this.nodes.root.parentNode, this.nodes.root);
+ return typeof (base = g.SITE).catalogPin === "function" ? base.catalogPin(this.nodes.root) : void 0;
+ }
+ }
+ },
+ isHidden: function(post) {
+ return !!Filter.test(post).hide;
+ },
+ valueF: {
+ postID: function(post) {
+ return ["" + post.ID];
+ },
+ name: function(post) {
+ return [post.info.name];
+ },
+ uniqueID: function(post) {
+ return [post.info.uniqueID || ''];
+ },
+ tripcode: function(post) {
+ return [post.info.tripcode];
+ },
+ capcode: function(post) {
+ return [post.info.capcode];
+ },
+ pass: function(post) {
+ return [post.info.pass];
+ },
+ email: function(post) {
+ return [post.info.email];
+ },
+ subject: function(post) {
+ return [post.info.subject || (post.isReply ? void 0 : '')];
+ },
+ comment: function(post) {
+ var base, ref, ref1;
+ return [((base = post.info).comment != null ? base.comment : base.comment = (ref = g.sites[post.siteID]) != null ? (ref1 = ref.Build) != null ? typeof ref1.parseComment === "function" ? ref1.parseComment(post.info.commentHTML.innerHTML) : void 0 : void 0 : void 0)];
+ },
+ flag: function(post) {
+ return [post.info.flag];
+ },
+ filename: function(post) {
+ return post.files.map(function(f) {
+ return f.name;
+ });
+ },
+ dimensions: function(post) {
+ return post.files.map(function(f) {
+ return f.dimensions;
+ });
+ },
+ filesize: function(post) {
+ return post.files.map(function(f) {
+ return f.size;
+ });
+ },
+ MD5: function(post) {
+ return post.files.map(function(f) {
+ return f.MD5;
+ });
+ }
+ },
+ values: function(key, post) {
+ if ($.hasOwn(Filter.valueF, key)) {
+ return Filter.valueF[key](post).filter(function(v) {
+ return v != null;
+ });
+ } else {
+ return [
+ key.split('+').map(function(k) {
+ var f;
+ if ((f = $.getOwn(Filter.valueF, k))) {
+ return f(post).map(function(v) {
+ return v || '';
+ }).join('\n');
+ } else {
+ return '';
+ }
+ }).join('\n')
+ ];
+ }
+ },
+ addFilter: function(type, re, cb) {
+ if (!$.hasOwn(Config.filter, type)) {
+ return;
+ }
+ return $.get(type, Conf[type], function(item) {
+ var save;
+ save = item[type];
+ save = save ? save + "\n" + re : re;
+ return $.set(type, save, cb);
+ });
+ },
+ removeFilters: function(type, res, cb) {
+ return $.get(type, Conf[type], function(item) {
+ var save;
+ save = item[type];
+ res = res.map(Filter.escape).join('|');
+ save = save.replace(RegExp("(?:$\n|^)(?:" + res + ")$", 'mg'), '');
+ return $.set(type, save, cb);
+ });
+ },
+ showFilters: function(type) {
+ var section, select;
+ Settings.open('Filter');
+ section = $('.section-container');
+ select = $('select[name=filter]', section);
+ select.value = type;
+ Settings.selectFilter.call(select);
+ return $.onExists(section, 'textarea', function(ta) {
+ var tl;
+ tl = ta.textLength;
+ ta.setSelectionRange(tl, tl);
+ return ta.focus();
+ });
+ },
+ quickFilterMD5: function() {
+ var files, filter, links, msg, notice, origin, post;
+ post = Get.postFromNode(this);
+ files = post.files.filter(function(f) {
+ return f.MD5;
+ });
+ if (!files.length) {
+ return;
+ }
+ filter = files.map(function(f) {
+ return "/" + f.MD5 + "/";
+ }).join('\n');
+ Filter.addFilter('MD5', filter);
+ origin = post.origin || post;
+ if (origin.isReply) {
+ PostHiding.hide(origin);
+ } else if (g.VIEW === 'index') {
+ ThreadHiding.hide(origin.thread);
+ }
+ if (!Conf['MD5 Quick Filter Notifications']) {
+ if (post.nodes.post.getBoundingClientRect().height) {
+ new Notice('info', 'MD5 filtered.', 2);
+ }
+ return;
+ }
+ notice = Filter.quickFilterMD5.notice;
+ if (notice) {
+ notice.filters.push(filter);
+ notice.posts.push(origin);
+ return $('span', notice.el).textContent = notice.filters.length + " MD5s filtered.";
+ } else {
+ msg = $.el('div', {innerHTML: "<span>MD5 filtered.</span> [<a href=\"javascript:;\">show</a>] [<a href=\"javascript:;\">undo</a>]"});
+ notice = Filter.quickFilterMD5.notice = new Notice('info', msg, void 0, function() {
+ return delete Filter.quickFilterMD5.notice;
+ });
+ notice.filters = [filter];
+ notice.posts = [origin];
+ links = $$('a', msg);
+ $.on(links[0], 'click', Filter.quickFilterCB.show.bind(notice));
+ return $.on(links[1], 'click', Filter.quickFilterCB.undo.bind(notice));
+ }
+ },
+ quickFilterCB: {
+ show: function() {
+ Filter.showFilters('MD5');
+ return this.close();
+ },
+ undo: function() {
+ var i, len, post, ref;
+ Filter.removeFilters('MD5', this.filters);
+ ref = this.posts;
+ for (i = 0, len = ref.length; i < len; i++) {
+ post = ref[i];
+ if (post.isReply) {
+ PostHiding.show(post);
+ } else if (g.VIEW === 'index') {
+ ThreadHiding.show(post.thread);
+ }
+ }
+ return this.close();
+ }
+ },
+ escape: function(value) {
+ return value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) {
+ if (c === '\n') {
+ return '\\n';
+ } else if (c === '\\') {
+ return '\\\\';
+ } else {
+ return "\\" + c;
+ }
+ });
+ },
+ menu: {
+ init: function() {
+ var div, entry, i, len, ref, ref1, type;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Menu'] && Conf['Filter'])) {
+ return;
+ }
+ div = $.el('div', {
+ textContent: 'Filter'
+ });
+ entry = {
+ el: div,
+ order: 50,
+ open: function(post) {
+ Filter.menu.post = post;
+ return true;
+ },
+ subEntries: []
+ };
+ ref1 = [['Name', 'name'], ['Unique ID', 'uniqueID'], ['Tripcode', 'tripcode'], ['Capcode', 'capcode'], ['Pass Date', 'pass'], ['Email', 'email'], ['Subject', 'subject'], ['Comment', 'comment'], ['Flag', 'flag'], ['Filename', 'filename'], ['Image dimensions', 'dimensions'], ['Filesize', 'filesize'], ['Image MD5', 'MD5']];
+ for (i = 0, len = ref1.length; i < len; i++) {
+ type = ref1[i];
+ entry.subEntries.push(Filter.menu.createSubEntry(type[0], type[1]));
+ }
+ return Menu.menu.addEntry(entry);
+ },
+ createSubEntry: function(text, type) {
+ var el;
+ el = $.el('a', {
+ href: 'javascript:;',
+ textContent: text
+ });
+ el.dataset.type = type;
+ $.on(el, 'click', Filter.menu.makeFilter);
+ return {
+ el: el,
+ open: function(post) {
+ return Filter.values(type, post).length;
+ }
+ };
+ },
+ makeFilter: function() {
+ var res, type, values;
+ type = this.dataset.type;
+ values = Filter.values(type, Filter.menu.post);
+ res = values.map(function(value) {
+ var re;
+ re = type === 'uniqueID' || type === 'MD5' ? value : Filter.escape(value);
+ if (type === 'uniqueID' || type === 'MD5') {
+ return "/" + re + "/";
+ } else {
+ return "/^" + re + "$/";
+ }
+ }).join('\n');
+ return Filter.addFilter(type, res, function() {
+ return Filter.showFilters(type);
+ });
+ }
+ }
+ };
+
+ return Filter;
+
+}).call(this);
+
+PostHiding = (function() {
+ var PostHiding;
+
+ PostHiding = {
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread') || !Conf['Reply Hiding Buttons'] && !(Conf['Menu'] && Conf['Reply Hiding Link'])) {
+ return;
+ }
+ if (Conf['Reply Hiding Buttons']) {
+ $.addClass(doc, "reply-hide");
+ }
+ this.db = new DataBoard('hiddenPosts');
+ return Callbacks.Post.push({
+ name: 'Reply Hiding',
+ cb: this.node
+ });
+ },
+ isHidden: function(boardID, threadID, postID) {
+ return !!(PostHiding.db && PostHiding.db.get({
+ boardID: boardID,
+ threadID: threadID,
+ postID: postID
+ }));
+ },
+ node: function() {
+ var button, data, sa, sideArrows;
+ if (!this.isReply || this.isClone || this.isFetchedQuote) {
+ return;
+ }
+ if (data = PostHiding.db.get({
+ boardID: this.board.ID,
+ threadID: this.thread.ID,
+ postID: this.ID
+ })) {
+ if (data.thisPost) {
+ PostHiding.hide(this, data.makeStub, data.hideRecursively);
+ } else {
+ Recursive.apply(PostHiding.hide, this, data.makeStub, true);
+ Recursive.add(PostHiding.hide, this, data.makeStub, true);
+ }
+ }
+ if (!Conf['Reply Hiding Buttons']) {
+ return;
+ }
+ button = PostHiding.makeButton(this, 'hide');
+ if ((sa = g.SITE.selectors.sideArrows)) {
+ sideArrows = $(sa, this.nodes.root);
+ $.replace(sideArrows.firstChild, button);
+ return sideArrows.className = 'replacedSideArrows';
+ } else {
+ return $.prepend(this.nodes.info, button);
+ }
+ },
+ menu: {
+ init: function() {
+ var apply, div, hideStubLink, makeStub, ref, replies, thisPost;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread') || !Conf['Menu'] || !Conf['Reply Hiding Link']) {
+ return;
+ }
+ div = $.el('div', {
+ className: 'hide-reply-link',
+ textContent: 'Hide'
+ });
+ apply = $.el('a', {
+ textContent: 'Apply',
+ href: 'javascript:;'
+ });
+ $.on(apply, 'click', PostHiding.menu.hide);
+ thisPost = UI.checkbox('thisPost', 'This post', true);
+ replies = UI.checkbox('replies', 'Hide replies', Conf['Recursive Hiding']);
+ makeStub = UI.checkbox('makeStub', 'Make stub', Conf['Stubs']);
+ Menu.menu.addEntry({
+ el: div,
+ order: 20,
+ open: function(post) {
+ if (!post.isReply || post.isClone || post.isHidden) {
+ return false;
+ }
+ PostHiding.menu.post = post;
+ return true;
+ },
+ subEntries: [
+ {
+ el: apply
+ }, {
+ el: thisPost
+ }, {
+ el: replies
+ }, {
+ el: makeStub
+ }
+ ]
+ });
+ div = $.el('div', {
+ className: 'show-reply-link',
+ textContent: 'Show'
+ });
+ apply = $.el('a', {
+ textContent: 'Apply',
+ href: 'javascript:;'
+ });
+ $.on(apply, 'click', PostHiding.menu.show);
+ thisPost = UI.checkbox('thisPost', 'This post', false);
+ replies = UI.checkbox('replies', 'Show replies', false);
+ hideStubLink = $.el('a', {
+ textContent: 'Hide stub',
+ href: 'javascript:;'
+ });
+ $.on(hideStubLink, 'click', PostHiding.menu.hideStub);
+ Menu.menu.addEntry({
+ el: div,
+ order: 20,
+ open: function(post) {
+ var data;
+ if (!post.isReply || post.isClone || !post.isHidden) {
+ return false;
+ }
+ if (!(data = PostHiding.db.get({
+ boardID: post.board.ID,
+ threadID: post.thread.ID,
+ postID: post.ID
+ }))) {
+ return false;
+ }
+ PostHiding.menu.post = post;
+ thisPost.firstChild.checked = post.isHidden;
+ replies.firstChild.checked = (data != null ? data.hideRecursively : void 0) != null ? data.hideRecursively : Conf['Recursive Hiding'];
+ return true;
+ },
+ subEntries: [
+ {
+ el: apply
+ }, {
+ el: thisPost
+ }, {
+ el: replies
+ }
+ ]
+ });
+ return Menu.menu.addEntry({
+ el: hideStubLink,
+ order: 15,
+ open: function(post) {
+ var data;
+ if (!post.isReply || post.isClone || !post.isHidden) {
+ return false;
+ }
+ if (!(data = PostHiding.db.get({
+ boardID: post.board.ID,
+ threadID: post.thread.ID,
+ postID: post.ID
+ }))) {
+ return false;
+ }
+ return PostHiding.menu.post = post;
+ }
+ });
+ },
+ hide: function() {
+ var makeStub, parent, post, replies, thisPost;
+ parent = this.parentNode;
+ thisPost = $('input[name=thisPost]', parent).checked;
+ replies = $('input[name=replies]', parent).checked;
+ makeStub = $('input[name=makeStub]', parent).checked;
+ post = PostHiding.menu.post;
+ if (thisPost) {
+ PostHiding.hide(post, makeStub, replies);
+ } else if (replies) {
+ Recursive.apply(PostHiding.hide, post, makeStub, true);
+ Recursive.add(PostHiding.hide, post, makeStub, true);
+ } else {
+ return;
+ }
+ PostHiding.saveHiddenState(post, true, thisPost, makeStub, replies);
+ return $.event('CloseMenu');
+ },
+ show: function() {
+ var data, parent, post, replies, thisPost;
+ parent = this.parentNode;
+ thisPost = $('input[name=thisPost]', parent).checked;
+ replies = $('input[name=replies]', parent).checked;
+ post = PostHiding.menu.post;
+ if (thisPost) {
+ PostHiding.show(post, replies);
+ } else if (replies) {
+ Recursive.apply(PostHiding.show, post, true);
+ Recursive.rm(PostHiding.hide, post, true);
+ } else {
+ return;
+ }
+ if (data = PostHiding.db.get({
+ boardID: post.board.ID,
+ threadID: post.thread.ID,
+ postID: post.ID
+ })) {
+ PostHiding.saveHiddenState(post, !(thisPost && replies), !thisPost, data.makeStub, !replies);
+ }
+ return $.event('CloseMenu');
+ },
+ hideStub: function() {
+ var data, post;
+ post = PostHiding.menu.post;
+ if (data = PostHiding.db.get({
+ boardID: post.board.ID,
+ threadID: post.thread.ID,
+ postID: post.ID
+ })) {
+ PostHiding.show(post, data.hideRecursively);
+ PostHiding.hide(post, false, data.hideRecursively);
+ PostHiding.saveHiddenState(post, true, true, false, data.hideRecursively);
+ }
+ $.event('CloseMenu');
+ }
+ },
+ makeButton: function(post, type) {
+ var a, span;
+ span = $.el('span', {
+ className: "fa fa-" + (type === 'hide' ? 'minus' : 'plus') + "-square-o",
+ textContent: ""
+ });
+ a = $.el('a', {
+ className: type + "-reply-button",
+ href: 'javascript:;'
+ });
+ $.add(a, span);
+ $.on(a, 'click', PostHiding.toggle);
+ return a;
+ },
+ saveHiddenState: function(post, isHiding, thisPost, makeStub, hideRecursively) {
+ var data;
+ data = {
+ boardID: post.board.ID,
+ threadID: post.thread.ID,
+ postID: post.ID
+ };
+ if (isHiding) {
+ data.val = {
+ thisPost: thisPost !== false,
+ makeStub: makeStub,
+ hideRecursively: hideRecursively
+ };
+ return PostHiding.db.set(data);
+ } else {
+ return PostHiding.db["delete"](data);
+ }
+ },
+ toggle: function() {
+ var post;
+ post = Get.postFromNode(this);
+ PostHiding[(post.isHidden ? 'show' : 'hide')](post);
+ return PostHiding.saveHiddenState(post, post.isHidden);
+ },
+ hide: function(post, makeStub, hideRecursively) {
+ var a, i, len, quotelink, ref;
+ if (makeStub == null) {
+ makeStub = Conf['Stubs'];
+ }
+ if (hideRecursively == null) {
+ hideRecursively = Conf['Recursive Hiding'];
+ }
+ if (post.isHidden) {
+ return;
+ }
+ post.isHidden = true;
+ if (hideRecursively) {
+ Recursive.apply(PostHiding.hide, post, makeStub, true);
+ Recursive.add(PostHiding.hide, post, makeStub, true);
+ }
+ ref = Get.allQuotelinksLinkingTo(post);
+ for (i = 0, len = ref.length; i < len; i++) {
+ quotelink = ref[i];
+ $.addClass(quotelink, 'filtered');
+ }
+ if (!makeStub) {
+ post.nodes.root.hidden = true;
+ return;
+ }
+ a = PostHiding.makeButton(post, 'show');
+ $.add(a, $.tn(" " + post.info.nameBlock));
+ post.nodes.stub = $.el('div', {
+ className: 'stub'
+ });
+ $.add(post.nodes.stub, a);
+ if (Conf['Menu']) {
+ $.add(post.nodes.stub, Menu.makeButton(post));
+ }
+ return $.prepend(post.nodes.root, post.nodes.stub);
+ },
+ show: function(post, showRecursively) {
+ var i, len, quotelink, ref;
+ if (showRecursively == null) {
+ showRecursively = Conf['Recursive Hiding'];
+ }
+ if (post.nodes.stub) {
+ $.rm(post.nodes.stub);
+ delete post.nodes.stub;
+ } else {
+ post.nodes.root.hidden = false;
+ }
+ post.isHidden = false;
+ if (showRecursively) {
+ Recursive.apply(PostHiding.show, post, true);
+ Recursive.rm(PostHiding.hide, post);
+ }
+ ref = Get.allQuotelinksLinkingTo(post);
+ for (i = 0, len = ref.length; i < len; i++) {
+ quotelink = ref[i];
+ $.rmClass(quotelink, 'filtered');
+ }
+ }
+ };
+
+ return PostHiding;
+
+}).call(this);
+
+Recursive = (function() {
+ var Recursive,
+ slice = [].slice,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Recursive = {
+ recursives: $.dict(),
+ init: function() {
+ var ref;
+ if ((ref = g.VIEW) !== 'index' && ref !== 'thread') {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'Recursive',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var i, j, k, len, len1, obj, quote, recursive, ref, ref1;
+ if (this.isClone || this.isFetchedQuote) {
+ return;
+ }
+ ref = this.quotes;
+ for (j = 0, len = ref.length; j < len; j++) {
+ quote = ref[j];
+ if (obj = Recursive.recursives[quote]) {
+ ref1 = obj.recursives;
+ for (i = k = 0, len1 = ref1.length; k < len1; i = ++k) {
+ recursive = ref1[i];
+ recursive.apply(null, [this].concat(slice.call(obj.args[i])));
+ }
+ }
+ }
+ },
+ add: function() {
+ var args, base, name, obj, post, recursive;
+ recursive = arguments[0], post = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
+ obj = (base = Recursive.recursives)[name = post.fullID] || (base[name] = {
+ recursives: [],
+ args: []
+ });
+ obj.recursives.push(recursive);
+ return obj.args.push(args);
+ },
+ rm: function(recursive, post) {
+ var i, j, len, obj, rec, ref;
+ if (!(obj = Recursive.recursives[post.fullID])) {
+ return;
+ }
+ ref = obj.recursives;
+ for (i = j = 0, len = ref.length; j < len; i = ++j) {
+ rec = ref[i];
+ if (!(rec === recursive)) {
+ continue;
+ }
+ obj.recursives.splice(i, 1);
+ obj.args.splice(i, 1);
+ }
+ },
+ apply: function() {
+ var args, fullID, post, recursive;
+ recursive = arguments[0], post = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
+ fullID = post.fullID;
+ return g.posts.forEach(function(post) {
+ if (indexOf.call(post.quotes, fullID) >= 0) {
+ return recursive.apply(null, [post].concat(slice.call(args)));
+ }
+ });
+ }
+ };
+
+ return Recursive;
+
+}).call(this);
+
+ThreadHiding = (function() {
+ var ThreadHiding;
+
+ ThreadHiding = {
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'catalog') || !Conf['Thread Hiding Buttons'] && !(Conf['Menu'] && Conf['Thread Hiding Link']) && !Conf['JSON Index']) {
+ return;
+ }
+ this.db = new DataBoard('hiddenThreads');
+ if (g.VIEW === 'catalog') {
+ return this.catalogWatch();
+ }
+ this.catalogSet(g.BOARD);
+ $.on(d, 'IndexRefreshInternal', this.onIndexRefresh);
+ if (Conf['Thread Hiding Buttons']) {
+ $.addClass(doc, 'thread-hide');
+ }
+ return Callbacks.Post.push({
+ name: 'Thread Hiding',
+ cb: this.node
+ });
+ },
+ catalogSet: function(board) {
+ var hiddenThreads, threadID;
+ if (!($.hasStorage && g.SITE.software === 'yotsuba')) {
+ return;
+ }
+ hiddenThreads = ThreadHiding.db.get({
+ boardID: board.ID,
+ defaultValue: $.dict()
+ });
+ for (threadID in hiddenThreads) {
+ hiddenThreads[threadID] = true;
+ }
+ return localStorage.setItem("4chan-hide-t-" + board, JSON.stringify(hiddenThreads));
+ },
+ catalogWatch: function() {
+ if (!($.hasStorage && g.SITE.software === 'yotsuba')) {
+ return;
+ }
+ this.hiddenThreads = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {};
+ return Main.ready(function() {
+ return new MutationObserver(ThreadHiding.catalogSave).observe($.id('threads'), {
+ attributes: true,
+ subtree: true,
+ attributeFilter: ['style']
+ });
+ });
+ },
+ catalogSave: function() {
+ var hiddenThreads2, threadID;
+ hiddenThreads2 = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {};
+ for (threadID in hiddenThreads2) {
+ if (!$.hasOwn(ThreadHiding.hiddenThreads, threadID)) {
+ ThreadHiding.db.set({
+ boardID: g.BOARD.ID,
+ threadID: threadID,
+ val: {
+ makeStub: Conf['Stubs']
+ }
+ });
+ }
+ }
+ for (threadID in ThreadHiding.hiddenThreads) {
+ if (!$.hasOwn(hiddenThreads2, threadID)) {
+ ThreadHiding.db["delete"]({
+ boardID: g.BOARD.ID,
+ threadID: threadID
+ });
+ }
+ }
+ return ThreadHiding.hiddenThreads = hiddenThreads2;
+ },
+ isHidden: function(boardID, threadID) {
+ return !!(ThreadHiding.db && ThreadHiding.db.get({
+ boardID: boardID,
+ threadID: threadID
+ }));
+ },
+ node: function() {
+ var data;
+ if (this.isReply || this.isClone || this.isFetchedQuote) {
+ return;
+ }
+ if (Conf['Thread Hiding Buttons']) {
+ $.prepend(this.nodes.root, ThreadHiding.makeButton(this.thread, 'hide'));
+ }
+ if (data = ThreadHiding.db.get({
+ boardID: this.board.ID,
+ threadID: this.ID
+ })) {
+ return ThreadHiding.hide(this.thread, data.makeStub);
+ }
+ },
+ onIndexRefresh: function() {
+ return g.BOARD.threads.forEach(function(thread) {
+ var root;
+ root = thread.nodes.root;
+ if (thread.isHidden && thread.stub && !root.contains(thread.stub)) {
+ return ThreadHiding.makeStub(thread, root);
+ }
+ });
+ },
+ menu: {
+ init: function() {
+ var apply, div, hideStubLink, makeStub;
+ if (g.VIEW !== 'index' || !Conf['Menu'] || !Conf['Thread Hiding Link']) {
+ return;
+ }
+ div = $.el('div', {
+ className: 'hide-thread-link',
+ textContent: 'Hide'
+ });
+ apply = $.el('a', {
+ textContent: 'Apply',
+ href: 'javascript:;'
+ });
+ $.on(apply, 'click', ThreadHiding.menu.hide);
+ makeStub = UI.checkbox('Stubs', 'Make stub');
+ Menu.menu.addEntry({
+ el: div,
+ order: 20,
+ open: function(arg) {
+ var isReply, thread;
+ thread = arg.thread, isReply = arg.isReply;
+ if (isReply || thread.isHidden || Conf['JSON Index'] && Conf['Index Mode'] === 'catalog') {
+ return false;
+ }
+ ThreadHiding.menu.thread = thread;
+ return true;
+ },
+ subEntries: [
+ {
+ el: apply
+ }, {
+ el: makeStub
+ }
+ ]
+ });
+ div = $.el('a', {
+ className: 'show-thread-link',
+ textContent: 'Show',
+ href: 'javascript:;'
+ });
+ $.on(div, 'click', ThreadHiding.menu.show);
+ Menu.menu.addEntry({
+ el: div,
+ order: 20,
+ open: function(arg) {
+ var isReply, thread;
+ thread = arg.thread, isReply = arg.isReply;
+ if (isReply || !thread.isHidden || Conf['JSON Index'] && Conf['Index Mode'] === 'catalog') {
+ return false;
+ }
+ ThreadHiding.menu.thread = thread;
+ return true;
+ }
+ });
+ hideStubLink = $.el('a', {
+ textContent: 'Hide stub',
+ href: 'javascript:;'
+ });
+ $.on(hideStubLink, 'click', ThreadHiding.menu.hideStub);
+ return Menu.menu.addEntry({
+ el: hideStubLink,
+ order: 15,
+ open: function(arg) {
+ var isReply, thread;
+ thread = arg.thread, isReply = arg.isReply;
+ if (isReply || !thread.isHidden || Conf['JSON Index'] && Conf['Index Mode'] === 'catalog') {
+ return false;
+ }
+ return ThreadHiding.menu.thread = thread;
+ }
+ });
+ },
+ hide: function() {
+ var makeStub, thread;
+ makeStub = $('input', this.parentNode).checked;
+ thread = ThreadHiding.menu.thread;
+ ThreadHiding.hide(thread, makeStub);
+ ThreadHiding.saveHiddenState(thread, makeStub);
+ return $.event('CloseMenu');
+ },
+ show: function() {
+ var thread;
+ thread = ThreadHiding.menu.thread;
+ ThreadHiding.show(thread);
+ ThreadHiding.saveHiddenState(thread);
+ return $.event('CloseMenu');
+ },
+ hideStub: function() {
+ var thread;
+ thread = ThreadHiding.menu.thread;
+ ThreadHiding.show(thread);
+ ThreadHiding.hide(thread, false);
+ ThreadHiding.saveHiddenState(thread, false);
+ $.event('CloseMenu');
+ }
+ },
+ makeButton: function(thread, type) {
+ var a;
+ a = $.el('a', {
+ className: type + "-thread-button",
+ href: 'javascript:;'
+ });
+ $.extend(a, {innerHTML: "<span class=\"fa fa-" + ((type === "hide") ? "minus" : "plus") + "-square\"></span>"});
+ a.dataset.fullID = thread.fullID;
+ $.on(a, 'click', ThreadHiding.toggle);
+ return a;
+ },
+ makeStub: function(thread, root) {
+ var a, numReplies, summary, threadDivider;
+ numReplies = $$(g.SITE.selectors.replyOriginal, root).length;
+ if (summary = $(g.SITE.selectors.summary, root)) {
+ numReplies += +summary.textContent.match(/\d+/);
+ }
+ a = ThreadHiding.makeButton(thread, 'show');
+ $.add(a, $.tn(" " + thread.OP.info.nameBlock + " (" + (numReplies === 1 ? '1 reply' : numReplies + " replies") + ")"));
+ thread.stub = $.el('div', {
+ className: 'stub'
+ });
+ if (Conf['Menu']) {
+ $.add(thread.stub, [a, Menu.makeButton(thread.OP)]);
+ } else {
+ $.add(thread.stub, a);
+ }
+ $.prepend(root, thread.stub);
+ if ((threadDivider = $(g.SITE.selectors.threadDivider, root))) {
+ return $.addClass(threadDivider, 'threadDivider');
+ }
+ },
+ saveHiddenState: function(thread, makeStub) {
+ if (thread.isHidden) {
+ ThreadHiding.db.set({
+ boardID: thread.board.ID,
+ threadID: thread.ID,
+ val: {
+ makeStub: makeStub
+ }
+ });
+ } else {
+ ThreadHiding.db["delete"]({
+ boardID: thread.board.ID,
+ threadID: thread.ID
+ });
+ }
+ return ThreadHiding.catalogSet(thread.board);
+ },
+ toggle: function(thread) {
+ if (!(thread instanceof Thread)) {
+ thread = g.threads.get(this.dataset.fullID);
+ }
+ if (thread.isHidden) {
+ ThreadHiding.show(thread);
+ } else {
+ ThreadHiding.hide(thread);
+ }
+ return ThreadHiding.saveHiddenState(thread);
+ },
+ hide: function(thread, makeStub) {
+ var threadRoot;
+ if (makeStub == null) {
+ makeStub = Conf['Stubs'];
+ }
+ if (thread.isHidden) {
+ return;
+ }
+ threadRoot = thread.nodes.root;
+ thread.isHidden = true;
+ Index.updateHideLabel();
+ if (thread.catalogView && !Index.showHiddenThreads) {
+ $.rm(thread.catalogView.nodes.root);
+ $.event('PostsRemoved', null, Index.root);
+ }
+ if (!makeStub) {
+ return threadRoot.hidden = true;
+ }
+ return ThreadHiding.makeStub(thread, threadRoot);
+ },
+ show: function(thread) {
+ var threadRoot;
+ if (thread.stub) {
+ $.rm(thread.stub);
+ delete thread.stub;
+ }
+ threadRoot = thread.nodes.root;
+ threadRoot.hidden = thread.isHidden = false;
+ Index.updateHideLabel();
+ if (thread.catalogView && Index.showHiddenThreads) {
+ $.rm(thread.catalogView.nodes.root);
+ return $.event('PostsRemoved', null, Index.root);
+ }
+ }
+ };
+
+ return ThreadHiding;
+
+}).call(this);
+
+BoardConfig = (function() {
+ var BoardConfig;
+
+ BoardConfig = {
+ cbs: [],
+ init: function() {
+ var boards, now, ref;
+ if (g.SITE.software !== 'yotsuba') {
+ return;
+ }
+ now = Date.now();
+ if (!((now - 2 * $.HOUR < (ref = Conf['boardConfig'].lastChecked || 0) && ref <= now))) {
+ return $.ajax(location.protocol + "//a.4cdn.org/boards.json", {
+ onloadend: this.load
+ });
+ } else {
+ boards = Conf['boardConfig'].boards;
+ return this.set(boards);
+ }
+ },
+ load: function() {
+ var board, boards, err, i, len, ref;
+ if (this.status === 200 && this.response && this.response.boards) {
+ boards = $.dict();
+ ref = this.response.boards;
+ for (i = 0, len = ref.length; i < len; i++) {
+ board = ref[i];
+ boards[board.board] = board;
+ }
+ $.set('boardConfig', {
+ boards: boards,
+ lastChecked: Date.now()
+ });
+ } else {
+ boards = Conf['boardConfig'].boards;
+ err = (function() {
+ switch (this.status) {
+ case 0:
+ return 'Connection Error';
+ case 200:
+ return 'Invalid Data';
+ default:
+ return "Error " + this.statusText + " (" + this.status + ")";
+ }
+ }).call(this);
+ new Notice('warning', "Failed to load board configuration. " + err, 20);
+ }
+ return BoardConfig.set(boards);
+ },
+ set: function(boards1) {
+ var ID, board, cb, i, len, ref, ref1;
+ this.boards = boards1;
+ ref = g.boards;
+ for (ID in ref) {
+ board = ref[ID];
+ board.config = this.boards[ID] || {};
+ }
+ ref1 = this.cbs;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ cb = ref1[i];
+ $.queueTask(cb);
+ }
+ },
+ ready: function(cb) {
+ if (this.boards) {
+ return cb();
+ } else {
+ return this.cbs.push(cb);
+ }
+ },
+ sfwBoards: function(sfw) {
+ var board, data, ref, results;
+ ref = this.boards || Conf['boardConfig'].boards;
+ results = [];
+ for (board in ref) {
+ data = ref[board];
+ if (!!data.ws_board === sfw) {
+ results.push(board);
+ }
+ }
+ return results;
+ },
+ isSFW: function(board) {
+ var ref;
+ return !!((ref = (this.boards || Conf['boardConfig'].boards)[board]) != null ? ref.ws_board : void 0);
+ },
+ domain: function(board) {
+ return "boards." + (BoardConfig.isSFW(board) ? '4channel' : '4chan') + ".org";
+ },
+ isArchived: function(board) {
+ var data;
+ data = (this.boards || Conf['boardConfig'].boards)[board];
+ return !data || data.is_archived;
+ },
+ noAudio: function(boardID) {
+ var boards;
+ if (g.SITE.software !== 'yotsuba') {
+ return false;
+ }
+ boards = this.boards || Conf['boardConfig'].boards;
+ return boards && boards[boardID] && !boards[boardID].webm_audio;
+ },
+ title: function(boardID) {
+ var ref, ref1;
+ return ((ref = this.boards || Conf['boardConfig'].boards) != null ? (ref1 = ref[boardID]) != null ? ref1.title : void 0 : void 0) || '';
+ }
+ };
+
+ return BoardConfig;
+
+}).call(this);
+
+Get = (function() {
+ var Get,
+ slice = [].slice,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Get = {
+ url: function() {
+ var IDs, args, f, site, type;
+ type = arguments[0], IDs = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
+ if ((site = g.sites[IDs.siteID]) && (f = $.getOwn(site.urls, type))) {
+ return f.apply(null, [IDs].concat(slice.call(args)));
+ } else {
+ return void 0;
+ }
+ },
+ threadExcerpt: function(thread) {
+ var OP, excerpt, ref, ref1;
+ OP = thread.OP;
+ excerpt = ("/" + (decodeURIComponent(thread.board.ID)) + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.commentDisplay().replace(/\n+/g, ' // ') || ((ref1 = OP.file) != null ? ref1.name : void 0) || ("No." + OP));
+ if (excerpt.length > 73) {
+ return excerpt.slice(0, 70) + "...";
+ }
+ return excerpt;
+ },
+ threadFromRoot: function(root) {
+ var board;
+ if (root == null) {
+ return null;
+ }
+ board = root.dataset.board;
+ return g.threads.get((board ? encodeURIComponent(board) : g.BOARD.ID) + "." + (root.id.match(/\d*$/)[0]));
+ },
+ threadFromNode: function(node) {
+ return Get.threadFromRoot($.x("ancestor-or-self::" + g.SITE.xpath.thread, node));
+ },
+ postFromRoot: function(root) {
+ var index, post;
+ if (root == null) {
+ return null;
+ }
+ post = g.posts.get(root.dataset.fullID);
+ index = root.dataset.clone;
+ if (index) {
+ return post.clones[+index];
+ } else {
+ return post;
+ }
+ },
+ postFromNode: function(root) {
+ return Get.postFromRoot($.x("ancestor-or-self::" + g.SITE.xpath.postContainer + "[1]", root));
+ },
+ postDataFromLink: function(link) {
+ var boardID, match, postID, ref, ref1, threadID;
+ if (link.dataset.postID) {
+ ref = link.dataset, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID;
+ threadID || (threadID = 0);
+ } else {
+ match = link.href.match(g.SITE.regexp.quotelink);
+ ref1 = match.slice(1), boardID = ref1[0], threadID = ref1[1], postID = ref1[2];
+ postID || (postID = threadID);
+ }
+ return {
+ boardID: boardID,
+ threadID: +threadID,
+ postID: +postID
+ };
+ },
+ allQuotelinksLinkingTo: function(post) {
+ var fullID, handleQuotes, i, len, posts, qPost, quote, quotelinks, ref;
+ quotelinks = [];
+ posts = g.posts;
+ fullID = post.fullID;
+ handleQuotes = function(qPost, type) {
+ var clone, i, len, ref;
+ quotelinks.push.apply(quotelinks, qPost.nodes[type]);
+ ref = qPost.clones;
+ for (i = 0, len = ref.length; i < len; i++) {
+ clone = ref[i];
+ quotelinks.push.apply(quotelinks, clone.nodes[type]);
+ }
+ };
+ posts.forEach(function(qPost) {
+ if (indexOf.call(qPost.quotes, fullID) >= 0) {
+ return handleQuotes(qPost, 'quotelinks');
+ }
+ });
+ if (Conf['Quote Backlinks']) {
+ ref = post.quotes;
+ for (i = 0, len = ref.length; i < len; i++) {
+ quote = ref[i];
+ if (qPost = posts.get(quote)) {
+ handleQuotes(qPost, 'backlinks');
+ }
+ }
+ }
+ return quotelinks.filter(function(quotelink) {
+ var boardID, postID, ref1;
+ ref1 = Get.postDataFromLink(quotelink), boardID = ref1.boardID, postID = ref1.postID;
+ return boardID === post.board.ID && postID === post.ID;
+ });
+ }
+ };
+
+ return Get;
+
+}).call(this);
+
+Header = (function() {
+ var Header,
+ slice = [].slice;
+
+ Header = {
+ init: function() {
+ var barFixedToggler, barPositionToggler, box, cs, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler;
+ $.onExists(doc, 'body', (function(_this) {
+ return function() {
+ if (!Main.isThisPageLegit()) {
+ return;
+ }
+ $.add(_this.bar, [_this.noticesRoot, _this.toggle]);
+ $.prepend(d.body, _this.bar);
+ $.add(d.body, Header.hover);
+ return _this.setBarPosition(Conf['Bottom Header']);
+ };
+ })(this));
+ this.menu = new UI.Menu('header');
+ menuButton = $.el('span', {
+ className: 'menu-button'
+ });
+ $.extend(menuButton, {innerHTML: "<i></i>"});
+ box = UI.checkbox;
+ barFixedToggler = box('Fixed Header', 'Fixed Header');
+ headerToggler = box('Header auto-hide', 'Auto-hide header');
+ scrollHeaderToggler = box('Header auto-hide on scroll', 'Auto-hide header on scroll');
+ barPositionToggler = box('Bottom Header', 'Bottom header');
+ linkJustifyToggler = box('Centered links', 'Centered links');
+ customNavToggler = box('Custom Board Navigation', 'Custom board navigation');
+ footerToggler = box('Bottom Board List', 'Hide bottom board list');
+ shortcutToggler = box('Shortcut Icons', 'Shortcut Icons');
+ editCustomNav = $.el('a', {
+ textContent: 'Edit custom board navigation',
+ href: 'javascript:;'
+ });
+ this.barFixedToggler = barFixedToggler.firstElementChild;
+ this.scrollHeaderToggler = scrollHeaderToggler.firstElementChild;
+ this.barPositionToggler = barPositionToggler.firstElementChild;
+ this.linkJustifyToggler = linkJustifyToggler.firstElementChild;
+ this.headerToggler = headerToggler.firstElementChild;
+ this.footerToggler = footerToggler.firstElementChild;
+ this.shortcutToggler = shortcutToggler.firstElementChild;
+ this.customNavToggler = customNavToggler.firstElementChild;
+ $.on(menuButton, 'click', this.menuToggle);
+ $.on(this.headerToggler, 'change', this.toggleBarVisibility);
+ $.on(this.barFixedToggler, 'change', this.toggleBarFixed);
+ $.on(this.barPositionToggler, 'change', this.toggleBarPosition);
+ $.on(this.scrollHeaderToggler, 'change', this.toggleHideBarOnScroll);
+ $.on(this.linkJustifyToggler, 'change', this.toggleLinkJustify);
+ $.on(this.footerToggler, 'change', this.toggleFooterVisibility);
+ $.on(this.shortcutToggler, 'change', this.toggleShortcutIcons);
+ $.on(this.customNavToggler, 'change', this.toggleCustomNav);
+ $.on(editCustomNav, 'click', this.editCustomNav);
+ this.setBarFixed(Conf['Fixed Header']);
+ this.setHideBarOnScroll(Conf['Header auto-hide on scroll']);
+ this.setBarVisibility(Conf['Header auto-hide']);
+ this.setLinkJustify(Conf['Centered links']);
+ this.setShortcutIcons(Conf['Shortcut Icons']);
+ this.setFooterVisibility(Conf['Bottom Board List']);
+ $.sync('Fixed Header', this.setBarFixed);
+ $.sync('Header auto-hide on scroll', this.setHideBarOnScroll);
+ $.sync('Bottom Header', this.setBarPosition);
+ $.sync('Shortcut Icons', this.setShortcutIcons);
+ $.sync('Header auto-hide', this.setBarVisibility);
+ $.sync('Centered links', this.setLinkJustify);
+ $.sync('Bottom Board List', this.setFooterVisibility);
+ this.addShortcut('menu', menuButton, 900);
+ this.menu.addEntry({
+ el: $.el('span', {
+ textContent: 'Header'
+ }),
+ order: 107,
+ subEntries: [
+ {
+ el: barFixedToggler
+ }, {
+ el: headerToggler
+ }, {
+ el: scrollHeaderToggler
+ }, {
+ el: barPositionToggler
+ }, {
+ el: linkJustifyToggler
+ }, {
+ el: footerToggler
+ }, {
+ el: shortcutToggler
+ }, {
+ el: customNavToggler
+ }, {
+ el: editCustomNav
+ }
+ ]
+ });
+ $.on(window, 'load popstate', Header.hashScroll);
+ $.on(d, 'CreateNotification', this.createNotification);
+ this.setBoardList();
+ $.onExists(doc, g.SITE.selectors.boardList + " + *", Header.generateFullBoardList);
+ Main.ready(function() {
+ var a, absbot, footer, i, len, ref;
+ if (g.SITE.software === 'yotsuba' && !(footer = $.id('boardNavDesktopFoot'))) {
+ if (!(absbot = $.id('absbot'))) {
+ return;
+ }
+ footer = $.id('boardNavDesktop').cloneNode(true);
+ footer.id = 'boardNavDesktopFoot';
+ $('#navtopright', footer).id = 'navbotright';
+ $('#settingsWindowLink', footer).id = 'settingsWindowLinkBot';
+ $.before(absbot, footer);
+ $.global(function() {
+ return window.cloneTopNav = function() {};
+ });
+ }
+ if ((Header.bottomBoardList = $(g.SITE.selectors.boardListBottom))) {
+ ref = $$('a', Header.bottomBoardList);
+ for (i = 0, len = ref.length; i < len; i++) {
+ a = ref[i];
+ if (a.hostname === location.hostname && a.pathname.split('/')[1] === g.BOARD.ID) {
+ a.className = 'current';
+ }
+ }
+ return CatalogLinks.setLinks(Header.bottomBoardList);
+ }
+ });
+ if (g.SITE.software === 'yotsuba' && (g.VIEW === 'catalog' || !Conf['Disable Native Extension'])) {
+ cs = $.el('a', {
+ href: 'javascript:;'
+ });
+ if (g.VIEW === 'catalog') {
+ cs.title = cs.textContent = 'Catalog Settings';
+ cs.className = 'fa fa-book';
+ } else {
+ cs.title = cs.textContent = '4chan Settings';
+ cs.className = 'native-settings';
+ }
+ $.on(cs, 'click', function() {
+ return $.id('settingsWindowLink').click();
+ });
+ this.addShortcut('native', cs, 810);
+ }
+ return this.enableDesktopNotifications();
+ },
+ bar: $.el('div', {
+ id: 'header-bar'
+ }),
+ noticesRoot: $.el('div', {
+ id: 'notifications'
+ }),
+ shortcuts: $.el('span', {
+ id: 'shortcuts'
+ }),
+ hover: $.el('div', {
+ id: 'hoverUI'
+ }),
+ toggle: $.el('div', {
+ id: 'scroll-marker'
+ }),
+ setBoardList: function() {
+ var boardList, btn;
+ Header.boardList = boardList = $.el('span', {
+ id: 'board-list'
+ });
+ $.extend(boardList, {innerHTML: "<span id=\"custom-board-list\"></span><span id=\"full-board-list\" hidden><span class=\"hide-board-list-container brackets-wrap\"><a href=\"javascript:;\" class=\"hide-board-list-button\">&nbsp;-&nbsp;</a></span> <span class=\"boardList\"></span></span>"});
+ btn = $('.hide-board-list-button', boardList);
+ $.on(btn, 'click', Header.toggleBoardList);
+ $.prepend(Header.bar, [Header.boardList, Header.shortcuts]);
+ Header.setCustomNav(Conf['Custom Board Navigation']);
+ Header.generateBoardList(Conf['boardnav']);
+ $.sync('Custom Board Navigation', Header.setCustomNav);
+ return $.sync('boardnav', Header.generateBoardList);
+ },
+ generateFullBoardList: function() {
+ var a, fullBoardList, i, len, nodes, ref;
+ if (g.SITE.transformBoardList) {
+ nodes = g.SITE.transformBoardList();
+ } else {
+ nodes = slice.call($(g.SITE.selectors.boardList).cloneNode(true).childNodes);
+ }
+ fullBoardList = $('.boardList', Header.boardList);
+ $.add(fullBoardList, nodes);
+ ref = $$('a', fullBoardList);
+ for (i = 0, len = ref.length; i < len; i++) {
+ a = ref[i];
+ if (a.hostname === location.hostname && a.pathname.split('/')[1] === g.BOARD.ID) {
+ a.className = 'current';
+ }
+ }
+ return CatalogLinks.setLinks(fullBoardList);
+ },
+ generateBoardList: function(boardnav) {
+ var list, nodes, re, t;
+ list = $('#custom-board-list', Header.boardList);
+ $.rmAll(list);
+ if (!boardnav) {
+ return;
+ }
+ boardnav = boardnav.replace(/(\r\n|\n|\r)/g, ' ');
+ re = /[\w@]+(-(all|title|replace|full|index|catalog|archive|expired|nt|(mode|sort|text):"[^"]+"(,"[^"]+")?))*|[^\w@]+/g;
+ nodes = (function() {
+ var i, len, ref, results;
+ ref = boardnav.match(re);
+ results = [];
+ for (i = 0, len = ref.length; i < len; i++) {
+ t = ref[i];
+ results.push(Header.mapCustomNavigation(t));
+ }
+ return results;
+ })();
+ $.add(list, nodes);
+ return CatalogLinks.setLinks(list);
+ },
+ mapCustomNavigation: function(t) {
+ var a, boardID, href, indexOptions, m, ref, ref1, text, url, urlIC;
+ if (/^[^\w@]/.test(t)) {
+ return $.tn(t);
+ }
+ text = url = null;
+ t = t.replace(/-text:"([^"]+)"(?:,"([^"]+)")?/g, function(m0, m1, m2) {
+ text = m1;
+ url = m2;
+ return '';
+ });
+ indexOptions = [];
+ t = t.replace(/-(?:mode|sort):"([^"]+)"/g, function(m0, m1) {
+ indexOptions.push(m1.toLowerCase().replace(/\ /g, '-'));
+ return '';
+ });
+ indexOptions = indexOptions.join('/');
+ if (/^toggle-all/.test(t)) {
+ a = $.el('a', {
+ className: 'show-board-list-button',
+ textContent: text || '+',
+ href: 'javascript:;'
+ });
+ $.on(a, 'click', Header.toggleBoardList);
+ return a;
+ }
+ if (/^external/.test(t)) {
+ a = $.el('a', {
+ href: url || 'javascript:;',
+ textContent: text || '+',
+ className: 'external'
+ });
+ if (/-nt/.test(t)) {
+ a.target = '_blank';
+ a.rel = 'noopener';
+ }
+ return a;
+ }
+ boardID = t.split('-')[0];
+ if (boardID === 'current') {
+ if ((ref = location.hostname) === 'boards.4chan.org' || ref === 'boards.4channel.org') {
+ boardID = g.BOARD.ID;
+ } else {
+ a = $.el('a', {
+ href: "/" + g.BOARD.ID + "/",
+ textContent: text || decodeURIComponent(g.BOARD.ID),
+ className: 'current'
+ });
+ if (/-nt/.test(t)) {
+ a.target = '_blank';
+ a.rel = 'noopener';
+ }
+ if (/-index/.test(t)) {
+ a.dataset.only = 'index';
+ } else if (/-catalog/.test(t)) {
+ a.dataset.only = 'catalog';
+ a.href += 'catalog.html';
+ } else if (/-(archive|expired)/.test(t)) {
+ a = a.firstChild;
+ }
+ return a;
+ }
+ }
+ a = (function() {
+ var ref1, urlV;
+ if (boardID === '@') {
+ return $.el('a', {
+ href: 'https://twitter.com/4chan',
+ title: '4chan Twitter',
+ textContent: '@'
+ });
+ }
+ a = $.el('a', {
+ href: "//" + (BoardConfig.domain(boardID)) + "/" + boardID + "/",
+ textContent: boardID,
+ title: BoardConfig.title(boardID)
+ });
+ if (((ref1 = g.VIEW) === 'catalog' || ref1 === 'archive') && (urlV = Get.url(g.VIEW, {
+ siteID: '4chan.org',
+ boardID: boardID
+ }))) {
+ a.href = urlV;
+ }
+ if (a.hostname === location.hostname && boardID === g.BOARD.ID) {
+ a.className = 'current';
+ }
+ return a;
+ })();
+ a.textContent = /-title/.test(t) || /-replace/.test(t) && a.hostname === location.hostname && boardID === g.BOARD.ID ? a.title || a.textContent : /-full/.test(t) ? ("/" + boardID + "/") + (a.title ? " - " + a.title : '') : text || boardID;
+ if (m = t.match(/-(index|catalog)/)) {
+ urlIC = CatalogLinks[m[1]]({
+ siteID: '4chan.org',
+ boardID: boardID
+ });
+ if (urlIC) {
+ a.dataset.only = m[1];
+ a.href = urlIC;
+ if (m[1] === 'catalog') {
+ $.addClass(a, 'catalog');
+ }
+ } else {
+ return a.firstChild;
+ }
+ }
+ if (Conf['JSON Index'] && indexOptions) {
+ a.dataset.indexOptions = indexOptions;
+ if (((ref1 = a.hostname) === 'boards.4chan.org' || ref1 === 'boards.4channel.org') && a.pathname.split('/')[2] === '') {
+ a.href += (a.hash ? '/' : '#') + indexOptions;
+ }
+ }
+ if (/-archive/.test(t)) {
+ if (href = Redirect.to('board', {
+ boardID: boardID
+ })) {
+ a.href = href;
+ } else {
+ return a.firstChild;
+ }
+ }
+ if (/-expired/.test(t)) {
+ if (BoardConfig.isArchived(boardID)) {
+ a.href = "//" + (BoardConfig.domain(boardID)) + "/" + boardID + "/archive";
+ } else {
+ return a.firstChild;
+ }
+ }
+ if (/-nt/.test(t)) {
+ a.target = '_blank';
+ a.rel = 'noopener';
+ }
+ if (boardID === '@') {
+ $.addClass(a, 'navSmall');
+ }
+ return a;
+ },
+ toggleBoardList: function() {
+ var bar, custom, full, showBoardList;
+ bar = Header.bar;
+ custom = $('#custom-board-list', bar);
+ full = $('#full-board-list', bar);
+ showBoardList = !full.hidden;
+ custom.hidden = !showBoardList;
+ return full.hidden = showBoardList;
+ },
+ setLinkJustify: function(centered) {
+ Header.linkJustifyToggler.checked = centered;
+ if (centered) {
+ return $.addClass(doc, 'centered-links');
+ } else {
+ return $.rmClass(doc, 'centered-links');
+ }
+ },
+ toggleLinkJustify: function() {
+ var centered;
+ $.event('CloseMenu');
+ centered = this.nodeName === 'INPUT' ? this.checked : void 0;
+ Header.setLinkJustify(centered);
+ return $.set('Centered links', centered);
+ },
+ setBarFixed: function(fixed) {
+ Header.barFixedToggler.checked = fixed;
+ if (fixed) {
+ $.addClass(doc, 'fixed');
+ return $.addClass(Header.bar, 'dialog');
+ } else {
+ $.rmClass(doc, 'fixed');
+ return $.rmClass(Header.bar, 'dialog');
+ }
+ },
+ toggleBarFixed: function() {
+ $.event('CloseMenu');
+ Header.setBarFixed(this.checked);
+ Conf['Fixed Header'] = this.checked;
+ return $.set('Fixed Header', this.checked);
+ },
+ setShortcutIcons: function(show) {
+ Header.shortcutToggler.checked = show;
+ if (show) {
+ return $.addClass(doc, 'shortcut-icons');
+ } else {
+ return $.rmClass(doc, 'shortcut-icons');
+ }
+ },
+ toggleShortcutIcons: function() {
+ $.event('CloseMenu');
+ Header.setShortcutIcons(this.checked);
+ Conf['Shortcut Icons'] = this.checked;
+ return $.set('Shortcut Icons', this.checked);
+ },
+ setBarVisibility: function(hide) {
+ Header.headerToggler.checked = hide;
+ $.event('CloseMenu');
+ (hide ? $.addClass : $.rmClass)(Header.bar, 'autohide');
+ return (hide ? $.addClass : $.rmClass)(doc, 'autohide');
+ },
+ toggleBarVisibility: function() {
+ var hide, message;
+ hide = this.nodeName === 'INPUT' ? this.checked : !$.hasClass(Header.bar, 'autohide');
+ Conf['Header auto-hide'] = hide;
+ $.set('Header auto-hide', hide);
+ Header.setBarVisibility(hide);
+ message = "The header bar will " + (hide ? 'automatically hide itself.' : 'remain visible.');
+ return new Notice('info', message, 2);
+ },
+ setHideBarOnScroll: function(hide) {
+ Header.scrollHeaderToggler.checked = hide;
+ if (hide) {
+ $.on(window, 'scroll', Header.hideBarOnScroll);
+ return;
+ }
+ $.off(window, 'scroll', Header.hideBarOnScroll);
+ $.rmClass(Header.bar, 'scroll');
+ return Header.bar.classList.toggle('autohide', Conf['Header auto-hide']);
+ },
+ toggleHideBarOnScroll: function() {
+ var hide;
+ hide = this.checked;
+ $.cb.checked.call(this);
+ return Header.setHideBarOnScroll(hide);
+ },
+ hideBarOnScroll: function() {
+ var offsetY;
+ offsetY = window.pageYOffset;
+ if (offsetY > (Header.previousOffset || 0)) {
+ $.addClass(Header.bar, 'autohide', 'scroll');
+ } else {
+ $.rmClass(Header.bar, 'autohide', 'scroll');
+ }
+ return Header.previousOffset = offsetY;
+ },
+ setBarPosition: function(bottom) {
+ var args, ref;
+ if ((ref = Header.barPositionToggler) != null) {
+ ref.checked = bottom;
+ }
+ $.event('CloseMenu');
+ args = bottom ? ['bottom-header', 'top-header', 'after'] : ['top-header', 'bottom-header', 'add'];
+ $.addClass(doc, args[0]);
+ $.rmClass(doc, args[1]);
+ return $[args[2]](Header.bar, Header.noticesRoot);
+ },
+ toggleBarPosition: function() {
+ $.cb.checked.call(this);
+ return Header.setBarPosition(this.checked);
+ },
+ setFooterVisibility: function(hide) {
+ Header.footerToggler.checked = hide;
+ return doc.classList.toggle('hide-bottom-board-list', hide);
+ },
+ toggleFooterVisibility: function() {
+ var hide, message;
+ $.event('CloseMenu');
+ hide = this.nodeName === 'INPUT' ? this.checked : $.hasClass(doc, 'hide-bottom-board-list');
+ Header.setFooterVisibility(hide);
+ $.set('Bottom Board List', hide);
+ message = hide ? 'The bottom navigation will now be hidden.' : 'The bottom navigation will remain visible.';
+ return new Notice('info', message, 2);
+ },
+ setCustomNav: function(show) {
+ var btn, cust, full, ref;
+ Header.customNavToggler.checked = show;
+ cust = $('#custom-board-list', Header.bar);
+ full = $('#full-board-list', Header.bar);
+ btn = $('.hide-board-list-container', full);
+ return ref = show ? [false, true, false] : [true, false, true], cust.hidden = ref[0], full.hidden = ref[1], btn.hidden = ref[2], ref;
+ },
+ toggleCustomNav: function() {
+ $.cb.checked.call(this);
+ return Header.setCustomNav(this.checked);
+ },
+ editCustomNav: function() {
+ var settings;
+ Settings.open('Advanced');
+ settings = $.id('fourchanx-settings');
+ return $('[name=boardnav]', settings).focus();
+ },
+ hashScroll: function(e) {
+ var el, hash;
+ if (e) {
+ if (e.state) {
+ return;
+ }
+ if (!history.state) {
+ history.replaceState({}, '');
+ }
+ }
+ if ((hash = location.hash.slice(1))) {
+ ReplyPruning.showIfHidden(hash);
+ if ((el = $.id(hash))) {
+ return $.queueTask(function() {
+ return Header.scrollTo(el);
+ });
+ }
+ }
+ },
+ scrollTo: function(root, down, needed) {
+ var height, x;
+ if (!root.offsetParent) {
+ return;
+ }
+ if (down) {
+ x = Header.getBottomOf(root);
+ if (Conf['Fixed Header'] && Conf['Header auto-hide on scroll'] && Conf['Bottom header']) {
+ height = Header.bar.getBoundingClientRect().height;
+ if (x <= 0) {
+ if (!Header.isHidden()) {
+ x += height;
+ }
+ } else {
+ if (Header.isHidden()) {
+ x -= height;
+ }
+ }
+ }
+ if (!(needed && x >= 0)) {
+ return window.scrollBy(0, -x);
+ }
+ } else {
+ x = Header.getTopOf(root);
+ if (Conf['Fixed Header'] && Conf['Header auto-hide on scroll'] && !Conf['Bottom header']) {
+ height = Header.bar.getBoundingClientRect().height;
+ if (x >= 0) {
+ if (!Header.isHidden()) {
+ x += height;
+ }
+ } else {
+ if (Header.isHidden()) {
+ x -= height;
+ }
+ }
+ }
+ if (!(needed && x >= 0)) {
+ return window.scrollBy(0, x);
+ }
+ }
+ },
+ scrollToIfNeeded: function(root, down) {
+ return Header.scrollTo(root, down, true);
+ },
+ getTopOf: function(root) {
+ var headRect, top;
+ top = root.getBoundingClientRect().top;
+ if (Conf['Fixed Header'] && !Conf['Bottom Header']) {
+ headRect = Header.toggle.getBoundingClientRect();
+ top -= headRect.top + headRect.height;
+ }
+ return top;
+ },
+ getBottomOf: function(root) {
+ var bottom, clientHeight, headRect;
+ clientHeight = doc.clientHeight;
+ bottom = clientHeight - root.getBoundingClientRect().bottom;
+ if (Conf['Fixed Header'] && Conf['Bottom Header']) {
+ headRect = Header.toggle.getBoundingClientRect();
+ bottom -= clientHeight - headRect.bottom + headRect.height;
+ }
+ return bottom;
+ },
+ isNodeVisible: function(node) {
+ var height;
+ if (d.hidden || !doc.contains(node)) {
+ return false;
+ }
+ height = node.getBoundingClientRect().height;
+ return Header.getTopOf(node) + height >= 0 && Header.getBottomOf(node) + height >= 0;
+ },
+ isHidden: function() {
+ var top;
+ top = Header.bar.getBoundingClientRect().top;
+ if (Conf['Bottom header']) {
+ return top === doc.clientHeight;
+ } else {
+ return top < 0;
+ }
+ },
+ addShortcut: function(id, el, index) {
+ var i, item, len, ref, shortcut;
+ shortcut = $.el('span', {
+ id: "shortcut-" + id,
+ className: 'shortcut brackets-wrap'
+ });
+ $.add(shortcut, el);
+ shortcut.dataset.index = index;
+ ref = $$('[data-index]', Header.shortcuts);
+ for (i = 0, len = ref.length; i < len; i++) {
+ item = ref[i];
+ if (!(+item.dataset.index > +index)) {
+ continue;
+ }
+ $.before(item, shortcut);
+ return;
+ }
+ return $.add(Header.shortcuts, shortcut);
+ },
+ rmShortcut: function(el) {
+ return $.rm(el.parentElement);
+ },
+ menuToggle: function(e) {
+ return Header.menu.toggle(e, this, g);
+ },
+ createNotification: function(e) {
+ var content, lifetime, notice, ref, type;
+ ref = e.detail, type = ref.type, content = ref.content, lifetime = ref.lifetime;
+ return notice = new Notice(type, content, lifetime);
+ },
+ areNotificationsEnabled: false,
+ enableDesktopNotifications: function() {
+ var authorize, disable, el, notice, ref;
+ if (!(window.Notification && Conf['Desktop Notifications'])) {
+ return;
+ }
+ switch (Notification.permission) {
+ case 'granted':
+ Header.areNotificationsEnabled = true;
+ return;
+ case 'denied':
+ return;
+ }
+ el = $.el('span', {innerHTML: "4chan X needs your permission to show desktop notifications. [<a href=\"https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions#why-is-4chan-x-asking-for-permission-to-show-desktop-notifications\" target=\"_blank\">FAQ</a>]<br><button>Authorize</button> or <button>Disable</button>"});
+ ref = $$('button', el), authorize = ref[0], disable = ref[1];
+ $.on(authorize, 'click', function() {
+ return Notification.requestPermission(function(status) {
+ Header.areNotificationsEnabled = status === 'granted';
+ if (status === 'default') {
+ return;
+ }
+ return notice.close();
+ });
+ });
+ $.on(disable, 'click', function() {
+ $.set('Desktop Notifications', false);
+ return notice.close();
+ });
+ return notice = new Notice('info', el);
+ }
+ };
+
+ return Header;
+
+}).call(this);
+
+Index = (function() {
+ var Index,
+ slice = [].slice,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Index = {
+ showHiddenThreads: false,
+ changed: {},
+ enabledOn: function(arg) {
+ var boardID, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ return Conf['JSON Index'] && g.sites[siteID].software === 'yotsuba' && boardID !== 'f';
+ },
+ init: function() {
+ var arr, entries, i, input, inputs, k, l, label, len1, len2, name, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, select, sortEntry, tRaw, watchSettings;
+ if (g.VIEW !== 'index') {
+ return;
+ }
+ $.one(d, '4chanXInitFinished', this.cb.initFinished);
+ $.on(d, 'PostsInserted', this.cb.postsInserted);
+ if (!this.enabledOn(g.BOARD)) {
+ return;
+ }
+ this.enabled = true;
+ Callbacks.Post.push({
+ name: 'Index Page Numbers',
+ cb: this.node
+ });
+ Callbacks.CatalogThread.push({
+ name: 'Catalog Features',
+ cb: this.catalogNode
+ });
+ this.search = ((ref = history.state) != null ? ref.searched : void 0) || '';
+ if ((ref1 = history.state) != null ? ref1.mode : void 0) {
+ Conf['Index Mode'] = (ref2 = history.state) != null ? ref2.mode : void 0;
+ }
+ this.currentSort = (ref3 = history.state) != null ? ref3.sort : void 0;
+ this.currentSort || (this.currentSort = typeof Conf['Index Sort'] === 'object' ? Conf['Index Sort'][g.BOARD.ID] || 'bump' : Conf['Index Sort']);
+ this.currentPage = this.getCurrentPage();
+ this.processHash();
+ $.addClass(doc, 'index-loading', (Conf['Index Mode'].replace(/\ /g, '-')) + "-mode");
+ $.on(window, 'popstate', this.cb.popstate);
+ $.on(d, 'scroll', this.scroll);
+ $.on(d, 'SortIndex', this.cb.resort);
+ this.button = $.el('a', {
+ className: 'fa fa-refresh',
+ title: 'Refresh',
+ href: 'javascript:;',
+ textContent: 'Refresh Index'
+ });
+ $.on(this.button, 'click', function() {
+ return Index.update();
+ });
+ Header.addShortcut('index-refresh', this.button, 590);
+ entries = [];
+ this.inputs = inputs = $.dict();
+ ref4 = Config.Index;
+ for (name in ref4) {
+ arr = ref4[name];
+ if (!(arr instanceof Array)) {
+ continue;
+ }
+ label = UI.checkbox(name, "" + name[0] + (name.slice(1).toLowerCase()));
+ label.title = arr[1];
+ entries.push({
+ el: label
+ });
+ input = label.firstChild;
+ $.on(input, 'change', $.cb.checked);
+ inputs[name] = input;
+ }
+ $.on(inputs['Show Replies'], 'change', this.cb.replies);
+ $.on(inputs['Catalog Hover Expand'], 'change', this.cb.hover);
+ $.on(inputs['Pin Watched Threads'], 'change', this.cb.resort);
+ $.on(inputs['Anchor Hidden Threads'], 'change', this.cb.resort);
+ watchSettings = function(e) {
+ if ((input = $.getOwn(inputs, e.target.name))) {
+ input.checked = e.target.checked;
+ return $.event('change', null, input);
+ }
+ };
+ $.on(d, 'OpenSettings', function() {
+ return $.on($.id('fourchanx-settings'), 'change', watchSettings);
+ });
+ sortEntry = UI.checkbox('Per-Board Sort Type', 'Per-board sort type', typeof Conf['Index Sort'] === 'object');
+ sortEntry.title = 'Set the sorting order of each board independently.';
+ $.on(sortEntry.firstChild, 'change', this.cb.perBoardSort);
+ entries.splice(3, 0, {
+ el: sortEntry
+ });
+ Header.menu.addEntry({
+ el: $.el('span', {
+ textContent: 'Index Navigation'
+ }),
+ order: 100,
+ subEntries: entries
+ });
+ this.navLinks = $.el('div', {
+ className: 'navLinks json-index'
+ });
+ $.extend(this.navLinks, {innerHTML: "<span class=\"brackets-wrap indexlink\"><a href=\"#index\">Index</a></span> <span class=\"brackets-wrap cataloglink\"><a href=\"#catalog\">Catalog</a></span> <span class=\"brackets-wrap archlistlink\"><a href=\"./archive\">Archive</a></span> <span class=\"brackets-wrap bottomlink\"><a href=\"#bottom\">Bottom</a></span> <span class=\"brackets-wrap\" id=\"index-last-refresh\"><a href=\"javascript:;\"><time title=\"Last index refresh\">...</time></a></span> <input type=\"search\" id=\"index-search\" class=\"field\" placeholder=\"Search\"><a id=\"index-search-clear\" href=\"javascript:;\" title=\"Clear search\">ร—</a><span id=\"hidden-label\" hidden> &mdash; <span id=\"hidden-count\"></span> <span id=\"hidden-toggle\">[<a href=\"javascript:;\">Show</a>]</span></span><span id=\"index-options\"><input type=\"checkbox\" id=\"index-rev\" name=\"Reverse Sort\" title=\"Reverse sort order\"><span id=\"lastlong-options\" hidden><input type=\"text\" title=\"Minimum letter count (without image)\"><input type=\"text\" title=\"Minimum letter count (with image)\"></span><select id=\"index-sort\" name=\"Index Sort\"><option disabled>Index Sort</option><option value=\"bump\">Bump order</option><option value=\"lastreply\">Last reply</option><option value=\"lastlong\">Last long reply</option><option value=\"birth\">Creation date</option><option value=\"replycount\">Reply count</option><option value=\"filecount\">File count</option><option value=\"activity\">Posts per minute</option></select><select id=\"index-size\" name=\"Index Size\"><option disabled>Image Size</option><option value=\"small\">Small</option><option value=\"large\">Large</option></select><select id=\"index-mode\" name=\"Index Mode\"><option disabled>Index Mode</option><option value=\"paged\">Paged</option><option value=\"infinite\">Infinite scrolling</option><option value=\"all pages\">All threads</option><option value=\"catalog\">Catalog</option></select></span>"});
+ $('.cataloglink a', this.navLinks).href = CatalogLinks.catalog();
+ if (!BoardConfig.isArchived(g.BOARD.ID)) {
+ $('.archlistlink', this.navLinks).hidden = true;
+ }
+ $.on($('#index-last-refresh a', this.navLinks), 'click', this.cb.refreshFront);
+ this.searchInput = $('#index-search', this.navLinks);
+ this.setupSearch();
+ $.on(this.searchInput, 'input', this.onSearchInput);
+ $.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch);
+ this.hideLabel = $('#hidden-label', this.navLinks);
+ $.on($('#hidden-toggle a', this.navLinks), 'click', this.cb.toggleHiddenThreads);
+ this.selectRev = $('#index-rev', this.navLinks);
+ this.selectMode = $('#index-mode', this.navLinks);
+ this.selectSort = $('#index-sort', this.navLinks);
+ this.selectSize = $('#index-size', this.navLinks);
+ $.on(this.selectRev, 'change', this.cb.sort);
+ $.on(this.selectMode, 'change', this.cb.mode);
+ $.on(this.selectSort, 'change', this.cb.sort);
+ $.on(this.selectSize, 'change', $.cb.value);
+ $.on(this.selectSize, 'change', this.cb.size);
+ ref5 = [this.selectMode, this.selectSize];
+ for (k = 0, len1 = ref5.length; k < len1; k++) {
+ select = ref5[k];
+ select.value = Conf[select.name];
+ }
+ this.selectRev.checked = /-rev$/.test(Index.currentSort);
+ this.selectSort.value = Index.currentSort.replace(/-rev$/, '');
+ this.lastLongOptions = $('#lastlong-options', this.navLinks);
+ this.lastLongInputs = $$('input', this.lastLongOptions);
+ this.lastLongThresholds = [0, 0];
+ this.lastLongOptions.hidden = this.selectSort.value !== 'lastlong';
+ ref6 = this.lastLongInputs;
+ for (i = l = 0, len2 = ref6.length; l < len2; i = ++l) {
+ input = ref6[i];
+ $.on(input, 'change', this.cb.lastLongThresholds);
+ tRaw = Conf["Last Long Reply Thresholds " + i];
+ input.value = this.lastLongThresholds[i] = typeof tRaw === 'object' ? (ref7 = tRaw[g.BOARD.ID]) != null ? ref7 : 100 : tRaw;
+ }
+ this.root = $.el('div', {
+ className: 'board json-index'
+ });
+ $.on(this.root, 'click', this.cb.hoverToggle);
+ this.cb.size();
+ this.cb.hover();
+ this.pagelist = $.el('div', {
+ className: 'pagelist json-index'
+ });
+ $.extend(this.pagelist, {innerHTML: "<div class=\"prev\"><a><button disabled>Previous</button></a></div><div class=\"pages\"></div><div class=\"next\"><a><button disabled>Next</button></a></div><div class=\"pages cataloglink\"><a href=\"./catalog\">Catalog</a></div>"});
+ $('.cataloglink a', this.pagelist).href = CatalogLinks.catalog();
+ $.on(this.pagelist, 'click', this.cb.pageNav);
+ this.update(true);
+ $.onExists(doc, 'title + *', function() {
+ return d.title = d.title.replace(/\ -\ Page\ \d+/, '');
+ });
+ $.onExists(doc, '.board > .thread > .postContainer, .board + *', function() {
+ var board, el, len3, m, ref8, timeEl, topNavPos;
+ g.SITE.Build.hat = $('.board > .thread > img:first-child');
+ if (g.SITE.Build.hat) {
+ g.BOARD.threads.forEach(function(thread) {
+ if (thread.nodes.root) {
+ return $.prepend(thread.nodes.root, g.SITE.Build.hat.cloneNode(false));
+ }
+ });
+ $.addClass(doc, 'hats-enabled');
+ $.addStyle(".catalog-thread::after {background-image: url(" + g.SITE.Build.hat.src + ");}");
+ }
+ board = $('.board');
+ $.replace(board, Index.root);
+ if (Index.loaded) {
+ $.event('PostsInserted', null, Index.root);
+ }
+ try {
+ d.implementation.createDocument(null, null, null).appendChild(board);
+ } catch (error) {}
+ ref8 = $$('.navLinks');
+ for (m = 0, len3 = ref8.length; m < len3; m++) {
+ el = ref8[m];
+ $.rm(el);
+ }
+ $.rm($.id('ctrl-top'));
+ topNavPos = $.id('delform').previousElementSibling;
+ $.before(topNavPos, $.el('hr'));
+ $.before(topNavPos, Index.navLinks);
+ timeEl = $('#index-last-refresh time', Index.navLinks);
+ if (timeEl.dataset.utc) {
+ return RelativeDates.update(timeEl);
+ }
+ });
+ return Main.ready(function() {
+ var pagelist;
+ if ((pagelist = $('.pagelist'))) {
+ $.replace(pagelist, Index.pagelist);
+ }
+ return $.rmClass(doc, 'index-loading');
+ });
+ },
+ scroll: function() {
+ var pageNum, threadIDs;
+ if (Index.req || !Index.liveThreadData || Conf['Index Mode'] !== 'infinite' || (window.scrollY <= doc.scrollHeight - (300 + window.innerHeight))) {
+ return;
+ }
+ if (Index.pageNum == null) {
+ Index.pageNum = Index.currentPage;
+ }
+ pageNum = ++Index.pageNum;
+ if (pageNum > Index.pagesNum) {
+ return Index.endNotice();
+ }
+ threadIDs = Index.threadsOnPage(pageNum);
+ return Index.buildStructure(threadIDs);
+ },
+ endNotice: (function() {
+ var notify, reset;
+ notify = false;
+ reset = function() {
+ return notify = false;
+ };
+ return function() {
+ if (notify) {
+ return;
+ }
+ notify = true;
+ new Notice('info', "Last page reached.", 2);
+ return setTimeout(reset, 3 * $.SECOND);
+ };
+ })(),
+ menu: {
+ init: function() {
+ if (!(g.VIEW === 'index' && Conf['Menu'] && Conf['Thread Hiding Link'] && Index.enabledOn(g.BOARD))) {
+ return;
+ }
+ return Menu.menu.addEntry({
+ el: $.el('a', {
+ href: 'javascript:;',
+ className: 'has-shortcut-text'
+ }, {innerHTML: "<span></span><span class=\"shortcut-text\">Shift+click</span>"}),
+ order: 20,
+ open: function(arg) {
+ var thread;
+ thread = arg.thread;
+ if (Conf['Index Mode'] !== 'catalog') {
+ return false;
+ }
+ this.el.firstElementChild.textContent = thread.isHidden ? 'Unhide' : 'Hide';
+ if (this.cb) {
+ $.off(this.el, 'click', this.cb);
+ }
+ this.cb = function() {
+ $.event('CloseMenu');
+ return Index.toggleHide(thread);
+ };
+ $.on(this.el, 'click', this.cb);
+ return true;
+ }
+ });
+ }
+ },
+ node: function() {
+ if (this.isReply || this.isClone || !(Index.threadPosition[this.ID] != null)) {
+ return;
+ }
+ return this.thread.setPage(Math.floor(Index.threadPosition[this.ID] / Index.threadsNumPerPage) + 1);
+ },
+ catalogNode: function() {
+ return $.on(this.nodes.root, 'mousedown click', (function(_this) {
+ return function(e) {
+ if (!(e.button === 0 && e.shiftKey)) {
+ return;
+ }
+ if (e.type === 'click') {
+ Index.toggleHide(_this.thread);
+ }
+ return e.preventDefault();
+ };
+ })(this));
+ },
+ toggleHide: function(thread) {
+ if (Index.showHiddenThreads) {
+ ThreadHiding.show(thread);
+ if (!ThreadHiding.db.get({
+ boardID: thread.board.ID,
+ threadID: thread.ID
+ })) {
+ return;
+ }
+ } else {
+ ThreadHiding.hide(thread);
+ }
+ return ThreadHiding.saveHiddenState(thread);
+ },
+ cycleSortType: function() {
+ var i, k, len1, type, types;
+ types = slice.call(Index.selectSort.options).filter(function(option) {
+ return !option.disabled;
+ });
+ for (i = k = 0, len1 = types.length; k < len1; i = ++k) {
+ type = types[i];
+ if (type.selected) {
+ break;
+ }
+ }
+ types[(i + 1) % types.length].selected = true;
+ return $.event('change', null, Index.selectSort);
+ },
+ cb: {
+ initFinished: function() {
+ Index.initFinishedFired = true;
+ return $.queueTask(function() {
+ return Index.cb.postsInserted();
+ });
+ },
+ postsInserted: function() {
+ var n;
+ if (!Index.initFinishedFired) {
+ return;
+ }
+ n = 0;
+ g.posts.forEach(function(post) {
+ if (!post.isFetchedQuote && !post.indexRefreshSeen && doc.contains(post.nodes.root)) {
+ post.indexRefreshSeen = true;
+ return n++;
+ }
+ });
+ if (n) {
+ return $.event('IndexRefresh');
+ }
+ },
+ toggleHiddenThreads: function() {
+ $('#hidden-toggle a', Index.navLinks).textContent = (Index.showHiddenThreads = !Index.showHiddenThreads) ? 'Hide' : 'Show';
+ Index.sort();
+ return Index.buildIndex();
+ },
+ mode: function() {
+ Index.pushState({
+ mode: this.value
+ });
+ return Index.pageLoad(false);
+ },
+ sort: function() {
+ var value;
+ value = Index.selectRev.checked ? Index.selectSort.value + "-rev" : Index.selectSort.value;
+ Index.pushState({
+ sort: value
+ });
+ return Index.pageLoad(false);
+ },
+ resort: function(e) {
+ var ref;
+ Index.changed.order = true;
+ if (!(e != null ? (ref = e.detail) != null ? ref.deferred : void 0 : void 0)) {
+ return Index.pageLoad(false);
+ }
+ },
+ perBoardSort: function() {
+ var i, k;
+ Conf['Index Sort'] = this.checked ? $.dict() : '';
+ Index.saveSort();
+ for (i = k = 0; k < 2; i = ++k) {
+ Conf["Last Long Reply Thresholds " + i] = this.checked ? $.dict() : '';
+ Index.saveLastLongThresholds(i);
+ }
+ },
+ lastLongThresholds: function() {
+ var i, value;
+ i = slice.call(this.parentNode.children).indexOf(this);
+ value = +this.value;
+ if (!Number.isFinite(value)) {
+ this.value = Index.lastLongThresholds[i];
+ return;
+ }
+ Index.lastLongThresholds[i] = value;
+ Index.saveLastLongThresholds(i);
+ Index.changed.order = true;
+ return Index.pageLoad(false);
+ },
+ size: function(e) {
+ if (Conf['Index Mode'] !== 'catalog') {
+ $.rmClass(Index.root, 'catalog-small');
+ $.rmClass(Index.root, 'catalog-large');
+ } else if (Conf['Index Size'] === 'small') {
+ $.addClass(Index.root, 'catalog-small');
+ $.rmClass(Index.root, 'catalog-large');
+ } else {
+ $.addClass(Index.root, 'catalog-large');
+ $.rmClass(Index.root, 'catalog-small');
+ }
+ if (e) {
+ return Index.buildIndex();
+ }
+ },
+ replies: function() {
+ return Index.buildIndex();
+ },
+ hover: function() {
+ return doc.classList.toggle('catalog-hover-expand', Conf['Catalog Hover Expand']);
+ },
+ hoverToggle: function(e) {
+ var input, thread;
+ if (Conf['Catalog Hover Toggle'] && $.hasClass(doc, 'catalog-mode') && !$.modifiedClick(e) && !$.x('ancestor-or-self::a', e.target)) {
+ input = Index.inputs['Catalog Hover Expand'];
+ input.checked = !input.checked;
+ $.event('change', null, input);
+ if ((thread = Get.threadFromNode(e.target))) {
+ Index.cb.catalogReplies.call(thread);
+ return Index.cb.hoverAdjust.call(thread.OP.nodes);
+ }
+ }
+ },
+ popstate: function(e) {
+ var mode, nCommands, page, ref, searched, sort;
+ if (e != null ? e.state : void 0) {
+ ref = e.state, searched = ref.searched, mode = ref.mode, sort = ref.sort;
+ page = Index.getCurrentPage();
+ Index.setState({
+ search: searched,
+ mode: mode,
+ sort: sort,
+ page: page
+ });
+ return Index.pageLoad(false);
+ } else {
+ nCommands = Index.processHash();
+ if (Conf['Refreshed Navigation'] && nCommands) {
+ return Index.update();
+ } else {
+ return Index.pageLoad();
+ }
+ }
+ },
+ pageNav: function(e) {
+ var a;
+ if ($.modifiedClick(e)) {
+ return;
+ }
+ switch (e.target.nodeName) {
+ case 'BUTTON':
+ e.target.blur();
+ a = e.target.parentNode;
+ break;
+ case 'A':
+ a = e.target;
+ break;
+ default:
+ return;
+ }
+ if (a.textContent === 'Catalog') {
+ return;
+ }
+ e.preventDefault();
+ return Index.userPageNav(+a.pathname.split(/\/+/)[2] || 1);
+ },
+ refreshFront: function() {
+ Index.pushState({
+ page: 1
+ });
+ return Index.update();
+ },
+ catalogReplies: function() {
+ if (Conf['Show Replies'] && $.hasClass(doc, 'catalog-hover-expand') && !this.catalogView.nodes.replies) {
+ return Index.buildCatalogReplies(this);
+ }
+ },
+ hoverAdjust: function() {
+ var rect, style, x;
+ if (!$.hasClass(doc, 'catalog-hover-expand')) {
+ return;
+ }
+ rect = this.post.getBoundingClientRect();
+ if ((x = $.minmax(0, -rect.left, doc.clientWidth - rect.right))) {
+ style = this.post.style;
+ style.left = x + "px";
+ style.right = (-x) + "px";
+ return $.one(this.root, 'mouseleave', function() {
+ return style.left = style.right = null;
+ });
+ }
+ }
+ },
+ scrollToIndex: function() {
+ return Header.scrollToIfNeeded((Index.navLinks.getBoundingClientRect().height ? Index.navLinks : Index.root));
+ },
+ getCurrentPage: function() {
+ return +window.location.pathname.split(/\/+/)[2] || 1;
+ },
+ userPageNav: function(page) {
+ Index.pushState({
+ page: page
+ });
+ if (Conf['Refreshed Navigation']) {
+ return Index.update();
+ } else {
+ return Index.pageLoad();
+ }
+ },
+ hashCommands: {
+ mode: {
+ 'paged': 'paged',
+ 'infinite-scrolling': 'infinite',
+ 'infinite': 'infinite',
+ 'all-threads': 'all pages',
+ 'all-pages': 'all pages',
+ 'catalog': 'catalog'
+ },
+ sort: {
+ 'bump-order': 'bump',
+ 'last-reply': 'lastreply',
+ 'last-long-reply': 'lastlong',
+ 'creation-date': 'birth',
+ 'reply-count': 'replycount',
+ 'file-count': 'filecount',
+ 'posts-per-minute': 'activity'
+ }
+ },
+ processHash: function() {
+ var command, commands, hash, k, leftover, len1, mode, ref, sort, state;
+ hash = ((ref = location.href.match(/#.*/)) != null ? ref[0] : void 0) || '';
+ state = {
+ replace: true
+ };
+ commands = hash.slice(1).split('/');
+ leftover = [];
+ for (k = 0, len1 = commands.length; k < len1; k++) {
+ command = commands[k];
+ if ((mode = $.getOwn(Index.hashCommands.mode, command))) {
+ state.mode = mode;
+ } else if (command === 'index') {
+ state.mode = Conf['Previous Index Mode'];
+ state.page = 1;
+ } else if ((sort = $.getOwn(Index.hashCommands.sort, command.replace(/-rev$/, '')))) {
+ state.sort = sort;
+ if (/-rev$/.test(command)) {
+ state.sort += '-rev';
+ }
+ } else if (/^s=/.test(command)) {
+ state.search = decodeURIComponent(command.slice(2)).replace(/\+/g, ' ').trim();
+ } else {
+ leftover.push(command);
+ }
+ }
+ hash = leftover.join('/');
+ if (hash) {
+ state.hash = "#" + hash;
+ }
+ Index.pushState(state);
+ return commands.length - leftover.length;
+ },
+ pushState: function(state) {
+ var hash, pageBeforeSearch, pathname, ref, replace, search;
+ search = state.search, hash = state.hash, replace = state.replace;
+ pageBeforeSearch = (ref = history.state) != null ? ref.oldpage : void 0;
+ if ((search != null) && search !== Index.search) {
+ state.page = search ? 1 : pageBeforeSearch || 1;
+ if (!search) {
+ pageBeforeSearch = void 0;
+ } else if (!Index.search) {
+ pageBeforeSearch = Index.currentPage;
+ }
+ }
+ Index.setState(state);
+ pathname = Index.currentPage === 1 ? "/" + g.BOARD + "/" : "/" + g.BOARD + "/" + Index.currentPage;
+ hash || (hash = '');
+ return history[replace ? 'replaceState' : 'pushState']({
+ mode: Conf['Index Mode'],
+ sort: Index.currentSort,
+ searched: Index.search,
+ oldpage: pageBeforeSearch
+ }, '', location.protocol + "//" + location.host + pathname + hash);
+ },
+ setState: function(arg) {
+ var hash, mode, page, ref, search, sort;
+ search = arg.search, mode = arg.mode, sort = arg.sort, page = arg.page, hash = arg.hash;
+ if ((search != null) && search !== Index.search) {
+ Index.changed.search = true;
+ Index.search = search;
+ }
+ if ((mode != null) && mode !== Conf['Index Mode']) {
+ Index.changed.mode = true;
+ Conf['Index Mode'] = mode;
+ $.set('Index Mode', mode);
+ if (!(mode === 'catalog' || Conf['Previous Index Mode'] === mode)) {
+ Conf['Previous Index Mode'] = mode;
+ $.set('Previous Index Mode', mode);
+ }
+ }
+ if ((sort != null) && sort !== Index.currentSort) {
+ Index.changed.sort = true;
+ Index.currentSort = sort;
+ Index.saveSort();
+ }
+ if ((ref = Conf['Index Mode']) === 'all pages' || ref === 'catalog') {
+ page = 1;
+ }
+ if ((page != null) && page !== Index.currentPage) {
+ Index.changed.page = true;
+ Index.currentPage = page;
+ }
+ if (hash != null) {
+ return Index.changed.hash = true;
+ }
+ },
+ savePerBoard: function(key, value) {
+ if (typeof Conf[key] === 'object') {
+ Conf[key][g.BOARD.ID] = value;
+ } else {
+ Conf[key] = value;
+ }
+ return $.set(key, Conf[key]);
+ },
+ saveSort: function() {
+ return Index.savePerBoard('Index Sort', Index.currentSort);
+ },
+ saveLastLongThresholds: function(i) {
+ return Index.savePerBoard("Last Long Reply Thresholds " + i, Index.lastLongThresholds[i]);
+ },
+ pageLoad: function(scroll) {
+ var hash, mode, order, page, ref, search, sort, threads;
+ if (scroll == null) {
+ scroll = true;
+ }
+ if (!Index.liveThreadData) {
+ return;
+ }
+ ref = Index.changed, threads = ref.threads, order = ref.order, search = ref.search, mode = ref.mode, sort = ref.sort, page = ref.page, hash = ref.hash;
+ threads || (threads = search);
+ order || (order = sort);
+ if (threads || order) {
+ Index.sort();
+ }
+ if (threads) {
+ Index.buildPagelist();
+ }
+ if (search) {
+ Index.setupSearch();
+ }
+ if (mode) {
+ Index.setupMode();
+ }
+ if (sort) {
+ Index.setupSort();
+ }
+ if (threads || mode || page || order) {
+ Index.buildIndex();
+ }
+ if (threads || page) {
+ Index.setPage();
+ }
+ if (scroll && !hash) {
+ Index.scrollToIndex();
+ }
+ if (hash) {
+ Header.hashScroll();
+ }
+ return Index.changed = {};
+ },
+ setupMode: function() {
+ var k, len1, mode, ref;
+ ref = ['paged', 'infinite', 'all pages', 'catalog'];
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ mode = ref[k];
+ $[mode === Conf['Index Mode'] ? 'addClass' : 'rmClass'](doc, (mode.replace(/\ /g, '-')) + "-mode");
+ }
+ Index.selectMode.value = Conf['Index Mode'];
+ Index.cb.size();
+ Index.showHiddenThreads = false;
+ return $('#hidden-toggle a', Index.navLinks).textContent = 'Show';
+ },
+ setupSort: function() {
+ Index.selectRev.checked = /-rev$/.test(Index.currentSort);
+ Index.selectSort.value = Index.currentSort.replace(/-rev$/, '');
+ return Index.lastLongOptions.hidden = Index.selectSort.value !== 'lastlong';
+ },
+ getPagesNum: function() {
+ if (Index.search) {
+ return Math.ceil(Index.sortedThreadIDs.length / Index.threadsNumPerPage);
+ } else {
+ return Index.pagesNum;
+ }
+ },
+ getMaxPageNum: function() {
+ return Math.max(1, Index.getPagesNum());
+ },
+ buildPagelist: function() {
+ var a, i, k, maxPageNum, nodes, pagesRoot, ref;
+ pagesRoot = $('.pages', Index.pagelist);
+ maxPageNum = Index.getMaxPageNum();
+ if (pagesRoot.childElementCount !== maxPageNum) {
+ nodes = [];
+ for (i = k = 1, ref = maxPageNum; k <= ref; i = k += 1) {
+ a = $.el('a', {
+ textContent: i,
+ href: i === 1 ? './' : i
+ });
+ nodes.push($.tn('['), a, $.tn('] '));
+ }
+ $.rmAll(pagesRoot);
+ return $.add(pagesRoot, nodes);
+ }
+ },
+ setPage: function() {
+ var a, href, maxPageNum, next, pageNum, pagesRoot, prev, strong;
+ pageNum = Index.currentPage;
+ maxPageNum = Index.getMaxPageNum();
+ pagesRoot = $('.pages', Index.pagelist);
+ prev = pagesRoot.previousSibling.firstChild;
+ next = pagesRoot.nextSibling.firstChild;
+ href = Math.max(pageNum - 1, 1);
+ prev.href = href === 1 ? './' : href;
+ prev.firstChild.disabled = href === pageNum;
+ href = Math.min(pageNum + 1, maxPageNum);
+ next.href = href === 1 ? './' : href;
+ next.firstChild.disabled = href === pageNum;
+ if (strong = $('strong', pagesRoot)) {
+ if (+strong.textContent === pageNum) {
+ return;
+ }
+ $.replace(strong, strong.firstChild);
+ } else {
+ strong = $.el('strong');
+ }
+ if ((a = pagesRoot.children[pageNum - 1])) {
+ $.before(a, strong);
+ return $.add(strong, a);
+ }
+ },
+ updateHideLabel: function() {
+ var hiddenCount, k, len1, ref, threadID;
+ if (!Index.hideLabel) {
+ return;
+ }
+ hiddenCount = 0;
+ ref = Index.liveThreadIDs;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ threadID = ref[k];
+ if (Index.isHidden(threadID)) {
+ hiddenCount++;
+ }
+ }
+ if (!hiddenCount) {
+ Index.hideLabel.hidden = true;
+ if (Index.showHiddenThreads) {
+ Index.cb.toggleHiddenThreads();
+ }
+ return;
+ }
+ Index.hideLabel.hidden = false;
+ return $('#hidden-count', Index.navLinks).textContent = hiddenCount === 1 ? '1 hidden thread' : hiddenCount + " hidden threads";
+ },
+ update: function(firstTime) {
+ var oldReq;
+ if ((oldReq = Index.req)) {
+ delete Index.req;
+ oldReq.abort();
+ }
+ if (Conf['Index Refresh Notifications']) {
+ Index.notice || (Index.notice = new Notice('info', 'Refreshing index...'));
+ Index.nTimeout || (Index.nTimeout = setTimeout(function() {
+ var ref;
+ return (ref = Index.notice) != null ? ref.el.lastElementChild.textContent += ' (disable JSON Index if this takes too long)' : void 0;
+ }, 3 * $.SECOND));
+ } else {
+ Index.nTimeout || (Index.nTimeout = setTimeout(function() {
+ return Index.notice || (Index.notice = new Notice('info', 'Refreshing index... (disable JSON Index if this takes too long)'));
+ }, 3 * $.SECOND));
+ }
+ if (!firstTime && d.readyState !== 'loading' && !$('.board + *')) {
+ location.reload();
+ return;
+ }
+ Index.req = $.whenModified(g.SITE.urls.catalogJSON({
+ boardID: g.BOARD.ID
+ }), 'Index', Index.load);
+ return $.addClass(Index.button, 'fa-spin');
+ },
+ load: function() {
+ var err, nTimeout, notice, ref, timeEl;
+ if (this !== Index.req) {
+ return;
+ }
+ $.rmClass(Index.button, 'fa-spin');
+ notice = Index.notice, nTimeout = Index.nTimeout;
+ if (nTimeout) {
+ clearTimeout(nTimeout);
+ }
+ delete Index.nTimeout;
+ delete Index.req;
+ delete Index.notice;
+ if ((ref = this.status) !== 200 && ref !== 304) {
+ err = "Index refresh failed. " + (this.status ? "Error " + this.statusText + " (" + this.status + ")" : 'Connection Error');
+ if (notice) {
+ notice.setType('warning');
+ notice.el.lastElementChild.textContent = err;
+ setTimeout(notice.close, $.SECOND);
+ } else {
+ new Notice('warning', err, 1);
+ }
+ return;
+ }
+ try {
+ if (this.status === 200) {
+ Index.parse(this.response);
+ } else if (this.status === 304) {
+ Index.pageLoad();
+ }
+ } catch (error) {
+ err = error;
+ c.error("Index failure: " + err.message, err.stack);
+ if (notice) {
+ notice.setType('error');
+ notice.el.lastElementChild.textContent = 'Index refresh failed.';
+ setTimeout(notice.close, $.SECOND);
+ } else {
+ new Notice('error', 'Index refresh failed.', 1);
+ }
+ return;
+ }
+ if (notice) {
+ if (Conf['Index Refresh Notifications']) {
+ notice.setType('success');
+ notice.el.lastElementChild.textContent = 'Index refreshed!';
+ setTimeout(notice.close, $.SECOND);
+ } else {
+ notice.close();
+ }
+ }
+ timeEl = $('#index-last-refresh time', Index.navLinks);
+ timeEl.dataset.utc = Date.parse(this.getResponseHeader('Last-Modified'));
+ return RelativeDates.update(timeEl);
+ },
+ parse: function(pages) {
+ $.cleanCache(function(url) {
+ return /^https?:\/\/a\.4cdn\.org\//.test(url);
+ });
+ Index.parseThreadList(pages);
+ Index.changed.threads = true;
+ return Index.pageLoad();
+ },
+ parseThreadList: function(pages) {
+ var ID, data, i, k, l, len1, len2, obj, ref, ref1, ref2, reply, results;
+ Index.pagesNum = pages.length;
+ Index.threadsNumPerPage = ((ref = pages[0]) != null ? ref.threads.length : void 0) || 1;
+ Index.liveThreadData = pages.reduce((function(arr, next) {
+ return arr.concat(next.threads);
+ }), []);
+ Index.liveThreadIDs = Index.liveThreadData.map(function(data) {
+ return data.no;
+ });
+ Index.liveThreadDict = $.dict();
+ Index.threadPosition = $.dict();
+ Index.parsedThreads = $.dict();
+ Index.replyData = $.dict();
+ ref1 = Index.liveThreadData;
+ for (i = k = 0, len1 = ref1.length; k < len1; i = ++k) {
+ data = ref1[i];
+ Index.liveThreadDict[data.no] = data;
+ Index.threadPosition[data.no] = i;
+ Index.parsedThreads[data.no] = obj = g.SITE.Build.parseJSON(data, g.BOARD);
+ obj.filterResults = results = Filter.test(obj);
+ obj.isOnTop = results.top;
+ obj.isHidden = results.hide || ThreadHiding.isHidden(obj.boardID, obj.threadID);
+ if (data.last_replies) {
+ ref2 = data.last_replies;
+ for (l = 0, len2 = ref2.length; l < len2; l++) {
+ reply = ref2[l];
+ Index.replyData[g.BOARD + "." + reply.no] = reply;
+ }
+ }
+ }
+ if (Index.liveThreadData[0]) {
+ g.SITE.Build.spoilerRange[g.BOARD.ID] = Index.liveThreadData[0].custom_spoiler;
+ }
+ g.BOARD.threads.forEach(function(thread) {
+ var ref3;
+ if (ref3 = thread.ID, indexOf.call(Index.liveThreadIDs, ref3) < 0) {
+ return thread.collect();
+ }
+ });
+ $.event('IndexUpdate', {
+ threads: (function() {
+ var len3, m, ref3, results1;
+ ref3 = Index.liveThreadIDs;
+ results1 = [];
+ for (m = 0, len3 = ref3.length; m < len3; m++) {
+ ID = ref3[m];
+ results1.push(g.BOARD + "." + ID);
+ }
+ return results1;
+ })()
+ });
+ },
+ isHidden: function(threadID) {
+ var thread;
+ if ((thread = g.BOARD.threads.get(threadID)) && thread.OP && !thread.OP.isFetchedQuote) {
+ return thread.isHidden;
+ } else {
+ return Index.parsedThreads[threadID].isHidden;
+ }
+ },
+ isHiddenReply: function(threadID, replyData) {
+ return PostHiding.isHidden(g.BOARD.ID, threadID, replyData.no) || Filter.isHidden(g.SITE.Build.parseJSON(replyData, g.BOARD));
+ },
+ buildThreads: function(threadIDs, isCatalog, withReplies) {
+ var ID, OP, err, errors, isStale, k, lastPost, len1, newPosts, newThreads, obj, opRoot, t, thread, threadData, threads;
+ threads = [];
+ newThreads = [];
+ newPosts = [];
+ for (k = 0, len1 = threadIDs.length; k < len1; k++) {
+ ID = threadIDs[k];
+ try {
+ threadData = Index.liveThreadDict[ID];
+ if ((thread = g.BOARD.threads.get(ID))) {
+ isStale = (thread.json !== threadData) && (JSON.stringify(thread.json) !== JSON.stringify(threadData));
+ if (isStale) {
+ thread.setCount('post', threadData.replies + 1, threadData.bumplimit);
+ thread.setCount('file', threadData.images + !!threadData.ext, threadData.imagelimit);
+ thread.setStatus('Sticky', !!threadData.sticky);
+ thread.setStatus('Closed', !!threadData.closed);
+ }
+ if (thread.catalogView) {
+ $.rm(thread.catalogView.nodes.replies);
+ thread.catalogView.nodes.replies = null;
+ }
+ } else {
+ thread = new Thread(ID, g.BOARD);
+ newThreads.push(thread);
+ }
+ lastPost = threadData.last_replies && threadData.last_replies.length ? threadData.last_replies[threadData.last_replies.length - 1].no : ID;
+ if (lastPost > thread.lastPost) {
+ thread.lastPost = lastPost;
+ }
+ thread.json = threadData;
+ threads.push(thread);
+ if ((OP = thread.OP) && !OP.isFetchedQuote) {
+ OP.setCatalogOP(isCatalog);
+ thread.setPage(Math.floor(Index.threadPosition[ID] / Index.threadsNumPerPage) + 1);
+ } else {
+ obj = Index.parsedThreads[ID];
+ opRoot = g.SITE.Build.post(obj);
+ OP = new Post(opRoot, thread, g.BOARD);
+ OP.filterResults = obj.filterResults;
+ newPosts.push(OP);
+ }
+ if (!(isCatalog && thread.nodes.root)) {
+ g.SITE.Build.thread(thread, threadData, withReplies);
+ }
+ } catch (error) {
+ err = error;
+ if (!errors) {
+ errors = [];
+ }
+ errors.push({
+ message: "Parsing of Thread No." + thread + " failed. Thread will be skipped.",
+ error: err,
+ html: opRoot != null ? opRoot.outerHTML : void 0
+ });
+ }
+ }
+ if (errors) {
+ Main.handleErrors(errors);
+ }
+ if (withReplies) {
+ newPosts = newPosts.concat(Index.buildReplies(threads));
+ }
+ Main.callbackNodes('Thread', newThreads);
+ Main.callbackNodes('Post', newPosts);
+ Index.updateHideLabel();
+ $.event('IndexRefreshInternal', {
+ threadIDs: (function() {
+ var l, len2, results1;
+ results1 = [];
+ for (l = 0, len2 = threads.length; l < len2; l++) {
+ t = threads[l];
+ results1.push(t.fullID);
+ }
+ return results1;
+ })(),
+ isCatalog: isCatalog
+ });
+ return threads;
+ },
+ buildReplies: function(threads) {
+ var data, err, errors, k, l, lastReplies, len1, len2, node, nodes, post, posts, thread;
+ posts = [];
+ for (k = 0, len1 = threads.length; k < len1; k++) {
+ thread = threads[k];
+ if (!(lastReplies = Index.liveThreadDict[thread.ID].last_replies)) {
+ continue;
+ }
+ nodes = [];
+ for (l = 0, len2 = lastReplies.length; l < len2; l++) {
+ data = lastReplies[l];
+ if ((post = thread.posts.get(data.no)) && !post.isFetchedQuote) {
+ nodes.push(post.nodes.root);
+ continue;
+ }
+ nodes.push(node = g.SITE.Build.postFromObject(data, thread.board.ID));
+ try {
+ posts.push(new Post(node, thread, thread.board));
+ } catch (error) {
+ err = error;
+ if (!errors) {
+ errors = [];
+ }
+ errors.push({
+ message: "Parsing of Post No." + data.no + " failed. Post will be skipped.",
+ error: err,
+ html: node != null ? node.outerHTML : void 0
+ });
+ }
+ }
+ $.add(thread.nodes.root, nodes);
+ }
+ if (errors) {
+ Main.handleErrors(errors);
+ }
+ return posts;
+ },
+ buildCatalogViews: function(threads) {
+ var ID, catalogThreads, k, len1, page, root, thread;
+ catalogThreads = [];
+ for (k = 0, len1 = threads.length; k < len1; k++) {
+ thread = threads[k];
+ if (!(!thread.catalogView)) {
+ continue;
+ }
+ ID = thread.ID;
+ page = Math.floor(Index.threadPosition[ID] / Index.threadsNumPerPage) + 1;
+ root = g.SITE.Build.catalogThread(thread, Index.liveThreadDict[ID], page);
+ catalogThreads.push(new CatalogThread(root, thread));
+ }
+ Main.callbackNodes('CatalogThread', catalogThreads);
+ },
+ sizeCatalogViews: function(threads) {
+ var height, k, len1, ratio, ref, size, thread, thumb, width;
+ size = Conf['Index Size'] === 'small' ? 150 : 250;
+ for (k = 0, len1 = threads.length; k < len1; k++) {
+ thread = threads[k];
+ thumb = thread.catalogView.nodes.thumb;
+ ref = thumb.dataset, width = ref.width, height = ref.height;
+ if (!width) {
+ continue;
+ }
+ ratio = size / Math.max(width, height);
+ thumb.style.width = width * ratio + 'px';
+ thumb.style.height = height * ratio + 'px';
+ }
+ },
+ buildCatalogReplies: function(thread) {
+ var data, k, lastReplies, len1, nodes, replies, reply;
+ nodes = thread.catalogView.nodes;
+ if (!(lastReplies = Index.liveThreadDict[thread.ID].last_replies)) {
+ return;
+ }
+ replies = [];
+ for (k = 0, len1 = lastReplies.length; k < len1; k++) {
+ data = lastReplies[k];
+ if (Index.isHiddenReply(thread.ID, data)) {
+ continue;
+ }
+ reply = g.SITE.Build.catalogReply(thread, data);
+ RelativeDates.update($('time', reply));
+ $.on($('.catalog-reply-preview', reply), 'mouseover', QuotePreview.mouseover);
+ replies.push(reply);
+ }
+ nodes.replies = $.el('div', {
+ className: 'catalog-replies'
+ });
+ $.add(nodes.replies, replies);
+ $.add(thread.OP.nodes.post, nodes.replies);
+ },
+ sort: function() {
+ var lastlong, lastlongD, liveThreadData, liveThreadIDs, repliesAvailable, sortType, thread, threadIDs, tmp_time;
+ liveThreadIDs = Index.liveThreadIDs, liveThreadData = Index.liveThreadData;
+ if (!liveThreadData) {
+ return;
+ }
+ tmp_time = new Date().getTime() / 1000;
+ sortType = Index.currentSort.replace(/-rev$/, '');
+ Index.sortedThreadIDs = (function() {
+ var k, len1;
+ switch (sortType) {
+ case 'lastreply':
+ case 'lastlong':
+ repliesAvailable = liveThreadData.some(function(thread) {
+ var ref;
+ return (ref = thread.last_replies) != null ? ref.length : void 0;
+ });
+ lastlong = function(thread) {
+ var i, k, len, r, ref, ref1;
+ if (!repliesAvailable) {
+ return thread.last_modified;
+ }
+ ref = thread.last_replies || [];
+ for (i = k = ref.length - 1; k >= 0; i = k += -1) {
+ r = ref[i];
+ if (Index.isHiddenReply(thread.no, r)) {
+ continue;
+ }
+ if (sortType === 'lastreply') {
+ return r;
+ }
+ len = r.com ? g.SITE.Build.parseComment(r.com).replace(/[^a-z]/ig, '').length : 0;
+ if (len >= Index.lastLongThresholds[+(!!r.ext)]) {
+ return r;
+ }
+ }
+ if (thread.omitted_posts && ((ref1 = thread.last_replies) != null ? ref1.length : void 0)) {
+ return thread.last_replies[0];
+ } else {
+ return thread;
+ }
+ };
+ lastlongD = $.dict();
+ for (k = 0, len1 = liveThreadData.length; k < len1; k++) {
+ thread = liveThreadData[k];
+ lastlongD[thread.no] = lastlong(thread).no;
+ }
+ return slice.call(liveThreadData).sort(function(a, b) {
+ return lastlongD[b.no] - lastlongD[a.no];
+ }).map(function(post) {
+ return post.no;
+ });
+ case 'bump':
+ return liveThreadIDs;
+ case 'birth':
+ return slice.call(liveThreadIDs).sort(function(a, b) {
+ return b - a;
+ });
+ case 'replycount':
+ return slice.call(liveThreadData).sort(function(a, b) {
+ return b.replies - a.replies;
+ }).map(function(post) {
+ return post.no;
+ });
+ case 'filecount':
+ return slice.call(liveThreadData).sort(function(a, b) {
+ return b.images - a.images;
+ }).map(function(post) {
+ return post.no;
+ });
+ case 'activity':
+ return slice.call(liveThreadData).sort(function(a, b) {
+ return (tmp_time - a.time) / (a.replies + 1) - (tmp_time - b.time) / (b.replies + 1);
+ }).map(function(post) {
+ return post.no;
+ });
+ default:
+ return liveThreadIDs;
+ }
+ })();
+ if (/-rev$/.test(Index.currentSort)) {
+ Index.sortedThreadIDs = slice.call(Index.sortedThreadIDs).reverse();
+ }
+ if (Index.search && (threadIDs = Index.querySearch(Index.search))) {
+ Index.sortedThreadIDs = threadIDs;
+ }
+ Index.sortOnTop(function(obj) {
+ return obj.isSticky;
+ });
+ Index.sortOnTop(function(obj) {
+ return obj.isOnTop || Conf['Pin Watched Threads'] && ThreadWatcher.isWatchedRaw(obj.boardID, obj.threadID);
+ });
+ if (Conf['Anchor Hidden Threads']) {
+ return Index.sortOnTop(function(obj) {
+ return !Index.isHidden(obj.threadID);
+ });
+ }
+ },
+ sortOnTop: function(match) {
+ var ID, bottomThreads, k, len1, ref, topThreads;
+ topThreads = [];
+ bottomThreads = [];
+ ref = Index.sortedThreadIDs;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ ID = ref[k];
+ (match(Index.parsedThreads[ID]) ? topThreads : bottomThreads).push(ID);
+ }
+ return Index.sortedThreadIDs = topThreads.concat(bottomThreads);
+ },
+ buildIndex: function() {
+ var threadIDs;
+ if (!Index.liveThreadData) {
+ return;
+ }
+ switch (Conf['Index Mode']) {
+ case 'all pages':
+ threadIDs = Index.sortedThreadIDs;
+ break;
+ case 'catalog':
+ threadIDs = Index.sortedThreadIDs.filter(function(ID) {
+ return !Index.isHidden(ID) !== Index.showHiddenThreads;
+ });
+ break;
+ default:
+ threadIDs = Index.threadsOnPage(Index.currentPage);
+ }
+ delete Index.pageNum;
+ $.rmAll(Index.root);
+ $.rmAll(Header.hover);
+ if (Index.loaded && Index.root.parentNode) {
+ $.event('PostsRemoved', null, Index.root);
+ }
+ if (Conf['Index Mode'] === 'catalog') {
+ Index.buildCatalog(threadIDs);
+ } else {
+ Index.buildStructure(threadIDs);
+ }
+ },
+ threadsOnPage: function(pageNum) {
+ var nodesPerPage, offset;
+ nodesPerPage = Index.threadsNumPerPage;
+ offset = nodesPerPage * (pageNum - 1);
+ return Index.sortedThreadIDs.slice(offset, offset + nodesPerPage);
+ },
+ buildStructure: function(threadIDs) {
+ var k, len1, nodes, thread, threads;
+ threads = Index.buildThreads(threadIDs, false, Conf['Show Replies']);
+ nodes = [];
+ for (k = 0, len1 = threads.length; k < len1; k++) {
+ thread = threads[k];
+ nodes.push(thread.nodes.root, $.el('hr'));
+ }
+ $.add(Index.root, nodes);
+ if (Index.root.parentNode) {
+ $.event('PostsInserted', null, Index.root);
+ }
+ Index.loaded = true;
+ },
+ buildCatalog: function(threadIDs) {
+ var fn, i, n, node0;
+ i = 0;
+ n = threadIDs.length;
+ node0 = null;
+ fn = function() {
+ var j;
+ if (node0 && !node0.parentNode) {
+ return;
+ }
+ j = i > 0 && Index.root.parentNode ? n : i + 30;
+ node0 = Index.buildCatalogPart(threadIDs.slice(i, j))[0];
+ i = j;
+ if (i < n) {
+ return $.queueTask(fn);
+ } else {
+ if (Index.root.parentNode) {
+ $.event('PostsInserted', null, Index.root);
+ }
+ return Index.loaded = true;
+ }
+ };
+ fn();
+ },
+ buildCatalogPart: function(threadIDs) {
+ var k, len1, nodes, thread, threads;
+ threads = Index.buildThreads(threadIDs, true);
+ Index.buildCatalogViews(threads);
+ Index.sizeCatalogViews(threads);
+ nodes = [];
+ for (k = 0, len1 = threads.length; k < len1; k++) {
+ thread = threads[k];
+ thread.OP.setCatalogOP(true);
+ $.add(thread.catalogView.nodes.root, thread.OP.nodes.root);
+ nodes.push(thread.catalogView.nodes.root);
+ $.on(thread.catalogView.nodes.root, 'mouseenter', Index.cb.catalogReplies.bind(thread));
+ $.on(thread.OP.nodes.root, 'mouseenter', Index.cb.hoverAdjust.bind(thread.OP.nodes));
+ }
+ $.add(Index.root, nodes);
+ return nodes;
+ },
+ clearSearch: function() {
+ Index.searchInput.value = '';
+ Index.onSearchInput();
+ return Index.searchInput.focus();
+ },
+ setupSearch: function() {
+ Index.searchInput.value = Index.search;
+ if (Index.search) {
+ return Index.searchInput.dataset.searching = 1;
+ } else {
+ return Index.searchInput.removeAttribute('data-searching');
+ }
+ },
+ onSearchInput: function() {
+ var search;
+ search = Index.searchInput.value.trim();
+ if (search === Index.search) {
+ return;
+ }
+ Index.pushState({
+ search: search,
+ replace: !!search === !!Index.search
+ });
+ return Index.pageLoad(false);
+ },
+ querySearch: function(query) {
+ var keywords, match, regexp;
+ if ((match = query.match(/^([\w+]+):\/(.*)\/(\w*)$/))) {
+ try {
+ regexp = RegExp(match[2], match[3]);
+ } catch (error) {
+ return [];
+ }
+ return Index.sortedThreadIDs.filter(function(ID) {
+ return regexp.test(Filter.values(match[1], Index.parsedThreads[ID]).join('\n'));
+ });
+ }
+ if (!(keywords = query.toLowerCase().match(/\S+/g))) {
+ return;
+ }
+ return Index.sortedThreadIDs.filter(function(ID) {
+ return Index.searchMatch(Index.parsedThreads[ID], keywords);
+ });
+ },
+ searchMatch: function(obj, keywords) {
+ var file, info, k, key, keyword, l, len1, len2, ref, text;
+ info = obj.info, file = obj.file;
+ if (info.comment == null) {
+ info.comment = g.SITE.Build.parseComment(info.commentHTML.innerHTML);
+ }
+ text = [];
+ ref = ['comment', 'subject', 'name', 'tripcode'];
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ key = ref[k];
+ if (key in info) {
+ text.push(info[key]);
+ }
+ }
+ if (file) {
+ text.push(file.name);
+ }
+ text = text.join(' ').toLowerCase();
+ for (l = 0, len2 = keywords.length; l < len2; l++) {
+ keyword = keywords[l];
+ if (-1 === text.indexOf(keyword)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ return Index;
+
+}).call(this);
+
+Polyfill = (function() {
+ var Polyfill;
+
+ Polyfill = {
+ init: function() {
+ var base;
+ this.toBlob();
+ $.global(this.toBlob);
+ (base = Element.prototype).matches || (base.matches = Element.prototype.mozMatchesSelector || Element.prototype.webkitMatchesSelector);
+ },
+ toBlob: function() {
+ if (HTMLCanvasElement.prototype.toBlob) {
+ return;
+ }
+ HTMLCanvasElement.prototype.toBlob = function(cb, type, encoderOptions) {
+ var data, i, j, l, ref, ui8a, url;
+ url = this.toDataURL(type, encoderOptions);
+ data = atob(url.slice(url.indexOf(',') + 1));
+ l = data.length;
+ ui8a = new Uint8Array(l);
+ for (i = j = 0, ref = l; j < ref; i = j += 1) {
+ ui8a[i] = data.charCodeAt(i);
+ }
+ return cb(new Blob([ui8a], {
+ type: type || 'image/png'
+ }));
+ };
+ }
+ };
+
+ return Polyfill;
+
+}).call(this);
+
+Settings = (function() {
+ var Settings,
+ slice = [].slice,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Settings = {
+ init: function() {
+ var add, link;
+ link = $.el('a', {
+ className: 'settings-link fa fa-wrench',
+ textContent: 'Settings',
+ title: '4chan X Settings',
+ href: 'javascript:;'
+ });
+ $.on(link, 'click', Settings.open);
+ Header.addShortcut('settings', link, 820);
+ add = this.addSection;
+ add('Main', this.main);
+ add('Filter', this.filter);
+ add('Sauce', this.sauce);
+ add('Advanced', this.advanced);
+ add('Keybinds', this.keybinds);
+ $.on(d, 'AddSettingsSection', Settings.addSection);
+ $.on(d, 'OpenSettings', function(e) {
+ return Settings.open(e.detail);
+ });
+ if (g.SITE.software === 'yotsuba' && Conf['Disable Native Extension']) {
+ if ($.hasStorage) {
+ return $.global(function() {
+ var settings;
+ try {
+ settings = JSON.parse(localStorage.getItem('4chan-settings')) || {};
+ if (settings.disableAll) {
+ return;
+ }
+ settings.disableAll = true;
+ return localStorage.setItem('4chan-settings', JSON.stringify(settings));
+ } catch (error) {
+ return Object.defineProperty(window, 'Config', {
+ value: {
+ disableAll: true
+ }
+ });
+ }
+ });
+ } else {
+ return $.global(function() {
+ return Object.defineProperty(window, 'Config', {
+ value: {
+ disableAll: true
+ }
+ });
+ });
+ }
+ }
+ },
+ open: function(openSection) {
+ var dialog, j, len, link, links, ref, section, sectionToOpen;
+ if (Settings.dialog) {
+ return;
+ }
+ $.event('CloseMenu');
+ Settings.dialog = dialog = $.el('div', {
+ id: 'overlay'
+ }, {innerHTML: "<div id=\"fourchanx-settings\" class=\"dialog\"><nav><div class=\"sections-list\"></div><p class=\"imp-exp-result warning\"></p><div class=\"credits\"><a class=\"export\">Export</a>&nbsp|&nbsp<a class=\"import\">Import</a>&nbsp|&nbsp<a class=\"reset\">Reset Settings</a>&nbsp|&nbsp<input type=\"file\" hidden><a href=\"https://www.4chan-x.net/\" target=\"_blank\">4chan X</a>&nbsp|&nbsp<a href=\"https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md\" target=\"_blank\">" + E(g.VERSION) + "</a>&nbsp|&nbsp<a href=\"https://gitreports.com/issue/ccd0/4chan-x\" target=\"_blank\">Issues</a>&nbsp|&nbsp<a href=\"javascript:;\" class=\"close fa fa-times\" title=\"Close\"></a></div></nav><div class=\"section-container\"><section></section></div></div>"});
+ $.on($('.export', dialog), 'click', Settings["export"]);
+ $.on($('.import', dialog), 'click', Settings["import"]);
+ $.on($('.reset', dialog), 'click', Settings.reset);
+ $.on($('input', dialog), 'change', Settings.onImport);
+ links = [];
+ ref = Settings.sections;
+ for (j = 0, len = ref.length; j < len; j++) {
+ section = ref[j];
+ link = $.el('a', {
+ className: "tab-" + section.hyphenatedTitle,
+ textContent: section.title,
+ href: 'javascript:;'
+ });
+ $.on(link, 'click', Settings.openSection.bind(section));
+ links.push(link, $.tn(' | '));
+ if (section.title === openSection) {
+ sectionToOpen = link;
+ }
+ }
+ links.pop();
+ $.add($('.sections-list', dialog), links);
+ if (openSection !== 'none') {
+ (sectionToOpen ? sectionToOpen : links[0]).click();
+ }
+ $.on($('.close', dialog), 'click', Settings.close);
+ $.on(window, 'beforeunload', Settings.close);
+ $.on(dialog, 'click', Settings.close);
+ $.on(dialog.firstElementChild, 'click', function(e) {
+ return e.stopPropagation();
+ });
+ $.add(d.body, dialog);
+ return $.event('OpenSettings', null, dialog);
+ },
+ close: function() {
+ var ref;
+ if (!Settings.dialog) {
+ return;
+ }
+ if ((ref = d.activeElement) != null) {
+ ref.blur();
+ }
+ $.rm(Settings.dialog);
+ return delete Settings.dialog;
+ },
+ sections: [],
+ addSection: function(title, open) {
+ var hyphenatedTitle, ref;
+ if (typeof title !== 'string') {
+ ref = title.detail, title = ref.title, open = ref.open;
+ }
+ hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-');
+ return Settings.sections.push({
+ title: title,
+ hyphenatedTitle: hyphenatedTitle,
+ open: open
+ });
+ },
+ openSection: function() {
+ var section, selected;
+ if (selected = $('.tab-selected', Settings.dialog)) {
+ $.rmClass(selected, 'tab-selected');
+ }
+ $.addClass($(".tab-" + this.hyphenatedTitle, Settings.dialog), 'tab-selected');
+ section = $('section', Settings.dialog);
+ $.rmAll(section);
+ section.className = "section-" + this.hyphenatedTitle;
+ this.open(section, g);
+ section.scrollTop = 0;
+ return $.event('OpenSettings', null, section);
+ },
+ warnings: {
+ localStorage: function(cb) {
+ var why;
+ if ($.cantSync) {
+ why = $.cantSet ? 'save your settings' : 'synchronize settings between tabs';
+ return cb($.el('li', {
+ textContent: "4chan X needs local storage to " + why + ".\nEnable it on boards." + (location.hostname.split('.')[1]) + ".org in your browser's privacy settings (may be listed as part of \"local data\" or \"cookies\")."
+ }));
+ }
+ },
+ ads: function(cb) {
+ return $.onExists(doc, '.adg-rects > .desktop', function(ad) {
+ return $.onExists(ad, 'iframe', function() {
+ var url;
+ url = Redirect.to('thread', {
+ boardID: 'qa',
+ threadID: 362590
+ });
+ return cb($.el('li', {innerHTML: "To protect yourself from <a href=\"" + E(url) + "\" target=\"_blank\">malicious ads</a>, you should <a href=\"https://github.com/gorhill/uBlock#ublock-origin\" target=\"_blank\">block ads</a> on 4chan."}));
+ });
+ });
+ }
+ },
+ main: function(section) {
+ var addCheckboxes, addWarning, button, div, fs, inputs, items, key, keyFS, obj, ref, ref1, warning, warnings;
+ warnings = $.el('fieldset', {
+ hidden: true
+ }, {innerHTML: "<legend>Warnings</legend><ul></ul>"});
+ addWarning = function(item) {
+ $.add($('ul', warnings), item);
+ return warnings.hidden = false;
+ };
+ ref = Settings.warnings;
+ for (key in ref) {
+ warning = ref[key];
+ warning(addWarning);
+ }
+ $.add(section, warnings);
+ items = $.dict();
+ inputs = $.dict();
+ addCheckboxes = function(root, obj) {
+ var arr, container, containers, description, div, input, level, results;
+ containers = [root];
+ results = [];
+ for (key in obj) {
+ arr = obj[key];
+ if (!(arr instanceof Array)) {
+ continue;
+ }
+ description = arr[1];
+ div = $.el('div', {innerHTML: "<label><input type=\"checkbox\" name=\"" + E(key) + "\">" + E(key) + "</label><span class=\"description\">: " + E(description) + "</span>"});
+ div.dataset.name = key;
+ input = $('input', div);
+ $.on(input, 'change', $.cb.checked);
+ $.on(input, 'change', function() {
+ return this.parentNode.parentNode.dataset.checked = this.checked;
+ });
+ items[key] = Conf[key];
+ inputs[key] = input;
+ level = arr[2] || 0;
+ if (containers.length <= level) {
+ container = $.el('div', {
+ className: 'suboption-list'
+ });
+ $.add(containers[containers.length - 1].lastElementChild, container);
+ containers[level] = container;
+ } else if (containers.length > level + 1) {
+ containers.splice(level + 1, containers.length - (level + 1));
+ }
+ results.push($.add(containers[level], div));
+ }
+ return results;
+ };
+ ref1 = Config.main;
+ for (keyFS in ref1) {
+ obj = ref1[keyFS];
+ fs = $.el('fieldset', {innerHTML: "<legend>" + E(keyFS) + "</legend>"});
+ addCheckboxes(fs, obj);
+ if (keyFS === 'Posting and Captchas') {
+ $.add(fs, $.el('p', {innerHTML: "For more info on captcha options and issues, see the <a href=\"https://github.com/ccd0/4chan-x/wiki/Captcha-FAQ\" target=\"_blank\">captcha FAQ</a>."}));
+ }
+ $.add(section, fs);
+ }
+ addCheckboxes($('div[data-name="JSON Index"] > .suboption-list', section), Config.Index);
+ if ($.engine !== 'gecko') {
+ $('div[data-name="Remember QR Size"]', section).hidden = true;
+ }
+ if ($.perProtocolSettings || location.protocol !== 'https:') {
+ $('div[data-name="Redirect to HTTPS"]', section).hidden = true;
+ }
+ if ($.platform !== 'crx') {
+ $('div[data-name="Work around CORB Bug"]', section).hidden = true;
+ }
+ $.get(items, function(items) {
+ var val;
+ for (key in items) {
+ val = items[key];
+ inputs[key].checked = val;
+ inputs[key].parentNode.parentNode.dataset.checked = val;
+ }
+ });
+ div = $.el('div', {innerHTML: "<button></button><span class=\"description\">: Clear manually-hidden threads and posts on all boards. Reload the page to apply."});
+ button = $('button', div);
+ $.get({
+ hiddenThreads: $.dict(),
+ hiddenPosts: $.dict()
+ }, function(arg) {
+ var ID, board, hiddenNum, hiddenPosts, hiddenThreads, ref2, ref3, ref4, ref5, site, thread;
+ hiddenThreads = arg.hiddenThreads, hiddenPosts = arg.hiddenPosts;
+ hiddenNum = 0;
+ for (ID in hiddenThreads) {
+ site = hiddenThreads[ID];
+ if (ID !== 'boards') {
+ ref2 = site.boards;
+ for (ID in ref2) {
+ board = ref2[ID];
+ hiddenNum += Object.keys(board).length;
+ }
+ }
+ }
+ ref3 = hiddenThreads.boards;
+ for (ID in ref3) {
+ board = ref3[ID];
+ hiddenNum += Object.keys(board).length;
+ }
+ for (ID in hiddenPosts) {
+ site = hiddenPosts[ID];
+ if (ID !== 'boards') {
+ ref4 = site.boards;
+ for (ID in ref4) {
+ board = ref4[ID];
+ for (ID in board) {
+ thread = board[ID];
+ hiddenNum += Object.keys(thread).length;
+ }
+ }
+ }
+ }
+ ref5 = hiddenPosts.boards;
+ for (ID in ref5) {
+ board = ref5[ID];
+ for (ID in board) {
+ thread = board[ID];
+ hiddenNum += Object.keys(thread).length;
+ }
+ }
+ return button.textContent = "Hidden: " + hiddenNum;
+ });
+ $.on(button, 'click', function() {
+ this.textContent = 'Hidden: 0';
+ return $.get('hiddenThreads', $.dict(), function(arg) {
+ var boardID, hiddenThreads, ref2;
+ hiddenThreads = arg.hiddenThreads;
+ if ($.hasStorage && g.SITE.software === 'yotsuba') {
+ for (boardID in (ref2 = hiddenThreads['4chan.org']) != null ? ref2.boards : void 0) {
+ localStorage.removeItem("4chan-hide-t-" + boardID);
+ }
+ for (boardID in hiddenThreads.boards) {
+ localStorage.removeItem("4chan-hide-t-" + boardID);
+ }
+ }
+ return $["delete"](['hiddenThreads', 'hiddenPosts']);
+ });
+ });
+ return $.after($('input[name="Stubs"]', section).parentNode.parentNode, div);
+ },
+ "export": function() {
+ var Conf2;
+ Conf2 = $.dict();
+ $.extend(Conf2, Conf);
+ return $.get(Conf2, function(Conf2) {
+ delete Conf2['boardConfig'];
+ return Settings.downloadExport({
+ version: g.VERSION,
+ date: Date.now(),
+ Conf: Conf2
+ });
+ });
+ },
+ downloadExport: function(data) {
+ var a, blob, p, url;
+ blob = new Blob([JSON.stringify(data, null, 2)], {
+ type: 'application/json'
+ });
+ url = URL.createObjectURL(blob);
+ a = $.el('a', {
+ download: "4chan X v" + g.VERSION + "-" + data.date + ".json",
+ href: url
+ });
+ p = $('.imp-exp-result', Settings.dialog);
+ $.rmAll(p);
+ $.add(p, a);
+ return a.click();
+ },
+ "import": function() {
+ return $('input[type=file]', this.parentNode).click();
+ },
+ onImport: function() {
+ var file, output, reader;
+ if (!(file = this.files[0])) {
+ return;
+ }
+ this.value = null;
+ output = $('.imp-exp-result');
+ if (!confirm('Your current settings will be entirely overwritten, are you sure?')) {
+ output.textContent = 'Import aborted.';
+ return;
+ }
+ reader = new FileReader();
+ reader.onload = function(e) {
+ var err;
+ try {
+ return Settings.loadSettings($.dict.json(e.target.result), function(err) {
+ if (err) {
+ return output.textContent = 'Import failed due to an error.';
+ } else if (confirm('Import successful. Reload now?')) {
+ return window.location.reload();
+ }
+ });
+ } catch (error) {
+ err = error;
+ output.textContent = 'Import failed due to an error.';
+ return c.error(err.stack);
+ }
+ };
+ return reader.readAsText(file);
+ },
+ convertFrom: {
+ loadletter: function(data) {
+ var base, boardID, convertSettings, key, ref, ref1, threadData, threadID, threads, val;
+ convertSettings = function(data, map) {
+ var newKey, prevKey;
+ for (prevKey in map) {
+ newKey = map[prevKey];
+ if (newKey) {
+ data.Conf[newKey] = data.Conf[prevKey];
+ }
+ delete data.Conf[prevKey];
+ }
+ return data;
+ };
+ data = convertSettings(data, {
+ 'Disable 4chan\'s extension': 'Disable Native Extension',
+ 'Comment Auto-Expansion': '',
+ 'Remove Slug': '',
+ 'Always HTTPS': 'Redirect to HTTPS',
+ 'Check for Updates': '',
+ 'Recursive Filtering': 'Recursive Hiding',
+ 'Reply Hiding': 'Reply Hiding Buttons',
+ 'Thread Hiding': 'Thread Hiding Buttons',
+ 'Show Stubs': 'Stubs',
+ 'Image Auto-Gif': 'Replace GIF',
+ 'Expand All WebM': 'Expand videos',
+ 'Reveal Spoilers': 'Reveal Spoiler Thumbnails',
+ 'Expand From Current': 'Expand from here',
+ 'Current Page': 'Page Count in Stats',
+ 'Current Page Position': '',
+ 'Alternative captcha': 'Use Recaptcha v1',
+ 'Alt index captcha': 'Use Recaptcha v1 on Index',
+ 'Auto Submit': 'Post on Captcha Completion',
+ 'Open Reply in New Tab': 'Open Post in New Tab',
+ 'Remember QR size': 'Remember QR Size',
+ 'Remember Subject': '',
+ 'Quote Inline': 'Quote Inlining',
+ 'Quote Preview': 'Quote Previewing',
+ 'Indicate OP quote': 'Mark OP Quotes',
+ 'Indicate You quote': 'Mark Quotes of You',
+ 'Indicate Cross-thread Quotes': 'Mark Cross-thread Quotes',
+ 'uniqueid': 'uniqueID',
+ 'mod': 'capcode',
+ 'email': '',
+ 'country': 'flag',
+ 'md5': 'MD5',
+ 'openEmptyQR': 'Open empty QR',
+ 'openQR': 'Open QR',
+ 'openOptions': 'Open settings',
+ 'close': 'Close',
+ 'spoiler': 'Spoiler tags',
+ 'sageru': 'Toggle sage',
+ 'code': 'Code tags',
+ 'sjis': 'SJIS tags',
+ 'submit': 'Submit QR',
+ 'watch': 'Watch',
+ 'update': 'Update',
+ 'unreadCountTo0': '',
+ 'expandAllImages': 'Expand images',
+ 'expandImage': 'Expand image',
+ 'zero': 'Front page',
+ 'nextPage': 'Next page',
+ 'previousPage': 'Previous page',
+ 'nextThread': 'Next thread',
+ 'previousThread': 'Previous thread',
+ 'expandThread': 'Expand thread',
+ 'openThreadTab': 'Open thread',
+ 'openThread': 'Open thread tab',
+ 'nextReply': 'Next reply',
+ 'previousReply': 'Previous reply',
+ 'hide': 'Hide',
+ 'Scrolling': 'Auto Scroll',
+ 'Verbose': ''
+ });
+ if ('Always CDN' in data.Conf) {
+ data.Conf['fourchanImageHost'] = data.Conf['Always CDN'] ? 'i.4cdn.org' : '';
+ delete data.Conf['Always CDN'];
+ }
+ data.Conf.sauces = data.Conf.sauces.replace(/\$\d/g, function(c) {
+ switch (c) {
+ case '$1':
+ return '%TURL';
+ case '$2':
+ return '%URL';
+ case '$3':
+ return '%MD5';
+ case '$4':
+ return '%board';
+ default:
+ return c;
+ }
+ });
+ ref = Config.hotkeys;
+ for (key in ref) {
+ val = ref[key];
+ if (key in data.Conf) {
+ data.Conf[key] = data.Conf[key].replace(/ctrl|alt|meta/g, function(s) {
+ return "" + (s[0].toUpperCase()) + s.slice(1);
+ }).replace(/(^|.+\+)[A-Z]$/g, function(s) {
+ return "Shift+" + s.slice(0, -1) + (s.slice(-1).toLowerCase());
+ });
+ }
+ }
+ if (data.WatchedThreads) {
+ data.Conf['watchedThreads'] = $.dict.clone({
+ '4chan.org': {
+ boards: {}
+ }
+ });
+ ref1 = data.WatchedThreads;
+ for (boardID in ref1) {
+ threads = ref1[boardID];
+ for (threadID in threads) {
+ threadData = threads[threadID];
+ ((base = data.Conf['watchedThreads']['4chan.org'].boards)[boardID] || (base[boardID] = $.dict()))[threadID] = {
+ excerpt: threadData.textContent
+ };
+ }
+ }
+ }
+ return data;
+ }
+ },
+ upgrade: function(data, version) {
+ var addCSS, addSauces, boardID, boards, changes, compareString, corrupted, db, hostname, j, k, key, l, lastChecked, len, len1, len2, len3, line, list, m, name, record, ref, ref1, ref10, ref11, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, rice, set, setD, siteProperties, software, type, uids, val, val2, value;
+ changes = $.dict();
+ set = function(key, value) {
+ return data[key] = changes[key] = value;
+ };
+ setD = function(key, value) {
+ if (data[key] == null) {
+ return set(key, value);
+ }
+ };
+ addSauces = function(sauces) {
+ if (data['sauces'] != null) {
+ sauces = sauces.filter(function(s) {
+ return data['sauces'].indexOf(s.match(/[^#;\s]+|$/)[0]) < 0;
+ });
+ if (sauces.length) {
+ return set('sauces', data['sauces'] + '\n\n' + sauces.join('\n'));
+ }
+ }
+ };
+ addCSS = function(css) {
+ if (data['usercss'] == null) {
+ set('usercss', Config['usercss']);
+ }
+ if (data['usercss'].indexOf(css) < 0) {
+ return set('usercss', css + '\n\n' + data['usercss']);
+ }
+ };
+ if ((corrupted = version[0] === '"')) {
+ try {
+ version = JSON.parse(version);
+ } catch (error) {}
+ }
+ compareString = version.replace(/\d+/g, function(x) {
+ return ('0000' + x).slice(-5);
+ });
+ if (compareString < '00001.00013.00014.00008') {
+ for (key in data) {
+ val = data[key];
+ if (!(typeof val === 'string' && typeof Conf[key] !== 'string' && (key !== 'Index Sort' && key !== 'Last Long Reply Thresholds 0' && key !== 'Last Long Reply Thresholds 1'))) {
+ continue;
+ }
+ corrupted = true;
+ break;
+ }
+ }
+ if (corrupted) {
+ for (key in data) {
+ val = data[key];
+ if (typeof val === 'string') {
+ try {
+ val2 = JSON.parse(val);
+ set(key, val2);
+ } catch (error) {}
+ }
+ }
+ }
+ if (compareString < '00001.00011.00008.00000') {
+ if (data['Fixed Thread Watcher'] == null) {
+ set('Fixed Thread Watcher', (ref = data['Toggleable Thread Watcher']) != null ? ref : true);
+ }
+ if (data['Exempt Archives from Encryption'] == null) {
+ set('Exempt Archives from Encryption', (ref1 = data['Except Archives from Encryption']) != null ? ref1 : false);
+ }
+ }
+ if (compareString < '00001.00011.00010.00001') {
+ if (data['selectedArchives'] != null) {
+ uids = {
+ "Moe": 0,
+ "4plebs Archive": 3,
+ "Nyafuu Archive": 4,
+ "Love is Over": 5,
+ "Rebecca Black Tech": 8,
+ "warosu": 10,
+ "fgts": 15,
+ "not4plebs": 22,
+ "DesuStorage": 23,
+ "fireden.net": 24,
+ "disabled": null
+ };
+ ref2 = data['selectedArchives'];
+ for (boardID in ref2) {
+ record = ref2[boardID];
+ for (type in record) {
+ name = record[type];
+ if ($.hasOwn(uids, name)) {
+ record[type] = uids[name];
+ }
+ }
+ }
+ set('selectedArchives', data['selectedArchives']);
+ }
+ }
+ if (compareString < '00001.00011.00016.00000') {
+ if ((rice = Config['usercss'].match(/\/\* Board title rice \*\/(?:\n.+)*/)[0])) {
+ if ((data['usercss'] != null) && data['usercss'].indexOf(rice) < 0) {
+ set('usercss', rice + '\n\n' + data['usercss']);
+ }
+ }
+ }
+ if (compareString < '00001.00011.00017.00000') {
+ ref3 = ['Persistent QR', 'Color User IDs', 'Fappe Tyme', 'Werk Tyme', 'Highlight Posts Quoting You', 'Highlight Own Posts'];
+ for (j = 0, len = ref3.length; j < len; j++) {
+ key = ref3[j];
+ if (data[key] == null) {
+ set(key, key === 'Persistent QR');
+ }
+ }
+ }
+ if (compareString < '00001.00011.00017.00006') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)http:\/\/iqdb\.org\//mg, '$1//iqdb.org/'));
+ }
+ }
+ if (compareString < '00001.00011.00019.00003' && !Settings.dialog) {
+ $.queueTask(function() {
+ return Settings.warnings.ads(function(item) {
+ return new Notice('warning', slice.call(item.childNodes));
+ });
+ });
+ }
+ if (compareString < '00001.00011.00020.00003') {
+ ref4 = {
+ 'Inline Cross-thread Quotes Only': false,
+ 'Pass Link': true
+ };
+ for (key in ref4) {
+ value = ref4[key];
+ if (data[key] == null) {
+ set(key, value);
+ }
+ }
+ }
+ if (compareString < '00001.00011.00021.00003') {
+ if (data['Remember Your Posts'] == null) {
+ set('Remember Your Posts', (ref5 = data['Mark Quotes of You']) != null ? ref5 : true);
+ }
+ }
+ if (compareString < '00001.00011.00022.00000') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*https:\/\/www\.google\.com\/searchbyimage\?image_url=%(?:IMG|URL))%3Fs\.jpg/mg, '$1'));
+ set('sauces', data['sauces'].replace(/^#?\s*https:\/\/www\.google\.com\/searchbyimage\?image_url=%(?:IMG|T?URL)(?=$|;)/mg, '$&&safe=off'));
+ }
+ }
+ if (compareString < '00001.00011.00022.00002') {
+ if ((data['Use Recaptcha v1 in Reports'] == null) && data['Use Recaptcha v1'] && !data['Use Recaptcha v2 in Reports']) {
+ set('Use Recaptcha v1 in Reports', true);
+ }
+ }
+ if (compareString < '00001.00011.00024.00000') {
+ if ((data['JSON Navigation'] != null) && (data['JSON Index'] == null)) {
+ set('JSON Index', data['JSON Navigation']);
+ }
+ }
+ if (compareString < '00001.00011.00026.00000') {
+ if ((data['Oekaki Links'] != null) && (data['Edit Link'] == null)) {
+ set('Edit Link', data['Oekaki Links']);
+ }
+ if (data['Inline Cross-thread Quotes Only'] == null) {
+ set('Inline Cross-thread Quotes Only', true);
+ }
+ }
+ if (compareString < '00001.00011.00030.00000') {
+ if (data['Quote Threading'] && (data['Thread Quotes'] == null)) {
+ set('Thread Quotes', true);
+ }
+ }
+ if (compareString < '00001.00011.00032.00000') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)http:\/\/3d\.iqdb\.org\//mg, '$1//3d.iqdb.org/'));
+ }
+ addSauces(['#https://desustorage.org/_/search/image/%sMD5/', '#https://boards.fireden.net/_/search/image/%sMD5/', '#https://foolz.fireden.net/_/search/image/%sMD5/', '#//www.gif-explode.com/%URL;types:gif']);
+ }
+ if (compareString < '00001.00011.00035.00000') {
+ addSauces(['https://whatanime.ga/?auto&url=%IMG;text:wait']);
+ }
+ if (compareString < '00001.00012.00000.00000') {
+ if (data['Exempt Archives from Encryption'] == null) {
+ set('Exempt Archives from Encryption', false);
+ }
+ if (data['Show New Thread Option in Threads'] == null) {
+ set('Show New Thread Option in Threads', false);
+ }
+ if (data['Show Name and Subject']) {
+ addCSS('#qr .persona .field {display: block !important;}');
+ }
+ if (data['QR Shortcut'] === false) {
+ addCSS('#shortcut-qr {display: none;}');
+ }
+ if (data['Bottom QR Link'] === false) {
+ addCSS('.qr-link-container-bottom {display: none;}');
+ }
+ }
+ if (compareString < '00001.00012.00000.00006') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)https:\/\/(?:desustorage|cuckchan)\.org\//mg, '$1https://desuarchive.org/'));
+ }
+ }
+ if (compareString < '00001.00012.00001.00000') {
+ if ((data['Persistent Thread Watcher'] == null) && (data['Toggleable Thread Watcher'] != null)) {
+ set('Persistent Thread Watcher', !data['Toggleable Thread Watcher']);
+ }
+ }
+ if (compareString < '00001.00012.00003.00000') {
+ ref6 = ['Image Hover in Catalog', 'Auto Watch', 'Auto Watch Reply'];
+ for (k = 0, len1 = ref6.length; k < len1; k++) {
+ key = ref6[k];
+ setD(key, false);
+ }
+ }
+ if (compareString < '00001.00013.00001.00002') {
+ addSauces(['#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights']);
+ }
+ if (compareString < '00001.00013.00005.00000') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)http:\/\/regex\.info\/exif\.cgi/mg, '$1http://exif.regex.info/exif.cgi'));
+ }
+ addSauces(Config['sauces'].match(/# Known filename formats:(?:\n.+)*|$/)[0].split('\n'));
+ }
+ if (compareString < '00001.00013.00007.00002') {
+ setD('Require OP Quote Link', true);
+ }
+ if (compareString < '00001.00013.00008.00000') {
+ setD('Download Link', true);
+ }
+ if (compareString < '00001.00013.00009.00003') {
+ if (data['jsWhitelist'] != null) {
+ list = data['jsWhitelist'].split('\n');
+ if (indexOf.call(list, 'https://cdnjs.cloudflare.com') < 0 && indexOf.call(list, 'https://cdn.mathjax.org') >= 0) {
+ set('jsWhitelist', data['jsWhitelist'] + '\n\nhttps://cdnjs.cloudflare.com');
+ }
+ }
+ }
+ if (compareString < '00001.00014.00000.00006') {
+ if (data['siteSoftware'] != null) {
+ set('siteSoftware', data['siteSoftware'] + '\n4cdn.org yotsuba');
+ }
+ }
+ if (compareString < '00001.00014.00003.00002') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)https:\/\/whatanime\.ga\//mg, '$1https://trace.moe/'));
+ }
+ }
+ if (compareString < '00001.00014.00004.00004') {
+ if ((data['siteSoftware'] != null) && !/^4channel\.org yotsuba$/m.test(data['siteSoftware'])) {
+ set('siteSoftware', data['siteSoftware'] + '\n4channel.org yotsuba');
+ }
+ }
+ if (compareString < '00001.00014.00005.00000') {
+ ref7 = DataBoard.keys;
+ for (l = 0, len2 = ref7.length; l < len2; l++) {
+ db = ref7[l];
+ if ((ref8 = data[db]) != null ? ref8.boards : void 0) {
+ ref9 = data[db], boards = ref9.boards, lastChecked = ref9.lastChecked;
+ data[db]['4chan.org'] = {
+ boards: boards,
+ lastChecked: lastChecked
+ };
+ delete data[db].boards;
+ delete data[db].lastChecked;
+ set(db, data[db]);
+ }
+ }
+ if ((data['siteSoftware'] != null) && (data['siteProperties'] == null)) {
+ siteProperties = $.dict();
+ ref10 = data['siteSoftware'].split('\n');
+ for (m = 0, len3 = ref10.length; m < len3; m++) {
+ line = ref10[m];
+ ref11 = line.split(' '), hostname = ref11[0], software = ref11[1];
+ siteProperties[hostname] = {
+ software: software
+ };
+ }
+ set('siteProperties', siteProperties);
+ }
+ }
+ if (compareString < '00001.00014.00006.00006') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/\/\/%\$1\.deviantart\.com\/gallery\/#\/d%\$2;regexp:\/\^\\w\+_by_\(\\w\+\)-d\(\[\\da-z\]\+\)\//g, '//www.deviantart.com/gallery/#/d%$1%$2;regexp:/^\\w+_by_\\w+[_-]d([\\da-z]{6})\\b|^d([\\da-z]{6})-[\\da-z]{8}-/'));
+ }
+ }
+ if (compareString < '00001.00014.00008.00000') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/https:\/\/www\.yandex\.com\/images\/search/g, 'https://yandex.com/images/search'));
+ }
+ }
+ if (compareString < '00001.00014.00009.00000') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)(?:http:)?\/\/(www\.pixiv\.net|www\.deviantart\.com|imgur\.com|flickr\.com)\//mg, '$1https://$2/'));
+ set('sauces', data['sauces'].replace(/https:\/\/yandex\.com\/images\/search\?rpt=imageview&img_url=%IMG/g, 'https://yandex.com/images/search?rpt=imageview&url=%IMG'));
+ }
+ }
+ if (compareString < '00001.00014.00009.00001') {
+ if ((data['Use Faster Image Host'] != null) && (data['fourchanImageHost'] == null)) {
+ set('fourchanImageHost', (data['Use Faster Image Host'] ? 'i.4cdn.org' : ''));
+ }
+ }
+ if (compareString < '00001.00014.00010.00001') {
+ if (data['Filter in Native Catalog'] == null) {
+ set('Filter in Native Catalog', false);
+ }
+ }
+ if (compareString < '00001.00014.00012.00008') {
+ if (data['boardnav'] == null) {
+ set('boardnav', "[ toggle-all ]\na-replace\nc-replace\ng-replace\nk-replace\nv-replace\nvg-replace\nvr-replace\nck-replace\nco-replace\nfit-replace\njp-replace\nmu-replace\nsp-replace\ntv-replace\nvp-replace\n[external-text:\"FAQ\",\"https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions\"]");
+ }
+ }
+ if (compareString < '00001.00014.00016.00001') {
+ if (data['archiveLists'] != null) {
+ set('archiveLists', data['archiveLists'].replace('https://mayhemydg.github.io/archives.json/archives.json', 'https://nstepien.github.io/archives.json/archives.json'));
+ }
+ }
+ if (compareString < '00001.00014.00016.00007') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/https:\/\/www\.deviantart\.com\/gallery\/#\/d%\$1%\$2;regexp:\/\^\\w\+_by_\\w\+\[_-\]d\(\[\\da-z\]\{6\}\)\\b\|\^d\(\[\\da-z\]\{6\}\)-\[\\da-z\]\{8\}-\//g, 'javascript:void(open("https://www.deviantart.com/"+%$1.replace(/_/g,"-")+"/art/"+parseInt(%$2,36)));regexp:/^\\w+_by_(\\w+)[_-]d([\\da-z]{6})\\b/').replace(/\/\/imgops\.com\/%URL/g, '//imgops.com/start?url=%URL'));
+ }
+ }
+ if (compareString < '00001.00014.00017.00002') {
+ if (data['jsWhitelist'] != null) {
+ set('jsWhitelist', data['jsWhitelist'] + '\n\nhttps://hcaptcha.com\nhttps://*.hcaptcha.com');
+ }
+ }
+ if (compareString < '00001.00014.00020.00004') {
+ if (data['archiveLists'] != null) {
+ set('archiveLists', data['archiveLists'].replace('https://nstepien.github.io/archives.json/archives.json', 'https://4chenz.github.io/archives.json/archives.json'));
+ }
+ }
+ return changes;
+ },
+ loadSettings: function(data, cb) {
+ if (data.version.split('.')[0] === '2') {
+ data = Settings.convertFrom.loadletter(data);
+ } else if (data.version !== g.VERSION) {
+ Settings.upgrade(data.Conf, data.version);
+ }
+ return $.clear(function(err) {
+ if (err) {
+ return cb(err);
+ }
+ return $.set(data.Conf, cb);
+ });
+ },
+ reset: function() {
+ if (confirm('Your current settings will be entirely wiped, are you sure?')) {
+ return $.clear(function(err) {
+ if (err) {
+ return $('.imp-exp-result').textContent = 'Import failed due to an error.';
+ } else if (confirm('Reset successful. Reload now?')) {
+ return window.location.reload();
+ }
+ });
+ }
+ },
+ filter: function(section) {
+ var select;
+ $.extend(section, {innerHTML: "<select name=\"filter\"><option value=\"guide\">Guide</option><option value=\"general\">General</option><option value=\"postID\">Post number</option><option value=\"name\">Name</option><option value=\"uniqueID\">Unique ID</option><option value=\"tripcode\">Tripcode</option><option value=\"capcode\">Capcode</option><option value=\"pass\">Pass Date</option><option value=\"email\">Email</option><option value=\"subject\">Subject</option><option value=\"comment\">Comment</option><option value=\"flag\">Flag</option><option value=\"filename\">Filename</option><option value=\"dimensions\">Image dimensions</option><option value=\"filesize\">Filesize</option><option value=\"MD5\">Image MD5</option></select><div></div>"});
+ select = $('select', section);
+ $.on(select, 'change', Settings.selectFilter);
+ return Settings.selectFilter.call(select);
+ },
+ selectFilter: function() {
+ var div, filterTypes, name, ta;
+ div = this.nextElementSibling;
+ if ((name = this.value) !== 'guide') {
+ if (!$.hasOwn(Config.filter, name)) {
+ return;
+ }
+ $.rmAll(div);
+ ta = $.el('textarea', {
+ name: name,
+ className: 'field',
+ spellcheck: false
+ });
+ $.on(ta, 'change', $.cb.value);
+ $.get(name, Conf[name], function(item) {
+ ta.value = item[name];
+ return $.add(div, ta);
+ });
+ return;
+ }
+ filterTypes = Object.keys(Config.filter).filter(function(x) {
+ return x !== 'general';
+ }).map(function(x, i) {
+ return {innerHTML: ((i) ? "," : "") + "<wbr>" + E(x)};
+ });
+ $.extend(div, {innerHTML: "<div class=\"warning\"><code>Filter</code> is disabled.</div><p>Use <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions\" target=\"_blank\">regular expressions</a>, one per line.<br>Lines starting with a <code>#</code> will be ignored.<br>For example, <code>/weeaboo/i</code> will filter posts containing the string `<code>weeaboo</code>`, case-insensitive.<br>MD5 and Unique ID filtering use exact string matching, not regular expressions.</p><ul>You can use these settings with each regular expression, separate them with semicolons:<li>Per boards, separate them with commas. It is global if not specified. Use <code>sfw</code> and <code>nsfw</code> to reference all worksafe or not-worksafe boards.<br>For example: <code>boards:a,jp;</code>.<br>To specify boards on a particular site, put the beginning of the domain and a slash character before the list.<br>Any initial <code>www.</code> should not be included, and all 4chan domains are considered <code>4chan.org</code>.<br>For example: <code>boards:4:a,jp,sama:a,z;</code>.<br>An asterisk can be used to specify all boards on a site.<br>For example: <code>boards:4:*;</code>.<br></li><li>Select boards to be excluded from the filter. The syntax is the same as for the <code>boards:</code> option above.<br>For example: <code>exclude:vg,v;</code>.</li><li>Filter OPs only along with their threads (`only`) or replies only (`no`).<br>For example: <code>op:only;</code> or <code>op:no;</code>.</li><li>Filter only posts with files (`only`) or only posts without files (`no`).<br>For example: <code>file:only;</code> or <code>file:no;</code>.</li><li>Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).<br>For example: <code>stub:yes;</code> or <code>stub:no;</code>.</li><li>Highlight instead of hiding. You can specify a class name to use with a userstyle.<br>For example: <code>highlight;</code> or <code>highlight:wallpaper;</code>.</li><li>Highlighted OPs will have their threads put on top of the board index by default.<br>For example: <code>top:yes;</code> or <code>top:no;</code>.</li><li>Show a desktop notification instead of hiding.<br>For example: <code>notify;</code>.</li><li>Filters in the \"General\" section apply to multiple fields, by default <code>subject,name,filename,comment</code>.<br>The fields can be specified with the <code>type</code> option, separated by commas.<br>For example: <code>type:" + E.cat(filterTypes) + ";</code>.<br>Types can also be combined with a <code>+</code> sign; this indicates the filter applies to the given fields joined by newlines.<br>For example: <code>type:filename+filesize+dimensions;</code>.<br></li></ul>"});
+ return $('.warning', div).hidden = Conf['Filter'];
+ },
+ sauce: function(section) {
+ var ta;
+ $.extend(section, {innerHTML: "<div class=\"warning\"><code>Sauce</code> is disabled.</div><input id=\"sauce-doc-expand\" type=\"checkbox\" hidden><div id=\"sauce-doc\"><label for=\"sauce-doc-expand\">[expand]</label><div>These parameters will be replaced by their corresponding values in the URL and displayed text:</div><ul><li><code>%IMG</code>: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.</li><li><code>%URL</code>: Full image URL.</li><li><code>%TURL</code>: Thumbnail URL.</li><li><code>%name</code>: Original file name.</li><li><code>%board</code>: Current board.</li><li><code>%MD5</code>: MD5 hash in base64.</li><li><code>%sMD5</code>: MD5 hash in base64 using <code>-</code> and <code>_</code>.</li><li><code>%hMD5</code>: MD5 hash in hexadecimal.</li><li><code>%$0</code>: Matched regular expression within the filename.</li><li><code>%$1</code>, <code>%$2</code>, <code>%$3</code>, ... : Subexpressions within the matched regular expression.</li><li><code>%%</code>, <code>%semi</code>: Literal <code>%</code> and <code>;</code>.</li></ul><div>Lines starting with a <code>#</code> will be ignored.</div><div>You can specify a display text by appending <code>;text:[text]</code> to the URL.</div><div>You can specify the applicable boards/sites by appending <code>;boards:[board1],[board2]</code>. See the Filter guide for details.</div><div>You can specify the applicable file types by appending <code>;types:[extension1],[extension2]</code>.</div><div>You can specify a regular expression the filename must match by appending <code>;regexp:[regular expression]</code>.</div></div><textarea hidden name=\"sauces\" class=\"field\" spellcheck=\"false\"></textarea>"});
+ $('.warning', section).hidden = Conf['Sauce'];
+ ta = $('textarea', section);
+ $.get('sauces', Conf['sauces'], function(item) {
+ ta.value = item['sauces'];
+ return ta.hidden = false;
+ });
+ return $.on(ta, 'change', $.cb.value);
+ },
+ advanced: function(section) {
+ var applyCSS, boardSelect, customCSS, event, input, inputs, interval, items, itemsArchive, j, k, l, len, len1, len2, len3, listImageHost, m, name, ref, ref1, ref2, ref3, ref4, table, textContent, updateArchives, warning;
+ $.extend(section, {innerHTML: "<fieldset><legend>Archives</legend><div class=\"warning\" data-feature=\"404 Redirect\"><code>404 Redirect</code> is disabled.</div><select id=\"archive-board-select\"></select><table id=\"archive-table\"><thead><th>Thread redirection</th><th>Post fetching</th><th>File redirection</th></thead><tbody></tbody></table><br><div><b>Archive Lists</b>: Each line below should be an archive list in <a href=\"https://github.com/MayhemYDG/archives.json/blob/gh-pages/CONTRIBUTING.md\" target=\"_blank\">this format</a> or a URL to load an archive list from.<br>Archive properties can be overriden by another item with the same <code>uid</code> (or if absent, its <code>name</code>).</div><textarea hidden name=\"archiveLists\" class=\"field\" spellcheck=\"false\"></textarea><button id=\"update-archives\">Update now</button> Last updated: <time id=\"lastarchivecheck\"></time> <label><input type=\"checkbox\" name=\"archiveAutoUpdate\"> Auto-update</label></fieldset><fieldset><legend>External Catalog</legend><div class=\"warning\" data-feature=\"External Catalog\"><code>External Catalog</code> is disabled. This will be used only as a fallback.</div><div>URLs of external catalog sites, where <code>%board</code> is to be replaced by the board name.<br>Each URL should be followed by <code>;boards:</code> and optionally <code>;exclude:</code> and a list of supported/excluded boards in the format explained in the Filter guide.</div><textarea hidden name=\"externalCatalogURLs\" class=\"field\" spellcheck=\"false\"></textarea></fieldset><fieldset><legend>Override 4chan Image Host</legend><div>Change 4chan image links to this domain. Leave blank for no change.</div><div><input name=\"fourchanImageHost\" class=\"field\" spellcheck=\"false\" list=\"list-fourchanImageHost\"></div><datalist id=\"list-fourchanImageHost\"></datalist></fieldset><fieldset><legend>Captcha Language</legend><div>Choose from <a href=\"https://developers.google.com/recaptcha/docs/language\" target=\"_blank\">list of language codes</a>. Leave blank to autoselect.</div><div><input name=\"captchaLanguage\" class=\"field\" spellcheck=\"false\"></div></fieldset><fieldset><legend>Custom Board Navigation</legend><div><textarea hidden name=\"boardnav\" class=\"field\" spellcheck=\"false\"></textarea></div><span class=\"note\">New lines will be converted into spaces.</span><br><br><div class=\"note\">In the following examples for /g/, <code>g</code> can be changed to a different board ID (<code>a</code>, <code>b</code>, etc...), the current board (<code>current</code>), or the Twitter link (<code>@</code>).</div><div>Board link: <code>g</code></div><div>Archive link: <code>g-archive</code></div><div>Internal archive link: <code>g-expired</code></div><div>Title link: <code>g-title</code></div><div>Board link (Replace with title when on that board): <code>g-replace</code></div><div>Full text link: <code>g-full</code></div><div>Custom text link: <code>g-text:&quot;Install Gentoo&quot;</code></div><div>Index-only link: <code>g-index</code></div><div>Catalog-only link: <code>g-catalog</code></div><div>Index mode: <code>g-mode:&quot;infinite scrolling&quot;</code></div><div>Index sort: <code>g-sort:&quot;creation date rev&quot;</code></div><div>External link: <code>external-text:&quot;Google&quot;,&quot;http://www.google.com&quot;</code></div><div>Open in new tab: <code>g-nt</code></div><div>Combinations are possible: <code>g-index-text:&quot;Technology Index&quot;</code></div><div>Full board list toggle: <code>toggle-all</code></div><br><div class=\"note\"><code>[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:&quot;Piracy&quot;]</code><br>will give you<br><code>[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]</code><br>if you are on /g/.</div></fieldset><fieldset><legend>Time Formatting <span class=\"warning\" data-feature=\"Time Formatting\">is disabled.</span></legend><div><input name=\"time\" class=\"field\" spellcheck=\"false\">: <span class=\"time-preview\"></span></div><div>Supported <a href=\"http://man7.org/linux/man-pages/man1/date.1.html\" target=\"_blank\">format specifiers</a>:</div><div>Day: <code>%a</code>, <code>%A</code>, <code>%d</code>, <code>%e</code></div><div>Month: <code>%m</code>, <code>%b</code>, <code>%B</code></div><div>Year: <code>%y</code>, <code>%Y</code></div><div>Hour: <code>%k</code>, <code>%H</code>, <code>%l</code>, <code>%I</code>, <code>%p</code>, <code>%P</code></div><div>Minute: <code>%M</code></div><div>Second: <code>%S</code></div><div>Literal <code>%</code>: <code>%%</code></div><div><a href=\"https://www.w3.org/International/articles/language-tags/\" target=\"_blank\">Language tag</a>: <input name=\"timeLocale\" class=\"field\" spellcheck=\"false\"></div></fieldset><fieldset><legend>Quote Backlinks formatting <span class=\"warning\" data-feature=\"Quote Backlinks\">is disabled.</span></legend><div><input name=\"backlink\" class=\"field\" spellcheck=\"false\">: <span class=\"backlink-preview\"></span></div></fieldset><fieldset><legend>Default pasted content filename</legend><div><input name=\"pastedname\" class=\"field\" spellcheck=\"false\">.png</div></fieldset><fieldset><legend>File Info Formatting <span class=\"warning\" data-feature=\"File Info Formatting\">is disabled.</span></legend><div><input name=\"fileInfo\" class=\"field\" spellcheck=\"false\">: <span class=\"file-info file-info-preview\"></span></div><div>Link: <code>%l</code> (truncated), <code>%L</code> (untruncated), <code>%T</code> (4chan filename)</div><div>Filename: <code>%n</code> (truncated), <code>%N</code> (untruncated), <code>%t</code> (4chan filename)</div><div>Download button: <code>%d</code></div><div>Quick filter MD5: <code>%f</code></div><div>Spoiler indicator: <code>%p</code></div><div>Size: <code>%B</code> (Bytes), <code>%K</code> (KB), <code>%M</code> (MB), <code>%s</code> (4chan default)</div><div>Resolution: <code>%r</code> (Displays &#039;PDF&#039; for PDF files)</div><div>Tag: <code>%g</code><div>Literal <code>%</code>: <code>%%</code></div></fieldset><fieldset><legend>Quick Reply Personas</legend><textarea hidden class=\"personafield field\" name=\"QR.personas\" spellcheck=\"false\"></textarea><p>One item per line.<br>Items will be added in the relevant input&#039;s auto-completion list.<br>Password items will always be used, since there is no password input.<br>Lines starting with a <code>#</code> will be ignored.</p><ul>You can use these settings with each item, separate them with semicolons:<li>Possible items are: <code>name</code>, <code>options</code> (or equivalently <code>email</code>), <code>subject</code> and <code>password</code>.</li><li>Wrap values of items with quotes, like this: <code>options:&quot;sage&quot;</code>.</li><li>Force values as defaults with the <code>always</code> keyword, for example: <code>options:&quot;sage&quot;;always</code>.</li><li>Select specific boards for an item, separated with commas, for example: <code>options:&quot;sage&quot;;boards:jp;always</code>.</li></ul></fieldset><fieldset><legend>Unread Favicon <span class=\"warning\" data-feature=\"Unread Favicon\">is disabled.</span></legend><select name=\"favicon\"><option value=\"ferongr\">ferongr</option><option value=\"xat-\">xat-</option><option value=\"4chanJS\">4chanJS</option><option value=\"Mayhem\">Mayhem</option><option value=\"Original\">Original</option><option value=\"Metro\">Metro</option></select><span class=\"favicon-preview\"></span></fieldset><fieldset><legend>Thread Updater <span class=\"warning\" data-feature=\"Thread Updater\">is disabled.</span></legend><div>Interval: <input type=\"number\" name=\"Interval\" class=\"field\" min=\"1\"> seconds</div></fieldset><fieldset><legend>Custom Cooldown Time</legend><div>Seconds: <input type=\"number\" name=\"customCooldown\" class=\"field\" min=\"0\"></div></fieldset><fieldset><legend><label><input type=\"checkbox\" name=\"Custom CSS\"> Custom CSS</label></legend><div>For more information about customizing 4chan X&#039;s CSS, see the <a href=\"https://github.com/ccd0/4chan-x/wiki/Styling-Guide\" target=\"_blank\">styling guide</a>.</div><button id=\"apply-css\">Apply CSS</button><textarea hidden name=\"usercss\" class=\"field\" spellcheck=\"false\"></textarea></fieldset><fieldset><legend>Javascript Whitelist</legend><div>Sources from which Javascript is allowed to be loaded by <a href=\"http://content-security-policy.com/#source_list\" target=\"_blank\">Content Security Policy</a>.<br>Lines starting with a <code>#</code> will be ignored.</div><textarea hidden name=\"jsWhitelist\" class=\"field\" spellcheck=\"false\"></textarea></fieldset><fieldset><legend>Known Banners</legend><div>List of known banners, used for click-to-change feature.</div><textarea hidden name=\"knownBanners\" class=\"field\" spellcheck=\"false\"></textarea></fieldset>"});
+ ref = $$('.warning', section);
+ for (j = 0, len = ref.length; j < len; j++) {
+ warning = ref[j];
+ warning.hidden = Conf[warning.dataset.feature];
+ }
+ inputs = $.dict();
+ ref1 = $$('[name]', section);
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ input = ref1[k];
+ inputs[input.name] = input;
+ }
+ $.on(inputs['archiveLists'], 'change', function() {
+ $.set('lastarchivecheck', 0);
+ Conf['lastarchivecheck'] = 0;
+ return $.id('lastarchivecheck').textContent = 'never';
+ });
+ items = $.dict();
+ for (name in inputs) {
+ input = inputs[name];
+ if (!(name !== 'Interval' && name !== 'Custom CSS')) {
+ continue;
+ }
+ items[name] = Conf[name];
+ event = (input.nodeName === 'SELECT' || ((ref2 = input.type) === 'checkbox' || ref2 === 'radio') || (input.nodeName === 'TEXTAREA' && !(name in Settings))) ? 'change' : 'input';
+ $.on(input, event, $.cb[input.type === 'checkbox' ? 'checked' : 'value']);
+ if (name in Settings) {
+ $.on(input, event, Settings[name]);
+ }
+ }
+ $.get(items, function(items) {
+ var key, val;
+ for (key in items) {
+ val = items[key];
+ input = inputs[key];
+ input[input.type === 'checkbox' ? 'checked' : 'value'] = val;
+ input.hidden = false;
+ if (key in Settings) {
+ Settings[key].call(input);
+ }
+ }
+ });
+ listImageHost = $.id('list-fourchanImageHost');
+ ref3 = ImageHost.suggestions;
+ for (l = 0, len2 = ref3.length; l < len2; l++) {
+ textContent = ref3[l];
+ $.add(listImageHost, $.el('option', {
+ textContent: textContent
+ }));
+ }
+ interval = inputs['Interval'];
+ customCSS = inputs['Custom CSS'];
+ applyCSS = $('#apply-css', section);
+ interval.value = Conf['Interval'];
+ customCSS.checked = Conf['Custom CSS'];
+ inputs['usercss'].disabled = !Conf['Custom CSS'];
+ applyCSS.disabled = !Conf['Custom CSS'];
+ $.on(interval, 'change', ThreadUpdater.cb.interval);
+ $.on(customCSS, 'change', Settings.togglecss);
+ $.on(applyCSS, 'click', function() {
+ return CustomCSS.update();
+ });
+ itemsArchive = $.dict();
+ ref4 = ['archives', 'selectedArchives', 'lastarchivecheck'];
+ for (m = 0, len3 = ref4.length; m < len3; m++) {
+ name = ref4[m];
+ itemsArchive[name] = Conf[name];
+ }
+ $.get(itemsArchive, function(itemsArchive) {
+ $.extend(Conf, itemsArchive);
+ Redirect.selectArchives();
+ return Settings.addArchiveTable(section);
+ });
+ boardSelect = $('#archive-board-select', section);
+ table = $('#archive-table', section);
+ updateArchives = $('#update-archives', section);
+ $.on(boardSelect, 'change', function() {
+ $('tbody > :not([hidden])', table).hidden = true;
+ return $("tbody > ." + this.value, table).hidden = false;
+ });
+ return $.on(updateArchives, 'click', function() {
+ return Redirect.update(function() {
+ return Settings.addArchiveTable(section);
+ });
+ });
+ },
+ addArchiveTable: function(section) {
+ var archBoards, archive, boardID, boardOptions, boardSelect, boards, data, files, id, item, j, k, l, len, len1, len2, len3, m, name, o, ref, ref1, ref2, ref3, ref4, row, rows, select, software, table, tbody, type, uid;
+ $('#lastarchivecheck', section).textContent = Conf['lastarchivecheck'] === 0 ? 'never' : new Date(Conf['lastarchivecheck']).toLocaleString();
+ boardSelect = $('#archive-board-select', section);
+ table = $('#archive-table', section);
+ tbody = $('tbody', section);
+ $.rmAll(boardSelect);
+ $.rmAll(tbody);
+ archBoards = $.dict();
+ ref = Conf['archives'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ ref1 = ref[j], uid = ref1.uid, name = ref1.name, boards = ref1.boards, files = ref1.files, software = ref1.software;
+ if (software !== 'fuuka' && software !== 'foolfuuka') {
+ continue;
+ }
+ for (k = 0, len1 = boards.length; k < len1; k++) {
+ boardID = boards[k];
+ o = archBoards[boardID] || (archBoards[boardID] = {
+ thread: [],
+ post: [],
+ file: []
+ });
+ archive = [uid != null ? uid : name, name];
+ o.thread.push(archive);
+ if (software === 'foolfuuka') {
+ o.post.push(archive);
+ }
+ if (indexOf.call(files, boardID) >= 0) {
+ o.file.push(archive);
+ }
+ }
+ }
+ rows = [];
+ boardOptions = [];
+ ref2 = Object.keys(archBoards).sort();
+ for (l = 0, len2 = ref2.length; l < len2; l++) {
+ boardID = ref2[l];
+ row = $.el('tr', {
+ className: "board-" + boardID
+ });
+ row.hidden = boardID !== g.BOARD.ID;
+ boardOptions.push($.el('option', {
+ textContent: "/" + boardID + "/",
+ value: "board-" + boardID,
+ selected: boardID === g.BOARD.ID
+ }));
+ o = archBoards[boardID];
+ ref3 = ['thread', 'post', 'file'];
+ for (m = 0, len3 = ref3.length; m < len3; m++) {
+ item = ref3[m];
+ $.add(row, Settings.addArchiveCell(boardID, o, item));
+ }
+ rows.push(row);
+ }
+ if (rows.length === 0) {
+ boardSelect.hidden = table.hidden = true;
+ return;
+ }
+ boardSelect.hidden = table.hidden = false;
+ if (!(g.BOARD.ID in archBoards)) {
+ rows[0].hidden = false;
+ }
+ $.add(boardSelect, boardOptions);
+ $.add(tbody, rows);
+ ref4 = Conf['selectedArchives'];
+ for (boardID in ref4) {
+ data = ref4[boardID];
+ for (type in data) {
+ id = data[type];
+ if ((select = $("select[data-boardid='" + boardID + "'][data-type='" + type + "']", tbody))) {
+ select.value = JSON.stringify(id);
+ if (!select.value) {
+ select.value = select.firstChild.value;
+ }
+ }
+ }
+ }
+ },
+ addArchiveCell: function(boardID, data, type) {
+ var archive, i, length, options, select, td;
+ length = data[type].length;
+ td = $.el('td', {
+ className: 'archive-cell'
+ });
+ if (!length) {
+ td.textContent = '--';
+ return td;
+ }
+ options = [];
+ i = 0;
+ while (i < length) {
+ archive = data[type][i++];
+ options.push($.el('option', {
+ value: JSON.stringify(archive[0]),
+ textContent: archive[1]
+ }));
+ }
+ $.extend(td, {innerHTML: "<select></select>"});
+ select = td.firstElementChild;
+ if (!(select.disabled = length === 1)) {
+ select.setAttribute('data-boardid', boardID);
+ select.setAttribute('data-type', type);
+ $.on(select, 'change', Settings.saveSelectedArchive);
+ }
+ $.add(select, options);
+ return td;
+ },
+ saveSelectedArchive: function() {
+ return $.get('selectedArchives', Conf['selectedArchives'], (function(_this) {
+ return function(arg) {
+ var name1, selectedArchives;
+ selectedArchives = arg.selectedArchives;
+ (selectedArchives[name1 = _this.dataset.boardid] || (selectedArchives[name1] = $.dict()))[_this.dataset.type] = JSON.parse(_this.value);
+ $.set('selectedArchives', selectedArchives);
+ Conf['selectedArchives'] = selectedArchives;
+ return Redirect.selectArchives();
+ };
+ })(this));
+ },
+ boardnav: function() {
+ return Header.generateBoardList(this.value);
+ },
+ time: function() {
+ return this.nextElementSibling.textContent = Time.format(this.value, new Date());
+ },
+ timeLocale: function() {
+ return Settings.time.call($('[name=time]', Settings.dialog));
+ },
+ backlink: function() {
+ return this.nextElementSibling.textContent = this.value.replace(/%(?:id|%)/g, function(x) {
+ return {
+ '%id': '123456789',
+ '%%': '%'
+ }[x];
+ });
+ },
+ fileInfo: function() {
+ var data;
+ data = {
+ isReply: true,
+ file: {
+ url: "//" + (ImageHost.host()) + "/g/1334437723720.jpg",
+ name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg',
+ size: '276 KB',
+ sizeInBytes: 276 * 1024,
+ dimensions: '1280x720',
+ isImage: true,
+ isVideo: false,
+ isSpoiler: true,
+ tag: 'Loop'
+ }
+ };
+ return FileInfo.format(this.value, data, this.nextElementSibling);
+ },
+ favicon: function() {
+ var f, i, icon, img, j, len, ref;
+ Favicon["switch"]();
+ if (g.VIEW === 'thread' && Conf['Unread Favicon']) {
+ Unread.update();
+ }
+ img = this.nextElementSibling.children;
+ f = Favicon;
+ ref = [f.SFW, f.unreadSFW, f.unreadSFWY, f.NSFW, f.unreadNSFW, f.unreadNSFWY, f.dead, f.unreadDead, f.unreadDeadY];
+ for (i = j = 0, len = ref.length; j < len; i = ++j) {
+ icon = ref[i];
+ if (!img[i]) {
+ $.add(this.nextElementSibling, $.el('img'));
+ }
+ img[i].src = icon;
+ }
+ },
+ togglecss: function() {
+ if ($('textarea[name=usercss]', $.x('ancestor::fieldset[1]', this)).disabled = $.id('apply-css').disabled = !this.checked) {
+ CustomCSS.rmStyle();
+ } else {
+ CustomCSS.addStyle();
+ }
+ return $.cb.checked.call(this);
+ },
+ keybinds: function(section) {
+ var arr, input, inputs, items, key, ref, tbody, tr;
+ $.extend(section, {innerHTML: "<div class=\"warning\"><code>Keybinds</code> are disabled.</div><div>Allowed keys: <kbd>a-z</kbd>, <kbd>0-9</kbd>, <kbd>Ctrl</kbd>, <kbd>Shift</kbd>, <kbd>Alt</kbd>, <kbd>Meta</kbd>, <kbd>Enter</kbd>, <kbd>Esc</kbd>, <kbd>Up</kbd>, <kbd>Down</kbd>, <kbd>Right</kbd>, <kbd>Left</kbd>.</div><div>Press <kbd>Backspace</kbd> to disable a keybind.</div><table><tbody><tr><th>Actions</th><th>Keybinds</th></tr></tbody></table>"});
+ $('.warning', section).hidden = Conf['Keybinds'];
+ tbody = $('tbody', section);
+ items = $.dict();
+ inputs = $.dict();
+ ref = Config.hotkeys;
+ for (key in ref) {
+ arr = ref[key];
+ tr = $.el('tr', {innerHTML: "<td>" + E(arr[1]) + "</td><td><input class=\"field\"></td>"});
+ input = $('input', tr);
+ input.name = key;
+ input.spellcheck = false;
+ items[key] = Conf[key];
+ inputs[key] = input;
+ $.on(input, 'keydown', Settings.keybind);
+ $.add(tbody, tr);
+ }
+ return $.get(items, function(items) {
+ var val;
+ for (key in items) {
+ val = items[key];
+ inputs[key].value = val;
+ }
+ });
+ },
+ keybind: function(e) {
+ var key;
+ if (e.keyCode === 9) {
+ return;
+ }
+ e.preventDefault();
+ e.stopPropagation();
+ if ((key = Keybinds.keyCode(e)) == null) {
+ return;
+ }
+ this.value = key;
+ return $.cb.value.call(this);
+ }
+ };
+
+ return Settings;
+
+}).call(this);
+
+Test = (function() {
+ return Test;
+
+}).call(this);
+
+UI = (function() {
+ var Menu, UI, checkbox, dialog, drag, dragend, dragstart, hover, hoverend, hoverstart, touchend, touchmove,
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
+ slice = [].slice;
+
+ dialog = function(id, properties) {
+ var child, el, i, len, move, ref;
+ el = $.el('div', {
+ className: 'dialog',
+ id: id
+ });
+ $.extend(el, properties);
+ el.style.cssText = Conf[id + ".position"];
+ move = $('.move', el);
+ $.on(move, 'touchstart mousedown', dragstart);
+ ref = move.children;
+ for (i = 0, len = ref.length; i < len; i++) {
+ child = ref[i];
+ if (!child.tagName) {
+ continue;
+ }
+ $.on(child, 'touchstart mousedown', function(e) {
+ return e.stopPropagation();
+ });
+ }
+ return el;
+ };
+
+ Menu = (function() {
+ var currentMenu, lastToggledButton;
+
+ currentMenu = null;
+
+ lastToggledButton = null;
+
+ function Menu(type) {
+ this.type = type;
+ this.addEntry = bind(this.addEntry, this);
+ this.onFocus = bind(this.onFocus, this);
+ this.keybinds = bind(this.keybinds, this);
+ this.close = bind(this.close, this);
+ this.setPosition = bind(this.setPosition, this);
+ $.on(d, 'AddMenuEntry', (function(_this) {
+ return function(arg) {
+ var detail;
+ detail = arg.detail;
+ if (detail.type !== _this.type) {
+ return;
+ }
+ delete detail.open;
+ return _this.addEntry(detail);
+ };
+ })(this));
+ this.entries = [];
+ }
+
+ Menu.prototype.makeMenu = function() {
+ var menu;
+ menu = $.el('div', {
+ className: 'dialog',
+ id: 'menu',
+ tabIndex: 0
+ });
+ menu.dataset.type = this.type;
+ $.on(menu, 'click', function(e) {
+ return e.stopPropagation();
+ });
+ $.on(menu, 'keydown', this.keybinds);
+ return menu;
+ };
+
+ Menu.prototype.toggle = function(e, button, data) {
+ var previousButton;
+ e.preventDefault();
+ e.stopPropagation();
+ if (currentMenu) {
+ previousButton = lastToggledButton;
+ currentMenu.close();
+ if (previousButton === button) {
+ return;
+ }
+ }
+ if (!this.entries.length) {
+ return;
+ }
+ return this.open(button, data);
+ };
+
+ Menu.prototype.open = function(button, data) {
+ var entry, i, len, menu, ref;
+ menu = this.menu = this.makeMenu();
+ currentMenu = this;
+ lastToggledButton = button;
+ this.entries.sort(function(first, second) {
+ return first.order - second.order;
+ });
+ ref = this.entries;
+ for (i = 0, len = ref.length; i < len; i++) {
+ entry = ref[i];
+ this.insertEntry(entry, menu, data);
+ }
+ $.addClass(lastToggledButton, 'active');
+ $.on(d, 'click CloseMenu', this.close);
+ $.on(d, 'scroll', this.setPosition);
+ $.on(window, 'resize', this.setPosition);
+ $.after(button, menu);
+ this.setPosition();
+ entry = $('.entry', menu);
+ this.focus(entry);
+ return menu.focus();
+ };
+
+ Menu.prototype.setPosition = function() {
+ var bLeft, bRect, bTop, bottom, cHeight, cWidth, left, mRect, ref, ref1, right, top;
+ mRect = this.menu.getBoundingClientRect();
+ bRect = lastToggledButton.getBoundingClientRect();
+ bTop = window.scrollY + bRect.top;
+ bLeft = window.scrollX + bRect.left;
+ cHeight = doc.clientHeight;
+ cWidth = doc.clientWidth;
+ ref = bRect.top + bRect.height + mRect.height < cHeight ? [bRect.bottom + "px", ''] : ['', (cHeight - bRect.top) + "px"], top = ref[0], bottom = ref[1];
+ ref1 = bRect.left + mRect.width < cWidth ? [bRect.left + "px", ''] : ['', (cWidth - bRect.right) + "px"], left = ref1[0], right = ref1[1];
+ $.extend(this.menu.style, {
+ top: top,
+ right: right,
+ bottom: bottom,
+ left: left
+ });
+ return this.menu.classList.toggle('left', right);
+ };
+
+ Menu.prototype.insertEntry = function(entry, parent, data) {
+ var err, i, len, ref, subEntry, submenu;
+ if (typeof entry.open === 'function') {
+ try {
+ if (!entry.open(data)) {
+ return;
+ }
+ } catch (error) {
+ err = error;
+ Main.handleErrors({
+ message: "Error in building the " + this.type + " menu.",
+ error: err
+ });
+ return;
+ }
+ }
+ $.add(parent, entry.el);
+ if (!entry.subEntries) {
+ return;
+ }
+ if (submenu = $('.submenu', entry.el)) {
+ $.rm(submenu);
+ }
+ submenu = $.el('div', {
+ className: 'dialog submenu'
+ });
+ ref = entry.subEntries;
+ for (i = 0, len = ref.length; i < len; i++) {
+ subEntry = ref[i];
+ this.insertEntry(subEntry, submenu, data);
+ }
+ $.add(entry.el, submenu);
+ };
+
+ Menu.prototype.close = function() {
+ $.rm(this.menu);
+ delete this.menu;
+ $.rmClass(lastToggledButton, 'active');
+ currentMenu = null;
+ lastToggledButton = null;
+ $.off(d, 'click scroll CloseMenu', this.close);
+ $.off(d, 'scroll', this.setPosition);
+ return $.off(window, 'resize', this.setPosition);
+ };
+
+ Menu.prototype.findNextEntry = function(entry, direction) {
+ var entries;
+ entries = slice.call(entry.parentNode.children);
+ entries.sort(function(first, second) {
+ return first.style.order - second.style.order;
+ });
+ return entries[entries.indexOf(entry) + direction];
+ };
+
+ Menu.prototype.keybinds = function(e) {
+ var entry, next, nextPrev, subEntry, submenu;
+ entry = $('.focused', this.menu);
+ while (subEntry = $('.focused', entry)) {
+ entry = subEntry;
+ }
+ switch (e.keyCode) {
+ case 27:
+ lastToggledButton.focus();
+ this.close();
+ break;
+ case 13:
+ case 32:
+ entry.click();
+ break;
+ case 38:
+ if (next = this.findNextEntry(entry, -1)) {
+ this.focus(next);
+ }
+ break;
+ case 40:
+ if (next = this.findNextEntry(entry, +1)) {
+ this.focus(next);
+ }
+ break;
+ case 39:
+ if ((submenu = $('.submenu', entry)) && (next = submenu.firstElementChild)) {
+ while (nextPrev = this.findNextEntry(next, -1)) {
+ next = nextPrev;
+ }
+ this.focus(next);
+ }
+ break;
+ case 37:
+ if (next = $.x('parent::*[contains(@class,"submenu")]/parent::*', entry)) {
+ this.focus(next);
+ }
+ break;
+ default:
+ return;
+ }
+ e.preventDefault();
+ return e.stopPropagation();
+ };
+
+ Menu.prototype.onFocus = function(e) {
+ e.stopPropagation();
+ return this.focus(e.target);
+ };
+
+ Menu.prototype.focus = function(entry) {
+ var bottom, cHeight, cWidth, eRect, focused, i, left, len, ref, ref1, ref2, right, sRect, style, submenu, top;
+ while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) {
+ $.rmClass(focused, 'focused');
+ }
+ ref = $$('.focused', entry);
+ for (i = 0, len = ref.length; i < len; i++) {
+ focused = ref[i];
+ $.rmClass(focused, 'focused');
+ }
+ $.addClass(entry, 'focused');
+ if (!(submenu = $('.submenu', entry))) {
+ return;
+ }
+ sRect = submenu.getBoundingClientRect();
+ eRect = entry.getBoundingClientRect();
+ cHeight = doc.clientHeight;
+ cWidth = doc.clientWidth;
+ ref1 = eRect.top + sRect.height < cHeight ? ['0px', 'auto'] : ['auto', '0px'], top = ref1[0], bottom = ref1[1];
+ ref2 = eRect.right + sRect.width < cWidth - 150 ? ['100%', 'auto'] : ['auto', '100%'], left = ref2[0], right = ref2[1];
+ style = submenu.style;
+ style.top = top;
+ style.bottom = bottom;
+ style.left = left;
+ return style.right = right;
+ };
+
+ Menu.prototype.addEntry = function(entry) {
+ this.parseEntry(entry);
+ return this.entries.push(entry);
+ };
+
+ Menu.prototype.parseEntry = function(entry) {
+ var el, i, len, subEntries, subEntry;
+ el = entry.el, subEntries = entry.subEntries;
+ $.addClass(el, 'entry');
+ $.on(el, 'focus mouseover', this.onFocus);
+ el.style.order = entry.order || 100;
+ if (!subEntries) {
+ return;
+ }
+ $.addClass(el, 'has-submenu');
+ for (i = 0, len = subEntries.length; i < len; i++) {
+ subEntry = subEntries[i];
+ this.parseEntry(subEntry);
+ }
+ };
+
+ return Menu;
+
+ })();
+
+ dragstart = function(e) {
+ var el, isTouching, o, rect, ref, screenHeight, screenWidth;
+ if (e.type === 'mousedown' && e.button !== 0) {
+ return;
+ }
+ e.preventDefault();
+ if (isTouching = e.type === 'touchstart') {
+ e = e.changedTouches[e.changedTouches.length - 1];
+ }
+ el = $.x('ancestor::div[contains(@class,"dialog")][1]', this);
+ rect = el.getBoundingClientRect();
+ screenHeight = doc.clientHeight;
+ screenWidth = doc.clientWidth;
+ o = {
+ id: el.id,
+ style: el.style,
+ dx: e.clientX - rect.left,
+ dy: e.clientY - rect.top,
+ height: screenHeight - rect.height,
+ width: screenWidth - rect.width,
+ screenHeight: screenHeight,
+ screenWidth: screenWidth,
+ isTouching: isTouching
+ };
+ ref = Conf['Header auto-hide'] || !Conf['Fixed Header'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = ref[0], o.bottomBorder = ref[1];
+ if (isTouching) {
+ o.identifier = e.identifier;
+ o.move = touchmove.bind(o);
+ o.up = touchend.bind(o);
+ $.on(d, 'touchmove', o.move);
+ return $.on(d, 'touchend touchcancel', o.up);
+ } else {
+ o.move = drag.bind(o);
+ o.up = dragend.bind(o);
+ $.on(d, 'mousemove', o.move);
+ return $.on(d, 'mouseup', o.up);
+ }
+ };
+
+ touchmove = function(e) {
+ var i, len, ref, touch;
+ ref = e.changedTouches;
+ for (i = 0, len = ref.length; i < len; i++) {
+ touch = ref[i];
+ if (touch.identifier === this.identifier) {
+ drag.call(this, touch);
+ return;
+ }
+ }
+ };
+
+ drag = function(e) {
+ var bottom, clientX, clientY, left, right, style, top;
+ clientX = e.clientX, clientY = e.clientY;
+ left = clientX - this.dx;
+ left = left < 10 ? 0 : this.width - left < 10 ? '' : left / this.screenWidth * 100 + '%';
+ top = clientY - this.dy;
+ top = top < (10 + this.topBorder) ? this.topBorder + 'px' : this.height - top < (10 + this.bottomBorder) ? '' : top / this.screenHeight * 100 + '%';
+ right = left === '' ? 0 : '';
+ bottom = top === '' ? this.bottomBorder + 'px' : '';
+ style = this.style;
+ style.left = left;
+ style.right = right;
+ style.top = top;
+ return style.bottom = bottom;
+ };
+
+ touchend = function(e) {
+ var i, len, ref, touch;
+ ref = e.changedTouches;
+ for (i = 0, len = ref.length; i < len; i++) {
+ touch = ref[i];
+ if (touch.identifier === this.identifier) {
+ dragend.call(this);
+ return;
+ }
+ }
+ };
+
+ dragend = function() {
+ if (this.isTouching) {
+ $.off(d, 'touchmove', this.move);
+ $.off(d, 'touchend touchcancel', this.up);
+ } else {
+ $.off(d, 'mousemove', this.move);
+ $.off(d, 'mouseup', this.up);
+ }
+ return $.set(this.id + ".position", this.style.cssText);
+ };
+
+ hoverstart = function(arg) {
+ var cb, el, endEvents, height, latestEvent, noRemove, o, rect, ref, root, width;
+ root = arg.root, el = arg.el, latestEvent = arg.latestEvent, endEvents = arg.endEvents, height = arg.height, width = arg.width, cb = arg.cb, noRemove = arg.noRemove;
+ rect = root.getBoundingClientRect();
+ o = {
+ root: root,
+ el: el,
+ style: el.style,
+ isImage: (ref = el.nodeName) === 'IMG' || ref === 'VIDEO',
+ cb: cb,
+ endEvents: endEvents,
+ latestEvent: latestEvent,
+ clientHeight: doc.clientHeight,
+ clientWidth: doc.clientWidth,
+ height: height,
+ width: width,
+ noRemove: noRemove,
+ clientX: (rect.left + rect.right) / 2,
+ clientY: (rect.top + rect.bottom) / 2
+ };
+ o.hover = hover.bind(o);
+ o.hoverend = hoverend.bind(o);
+ o.hover(o.latestEvent);
+ new MutationObserver(function() {
+ if (el.parentNode) {
+ return o.hover(o.latestEvent);
+ }
+ }).observe(el, {
+ childList: true
+ });
+ $.on(root, endEvents, o.hoverend);
+ if ($.x('ancestor::div[contains(@class,"inline")][1]', root)) {
+ $.on(d, 'keydown', o.hoverend);
+ }
+ $.on(root, 'mousemove', o.hover);
+ o.workaround = function(e) {
+ if (!root.contains(e.target)) {
+ return o.hoverend(e);
+ }
+ };
+ return $.on(doc, 'mousemove', o.workaround);
+ };
+
+ hoverstart.padding = 25;
+
+ hover = function(e) {
+ var clientX, clientY, height, left, marginX, ref, ref1, right, style, threshold, top, width;
+ this.latestEvent = e;
+ height = (this.height || this.el.offsetHeight) + hoverstart.padding;
+ width = this.width || this.el.offsetWidth;
+ ref = Conf['Follow Cursor'] ? e : this, clientX = ref.clientX, clientY = ref.clientY;
+ top = this.isImage ? Math.max(0, clientY * (this.clientHeight - height) / this.clientHeight) : Math.max(0, Math.min(this.clientHeight - height, clientY - 120));
+ threshold = this.clientWidth / 2;
+ if (!this.isImage) {
+ threshold = Math.max(threshold, this.clientWidth - 400);
+ }
+ marginX = (clientX <= threshold ? clientX : this.clientWidth - clientX) + 45;
+ if (this.isImage) {
+ marginX = Math.min(marginX, this.clientWidth - width);
+ }
+ marginX += 'px';
+ ref1 = clientX <= threshold ? [marginX, ''] : ['', marginX], left = ref1[0], right = ref1[1];
+ style = this.style;
+ style.top = top + 'px';
+ style.left = left;
+ return style.right = right;
+ };
+
+ hoverend = function(e) {
+ if (e.type === 'keydown' && e.keyCode !== 13 || e.target.nodeName === "TEXTAREA") {
+ return;
+ }
+ if (!this.noRemove) {
+ $.rm(this.el);
+ }
+ $.off(this.root, this.endEvents, this.hoverend);
+ $.off(d, 'keydown', this.hoverend);
+ $.off(this.root, 'mousemove', this.hover);
+ $.off(doc, 'mousemove', this.workaround);
+ if (this.cb) {
+ return this.cb.call(this);
+ }
+ };
+
+ checkbox = function(name, text, checked) {
+ var input, label;
+ if (checked == null) {
+ checked = Conf[name];
+ }
+ label = $.el('label');
+ input = $.el('input', {
+ type: 'checkbox',
+ name: name,
+ checked: checked
+ });
+ $.add(label, [input, $.tn(" " + text)]);
+ return label;
+ };
+
+ UI = {
+ dialog: dialog,
+ Menu: Menu,
+ hover: hoverstart,
+ checkbox: checkbox
+ };
+
+ return UI;
+
+}).call(this);
+
+FappeTyme = (function() {
+ var FappeTyme;
+
+ FappeTyme = {
+ init: function() {
+ var el, i, indicator, lc, len, ref, ref1, type;
+ if (!((Conf['Fappe Tyme'] || Conf['Werk Tyme']) && ((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive'))) {
+ return;
+ }
+ this.nodes = {};
+ this.enabled = {
+ fappe: false,
+ werk: Conf['werk']
+ };
+ ref1 = ["Fappe", "Werk"];
+ for (i = 0, len = ref1.length; i < len; i++) {
+ type = ref1[i];
+ if (!Conf[type + " Tyme"]) {
+ continue;
+ }
+ lc = type.toLowerCase();
+ el = UI.checkbox(lc, type + " Tyme", false);
+ el.title = type + " Tyme";
+ this.nodes[lc] = el.firstElementChild;
+ if (Conf[lc]) {
+ this.set(lc, true);
+ }
+ $.on(this.nodes[lc], 'change', this.toggle.bind(this, lc));
+ Header.menu.addEntry({
+ el: el,
+ order: 97
+ });
+ indicator = $.el('span', {
+ className: 'indicator',
+ textContent: type[0],
+ title: type + " Tyme active"
+ });
+ $.on(indicator, 'click', function() {
+ var check;
+ check = $.getOwn(FappeTyme.nodes, this.parentNode.id.replace('shortcut-', ''));
+ check.checked = !check.checked;
+ return $.event('change', null, check);
+ });
+ Header.addShortcut(lc, indicator, 410);
+ }
+ if (Conf['Werk Tyme']) {
+ $.sync('werk', this.set.bind(this, 'werk'));
+ }
+ Callbacks.Post.push({
+ name: 'Fappe Tyme',
+ cb: this.node
+ });
+ return Callbacks.CatalogThread.push({
+ name: 'Werk Tyme',
+ cb: this.catalogNode
+ });
+ },
+ node: function() {
+ return this.nodes.root.classList.toggle('noFile', !this.files.length);
+ },
+ catalogNode: function() {
+ var file, filename;
+ file = this.thread.OP.files[0];
+ if (!file) {
+ return;
+ }
+ filename = $.el('div', {
+ textContent: file.name,
+ className: 'werkTyme-filename'
+ });
+ return $.add(this.nodes.thumb.parentNode, filename);
+ },
+ set: function(type, enabled) {
+ this.enabled[type] = this.nodes[type].checked = enabled;
+ return $[(enabled ? 'add' : 'rm') + "Class"](doc, type + "Tyme");
+ },
+ toggle: function(type) {
+ this.set(type, !this.enabled[type]);
+ if (type === 'werk') {
+ return $.cb.checked.call(this.nodes[type]);
+ }
+ }
+ };
+
+ return FappeTyme;
+
+}).call(this);
+
+Gallery = (function() {
+ var Gallery;
+
+ Gallery = {
+ init: function() {
+ var el, ref;
+ if (!(this.enabled = Conf['Gallery'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) {
+ return;
+ }
+ this.delay = Conf['Slide Delay'];
+ el = $.el('a', {
+ href: 'javascript:;',
+ title: 'Gallery',
+ className: 'fa fa-picture-o',
+ textContent: 'Gallery'
+ });
+ $.on(el, 'click', this.cb.toggle);
+ Header.addShortcut('gallery', el, 530);
+ return Callbacks.Post.push({
+ name: 'Gallery',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var file, i, len, ref, results;
+ ref = this.files;
+ results = [];
+ for (i = 0, len = ref.length; i < len; i++) {
+ file = ref[i];
+ if (!file.thumb) {
+ continue;
+ }
+ if (Gallery.nodes) {
+ Gallery.generateThumb(this, file);
+ Gallery.nodes.total.textContent = Gallery.images.length;
+ }
+ if (!(Conf['Image Expansion'] || (g.SITE.software === 'tinyboard' && Main.jsEnabled))) {
+ results.push($.on(file.thumbLink, 'click', Gallery.cb.image));
+ } else {
+ results.push(void 0);
+ }
+ }
+ return results;
+ },
+ build: function(image) {
+ var candidate, cb, dialog, entry, file, i, j, k, key, len, len1, len2, menuButton, nodes, post, postThumb, ref, ref1, ref2, ref3, thumb, value;
+ cb = Gallery.cb;
+ if (Conf['Fullscreen Gallery']) {
+ $.one(d, 'fullscreenchange mozfullscreenchange webkitfullscreenchange', function() {
+ return $.on(d, 'fullscreenchange mozfullscreenchange webkitfullscreenchange', cb.close);
+ });
+ if (typeof doc.mozRequestFullScreen === "function") {
+ doc.mozRequestFullScreen();
+ }
+ if (typeof doc.webkitRequestFullScreen === "function") {
+ doc.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
+ }
+ }
+ Gallery.images = [];
+ nodes = Gallery.nodes = {};
+ Gallery.fileIDs = $.dict();
+ Gallery.slideshow = false;
+ nodes.el = dialog = $.el('div', {
+ id: 'a-gallery'
+ });
+ $.extend(dialog, {innerHTML: "<div class=\"gal-viewport\"><span class=\"gal-buttons\"><a href=\"javascript:;\" class=\"gal-start\" title=\"Start slideshow\"><i></i></a><a href=\"javascript:;\" class=\"gal-stop\" title=\"Stop slideshow\"><i></i></a><a href=\"javascript:;\" class=\"menu-button\"><i></i></a><a href=\"javascript:;\" class=\"gal-close\">ร—</a></span><div class=\"gal-labels\"><span class=\"gal-count\"><span class=\"count\"></span> / <span class=\"total\"></span></span><a class=\"gal-name\" target=\"_blank\"></a><span class=\"gal-sauce\"></span></div><div class=\"gal-prev\"></div><div class=\"gal-image\"><a href=\"javascript:;\"><img></a></div><div class=\"gal-next\"></div></div><div class=\"gal-thumbnails\"></div>"});
+ ref = {
+ buttons: '.gal-buttons',
+ frame: '.gal-image',
+ name: '.gal-name',
+ count: '.count',
+ total: '.total',
+ sauce: '.gal-sauce',
+ thumbs: '.gal-thumbnails',
+ next: '.gal-image a',
+ current: '.gal-image img'
+ };
+ for (key in ref) {
+ value = ref[key];
+ nodes[key] = $(value, dialog);
+ }
+ menuButton = $('.menu-button', dialog);
+ nodes.menu = new UI.Menu('gallery');
+ $.on(nodes.frame, 'click', cb.blank);
+ if (Conf['Mouse Wheel Volume']) {
+ $.on(nodes.frame, 'wheel', Volume.wheel);
+ }
+ $.on(nodes.next, 'click', cb.click);
+ $.on(nodes.name, 'click', ImageCommon.download);
+ $.on($('.gal-prev', dialog), 'click', cb.prev);
+ $.on($('.gal-next', dialog), 'click', cb.next);
+ $.on($('.gal-start', dialog), 'click', cb.start);
+ $.on($('.gal-stop', dialog), 'click', cb.stop);
+ $.on($('.gal-close', dialog), 'click', cb.close);
+ $.on(menuButton, 'click', function(e) {
+ return nodes.menu.toggle(e, this, g);
+ });
+ ref1 = Gallery.menu.createSubEntries();
+ for (i = 0, len = ref1.length; i < len; i++) {
+ entry = ref1[i];
+ entry.order = 0;
+ nodes.menu.addEntry(entry);
+ }
+ $.on(d, 'keydown', cb.keybinds);
+ if (Conf['Keybinds']) {
+ $.off(d, 'keydown', Keybinds.keydown);
+ }
+ $.on(window, 'resize', Gallery.cb.setHeight);
+ ref2 = $$(g.SITE.selectors.file.thumb);
+ for (j = 0, len1 = ref2.length; j < len1; j++) {
+ postThumb = ref2[j];
+ if (!(post = Get.postFromNode(postThumb))) {
+ continue;
+ }
+ ref3 = post.files;
+ for (k = 0, len2 = ref3.length; k < len2; k++) {
+ file = ref3[k];
+ if (!file.thumb) {
+ continue;
+ }
+ Gallery.generateThumb(post, file);
+ if (!image && Gallery.fileIDs[post.fullID + "." + file.index]) {
+ candidate = file.thumbLink;
+ if (Header.getTopOf(candidate) + candidate.getBoundingClientRect().height >= 0) {
+ image = candidate;
+ }
+ }
+ }
+ }
+ $.addClass(doc, 'gallery-open');
+ $.add(d.body, dialog);
+ nodes.thumbs.scrollTop = 0;
+ nodes.current.parentElement.scrollTop = 0;
+ if (image) {
+ thumb = $("[href='" + image.href + "']", nodes.thumbs);
+ }
+ thumb || (thumb = Gallery.images[Gallery.images.length - 1]);
+ if (thumb) {
+ Gallery.open(thumb);
+ }
+ doc.style.overflow = 'hidden';
+ return nodes.total.textContent = Gallery.images.length;
+ },
+ generateThumb: function(post, file) {
+ var thumb, thumbImg;
+ if (post.isClone || post.isHidden) {
+ return;
+ }
+ if (!(file && file.thumb && (file.isImage || file.isVideo || Conf['PDF in Gallery']))) {
+ return;
+ }
+ if (Gallery.fileIDs[post.fullID + "." + file.index]) {
+ return;
+ }
+ Gallery.fileIDs[post.fullID + "." + file.index] = true;
+ thumb = $.el('a', {
+ className: 'gal-thumb',
+ href: file.url,
+ target: '_blank',
+ title: file.name
+ });
+ thumb.dataset.id = Gallery.images.length;
+ thumb.dataset.post = post.fullID;
+ thumb.dataset.file = file.index;
+ thumbImg = file.thumb.cloneNode(false);
+ thumbImg.style.cssText = '';
+ $.add(thumb, thumbImg);
+ $.on(thumb, 'click', Gallery.cb.open);
+ Gallery.images.push(thumb);
+ return $.add(Gallery.nodes.thumbs, thumb);
+ },
+ load: function(thumb, errorCB) {
+ var elType, ext, file;
+ ext = thumb.href.match(/\w*$/);
+ elType = $.getOwn({
+ 'webm': 'video',
+ 'mp4': 'video',
+ 'ogv': 'video',
+ 'pdf': 'iframe'
+ }, ext) || 'img';
+ file = $.el(elType);
+ $.extend(file.dataset, thumb.dataset);
+ $.on(file, 'error', errorCB);
+ file.src = thumb.href;
+ return file;
+ },
+ open: function(thumb) {
+ var el, file, i, len, link, newID, node, nodes, oldID, post, ref, ref1, sauces;
+ nodes = Gallery.nodes;
+ oldID = +nodes.current.dataset.id;
+ newID = +thumb.dataset.id;
+ if (el = Gallery.images[oldID]) {
+ $.rmClass(el, 'gal-highlight');
+ }
+ $.addClass(thumb, 'gal-highlight');
+ nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight / 2 - nodes.thumbs.clientHeight / 2;
+ if (((ref = Gallery.cache) != null ? ref.dataset.id : void 0) === '' + newID) {
+ file = Gallery.cache;
+ $.off(file, 'error', Gallery.cacheError);
+ $.on(file, 'error', Gallery.error);
+ } else {
+ file = Gallery.load(thumb, Gallery.error);
+ }
+ $.off(nodes.current, 'error', Gallery.error);
+ ImageCommon.pause(nodes.current);
+ $.replace(nodes.current, file);
+ nodes.current = file;
+ if (file.nodeName === 'VIDEO') {
+ file.loop = true;
+ Volume.setup(file);
+ if (Conf['Autoplay']) {
+ file.play();
+ }
+ if (Conf['Show Controls']) {
+ ImageCommon.addControls(file);
+ }
+ }
+ doc.classList.toggle('gal-pdf', file.nodeName === 'IFRAME');
+ Gallery.cb.setHeight();
+ nodes.count.textContent = +thumb.dataset.id + 1;
+ nodes.name.download = nodes.name.textContent = thumb.title;
+ nodes.name.href = thumb.href;
+ nodes.frame.scrollTop = 0;
+ nodes.next.focus();
+ $.rmAll(nodes.sauce);
+ if (Conf['Sauce'] && Sauce.links && (post = g.posts.get(file.dataset.post))) {
+ sauces = [];
+ ref1 = Sauce.links;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ link = ref1[i];
+ if ((node = Sauce.createSauceLink(link, post, post.files[+file.dataset.file]))) {
+ sauces.push($.tn(' '), node);
+ }
+ }
+ $.add(nodes.sauce, sauces);
+ }
+ if (Gallery.slideshow && (newID > oldID || (oldID === Gallery.images.length - 1 && newID === 0))) {
+ Gallery.setupTimer();
+ } else {
+ Gallery.cb.stop();
+ }
+ if (Conf['Scroll to Post'] && (post = g.posts.get(file.dataset.post))) {
+ Header.scrollTo(post.nodes.root);
+ }
+ if (isNaN(oldID) || newID === (oldID + 1) % Gallery.images.length) {
+ return Gallery.cache = Gallery.load(Gallery.images[(newID + 1) % Gallery.images.length], Gallery.cacheError);
+ }
+ },
+ error: function() {
+ var file, post, ref;
+ if (((ref = this.error) != null ? ref.code : void 0) === MediaError.MEDIA_ERR_DECODE) {
+ return new Notice('error', 'Corrupt or unplayable video', 30);
+ }
+ if (ImageCommon.isFromArchive(this)) {
+ return;
+ }
+ post = g.posts.get(this.dataset.post);
+ file = post.files[+this.dataset.file];
+ return ImageCommon.error(this, post, file, null, (function(_this) {
+ return function(url) {
+ if (!url) {
+ return;
+ }
+ Gallery.images[+_this.dataset.id].href = url;
+ if (Gallery.nodes.current === _this) {
+ return _this.src = url;
+ }
+ };
+ })(this));
+ },
+ cacheError: function() {
+ return delete Gallery.cache;
+ },
+ cleanupTimer: function() {
+ var current;
+ clearTimeout(Gallery.timeoutID);
+ current = Gallery.nodes.current;
+ $.off(current, 'canplaythrough load', Gallery.startTimer);
+ return $.off(current, 'ended', Gallery.cb.next);
+ },
+ startTimer: function() {
+ return Gallery.timeoutID = setTimeout(Gallery.checkTimer, Gallery.delay * $.SECOND);
+ },
+ setupTimer: function() {
+ var current, isVideo;
+ Gallery.cleanupTimer();
+ current = Gallery.nodes.current;
+ isVideo = current.nodeName === 'VIDEO';
+ if (isVideo) {
+ current.play();
+ }
+ if ((isVideo ? current.readyState >= 4 : current.complete) || current.nodeName === 'IFRAME') {
+ return Gallery.startTimer();
+ } else {
+ return $.on(current, (isVideo ? 'canplaythrough' : 'load'), Gallery.startTimer);
+ }
+ },
+ checkTimer: function() {
+ var current;
+ current = Gallery.nodes.current;
+ if (current.nodeName === 'VIDEO' && !current.paused) {
+ $.on(current, 'ended', Gallery.cb.next);
+ return current.loop = false;
+ } else {
+ return Gallery.cb.next();
+ }
+ },
+ cb: {
+ keybinds: function(e) {
+ var cb, key;
+ if (!(key = Keybinds.keyCode(e))) {
+ return;
+ }
+ cb = (function() {
+ switch (key) {
+ case Conf['Close']:
+ case Conf['Open Gallery']:
+ return Gallery.cb.close;
+ case Conf['Next Gallery Image']:
+ return Gallery.cb.next;
+ case Conf['Advance Gallery']:
+ return Gallery.cb.advance;
+ case Conf['Previous Gallery Image']:
+ return Gallery.cb.prev;
+ case Conf['Pause']:
+ return Gallery.cb.pause;
+ case Conf['Slideshow']:
+ return Gallery.cb.toggleSlideshow;
+ case Conf['Rotate image anticlockwise']:
+ return Gallery.cb.rotateLeft;
+ case Conf['Rotate image clockwise']:
+ return Gallery.cb.rotateRight;
+ case Conf['Download Gallery Image']:
+ return Gallery.cb.download;
+ }
+ })();
+ if (!cb) {
+ return;
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ return cb();
+ },
+ open: function(e) {
+ if (e) {
+ e.preventDefault();
+ }
+ if (this) {
+ return Gallery.open(this);
+ }
+ },
+ image: function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ return Gallery.build(this);
+ },
+ prev: function() {
+ return Gallery.cb.open.call(Gallery.images[+Gallery.nodes.current.dataset.id - 1] || Gallery.images[Gallery.images.length - 1]);
+ },
+ next: function() {
+ return Gallery.cb.open.call(Gallery.images[+Gallery.nodes.current.dataset.id + 1] || Gallery.images[0]);
+ },
+ click: function(e) {
+ if (ImageCommon.onControls(e)) {
+ return;
+ }
+ e.preventDefault();
+ return Gallery.cb.advance();
+ },
+ advance: function() {
+ if (!Conf['Autoplay'] && Gallery.nodes.current.paused) {
+ return Gallery.nodes.current.play();
+ } else {
+ return Gallery.cb.next();
+ }
+ },
+ toggle: function() {
+ return (Gallery.nodes ? Gallery.cb.close : Gallery.build)();
+ },
+ blank: function(e) {
+ if (e.target === this) {
+ return Gallery.cb.close();
+ }
+ },
+ toggleSlideshow: function() {
+ return Gallery.cb[Gallery.slideshow ? 'stop' : 'start']();
+ },
+ download: function() {
+ var name;
+ name = $('.gal-name');
+ return name.click();
+ },
+ pause: function() {
+ var current;
+ Gallery.cb.stop();
+ current = Gallery.nodes.current;
+ if (current.nodeName === 'VIDEO') {
+ return current[current.paused ? 'play' : 'pause']();
+ }
+ },
+ start: function() {
+ $.addClass(Gallery.nodes.buttons, 'gal-playing');
+ Gallery.slideshow = true;
+ return Gallery.setupTimer();
+ },
+ stop: function() {
+ var current;
+ if (!Gallery.slideshow) {
+ return;
+ }
+ Gallery.cleanupTimer();
+ current = Gallery.nodes.current;
+ if (current.nodeName === 'VIDEO') {
+ current.loop = true;
+ }
+ $.rmClass(Gallery.nodes.buttons, 'gal-playing');
+ return Gallery.slideshow = false;
+ },
+ rotateLeft: function() {
+ return Gallery.cb.rotate(270);
+ },
+ rotateRight: function() {
+ return Gallery.cb.rotate(90);
+ },
+ rotate: $.debounce(100, function(delta) {
+ var current;
+ current = Gallery.nodes.current;
+ if (current.nodeName === 'IFRAME') {
+ return;
+ }
+ current.dataRotate = ((current.dataRotate || 0) + delta) % 360;
+ current.style.transform = "rotate(" + current.dataRotate + "deg)";
+ return Gallery.cb.setHeight();
+ }),
+ close: function() {
+ $.off(Gallery.nodes.current, 'error', Gallery.error);
+ ImageCommon.pause(Gallery.nodes.current);
+ $.rm(Gallery.nodes.el);
+ $.rmClass(doc, 'gallery-open');
+ if (Conf['Fullscreen Gallery']) {
+ $.off(d, 'fullscreenchange mozfullscreenchange webkitfullscreenchange', Gallery.cb.close);
+ if (typeof d.mozCancelFullScreen === "function") {
+ d.mozCancelFullScreen();
+ }
+ if (typeof d.webkitExitFullscreen === "function") {
+ d.webkitExitFullscreen();
+ }
+ }
+ delete Gallery.nodes;
+ delete Gallery.fileIDs;
+ doc.style.overflow = '';
+ $.off(d, 'keydown', Gallery.cb.keybinds);
+ if (Conf['Keybinds']) {
+ $.on(d, 'keydown', Keybinds.keydown);
+ }
+ $.off(window, 'resize', Gallery.cb.setHeight);
+ return clearTimeout(Gallery.timeoutID);
+ },
+ setFitness: function() {
+ return (this.checked ? $.addClass : $.rmClass)(doc, "gal-" + (this.name.toLowerCase().replace(/\s+/g, '-')));
+ },
+ setHeight: $.debounce(100, function() {
+ var containerHeight, containerWidth, current, dim, frame, height, margin, minHeight, ref, ref1, ref2, ref3, style, width;
+ ref = Gallery.nodes, current = ref.current, frame = ref.frame;
+ style = current.style;
+ if (Conf['Stretch to Fit'] && (dim = (ref1 = g.posts.get(current.dataset.post)) != null ? ref1.files[+current.dataset.file].dimensions : void 0)) {
+ ref2 = dim.split('x'), width = ref2[0], height = ref2[1];
+ containerWidth = frame.clientWidth;
+ containerHeight = doc.clientHeight - 25;
+ if ((current.dataRotate || 0) % 180 === 90) {
+ ref3 = [containerHeight, containerWidth], containerWidth = ref3[0], containerHeight = ref3[1];
+ }
+ minHeight = Math.min(containerHeight, height / width * containerWidth);
+ style.minHeight = minHeight + 'px';
+ style.minWidth = (width / height * minHeight) + 'px';
+ } else {
+ style.minHeight = style.minWidth = '';
+ }
+ if ((current.dataRotate || 0) % 180 === 90) {
+ style.maxWidth = Conf['Fit Height'] ? (doc.clientHeight - 25) + "px" : 'none';
+ style.maxHeight = Conf['Fit Width'] ? frame.clientWidth + "px" : 'none';
+ margin = (current.clientWidth - current.clientHeight) / 2;
+ return style.margin = margin + "px " + (-margin) + "px";
+ } else {
+ return style.maxWidth = style.maxHeight = style.margin = '';
+ }
+ }),
+ setDelay: function() {
+ return Gallery.delay = +this.value;
+ }
+ },
+ menu: {
+ init: function() {
+ var el;
+ if (!Gallery.enabled) {
+ return;
+ }
+ el = $.el('span', {
+ textContent: 'Gallery',
+ className: 'gallery-link'
+ });
+ return Header.menu.addEntry({
+ el: el,
+ order: 105,
+ subEntries: Gallery.menu.createSubEntries()
+ });
+ },
+ createSubEntry: function(name) {
+ var input, label;
+ label = UI.checkbox(name, name);
+ input = label.firstElementChild;
+ if (name === 'Hide Thumbnails' || name === 'Fit Width' || name === 'Fit Height') {
+ $.on(input, 'change', Gallery.cb.setFitness);
+ }
+ $.event('change', null, input);
+ $.on(input, 'change', $.cb.checked);
+ if (name === 'Hide Thumbnails' || name === 'Fit Width' || name === 'Fit Height' || name === 'Stretch to Fit') {
+ $.on(input, 'change', Gallery.cb.setHeight);
+ }
+ return {
+ el: label
+ };
+ },
+ createSubEntries: function() {
+ var delayInput, delayLabel, item, subEntries;
+ subEntries = (function() {
+ var i, len, ref, results;
+ ref = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Stretch to Fit', 'Scroll to Post'];
+ results = [];
+ for (i = 0, len = ref.length; i < len; i++) {
+ item = ref[i];
+ results.push(Gallery.menu.createSubEntry(item));
+ }
+ return results;
+ })();
+ delayLabel = $.el('label', {innerHTML: "Slide Delay: <input type=\"number\" name=\"Slide Delay\" min=\"0\" step=\"any\" class=\"field\">"});
+ delayInput = delayLabel.firstElementChild;
+ delayInput.value = Gallery.delay;
+ $.on(delayInput, 'change', Gallery.cb.setDelay);
+ $.on(delayInput, 'change', $.cb.value);
+ subEntries.push({
+ el: delayLabel
+ });
+ return subEntries;
+ }
+ }
+ };
+
+ return Gallery;
+
+}).call(this);
+
+ImageCommon = (function() {
+ var ImageCommon,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ ImageCommon = {
+ pause: function(video) {
+ if (video.nodeName !== 'VIDEO') {
+ return;
+ }
+ video.pause();
+ $.off(video, 'volumechange', Volume.change);
+ return video.muted = true;
+ },
+ rewind: function(el) {
+ if (el.nodeName === 'VIDEO') {
+ if (el.readyState >= el.HAVE_METADATA) {
+ return el.currentTime = 0;
+ }
+ } else if (/\.gif$/.test(el.src)) {
+ return $.queueTask(function() {
+ return el.src = el.src;
+ });
+ }
+ },
+ pushCache: function(el) {
+ ImageCommon.cache = el;
+ return $.on(el, 'error', ImageCommon.cacheError);
+ },
+ popCache: function() {
+ var el;
+ el = ImageCommon.cache;
+ $.off(el, 'error', ImageCommon.cacheError);
+ delete ImageCommon.cache;
+ return el;
+ },
+ cacheError: function() {
+ if (ImageCommon.cache === this) {
+ return delete ImageCommon.cache;
+ }
+ },
+ decodeError: function(file, fileObj) {
+ var message, ref;
+ if (((ref = file.error) != null ? ref.code : void 0) !== MediaError.MEDIA_ERR_DECODE) {
+ return false;
+ }
+ if (!(message = $('.warning', fileObj.thumb.parentNode))) {
+ message = $.el('div', {
+ className: 'warning'
+ });
+ $.after(fileObj.thumb, message);
+ }
+ message.textContent = 'Error: Corrupt or unplayable video';
+ return true;
+ },
+ isFromArchive: function(file) {
+ return g.SITE.software === 'yotsuba' && !ImageHost.test(file.src.split('/')[2]);
+ },
+ error: function(file, post, fileObj, delay, cb) {
+ var base, parseJSON, redirect, src, threadJSON, timeoutID, url;
+ src = fileObj.url.split('/');
+ url = null;
+ if (g.SITE.software === 'yotsuba' && Conf['404 Redirect']) {
+ url = Redirect.to('file', {
+ boardID: post.board.ID,
+ filename: src[src.length - 1]
+ });
+ }
+ if (!(url && Redirect.securityCheck(url))) {
+ url = null;
+ }
+ if ((post.isDead || fileObj.isDead) && !ImageCommon.isFromArchive(file)) {
+ return cb(url);
+ }
+ if (delay != null) {
+ timeoutID = setTimeout((function() {
+ return cb(url);
+ }), delay);
+ }
+ if (post.isDead || fileObj.isDead) {
+ return;
+ }
+ redirect = function() {
+ if (!ImageCommon.isFromArchive(file)) {
+ if (delay != null) {
+ clearTimeout(timeoutID);
+ }
+ return cb(url);
+ }
+ };
+ threadJSON = typeof (base = g.SITE.urls).threadJSON === "function" ? base.threadJSON(post) : void 0;
+ if (!threadJSON) {
+ return;
+ }
+ parseJSON = function(isArchiveURL) {
+ var archivedThreadJSON, base1, i, len, postObj, ref, ref1;
+ if (this.status === 404) {
+ if (!isArchiveURL && (archivedThreadJSON = typeof (base1 = g.SITE.urls).archivedThreadJSON === "function" ? base1.archivedThreadJSON(post) : void 0)) {
+ $.ajax(archivedThreadJSON, {
+ onloadend: function() {
+ return parseJSON.call(this, true);
+ }
+ });
+ } else {
+ post.kill(!post.isClone, fileObj.index);
+ }
+ }
+ if (this.status !== 200) {
+ return redirect();
+ }
+ ref = this.response.posts;
+ for (i = 0, len = ref.length; i < len; i++) {
+ postObj = ref[i];
+ if (postObj.no === post.ID) {
+ break;
+ }
+ }
+ if (postObj.no !== post.ID) {
+ post.kill();
+ return redirect();
+ } else if (ref1 = fileObj.docIndex, indexOf.call(g.SITE.Build.parseJSON(postObj, post.board).filesDeleted, ref1) >= 0) {
+ post.kill(true);
+ return redirect();
+ } else {
+ return url = fileObj.url;
+ }
+ };
+ return $.ajax(threadJSON, {
+ onloadend: function() {
+ return parseJSON.call(this);
+ }
+ });
+ },
+ addControls: function(video) {
+ var handler;
+ handler = function() {
+ var t;
+ $.off(video, 'mouseover', handler);
+ t = new Date().getTime();
+ return $.asap((function() {
+ return $.engine !== 'gecko' || (video.readyState >= 3 && video.currentTime <= Math.max(0.1, video.duration - 0.5)) || new Date().getTime() >= t + 1000;
+ }), function() {
+ return video.controls = true;
+ });
+ };
+ return $.on(video, 'mouseover', handler);
+ },
+ onControls: function(e) {
+ return (Conf['Show Controls'] && Conf['Click Passthrough'] && e.target.nodeName === 'VIDEO') || (e.target.controls && e.target.getBoundingClientRect().bottom - e.clientY < 35);
+ },
+ download: function(e) {
+ var download, href, ref;
+ if (this.protocol === 'blob:') {
+ return true;
+ }
+ e.preventDefault();
+ ref = this, href = ref.href, download = ref.download;
+ return CrossOrigin.file(href, function(blob) {
+ var a;
+ if (blob) {
+ a = $.el('a', {
+ href: URL.createObjectURL(blob),
+ download: download,
+ hidden: true
+ });
+ $.add(d.body, a);
+ a.click();
+ return $.rm(a);
+ } else {
+ return new Notice('warning', "Could not download " + href, 20);
+ }
+ });
+ }
+ };
+
+ return ImageCommon;
+
+}).call(this);
+
+ImageExpand = (function() {
+ var ImageExpand,
+ slice = [].slice;
+
+ ImageExpand = {
+ init: function() {
+ var ref;
+ if (!(this.enabled = Conf['Image Expansion'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) {
+ return;
+ }
+ this.EAI = $.el('a', {
+ className: 'expand-all-shortcut fa fa-expand',
+ textContent: 'EAI',
+ title: 'Expand All Images',
+ href: 'javascript:;'
+ });
+ $.on(this.EAI, 'click', this.cb.toggleAll);
+ Header.addShortcut('expand-all', this.EAI, 520);
+ $.on(d, 'scroll visibilitychange', this.cb.playVideos);
+ this.videoControls = $.el('span', {
+ className: 'video-controls'
+ });
+ $.extend(this.videoControls, {innerHTML: " <a href=\"javascript:;\" title=\"You can also contract the video by dragging it to the left.\">contract</a>"});
+ return Callbacks.Post.push({
+ name: 'Image Expansion',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var ref;
+ if (!(this.file && (this.file.isImage || this.file.isVideo))) {
+ return;
+ }
+ $.on(this.file.thumbLink, 'click', ImageExpand.cb.toggle);
+ if (this.isClone) {
+ if (this.file.isExpanding) {
+ ImageExpand.contract(this);
+ return ImageExpand.expand(this);
+ } else if (this.file.isExpanded && this.file.isVideo) {
+ Volume.setup(this.file.fullImage);
+ ImageExpand.setupVideoCB(this);
+ return ImageExpand.setupVideo(this, !((ref = this.origin.file.fullImage) != null ? ref.paused : void 0) || this.origin.file.wasPlaying, this.file.fullImage.controls);
+ }
+ } else if (ImageExpand.on && !this.isHidden && !this.isFetchedQuote && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) {
+ return ImageExpand.expand(this);
+ }
+ },
+ cb: {
+ toggle: function(e) {
+ var file, post, ref;
+ if ($.modifiedClick(e)) {
+ return;
+ }
+ post = Get.postFromNode(this);
+ file = post.file;
+ if (file.isExpanded && ImageCommon.onControls(e)) {
+ return;
+ }
+ e.preventDefault();
+ if (!Conf['Autoplay'] && ((ref = file.fullImage) != null ? ref.paused : void 0)) {
+ return file.fullImage.play();
+ } else {
+ return ImageExpand.toggle(post);
+ }
+ },
+ toggleAll: function() {
+ var func, threadRoot, toggle;
+ $.event('CloseMenu');
+ threadRoot = Nav.getThread();
+ toggle = function(post) {
+ var file;
+ file = post.file;
+ if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) {
+ return;
+ }
+ if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || !Conf['Expand videos'] && file.isVideo || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0 || Conf['Expand thread only'] && g.VIEW === 'index' && !(threadRoot != null ? threadRoot.contains(file.thumb) : void 0))) {
+ return;
+ }
+ return $.queueTask(func, post);
+ };
+ if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) {
+ ImageExpand.EAI.className = 'contract-all-shortcut fa fa-compress';
+ ImageExpand.EAI.title = 'Contract All Images';
+ func = ImageExpand.expand;
+ } else {
+ ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand';
+ ImageExpand.EAI.title = 'Expand All Images';
+ func = ImageExpand.contract;
+ }
+ return g.posts.forEach(function(post) {
+ var i, len, ref;
+ ref = [post].concat(slice.call(post.clones));
+ for (i = 0, len = ref.length; i < len; i++) {
+ post = ref[i];
+ toggle(post);
+ }
+ });
+ },
+ playVideos: function() {
+ return g.posts.forEach(function(post) {
+ var file, i, len, ref, video, visible;
+ ref = [post].concat(slice.call(post.clones));
+ for (i = 0, len = ref.length; i < len; i++) {
+ post = ref[i];
+ file = post.file;
+ if (!(file && file.isVideo && file.isExpanded)) {
+ continue;
+ }
+ video = file.fullImage;
+ visible = ($.hasAudio(video) && !video.muted) || Header.isNodeVisible(video);
+ if (visible && file.wasPlaying) {
+ delete file.wasPlaying;
+ video.play();
+ } else if (!visible && !video.paused) {
+ file.wasPlaying = true;
+ video.pause();
+ }
+ }
+ });
+ },
+ setFitness: function() {
+ return $[this.checked ? 'addClass' : 'rmClass'](doc, this.name.toLowerCase().replace(/\s+/g, '-'));
+ }
+ },
+ toggle: function(post) {
+ var next;
+ if (!(post.file.isExpanding || post.file.isExpanded)) {
+ post.file.scrollIntoView = Conf['Scroll into view'];
+ ImageExpand.expand(post);
+ return;
+ }
+ ImageExpand.contract(post);
+ if (Conf['Advance on contract']) {
+ next = post.nodes.root;
+ while (next = $.x("following::div[contains(@class,'postContainer')][1]", next)) {
+ if (!($('.stub', next) || next.offsetHeight === 0)) {
+ break;
+ }
+ }
+ if (next) {
+ return Header.scrollTo(next);
+ }
+ }
+ },
+ contract: function(post) {
+ var bottom, cb, el, eventName, file, i, len, oldHeight, ref, ref1, scrollY, top, x;
+ file = post.file;
+ if (el = file.fullImage) {
+ top = Header.getTopOf(el);
+ bottom = top + el.getBoundingClientRect().height;
+ oldHeight = d.body.clientHeight;
+ scrollY = window.scrollY;
+ }
+ $.rmClass(post.nodes.root, 'expanded-image');
+ $.rmClass(file.thumb, 'expanding');
+ $.rm(file.videoControls);
+ file.thumbLink.href = file.url;
+ file.thumbLink.target = '_blank';
+ ref = ['isExpanding', 'isExpanded', 'videoControls', 'wasPlaying', 'scrollIntoView'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ x = ref[i];
+ delete file[x];
+ }
+ if (!el) {
+ return;
+ }
+ if (doc.contains(el)) {
+ if (bottom <= 0) {
+ window.scrollBy(0, scrollY - window.scrollY + d.body.clientHeight - oldHeight);
+ } else {
+ Header.scrollToIfNeeded(post.nodes.root);
+ }
+ if (window.scrollX > 0) {
+ window.scrollBy(-window.scrollX, 0);
+ }
+ }
+ $.off(el, 'error', ImageExpand.error);
+ ImageCommon.pushCache(el);
+ if (file.isVideo) {
+ ImageCommon.pause(el);
+ ref1 = ImageExpand.videoCB;
+ for (eventName in ref1) {
+ cb = ref1[eventName];
+ $.off(el, eventName, cb);
+ }
+ }
+ if (Conf['Restart when Opened']) {
+ ImageCommon.rewind(file.thumb);
+ }
+ delete file.fullImage;
+ return $.queueTask(function() {
+ if (file.isExpanding || file.isExpanded) {
+ return;
+ }
+ $.rmClass(el, 'full-image');
+ if (el.id) {
+ return;
+ }
+ return $.rm(el);
+ });
+ },
+ expand: function(post, src) {
+ var el, file, isVideo, ref, thumb, thumbLink;
+ file = post.file;
+ thumb = file.thumb, thumbLink = file.thumbLink, isVideo = file.isVideo;
+ if (post.isHidden || file.isExpanding || file.isExpanded) {
+ return;
+ }
+ $.addClass(thumb, 'expanding');
+ file.isExpanding = true;
+ if (file.fullImage) {
+ el = file.fullImage;
+ } else if (((ref = ImageCommon.cache) != null ? ref.dataset.fileID : void 0) === (post.fullID + "." + file.index)) {
+ el = file.fullImage = ImageCommon.popCache();
+ $.on(el, 'error', ImageExpand.error);
+ if (Conf['Restart when Opened'] && el.id !== 'ihover') {
+ ImageCommon.rewind(el);
+ }
+ el.removeAttribute('id');
+ } else {
+ el = file.fullImage = $.el((isVideo ? 'video' : 'img'));
+ el.dataset.fileID = post.fullID + "." + file.index;
+ $.on(el, 'error', ImageExpand.error);
+ el.src = src || file.url;
+ }
+ el.className = 'full-image';
+ $.after(thumb, el);
+ if (isVideo) {
+ if (!file.videoControls) {
+ file.videoControls = ImageExpand.videoControls.cloneNode(true);
+ $.add(file.text, file.videoControls);
+ }
+ thumbLink.removeAttribute('href');
+ thumbLink.removeAttribute('target');
+ el.loop = true;
+ Volume.setup(el);
+ ImageExpand.setupVideoCB(post);
+ }
+ if (!isVideo) {
+ return $.asap((function() {
+ return el.naturalHeight;
+ }), function() {
+ return ImageExpand.completeExpand(post);
+ });
+ } else if (el.readyState >= el.HAVE_METADATA) {
+ return ImageExpand.completeExpand(post);
+ } else {
+ return $.on(el, 'loadedmetadata', function() {
+ return ImageExpand.completeExpand(post);
+ });
+ }
+ },
+ completeExpand: function(post) {
+ var bottom, file, imageBottom, oldHeight, scrollY;
+ file = post.file;
+ if (!file.isExpanding) {
+ return;
+ }
+ bottom = Header.getTopOf(file.thumb) + file.thumb.getBoundingClientRect().height;
+ oldHeight = d.body.clientHeight;
+ scrollY = window.scrollY;
+ $.addClass(post.nodes.root, 'expanded-image');
+ $.rmClass(file.thumb, 'expanding');
+ file.isExpanded = true;
+ delete file.isExpanding;
+ if (doc.contains(post.nodes.root) && bottom <= 0) {
+ window.scrollBy(0, scrollY - window.scrollY + d.body.clientHeight - oldHeight);
+ }
+ if (file.scrollIntoView) {
+ delete file.scrollIntoView;
+ imageBottom = Math.min(doc.clientHeight - file.fullImage.getBoundingClientRect().bottom - 25, Header.getBottomOf(file.fullImage));
+ if (imageBottom < 0) {
+ window.scrollBy(0, Math.min(-imageBottom, Header.getTopOf(file.fullImage)));
+ }
+ }
+ if (file.isVideo) {
+ return ImageExpand.setupVideo(post, Conf['Autoplay'], Conf['Show Controls']);
+ }
+ },
+ setupVideo: function(post, playing, controls) {
+ var fullImage;
+ fullImage = post.file.fullImage;
+ if (!playing) {
+ fullImage.controls = controls;
+ return;
+ }
+ fullImage.controls = false;
+ $.asap((function() {
+ return doc.contains(fullImage);
+ }), function() {
+ if (!d.hidden && Header.isNodeVisible(fullImage)) {
+ return fullImage.play();
+ } else {
+ return post.file.wasPlaying = true;
+ }
+ });
+ if (controls) {
+ return ImageCommon.addControls(fullImage);
+ }
+ },
+ videoCB: (function() {
+ var mousedown;
+ mousedown = false;
+ return {
+ mouseover: function() {
+ return mousedown = false;
+ },
+ mousedown: function(e) {
+ if (e.button === 0) {
+ return mousedown = true;
+ }
+ },
+ mouseup: function(e) {
+ if (e.button === 0) {
+ return mousedown = false;
+ }
+ },
+ mouseout: function(e) {
+ if (((e.buttons & 1) || mousedown) && e.clientX <= this.getBoundingClientRect().left) {
+ return ImageExpand.toggle(Get.postFromNode(this));
+ }
+ }
+ };
+ })(),
+ setupVideoCB: function(post) {
+ var cb, eventName, ref;
+ ref = ImageExpand.videoCB;
+ for (eventName in ref) {
+ cb = ref[eventName];
+ $.on(post.file.fullImage, eventName, cb);
+ }
+ if (post.file.videoControls) {
+ return $.on(post.file.videoControls.firstElementChild, 'click', function() {
+ return ImageExpand.toggle(post);
+ });
+ }
+ },
+ error: function() {
+ var post;
+ post = Get.postFromNode(this);
+ $.rm(this);
+ delete post.file.fullImage;
+ if (!(post.file.isExpanding || post.file.isExpanded)) {
+ return;
+ }
+ if (ImageCommon.decodeError(this, post.file)) {
+ return ImageExpand.contract(post);
+ }
+ if (ImageCommon.isFromArchive(this)) {
+ return ImageExpand.contract(post);
+ }
+ return ImageCommon.error(this, post, post.file, 10 * $.SECOND, function(URL) {
+ if (post.file.isExpanding || post.file.isExpanded) {
+ ImageExpand.contract(post);
+ if (URL) {
+ return ImageExpand.expand(post, URL);
+ }
+ }
+ });
+ },
+ menu: {
+ init: function() {
+ var conf, createSubEntry, el, name, ref, subEntries;
+ if (!ImageExpand.enabled) {
+ return;
+ }
+ el = $.el('span', {
+ textContent: 'Image Expansion',
+ className: 'image-expansion-link'
+ });
+ createSubEntry = ImageExpand.menu.createSubEntry;
+ subEntries = [];
+ ref = Config.imageExpansion;
+ for (name in ref) {
+ conf = ref[name];
+ subEntries.push(createSubEntry(name, conf[1]));
+ }
+ return Header.menu.addEntry({
+ el: el,
+ order: 105,
+ subEntries: subEntries
+ });
+ },
+ createSubEntry: function(name, desc) {
+ var input, label;
+ label = UI.checkbox(name, name);
+ label.title = desc;
+ input = label.firstElementChild;
+ if (name === 'Fit width' || name === 'Fit height') {
+ $.on(input, 'change', ImageExpand.cb.setFitness);
+ }
+ $.event('change', null, input);
+ $.on(input, 'change', $.cb.checked);
+ return {
+ el: label
+ };
+ }
+ }
+ };
+
+ return ImageExpand;
+
+}).call(this);
+
+ImageHost = (function() {
+ var ImageHost;
+
+ ImageHost = {
+ init: function() {
+ var ref;
+ if (!((this.useFaster = /\S/.test(Conf['fourchanImageHost'])) && g.SITE.software === 'yotsuba' && ((ref = g.VIEW) === 'index' || ref === 'thread'))) {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'Image Host Rewriting',
+ cb: this.node
+ });
+ },
+ suggestions: ['i.4cdn.org', 'is2.4chan.org'],
+ host: function() {
+ return Conf['fourchanImageHost'].trim() || 'i.4cdn.org';
+ },
+ flashHost: function() {
+ return 'i.4cdn.org';
+ },
+ thumbHost: function() {
+ return 'i.4cdn.org';
+ },
+ test: function(hostname) {
+ return hostname === 'i.4cdn.org' || ImageHost.regex.test(hostname);
+ },
+ regex: /^is\d*\.4chan(?:nel)?\.org$/,
+ node: function() {
+ var host;
+ if (this.isClone) {
+ return;
+ }
+ host = ImageHost.host();
+ if (this.file && ImageHost.test(this.file.url.split('/')[2]) && !/\.swf$/.test(this.file.url)) {
+ this.file.link.hostname = host;
+ if (this.file.thumbLink) {
+ this.file.thumbLink.hostname = host;
+ }
+ this.file.url = this.file.link.href;
+ }
+ return ImageHost.fixLinks($$('a', this.nodes.comment));
+ },
+ fixLinks: function(links) {
+ var host, i, len, link;
+ for (i = 0, len = links.length; i < len; i++) {
+ link = links[i];
+ if (!(ImageHost.test(link.hostname) && !/\.swf$/.test(link.pathname))) {
+ continue;
+ }
+ host = ImageHost.host();
+ if (link.hostname !== host) {
+ link.hostname = host;
+ }
+ }
+ }
+ };
+
+ return ImageHost;
+
+}).call(this);
+
+ImageHover = (function() {
+ var ImageHover;
+
+ ImageHover = {
+ init: function() {
+ var ref;
+ if ((ref = g.VIEW) !== 'index' && ref !== 'thread') {
+ return;
+ }
+ if (Conf['Image Hover']) {
+ Callbacks.Post.push({
+ name: 'Image Hover',
+ cb: this.node
+ });
+ }
+ if (Conf['Image Hover in Catalog']) {
+ return Callbacks.CatalogThread.push({
+ name: 'Image Hover',
+ cb: this.catalogNode
+ });
+ }
+ },
+ node: function() {
+ var file, i, len, ref, results;
+ ref = this.files;
+ results = [];
+ for (i = 0, len = ref.length; i < len; i++) {
+ file = ref[i];
+ if ((file.isImage || file.isVideo) && file.thumb) {
+ results.push($.on(file.thumb, 'mouseover', ImageHover.mouseover(this, file)));
+ }
+ }
+ return results;
+ },
+ catalogNode: function() {
+ var file;
+ file = this.thread.OP.files[0];
+ if (!(file && (file.isImage || file.isVideo))) {
+ return;
+ }
+ return $.on(this.nodes.thumb, 'mouseover', ImageHover.mouseover(this.thread.OP, file));
+ },
+ mouseover: function(post, file) {
+ return function(e) {
+ var base, el, error, height, isVideo, maxHeight, maxWidth, ref, ref1, scale, width, x;
+ if (!doc.contains(this)) {
+ return;
+ }
+ isVideo = file.isVideo;
+ if (file.isExpanding || file.isExpanded || (typeof (base = g.SITE).isThumbExpanded === "function" ? base.isThumbExpanded(file) : void 0)) {
+ return;
+ }
+ error = ImageHover.error(post, file);
+ if (((ref = ImageCommon.cache) != null ? ref.dataset.fileID : void 0) === (post.fullID + "." + file.index)) {
+ el = ImageCommon.popCache();
+ $.on(el, 'error', error);
+ } else {
+ el = $.el((isVideo ? 'video' : 'img'));
+ el.dataset.fileID = post.fullID + "." + file.index;
+ $.on(el, 'error', error);
+ el.src = file.url;
+ }
+ if (Conf['Restart when Opened']) {
+ ImageCommon.rewind(el);
+ ImageCommon.rewind(this);
+ }
+ el.id = 'ihover';
+ $.add(Header.hover, el);
+ if (isVideo) {
+ el.loop = true;
+ el.controls = false;
+ Volume.setup(el);
+ if (Conf['Autoplay']) {
+ el.play();
+ if (this.nodeName === 'VIDEO') {
+ this.currentTime = el.currentTime;
+ }
+ }
+ }
+ if (file.dimensions) {
+ ref1 = (function() {
+ var i, len, ref1, results;
+ ref1 = file.dimensions.split('x');
+ results = [];
+ for (i = 0, len = ref1.length; i < len; i++) {
+ x = ref1[i];
+ results.push(+x);
+ }
+ return results;
+ })(), width = ref1[0], height = ref1[1];
+ maxWidth = doc.clientWidth;
+ maxHeight = doc.clientHeight - UI.hover.padding;
+ scale = Math.min(1, maxWidth / width, maxHeight / height);
+ width *= scale;
+ height *= scale;
+ el.style.maxWidth = width + "px";
+ el.style.maxHeight = height + "px";
+ }
+ return UI.hover({
+ root: this,
+ el: el,
+ latestEvent: e,
+ endEvents: 'mouseout click',
+ height: height,
+ width: width,
+ noRemove: true,
+ cb: function() {
+ $.off(el, 'error', error);
+ ImageCommon.pushCache(el);
+ ImageCommon.pause(el);
+ $.rm(el);
+ return el.removeAttribute('style');
+ }
+ });
+ };
+ },
+ error: function(post, file) {
+ return function() {
+ if (ImageCommon.decodeError(this, file)) {
+ return;
+ }
+ return ImageCommon.error(this, post, file, 3 * $.SECOND, (function(_this) {
+ return function(URL) {
+ if (URL) {
+ return _this.src = URL + (_this.src === URL ? '?' + Date.now() : '');
+ } else {
+ return $.rm(_this);
+ }
+ };
+ })(this));
+ };
+ }
+ };
+
+ return ImageHover;
+
+}).call(this);
+
+ImageLoader = (function() {
+ var ImageLoader,
+ slice = [].slice;
+
+ ImageLoader = {
+ init: function() {
+ var el, ref, ref1, replace;
+ if ((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') {
+ return;
+ }
+ replace = Conf['Replace JPG'] || Conf['Replace PNG'] || Conf['Replace GIF'] || Conf['Replace WEBM'];
+ if (!(Conf['Image Prefetching'] || replace)) {
+ return;
+ }
+ Callbacks.Post.push({
+ name: 'Image Replace',
+ cb: this.node
+ });
+ $.on(d, 'PostsInserted', function() {
+ if (ImageLoader.prefetchEnabled || replace) {
+ return g.posts.forEach(ImageLoader.prefetchAll);
+ }
+ });
+ if (Conf['Replace WEBM']) {
+ $.on(d, 'scroll visibilitychange 4chanXInitFinished PostsInserted', this.playVideos);
+ }
+ if (!(Conf['Image Prefetching'] && ((ref1 = g.VIEW) === 'index' || ref1 === 'thread'))) {
+ return;
+ }
+ el = $.el('a', {
+ href: 'javascript:;',
+ title: 'Prefetch Images',
+ className: 'fa fa-bolt disabled',
+ textContent: 'Prefetch'
+ });
+ $.on(el, 'click', this.toggle);
+ return Header.addShortcut('prefetch', el, 525);
+ },
+ node: function() {
+ var file, i, len, ref;
+ if (this.isClone) {
+ return;
+ }
+ ref = this.files;
+ for (i = 0, len = ref.length; i < len; i++) {
+ file = ref[i];
+ if (Conf['Replace WEBM'] && file.isVideo) {
+ ImageLoader.replaceVideo(this, file);
+ }
+ ImageLoader.prefetch(this, file);
+ }
+ },
+ replaceVideo: function(post, file) {
+ var attr, i, len, ref, thumb, video;
+ thumb = file.thumb;
+ video = $.el('video', {
+ preload: 'none',
+ loop: true,
+ muted: true,
+ poster: thumb.src || thumb.dataset.src,
+ textContent: thumb.alt,
+ className: thumb.className
+ });
+ video.setAttribute('muted', 'muted');
+ video.dataset.md5 = thumb.dataset.md5;
+ ref = ['height', 'width', 'maxHeight', 'maxWidth'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ attr = ref[i];
+ video.style[attr] = thumb.style[attr];
+ }
+ video.src = file.url;
+ $.replace(thumb, video);
+ file.thumb = video;
+ return file.videoThumb = true;
+ },
+ prefetch: function(post, file) {
+ var clone, el, i, isImage, isVideo, len, ref, ref1, replace, thumb, type, url;
+ isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, url = file.url;
+ if (file.isPrefetched || !(isImage || isVideo) || post.isHidden || post.thread.isHidden) {
+ return;
+ }
+ if (isVideo) {
+ type = 'WEBM';
+ } else {
+ type = (ref = url.match(/\.([^.]+)$/)) != null ? ref[1].toUpperCase() : void 0;
+ if (type === 'JPEG') {
+ type = 'JPG';
+ }
+ }
+ replace = Conf["Replace " + type] && !/spoiler/.test(thumb.src || thumb.dataset.src);
+ if (!(replace || ImageLoader.prefetchEnabled)) {
+ return;
+ }
+ if ($.hasClass(doc, 'catalog-mode')) {
+ return;
+ }
+ if (![post].concat(slice.call(post.clones)).some(function(clone) {
+ return doc.contains(clone.nodes.root);
+ })) {
+ return;
+ }
+ file.isPrefetched = true;
+ if (file.videoThumb) {
+ ref1 = post.clones;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ clone = ref1[i];
+ clone.file.thumb.preload = 'auto';
+ }
+ thumb.preload = 'auto';
+ if ($.engine === 'gecko') {
+ $.on(thumb, 'loadeddata', function() {
+ return this.removeAttribute('poster');
+ });
+ }
+ return;
+ }
+ el = $.el(isImage ? 'img' : 'video');
+ if (isVideo) {
+ el.preload = 'auto';
+ }
+ if (replace && isImage) {
+ $.on(el, 'load', function() {
+ var j, len1, ref2;
+ ref2 = post.clones;
+ for (j = 0, len1 = ref2.length; j < len1; j++) {
+ clone = ref2[j];
+ clone.file.thumb.src = url;
+ }
+ return thumb.src = url;
+ });
+ }
+ return el.src = url;
+ },
+ prefetchAll: function(post) {
+ var file, i, len, ref;
+ ref = post.files;
+ for (i = 0, len = ref.length; i < len; i++) {
+ file = ref[i];
+ ImageLoader.prefetch(post, file);
+ }
+ },
+ toggle: function() {
+ ImageLoader.prefetchEnabled = !ImageLoader.prefetchEnabled;
+ this.classList.toggle('disabled', !ImageLoader.prefetchEnabled);
+ if (ImageLoader.prefetchEnabled) {
+ g.posts.forEach(ImageLoader.prefetchAll);
+ }
+ },
+ playVideos: function() {
+ var qpClone, ref;
+ qpClone = (ref = $.id('qp')) != null ? ref.firstElementChild : void 0;
+ return g.posts.forEach(function(post) {
+ var file, i, j, len, len1, ref1, ref2, thumb;
+ ref1 = [post].concat(slice.call(post.clones));
+ for (i = 0, len = ref1.length; i < len; i++) {
+ post = ref1[i];
+ ref2 = post.files;
+ for (j = 0, len1 = ref2.length; j < len1; j++) {
+ file = ref2[j];
+ if (!file.videoThumb) {
+ continue;
+ }
+ thumb = file.thumb;
+ if (Header.isNodeVisible(thumb) || post.nodes.root === qpClone) {
+ thumb.play();
+ } else {
+ thumb.pause();
+ }
+ }
+ }
+ });
+ }
+ };
+
+ return ImageLoader;
+
+}).call(this);
+
+Metadata = (function() {
+ var Metadata;
+
+ Metadata = {
+ init: function() {
+ var ref;
+ if (!(Conf['WEBM Metadata'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'WEBM Metadata',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var el, file, i, j, len1, ref;
+ ref = this.files;
+ for (i = j = 0, len1 = ref.length; j < len1; i = ++j) {
+ file = ref[i];
+ if (!(/webm$/i.test(file.url))) {
+ continue;
+ }
+ if (this.isClone) {
+ el = $('.webm-title', file.text);
+ } else {
+ el = $.el('span', {
+ className: 'webm-title'
+ });
+ el.dataset.index = i;
+ $.extend(el, {innerHTML: "<a href=\"javascript:;\"></a>"});
+ $.add(file.text, [$.tn(' '), el]);
+ }
+ if (el.children.length === 1) {
+ $.one(el.lastElementChild, 'mouseover focus', Metadata.load);
+ }
+ }
+ },
+ load: function() {
+ var index;
+ $.rmClass(this.parentNode, 'error');
+ $.addClass(this.parentNode, 'loading');
+ index = this.parentNode.dataset.index;
+ return CrossOrigin.binary(Get.postFromNode(this).files[+index].url, (function(_this) {
+ return function(data) {
+ var output, title;
+ $.rmClass(_this.parentNode, 'loading');
+ if (data != null) {
+ title = Metadata.parse(data);
+ output = $.el('span', {
+ textContent: title || ''
+ });
+ if (title == null) {
+ $.addClass(_this.parentNode, 'not-found');
+ }
+ $.before(_this, output);
+ _this.parentNode.tabIndex = 0;
+ if (d.activeElement === _this) {
+ _this.parentNode.focus();
+ }
+ return _this.tabIndex = -1;
+ } else {
+ $.addClass(_this.parentNode, 'error');
+ return $.one(_this, 'click', Metadata.load);
+ }
+ };
+ })(this), {
+ Range: 'bytes=0-9999'
+ });
+ },
+ parse: function(data) {
+ var element, i, readInt, size, title;
+ readInt = function() {
+ var len, n;
+ n = data[i++];
+ len = 0;
+ while (n < (0x80 >> len)) {
+ len++;
+ }
+ n ^= 0x80 >> len;
+ while (len-- && i < data.length) {
+ n = (n << 8) ^ data[i++];
+ }
+ return n;
+ };
+ i = 0;
+ while (i < data.length) {
+ element = readInt();
+ size = readInt();
+ if (element === 0x3BA9) {
+ title = '';
+ while (size-- && i < data.length) {
+ title += String.fromCharCode(data[i++]);
+ }
+ return decodeURIComponent(escape(title));
+ } else if (element !== 0x8538067 && element !== 0x549A966) {
+ i += size;
+ }
+ }
+ return null;
+ }
+ };
+
+ return Metadata;
+
+}).call(this);
+
+RevealSpoilers = (function() {
+ var RevealSpoilers;
+
+ RevealSpoilers = {
+ init: function() {
+ var ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Reveal Spoiler Thumbnails'])) {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'Reveal Spoiler Thumbnails',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var file, i, len, ref, thumb;
+ if (this.isClone) {
+ return;
+ }
+ ref = this.files;
+ for (i = 0, len = ref.length; i < len; i++) {
+ file = ref[i];
+ if (!(file.thumb && file.isSpoiler)) {
+ continue;
+ }
+ thumb = file.thumb;
+ thumb.removeAttribute('style');
+ thumb.style.maxHeight = thumb.style.maxWidth = this.isReply ? '125px' : '250px';
+ if (thumb.src) {
+ thumb.src = file.thumbURL;
+ } else {
+ thumb.dataset.src = file.thumbURL;
+ }
+ }
+ }
+ };
+
+ return RevealSpoilers;
+
+}).call(this);
+
+Sauce = (function() {
+ var Sauce,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Sauce = {
+ init: function() {
+ var j, len, link, linkData, links, ref, ref1;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Sauce'])) {
+ return;
+ }
+ $.addClass(doc, 'show-sauce');
+ links = [];
+ ref1 = Conf['sauces'].split('\n');
+ for (j = 0, len = ref1.length; j < len; j++) {
+ link = ref1[j];
+ if (link[0] !== '#' && (linkData = this.parseLink(link))) {
+ links.push(linkData);
+ }
+ }
+ if (!links.length) {
+ return;
+ }
+ this.links = links;
+ this.link = $.el('a', {
+ target: '_blank',
+ className: 'sauce'
+ });
+ return Callbacks.Post.push({
+ name: 'Sauce',
+ cb: this.node
+ });
+ },
+ parseLink: function(link) {
+ var err, i, j, len, m, part, parts, ref, ref1, regexp;
+ if (!(link = link.trim())) {
+ return null;
+ }
+ parts = $.dict();
+ ref = link.split(/;(?=(?:text|boards|types|regexp|sandbox):?)/);
+ for (i = j = 0, len = ref.length; j < len; i = ++j) {
+ part = ref[i];
+ if (i === 0) {
+ parts['url'] = part;
+ } else {
+ m = part.match(/^(\w*):?(.*)$/);
+ parts[m[1]] = m[2];
+ }
+ }
+ parts['text'] || (parts['text'] = ((ref1 = parts['url'].match(/(\w+)\.\w+\//)) != null ? ref1[1] : void 0) || '?');
+ if ('boards' in parts) {
+ parts['boards'] = Filter.parseBoards(parts['boards']);
+ }
+ if ('regexp' in parts) {
+ try {
+ if ((regexp = parts['regexp'].match(/^\/(.*)\/(\w*)$/))) {
+ parts['regexp'] = RegExp(regexp[1], regexp[2]);
+ } else {
+ parts['regexp'] = RegExp(parts['regexp']);
+ }
+ } catch (error) {
+ err = error;
+ new Notice('warning', [$.tn("Invalid regexp for Sauce link:"), $.el('br'), $.tn(link), $.el('br'), $.tn(err.message)], 60);
+ return null;
+ }
+ }
+ return parts;
+ },
+ createSauceLink: function(link, post, file) {
+ var a, base, ext, j, key, len, matches, missing, parts, ref;
+ ext = file.url.match(/[^.]*$/)[0];
+ parts = $.dict();
+ $.extend(parts, link);
+ if (!(!parts['boards'] || parts['boards'][post.siteID + "/" + post.boardID] || parts['boards'][post.siteID + "/*"])) {
+ return null;
+ }
+ if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) {
+ return null;
+ }
+ if (!(!parts['regexp'] || (matches = file.name.match(parts['regexp'])))) {
+ return null;
+ }
+ missing = [];
+ ref = ['url', 'text'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ key = ref[j];
+ parts[key] = parts[key].replace(/%(T?URL|IMG|[sh]?MD5|board|name|%|semi|\$\d+)/g, function(orig, parameter) {
+ var type;
+ if (parameter[0] === '$') {
+ if (!matches) {
+ return orig;
+ }
+ type = matches[parameter.slice(1)] || '';
+ } else {
+ type = Sauce.formatters[parameter](post, file, ext);
+ if (type == null) {
+ missing.push(parameter);
+ return '';
+ }
+ }
+ if (key === 'url' && (parameter !== '%' && parameter !== 'semi')) {
+ if (/^javascript:/i.test(parts['url'])) {
+ type = JSON.stringify(type);
+ }
+ type = encodeURIComponent(type);
+ }
+ return type;
+ });
+ }
+ if ((typeof (base = g.SITE).areMD5sDeferred === "function" ? base.areMD5sDeferred(post.board) : void 0) && missing.length && !missing.filter(function(x) {
+ return !/^.?MD5$/.test(x);
+ }).length) {
+ a = Sauce.link.cloneNode(false);
+ a.dataset.skip = '1';
+ return a;
+ }
+ if (missing.length) {
+ return null;
+ }
+ a = Sauce.link.cloneNode(false);
+ a.href = parts['url'];
+ a.textContent = parts['text'];
+ if (/^javascript:/i.test(parts['url'])) {
+ a.removeAttribute('target');
+ }
+ return a;
+ },
+ node: function() {
+ var file, j, len, ref;
+ if (this.isClone) {
+ return;
+ }
+ ref = this.files;
+ for (j = 0, len = ref.length; j < len; j++) {
+ file = ref[j];
+ Sauce.file(this, file);
+ }
+ },
+ file: function(post, file) {
+ var j, len, link, node, nodes, observer, ref, skipped;
+ nodes = [];
+ skipped = [];
+ ref = Sauce.links;
+ for (j = 0, len = ref.length; j < len; j++) {
+ link = ref[j];
+ if ((node = Sauce.createSauceLink(link, post, file))) {
+ nodes.push($.tn(' '), node);
+ if (node.dataset.skip) {
+ skipped.push([link, node]);
+ }
+ }
+ }
+ $.add(file.text, nodes);
+ if (skipped.length) {
+ observer = new MutationObserver(function() {
+ var k, len1, node2, ref1;
+ if (file.text.dataset.md5) {
+ for (k = 0, len1 = skipped.length; k < len1; k++) {
+ ref1 = skipped[k], link = ref1[0], node = ref1[1];
+ if ((node2 = Sauce.createSauceLink(link, post, file))) {
+ $.replace(node, node2);
+ }
+ }
+ return observer.disconnect();
+ }
+ });
+ return observer.observe(file.text, {
+ attributes: true
+ });
+ }
+ },
+ formatters: {
+ TURL: function(post, file) {
+ return file.thumbURL;
+ },
+ URL: function(post, file) {
+ return file.url;
+ },
+ IMG: function(post, file, ext) {
+ if (ext === 'gif' || ext === 'jpg' || ext === 'jpeg' || ext === 'png') {
+ return file.url;
+ } else {
+ return file.thumbURL;
+ }
+ },
+ MD5: function(post, file) {
+ return file.MD5;
+ },
+ sMD5: function(post, file) {
+ var ref;
+ return (ref = file.MD5) != null ? ref.replace(/[+\/=]/g, function(c) {
+ return {
+ '+': '-',
+ '/': '_',
+ '=': ''
+ }[c];
+ }) : void 0;
+ },
+ hMD5: function(post, file) {
+ var c;
+ if (file.MD5) {
+ return ((function() {
+ var j, len, ref, results;
+ ref = atob(file.MD5);
+ results = [];
+ for (j = 0, len = ref.length; j < len; j++) {
+ c = ref[j];
+ results.push(("0" + (c.charCodeAt(0).toString(16))).slice(-2));
+ }
+ return results;
+ })()).join('');
+ }
+ },
+ board: function(post) {
+ return post.board.ID;
+ },
+ name: function(post, file) {
+ return file.name;
+ },
+ '%': function() {
+ return '%';
+ },
+ semi: function() {
+ return ';';
+ }
+ }
+ };
+
+ return Sauce;
+
+}).call(this);
+
+Volume = (function() {
+ var Volume;
+
+ Volume = {
+ init: function() {
+ var base, ref, unmuteEntry, volumeEntry;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && (Conf['Image Expansion'] || Conf['Image Hover'] || Conf['Image Hover in Catalog'] || Conf['Gallery']))) {
+ return;
+ }
+ $.sync('Allow Sound', function(x) {
+ var ref1;
+ Conf['Allow Sound'] = x;
+ return (ref1 = Volume.inputs) != null ? ref1.unmute.checked = x : void 0;
+ });
+ $.sync('Default Volume', function(x) {
+ var ref1;
+ Conf['Default Volume'] = x;
+ return (ref1 = Volume.inputs) != null ? ref1.volume.value = x : void 0;
+ });
+ if (Conf['Mouse Wheel Volume']) {
+ Callbacks.Post.push({
+ name: 'Mouse Wheel Volume',
+ cb: this.node
+ });
+ }
+ if (typeof (base = g.SITE).noAudio === "function" ? base.noAudio(g.BOARD) : void 0) {
+ return;
+ }
+ if (Conf['Mouse Wheel Volume']) {
+ Callbacks.CatalogThread.push({
+ name: 'Mouse Wheel Volume',
+ cb: this.catalogNode
+ });
+ }
+ unmuteEntry = UI.checkbox('Allow Sound', 'Allow Sound');
+ unmuteEntry.title = Config.main['Images and Videos']['Allow Sound'][1];
+ volumeEntry = $.el('label', {
+ title: 'Default volume for videos.'
+ });
+ $.extend(volumeEntry, {innerHTML: "<input name=\"Default Volume\" type=\"range\" min=\"0\" max=\"1\" step=\"0.01\" value=\"" + E(Conf["Default Volume"]) + "\"> Volume"});
+ this.inputs = {
+ unmute: unmuteEntry.firstElementChild,
+ volume: volumeEntry.firstElementChild
+ };
+ $.on(this.inputs.unmute, 'change', $.cb.checked);
+ $.on(this.inputs.volume, 'change', $.cb.value);
+ Header.menu.addEntry({
+ el: unmuteEntry,
+ order: 200
+ });
+ return Header.menu.addEntry({
+ el: volumeEntry,
+ order: 201
+ });
+ },
+ setup: function(video) {
+ video.muted = !Conf['Allow Sound'];
+ video.volume = Conf['Default Volume'];
+ return $.on(video, 'volumechange', Volume.change);
+ },
+ change: function() {
+ var items, key, muted, ref, val, volume;
+ ref = this, muted = ref.muted, volume = ref.volume;
+ items = {
+ 'Allow Sound': !muted,
+ 'Default Volume': volume
+ };
+ for (key in items) {
+ val = items[key];
+ if (Conf[key] === val) {
+ delete items[key];
+ }
+ }
+ $.set(items);
+ $.extend(Conf, items);
+ if (Volume.inputs) {
+ Volume.inputs.unmute.checked = !muted;
+ return Volume.inputs.volume.value = volume;
+ }
+ },
+ node: function() {
+ var base, file, i, len, ref;
+ if (typeof (base = g.SITE).noAudio === "function" ? base.noAudio(this.board) : void 0) {
+ return;
+ }
+ ref = this.files;
+ for (i = 0, len = ref.length; i < len; i++) {
+ file = ref[i];
+ if (!file.isVideo) {
+ continue;
+ }
+ if (file.thumb) {
+ $.on(file.thumb, 'wheel', Volume.wheel.bind(Header.hover));
+ }
+ $.on($('.file-info', file.text) || file.link, 'wheel', Volume.wheel.bind(file.thumbLink));
+ }
+ },
+ catalogNode: function() {
+ var file;
+ file = this.thread.OP.files[0];
+ if (!(file != null ? file.isVideo : void 0)) {
+ return;
+ }
+ return $.on(this.nodes.thumb, 'wheel', Volume.wheel.bind(Header.hover));
+ },
+ wheel: function(e) {
+ var el, volume;
+ if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) {
+ return;
+ }
+ if (!(el = $('video:not([data-md5])', this))) {
+ return;
+ }
+ if (el.muted || !$.hasAudio(el)) {
+ return;
+ }
+ volume = el.volume + 0.1;
+ if (e.deltaY < 0) {
+ volume *= 1.1;
+ }
+ if (e.deltaY > 0) {
+ volume /= 1.1;
+ }
+ el.volume = $.minmax(volume - 0.1, 0, 1);
+ return e.preventDefault();
+ }
+ };
+
+ return Volume;
+
+}).call(this);
+
+Embedding = (function() {
+ var Embedding,
+ slice = [].slice;
+
+ Embedding = {
+ init: function() {
+ var j, len, ref, ref1, type;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Linkify'] && (Conf['Embedding'] || Conf['Link Title'] || Conf['Cover Preview']))) {
+ return;
+ }
+ this.types = $.dict();
+ ref1 = this.ordered_types;
+ for (j = 0, len = ref1.length; j < len; j++) {
+ type = ref1[j];
+ this.types[type.key] = type;
+ }
+ if (Conf['Embedding'] && g.VIEW !== 'archive') {
+ this.dialog = UI.dialog('embedding', {innerHTML: "<div><div class=\"move\"></div><a href=\"javascript:;\" class=\"jump\" title=\"Jump to post\">โ†’</a><a href=\"javascript:;\" class=\"close\" title=\"Close\">ร—</a></div><div id=\"media-embed\"><div></div></div>"});
+ this.media = $('#media-embed', this.dialog);
+ $.one(d, '4chanXInitFinished', this.ready);
+ $.on(d, 'IndexRefreshInternal', function() {
+ return g.posts.forEach(function(post) {
+ var embed, k, l, len1, len2, ref2, ref3;
+ ref2 = [post].concat(slice.call(post.clones));
+ for (k = 0, len1 = ref2.length; k < len1; k++) {
+ post = ref2[k];
+ ref3 = post.nodes.embedlinks;
+ for (l = 0, len2 = ref3.length; l < len2; l++) {
+ embed = ref3[l];
+ Embedding.cb.catalogRemove.call(embed);
+ }
+ }
+ });
+ });
+ }
+ if (Conf['Link Title']) {
+ return $.on(d, '4chanXInitFinished PostsInserted', function() {
+ var key, ref2, ref3, service;
+ ref2 = Embedding.types;
+ for (key in ref2) {
+ service = ref2[key];
+ if ((ref3 = service.title) != null ? ref3.batchSize : void 0) {
+ Embedding.flushTitles(service.title);
+ }
+ }
+ });
+ }
+ },
+ events: function(post) {
+ var data, el, i, items;
+ if (g.VIEW === 'archive') {
+ return;
+ }
+ if (Conf['Embedding']) {
+ i = 0;
+ items = post.nodes.embedlinks = $$('.embedder', post.nodes.comment);
+ while (el = items[i++]) {
+ $.on(el, 'click', Embedding.cb.click);
+ if ($.hasClass(el, 'embedded')) {
+ Embedding.cb.toggle.call(el);
+ }
+ }
+ }
+ if (Conf['Cover Preview']) {
+ i = 0;
+ items = $$('.linkify', post.nodes.comment);
+ while (el = items[i++]) {
+ if ((data = Embedding.services(el))) {
+ Embedding.preview(data);
+ }
+ }
+ }
+ },
+ process: function(link, post) {
+ var data;
+ if (!(Conf['Embedding'] || Conf['Link Title'] || Conf['Cover Preview'])) {
+ return;
+ }
+ if ($.x('ancestor::pre', link)) {
+ return;
+ }
+ if (data = Embedding.services(link)) {
+ data.post = post;
+ if (Conf['Embedding'] && g.VIEW !== 'archive') {
+ Embedding.embed(data);
+ }
+ if (Conf['Link Title']) {
+ Embedding.title(data);
+ }
+ if (Conf['Cover Preview'] && g.VIEW !== 'archive') {
+ return Embedding.preview(data);
+ }
+ }
+ },
+ services: function(link) {
+ var href, j, len, match, ref, type;
+ href = link.href;
+ ref = Embedding.ordered_types;
+ for (j = 0, len = ref.length; j < len; j++) {
+ type = ref[j];
+ if ((match = type.regExp.exec(href))) {
+ return {
+ key: type.key,
+ uid: match[1],
+ options: match[2],
+ link: link
+ };
+ }
+ }
+ },
+ embed: function(data) {
+ var embed, href, key, link, name, options, post, ref, uid, value;
+ key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post;
+ href = link.href;
+ $.addClass(link, key.toLowerCase());
+ embed = $.el('a', {
+ className: 'embedder',
+ href: 'javascript:;'
+ }, {innerHTML: "(<span>un</span>embed)"});
+ ref = {
+ key: key,
+ uid: uid,
+ options: options,
+ href: href
+ };
+ for (name in ref) {
+ value = ref[name];
+ embed.dataset[name] = value;
+ }
+ $.on(embed, 'click', Embedding.cb.click);
+ $.after(link, [$.tn(' '), embed]);
+ post.nodes.embedlinks.push(embed);
+ if (Conf['Auto-embed'] && !Conf['Floating Embeds'] && !post.isFetchedQuote) {
+ if ($.hasClass(doc, 'catalog-mode')) {
+ return $.addClass(embed, 'embed-removed');
+ } else {
+ return Embedding.cb.toggle.call(embed);
+ }
+ }
+ },
+ ready: function() {
+ if (!Main.isThisPageLegit()) {
+ return;
+ }
+ $.addClass(Embedding.dialog, 'empty');
+ $.on($('.close', Embedding.dialog), 'click', Embedding.closeFloat);
+ $.on($('.move', Embedding.dialog), 'mousedown', Embedding.dragEmbed);
+ $.on($('.jump', Embedding.dialog), 'click', function() {
+ if (doc.contains(Embedding.lastEmbed)) {
+ return Header.scrollTo(Embedding.lastEmbed);
+ }
+ });
+ return $.add(d.body, Embedding.dialog);
+ },
+ closeFloat: function() {
+ delete Embedding.lastEmbed;
+ $.addClass(Embedding.dialog, 'empty');
+ return $.replace(Embedding.media.firstChild, $.el('div'));
+ },
+ dragEmbed: function() {
+ var style;
+ style = Embedding.media.style;
+ if (Embedding.dragEmbed.mouseup) {
+ $.off(d, 'mouseup', Embedding.dragEmbed);
+ Embedding.dragEmbed.mouseup = false;
+ style.pointerEvents = '';
+ return;
+ }
+ $.on(d, 'mouseup', Embedding.dragEmbed);
+ Embedding.dragEmbed.mouseup = true;
+ return style.pointerEvents = 'none';
+ },
+ title: function(data) {
+ var key, link, options, post, service, uid;
+ key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post;
+ if (!(service = Embedding.types[key].title)) {
+ return;
+ }
+ $.addClass(link, key.toLowerCase());
+ if (service.batchSize) {
+ (service.queue || (service.queue = [])).push(data);
+ if (service.queue.length >= service.batchSize) {
+ return Embedding.flushTitles(service);
+ }
+ } else {
+ return CrossOrigin.cache(service.api(uid), (function() {
+ return Embedding.cb.title(this, data);
+ }));
+ }
+ },
+ flushTitles: function(service) {
+ var cb, data, queue;
+ queue = service.queue;
+ if (!(queue != null ? queue.length : void 0)) {
+ return;
+ }
+ service.queue = [];
+ cb = function() {
+ var data, j, len;
+ for (j = 0, len = queue.length; j < len; j++) {
+ data = queue[j];
+ Embedding.cb.title(this, data);
+ }
+ };
+ return CrossOrigin.cache(service.api((function() {
+ var j, len, results;
+ results = [];
+ for (j = 0, len = queue.length; j < len; j++) {
+ data = queue[j];
+ results.push(data.uid);
+ }
+ return results;
+ })()), cb);
+ },
+ preview: function(data) {
+ var key, link, service, uid;
+ key = data.key, uid = data.uid, link = data.link;
+ if (!(service = Embedding.types[key].preview)) {
+ return;
+ }
+ return $.on(link, 'mouseover', function(e) {
+ var el, height, src;
+ src = service.url(uid);
+ height = service.height;
+ el = $.el('img', {
+ src: src,
+ id: 'ihover'
+ });
+ $.add(Header.hover, el);
+ return UI.hover({
+ root: link,
+ el: el,
+ latestEvent: e,
+ endEvents: 'mouseout click',
+ height: height
+ });
+ });
+ },
+ cb: {
+ click: function(e) {
+ var div;
+ e.preventDefault();
+ if (!$.hasClass(this, 'embedded') && (Conf['Floating Embeds'] || $.hasClass(doc, 'catalog-mode'))) {
+ if (!(div = Embedding.media.firstChild)) {
+ return;
+ }
+ $.replace(div, Embedding.cb.embed(this));
+ Embedding.lastEmbed = Get.postFromNode(this).nodes.root;
+ return $.rmClass(Embedding.dialog, 'empty');
+ } else {
+ return Embedding.cb.toggle.call(this);
+ }
+ },
+ toggle: function() {
+ if ($.hasClass(this, "embedded")) {
+ $.rm(this.nextElementSibling);
+ } else {
+ $.after(this, Embedding.cb.embed(this));
+ }
+ return $.toggleClass(this, 'embedded');
+ },
+ embed: function(a) {
+ var container, el, type;
+ container = $.el('div', {
+ className: 'media-embed'
+ });
+ $.add(container, el = (type = Embedding.types[a.dataset.key]).el(a));
+ el.style.cssText = type.style != null ? type.style : 'border: none; width: 640px; height: 360px;';
+ return container;
+ },
+ catalogRemove: function() {
+ var isCatalog;
+ isCatalog = $.hasClass(doc, 'catalog-mode');
+ if ((isCatalog && $.hasClass(this, 'embedded')) || (!isCatalog && $.hasClass(this, 'embed-removed'))) {
+ Embedding.cb.toggle.call(this);
+ return $.toggleClass(this, 'embed-removed');
+ }
+ },
+ title: function(req, data) {
+ var base1, j, k, key, len, len1, link, link2, options, post, post2, ref, ref1, service, status, text, uid;
+ key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post;
+ service = Embedding.types[key].title;
+ status = req.status;
+ if ((status === 200 || status === 304) && service.status) {
+ status = service.status(req.response)[0];
+ }
+ if (!status) {
+ return;
+ }
+ text = "[" + key + "] " + ((function() {
+ switch (status) {
+ case 200:
+ case 304:
+ text = service.text(req.response, uid);
+ if (typeof text === 'string') {
+ return text;
+ } else {
+ return text = link.textContent;
+ }
+ break;
+ case 404:
+ return "Not Found";
+ case 403:
+ case 401:
+ return "Forbidden or Private";
+ default:
+ return status + "'d";
+ }
+ })());
+ link.dataset.original = link.textContent;
+ link.textContent = text;
+ ref = post.clones;
+ for (j = 0, len = ref.length; j < len; j++) {
+ post2 = ref[j];
+ ref1 = $$('a.linkify', post2.nodes.comment);
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ link2 = ref1[k];
+ if (!(link2.href === link.href)) {
+ continue;
+ }
+ if ((base1 = link2.dataset).original == null) {
+ base1.original = link2.textContent;
+ }
+ link2.textContent = text;
+ }
+ }
+ }
+ },
+ ordered_types: [
+ {
+ key: 'audio',
+ regExp: /^[^?#]+\.(?:mp3|m4a|oga|wav|flac)(?:[?#]|$)/i,
+ style: '',
+ el: function(a) {
+ return $.el('audio', {
+ controls: true,
+ preload: 'auto',
+ src: a.dataset.href
+ });
+ }
+ }, {
+ key: 'image',
+ regExp: /^[^?#]+\.(?:gif|png|jpg|jpeg|bmp|webp)(?::\w+)?(?:[?#]|$)/i,
+ style: '',
+ el: function(a) {
+ return $.el('div', {innerHTML: "<a target=\"_blank\" href=\"" + E(a.dataset.href) + "\"><img src=\"" + E(a.dataset.href) + "\" style=\"max-width: 80vw; max-height: 80vh;\"></a>"});
+ }
+ }, {
+ key: 'video',
+ regExp: /^[^?#]+\.(?:og[gv]|webm|mp4)(?:[?#]|$)/i,
+ style: 'max-width: 80vw; max-height: 80vh;',
+ el: function(a) {
+ var el;
+ el = $.el('video', {
+ hidden: true,
+ controls: true,
+ preload: 'auto',
+ src: a.dataset.href,
+ loop: ImageHost.test(a.dataset.href.split('/')[2])
+ });
+ $.on(el, 'loadedmetadata', function() {
+ if (el.videoHeight === 0 && el.parentNode) {
+ return $.replace(el, Embedding.types.audio.el(a));
+ } else {
+ return el.hidden = false;
+ }
+ });
+ return el;
+ }
+ }, {
+ key: 'PeerTube',
+ regExp: /^(\w+:\/\/[^\/]+\/videos\/watch\/\w{8}-\w{4}-\w{4}-\w{4}-\w{12})(.*)/,
+ el: function(a) {
+ var el, options, start;
+ options = (start = a.dataset.options.match(/[?&](start=\w+)/)) ? "?" + start[1] : '';
+ el = $.el('iframe', {
+ src: a.dataset.uid.replace('/videos/watch/', '/videos/embed/') + options
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ }
+ }, {
+ key: 'BitChute',
+ regExp: /^\w+:\/\/(?:www\.)?bitchute\.com\/video\/([\w\-]+)/,
+ el: function(a) {
+ var el;
+ el = $.el('iframe', {
+ src: "https://www.bitchute.com/embed/" + a.dataset.uid + "/"
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ }
+ }, {
+ key: 'Clyp',
+ regExp: /^\w+:\/\/(?:www\.)?clyp\.it\/(\w{8})/,
+ style: 'border: 0; width: 640px; height: 160px;',
+ el: function(a) {
+ return $.el('iframe', {
+ src: "https://clyp.it/" + a.dataset.uid + "/widget"
+ });
+ },
+ title: {
+ api: function(uid) {
+ return "https://api.clyp.it/oembed?url=https://clyp.it/" + uid;
+ },
+ text: function(_) {
+ return _.title;
+ }
+ }
+ }, {
+ key: 'Dailymotion',
+ regExp: /^\w+:\/\/(?:(?:www\.)?dailymotion\.com\/(?:embed\/)?video|dai\.ly)\/([A-Za-z0-9]+)[^?]*(.*)/,
+ el: function(a) {
+ var el, options, start;
+ options = (start = a.dataset.options.match(/[?&](start=\d+)/)) ? "?" + start[1] : '';
+ el = $.el('iframe', {
+ src: "//www.dailymotion.com/embed/video/" + a.dataset.uid + options
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ },
+ title: {
+ api: function(uid) {
+ return "https://api.dailymotion.com/video/" + uid;
+ },
+ text: function(_) {
+ return _.title;
+ }
+ },
+ preview: {
+ url: function(uid) {
+ return "https://www.dailymotion.com/thumbnail/video/" + uid;
+ },
+ height: 240
+ }
+ }, {
+ key: 'Gfycat',
+ regExp: /^\w+:\/\/(?:www\.)?gfycat\.com\/(?:iframe\/)?(\w+)/,
+ el: function(a) {
+ var el;
+ el = $.el('iframe', {
+ src: "//gfycat.com/ifr/" + a.dataset.uid
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ }
+ }, {
+ key: 'Gist',
+ regExp: /^\w+:\/\/gist\.github\.com\/[\w\-]+\/(\w+)/,
+ style: '',
+ el: (function() {
+ var counter;
+ counter = 0;
+ return function(a) {
+ var el;
+ el = $.el('pre', {
+ hidden: true,
+ id: "gist-embed-" + (counter++)
+ });
+ CrossOrigin.cache("https://api.github.com/gists/" + a.dataset.uid, function() {
+ el.textContent = Object.values(this.response.files)[0].content;
+ el.className = 'prettyprint';
+ $.global(function() {
+ return typeof window.prettyPrint === "function" ? window.prettyPrint((function() {}), document.getElementById(document.currentScript.dataset.id).parentNode) : void 0;
+ }, {
+ id: el.id
+ });
+ return el.hidden = false;
+ });
+ return el;
+ };
+ })(),
+ title: {
+ api: function(uid) {
+ return "https://api.github.com/gists/" + uid;
+ },
+ text: function(arg) {
+ var file, files;
+ files = arg.files;
+ for (file in files) {
+ if (files.hasOwnProperty(file)) {
+ return file;
+ }
+ }
+ }
+ }
+ }, {
+ key: 'InstallGentoo',
+ regExp: /^\w+:\/\/paste\.installgentoo\.com\/view\/(?:raw\/|download\/|embed\/)?(\w+)/,
+ el: function(a) {
+ return $.el('iframe', {
+ src: "https://paste.installgentoo.com/view/embed/" + a.dataset.uid
+ });
+ }
+ }, {
+ key: 'LiveLeak',
+ regExp: /^\w+:\/\/(?:\w+\.)?liveleak\.com\/.*\?.*[tif]=(\w+)/,
+ el: function(a) {
+ var el;
+ el = $.el('iframe', {
+ src: "https://www.liveleak.com/e/" + a.dataset.uid
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ }
+ }, {
+ key: 'Loopvid',
+ regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|m2|pc|1c|pi|ni|wl|ko|mm|ic|gc)\/[\w\-\/]+(?:,[\w\-\/]+)*|fc\/\w+\/\d+|https?:\/\/.+)/,
+ style: 'max-width: 80vw; max-height: 80vh;',
+ el: function(a) {
+ var _, base, el, host, j, k, l, len, len1, len2, name, names, ref, ref1, type, types, url, urls;
+ el = $.el('video', {
+ controls: true,
+ preload: 'auto',
+ loop: true
+ });
+ if (/^http/.test(a.dataset.uid)) {
+ $.add(el, $.el('source', {
+ src: a.dataset.uid
+ }));
+ return el;
+ }
+ ref = a.dataset.uid.match(/(\w+)\/(.*)/), _ = ref[0], host = ref[1], names = ref[2];
+ types = (function() {
+ switch (host) {
+ case 'gd':
+ case 'wu':
+ case 'fc':
+ return [''];
+ case 'gc':
+ return ['giant', 'fat', 'zippy'];
+ default:
+ return ['.webm', '.mp4'];
+ }
+ })();
+ ref1 = names.split(',');
+ for (j = 0, len = ref1.length; j < len; j++) {
+ name = ref1[j];
+ for (k = 0, len1 = types.length; k < len1; k++) {
+ type = types[k];
+ base = "" + name + type;
+ urls = (function() {
+ switch (host) {
+ case 'pf':
+ return ["https://kastden.org/_loopvid_media/pf/" + base, "https://web.archive.org/web/2/http://a.pomf.se/" + base];
+ case 'kd':
+ return ["https://kastden.org/loopvid/" + base];
+ case 'lv':
+ return ["https://lv.kastden.org/" + base];
+ case 'gd':
+ return ["https://docs.google.com/uc?export=download&id=" + base];
+ case 'gh':
+ return ["https://googledrive.com/host/" + base];
+ case 'db':
+ return ["https://dl.dropboxusercontent.com/u/" + base];
+ case 'dx':
+ return ["https://dl.dropboxusercontent.com/" + base];
+ case 'nn':
+ return ["https://kastden.org/_loopvid_media/nn/" + base];
+ case 'cp':
+ return ["https://copy.com/" + base];
+ case 'wu':
+ return ["http://webmup.com/" + base + "/vid.webm"];
+ case 'ig':
+ return ["https://i.imgur.com/" + base];
+ case 'ky':
+ return ["https://kastden.org/_loopvid_media/ky/" + base];
+ case 'mf':
+ return ["https://kastden.org/_loopvid_media/mf/" + base, "https://web.archive.org/web/2/https://d.maxfile.ro/" + base];
+ case 'm2':
+ return ["https://kastden.org/_loopvid_media/m2/" + base];
+ case 'pc':
+ return ["https://kastden.org/_loopvid_media/pc/" + base, "https://web.archive.org/web/2/http://a.pomf.cat/" + base];
+ case '1c':
+ return ["http://b.1339.cf/" + base];
+ case 'pi':
+ return ["https://kastden.org/_loopvid_media/pi/" + base, "https://web.archive.org/web/2/https://u.pomf.is/" + base];
+ case 'ni':
+ return ["https://kastden.org/_loopvid_media/ni/" + base, "https://web.archive.org/web/2/https://u.nya.is/" + base];
+ case 'wl':
+ return ["http://webm.land/media/" + base];
+ case 'ko':
+ return ["https://kordy.kastden.org/loopvid/" + base];
+ case 'mm':
+ return ["https://kastden.org/_loopvid_media/mm/" + base, "https://web.archive.org/web/2/https://my.mixtape.moe/" + base];
+ case 'ic':
+ return ["https://media.8ch.net/file_store/" + base];
+ case 'fc':
+ return ["//" + (ImageHost.host()) + "/" + base + ".webm"];
+ case 'gc':
+ return ["https://" + type + ".gfycat.com/" + name + ".webm"];
+ }
+ })();
+ for (l = 0, len2 = urls.length; l < len2; l++) {
+ url = urls[l];
+ $.add(el, $.el('source', {
+ src: url
+ }));
+ }
+ }
+ }
+ return el;
+ }
+ }, {
+ key: 'Openings.moe',
+ regExp: /^\w+:\/\/openings.moe\/\?video=([^.&=]+)/,
+ style: 'width: 1280px; height: 720px; max-width: 80vw; max-height: 80vh;',
+ el: function(a) {
+ var el;
+ el = $.el('iframe', {
+ src: "https://openings.moe/?video=" + a.dataset.uid
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ }
+ }, {
+ key: 'Pastebin',
+ regExp: /^\w+:\/\/(?:\w+\.)?pastebin\.com\/(?!u\/)(?:[\w.]+(?:\/|\?i\=))?(\w+)/,
+ el: function(a) {
+ var div;
+ return div = $.el('iframe', {
+ src: "//pastebin.com/embed_iframe.php?i=" + a.dataset.uid
+ });
+ }
+ }, {
+ key: 'SoundCloud',
+ regExp: /^\w+:\/\/(?:www\.)?(?:soundcloud\.com\/|snd\.sc\/)([\w\-\/]+)/,
+ style: 'border: 0; width: 500px; height: 400px;',
+ el: function(a) {
+ return $.el('iframe', {
+ src: "https://w.soundcloud.com/player/?visual=true&show_comments=false&url=https%3A%2F%2Fsoundcloud.com%2F" + (encodeURIComponent(a.dataset.uid))
+ });
+ },
+ title: {
+ api: function(uid) {
+ return location.protocol + "//soundcloud.com/oembed?format=json&url=https%3A%2F%2Fsoundcloud.com%2F" + (encodeURIComponent(uid));
+ },
+ text: function(_) {
+ return _.title;
+ }
+ }
+ }, {
+ key: 'StrawPoll',
+ regExp: /^\w+:\/\/(?:www\.)?strawpoll\.me\/(?:embed_\d+\/)?(\d+(?:\/r)?)/,
+ style: 'border: 0; width: 600px; height: 406px;',
+ el: function(a) {
+ return $.el('iframe', {
+ src: "https://www.strawpoll.me/embed_1/" + a.dataset.uid
+ });
+ }
+ }, {
+ key: 'Streamable',
+ regExp: /^\w+:\/\/(?:www\.)?streamable\.com\/(\w+)/,
+ el: function(a) {
+ var el;
+ el = $.el('iframe', {
+ src: "https://streamable.com/o/" + a.dataset.uid
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ },
+ title: {
+ api: function(uid) {
+ return "https://api.streamable.com/oembed?url=https://streamable.com/" + uid;
+ },
+ text: function(_) {
+ return _.title;
+ }
+ }
+ }, {
+ key: 'TwitchTV',
+ regExp: /^\w+:\/\/(?:www\.|secure\.|clips\.|m\.)?twitch\.tv\/(\w[^#\&\?]*)/,
+ el: function(a) {
+ var el, m, time, url;
+ m = a.dataset.href.match(/^\w+:\/\/(?:(clips\.)|\w+\.)?twitch\.tv\/(?:\w+\/)?(clip\/)?(\w[^#\&\?]*)/);
+ if (m[1] || m[2]) {
+ url = "//clips.twitch.tv/embed?clip=" + m[3] + "&parent=" + location.hostname;
+ } else {
+ m = a.dataset.uid.match(/(\w+)(?:\/(?:v\/)?(\d+))?/);
+ url = "//player.twitch.tv/?" + (m[2] ? "video=v" + m[2] : "channel=" + m[1]) + "&autoplay=false&parent=" + location.hostname;
+ if ((time = a.dataset.href.match(/\bt=(\w+)/))) {
+ url += "&time=" + time[1];
+ }
+ }
+ el = $.el('iframe', {
+ src: url
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ }
+ }, {
+ key: 'Twitter',
+ regExp: /^\w+:\/\/(?:www\.|mobile\.)?twitter\.com\/(\w+\/status\/\d+)/,
+ style: 'border: none; width: 550px; height: 250px; overflow: hidden; resize: both;',
+ el: function(a) {
+ var cont, el, onMessage;
+ el = $.el('iframe');
+ $.on(el, 'load', function() {
+ return this.contentWindow.postMessage({
+ element: 't',
+ query: 'height'
+ }, 'https://twitframe.com');
+ });
+ onMessage = function(e) {
+ if (e.source === el.contentWindow && e.origin === 'https://twitframe.com') {
+ $.off(window, 'message', onMessage);
+ return (cont || el).style.height = (+$.minmax(e.data.height, 250, 0.8 * doc.clientHeight)) + "px";
+ }
+ };
+ $.on(window, 'message', onMessage);
+ el.src = "https://twitframe.com/show?url=https://twitter.com/" + a.dataset.uid;
+ if ($.engine === 'gecko') {
+ el.style.cssText = 'border: none; width: 100%; height: 100%;';
+ cont = $.el('div');
+ $.add(cont, el);
+ return cont;
+ } else {
+ return el;
+ }
+ }
+ }, {
+ key: 'VidLii',
+ regExp: /^\w+:\/\/(?:www\.)?vidlii\.com\/watch\?v=(\w{11})/,
+ style: 'border: none; width: 640px; height: 392px;',
+ el: function(a) {
+ var el;
+ el = $.el('iframe', {
+ src: "https://www.vidlii.com/embed?v=" + a.dataset.uid + "&a=0"
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ }
+ }, {
+ key: 'Vimeo',
+ regExp: /^\w+:\/\/(?:www\.)?vimeo\.com\/(\d+)/,
+ el: function(a) {
+ var el;
+ el = $.el('iframe', {
+ src: "//player.vimeo.com/video/" + a.dataset.uid + "?wmode=opaque"
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ },
+ title: {
+ api: function(uid) {
+ return "https://vimeo.com/api/oembed.json?url=https://vimeo.com/" + uid;
+ },
+ text: function(_) {
+ return _.title;
+ }
+ }
+ }, {
+ key: 'Vine',
+ regExp: /^\w+:\/\/(?:www\.)?vine\.co\/v\/(\w+)/,
+ style: 'border: none; width: 500px; height: 500px;',
+ el: function(a) {
+ return $.el('iframe', {
+ src: "https://vine.co/v/" + a.dataset.uid + "/card"
+ });
+ }
+ }, {
+ key: 'Vocaroo',
+ regExp: /^\w+:\/\/(?:(?:www\.|old\.)?vocaroo\.com|voca\.ro)\/((?:i\/)?\w+)/,
+ style: '',
+ el: function(a) {
+ var el;
+ el = $.el('iframe');
+ el.width = 300;
+ el.height = 60;
+ el.setAttribute('frameborder', 0);
+ el.src = "https://vocaroo.com/embed/" + (a.dataset.uid.replace(/^i\//, '')) + "?autoplay=0";
+ return el;
+ }
+ }, {
+ key: 'YouTube',
+ regExp: /^\w+:\/\/(?:youtu.be\/|[\w.]*youtube[\w.]*\/.*(?:v=|\bembed\/|\bv\/))([\w\-]{11})(.*)/,
+ el: function(a) {
+ var el, start;
+ start = a.dataset.options.match(/\b(?:star)?t\=(\w+)/);
+ if (start) {
+ start = start[1];
+ }
+ if (start && !/^\d+$/.test(start)) {
+ start += ' 0h0m0s';
+ start = 3600 * start.match(/(\d+)h/)[1] + 60 * start.match(/(\d+)m/)[1] + 1 * start.match(/(\d+)s/)[1];
+ }
+ el = $.el('iframe', {
+ src: "//www.youtube.com/embed/" + a.dataset.uid + "?rel=0&wmode=opaque" + (start ? '&start=' + start : '')
+ });
+ el.setAttribute("allowfullscreen", "true");
+ return el;
+ },
+ title: {
+ api: function(uid) {
+ return "https://www.youtube.com/oembed?url=https%3A//www.youtube.com/watch%3Fv%3D" + uid + "&format=json";
+ },
+ text: function(_) {
+ return _.title;
+ },
+ status: function(_) {
+ var m;
+ if (_.error) {
+ m = _.error.match(/^(\d*)\s*(.*)/);
+ return [+m[1], m[2]];
+ } else {
+ return [200, 'OK'];
+ }
+ }
+ },
+ preview: {
+ url: function(uid) {
+ return "https://img.youtube.com/vi/" + uid + "/0.jpg";
+ },
+ height: 360
+ }
+ }
+ ]
+ };
+
+ return Embedding;
+
+}).call(this);
+
+Linkify = (function() {
+ var Linkify;
+
+ Linkify = {
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') || !Conf['Linkify']) {
+ return;
+ }
+ if (Conf['Comment Expansion']) {
+ ExpandComment.callbacks.push(this.node);
+ }
+ Callbacks.Post.push({
+ name: 'Linkify',
+ cb: this.node
+ });
+ return Embedding.init();
+ },
+ node: function() {
+ var base, j, k, len, len1, link, links, ref;
+ if (this.isClone) {
+ return Embedding.events(this);
+ }
+ if (!Linkify.regString.test(this.info.comment)) {
+ return;
+ }
+ ref = $$('a', this.nodes.comment);
+ for (j = 0, len = ref.length; j < len; j++) {
+ link = ref[j];
+ if (!(typeof (base = g.SITE).isLinkified === "function" ? base.isLinkified(link) : void 0)) {
+ continue;
+ }
+ $.addClass(link, 'linkify');
+ if (ImageHost.useFaster) {
+ ImageHost.fixLinks([link]);
+ }
+ Embedding.process(link, this);
+ }
+ links = Linkify.process(this.nodes.comment);
+ if (ImageHost.useFaster) {
+ ImageHost.fixLinks(links);
+ }
+ for (k = 0, len1 = links.length; k < len1; k++) {
+ link = links[k];
+ Embedding.process(link, this);
+ }
+ },
+ process: function(node) {
+ var data, end, endNode, i, index, length, links, part1, part2, ref, ref1, result, saved, snapshot, space, test, word;
+ test = /[^\s"]+/g;
+ space = /[\s"]/;
+ snapshot = $.X('.//br|.//text()', node);
+ i = 0;
+ links = [];
+ while (node = snapshot.snapshotItem(i++)) {
+ data = node.data;
+ if (!data || node.parentElement.nodeName === "A") {
+ continue;
+ }
+ while (result = test.exec(data)) {
+ index = result.index;
+ endNode = node;
+ word = result[0];
+ if ((length = index + word.length) === data.length) {
+ test.lastIndex = 0;
+ while ((saved = snapshot.snapshotItem(i++))) {
+ if (saved.nodeName === 'BR' || (saved.parentElement.nodeName === 'P' && !saved.previousSibling)) {
+ if ((part1 = word.match(/(https?:\/\/)?([a-z\d-]+\.)*[a-z\d-]+$/i)) && (part2 = (ref = snapshot.snapshotItem(i)) != null ? (ref1 = ref.data) != null ? ref1.match(/^(\.[a-z\d-]+)*\//i) : void 0 : void 0) && (part1[0] + part2[0]).search(Linkify.regString) === 0) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (saved.parentElement.nodeName === "A" && !Linkify.regString.test(word)) {
+ break;
+ }
+ endNode = saved;
+ data = saved.data;
+ if (end = space.exec(data)) {
+ word += data.slice(0, end.index);
+ test.lastIndex = length = end.index;
+ i--;
+ break;
+ } else {
+ length = data.length;
+ word += data;
+ }
+ }
+ }
+ if (Linkify.regString.test(word)) {
+ links.push(Linkify.makeRange(node, endNode, index, length));
+ }
+ if (!(test.lastIndex && node === endNode)) {
+ break;
+ }
+ }
+ }
+ i = links.length;
+ while (i--) {
+ links[i] = Linkify.makeLink(links[i]);
+ }
+ return links;
+ },
+ regString: /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/?])|([-a-z\d]+[.])+(aero|asia|biz|cat|com|coop|dance|info|int|jobs|mobi|moe|museum|name|net|org|post|pro|tel|travel|xxx|xyz|edu|gov|mil|[a-z]{2})([:\/]|(?![^\s"]))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i,
+ makeRange: function(startNode, endNode, startOffset, endOffset) {
+ var range;
+ range = document.createRange();
+ range.setStart(startNode, startOffset);
+ range.setEnd(endNode, endOffset);
+ return range;
+ },
+ makeLink: function(range) {
+ var a, encodedDomain, i, t, text;
+ text = range.toString();
+ i = text.search(Linkify.regString);
+ if (i > 0) {
+ text = text.slice(i);
+ while (range.startOffset + i >= range.startContainer.data.length) {
+ i--;
+ }
+ if (i) {
+ range.setStart(range.startContainer, range.startOffset + i);
+ }
+ }
+ i = 0;
+ while (/[)\]}>.,]/.test(t = text.charAt(text.length - (1 + i)))) {
+ if (!(/[.,]/.test(t) || (text.match(/[()\[\]{}<>]/g)).length % 2)) {
+ break;
+ }
+ i++;
+ }
+ if (i) {
+ text = text.slice(0, -i);
+ while (range.endOffset - i < 0) {
+ i--;
+ }
+ if (i) {
+ range.setEnd(range.endContainer, range.endOffset - i);
+ }
+ }
+ if (!/((mailto|magnet):|.+:\/\/)/.test(text)) {
+ text = (/@/.test(text) ? 'mailto:' : 'http://') + text;
+ }
+ if (encodedDomain = text.match(/^(https?:\/\/[^\/]*%[0-9a-f]{2})(.*)$/i)) {
+ text = encodedDomain[1].replace(/%([0-9a-f]{2})/ig, function(x, y) {
+ if (y === '25') {
+ return x;
+ } else {
+ return String.fromCharCode(parseInt(y, 16));
+ }
+ }) + encodedDomain[2];
+ }
+ a = $.el('a', {
+ className: 'linkify',
+ rel: 'noreferrer noopener',
+ target: '_blank',
+ href: text
+ });
+ $.add(a, range.extractContents());
+ range.insertNode(a);
+ return a;
+ }
+ };
+
+ return Linkify;
+
+}).call(this);
+
+ArchiveLink = (function() {
+ var ArchiveLink;
+
+ ArchiveLink = {
+ init: function() {
+ var div, entry, i, len, ref, ref1, type;
+ if (!(g.SITE.software === 'yotsuba' && ((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Menu'] && Conf['Archive Link'])) {
+ return;
+ }
+ div = $.el('div', {
+ textContent: 'Archive'
+ });
+ entry = {
+ el: div,
+ order: 60,
+ open: function(arg) {
+ var ID, board, thread;
+ ID = arg.ID, thread = arg.thread, board = arg.board;
+ return !!Redirect.to('thread', {
+ postID: ID,
+ threadID: thread.ID,
+ boardID: board.ID
+ });
+ },
+ subEntries: []
+ };
+ ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Capcode', 'capcode'], ['Subject', 'subject'], ['Flag', 'country'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
+ for (i = 0, len = ref1.length; i < len; i++) {
+ type = ref1[i];
+ entry.subEntries.push(this.createSubEntry(type[0], type[1]));
+ }
+ return Menu.menu.addEntry(entry);
+ },
+ createSubEntry: function(text, type) {
+ var el, open;
+ el = $.el('a', {
+ textContent: text,
+ target: '_blank'
+ });
+ open = type === 'post' ? function(arg) {
+ var ID, board, thread;
+ ID = arg.ID, thread = arg.thread, board = arg.board;
+ el.href = Redirect.to('thread', {
+ postID: ID,
+ threadID: thread.ID,
+ boardID: board.ID
+ });
+ return true;
+ } : function(post) {
+ var ref, typeParam, value;
+ typeParam = type === 'country' && post.info.flagCodeTroll ? 'troll_country' : type;
+ value = type === 'country' ? post.info.flagCode || ((ref = post.info.flagCodeTroll) != null ? ref.toLowerCase() : void 0) : Filter.values(type, post)[0];
+ if (!value) {
+ return false;
+ }
+ el.href = Redirect.to('search', {
+ boardID: post.board.ID,
+ type: typeParam,
+ value: value,
+ isSearch: true
+ });
+ return true;
+ };
+ return {
+ el: el,
+ open: open
+ };
+ }
+ };
+
+ return ArchiveLink;
+
+}).call(this);
+
+CopyTextLink = (function() {
+ var CopyTextLink;
+
+ CopyTextLink = {
+ init: function() {
+ var a, ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Menu'] && Conf['Copy Text Link'])) {
+ return;
+ }
+ a = $.el('a', {
+ className: 'copy-text-link',
+ href: 'javascript:;',
+ textContent: 'Copy Text'
+ });
+ $.on(a, 'click', CopyTextLink.copy);
+ return Menu.menu.addEntry({
+ el: a,
+ order: 12,
+ open: function(post) {
+ CopyTextLink.text = (post.origin || post).commentOrig();
+ return true;
+ }
+ });
+ },
+ copy: function() {
+ var el;
+ el = $.el('textarea', {
+ className: 'copy-text-element',
+ value: CopyTextLink.text
+ });
+ $.add(d.body, el);
+ el.select();
+ try {
+ d.execCommand('copy');
+ } catch (error) {}
+ return $.rm(el);
+ }
+ };
+
+ return CopyTextLink;
+
+}).call(this);
+
+DeleteLink = (function() {
+ var DeleteLink;
+
+ DeleteLink = {
+ auto: [$.dict(), $.dict()],
+ init: function() {
+ var div, fileEl, fileEntry, postEl, postEntry, ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Menu'] && Conf['Delete Link'])) {
+ return;
+ }
+ div = $.el('div', {
+ className: 'delete-link',
+ textContent: 'Delete'
+ });
+ postEl = $.el('a', {
+ className: 'delete-post',
+ href: 'javascript:;'
+ });
+ fileEl = $.el('a', {
+ className: 'delete-file',
+ href: 'javascript:;'
+ });
+ this.nodes = {
+ menu: div.firstChild,
+ links: [postEl, fileEl]
+ };
+ postEntry = {
+ el: postEl,
+ open: function() {
+ postEl.textContent = DeleteLink.linkText(false);
+ $.on(postEl, 'click', DeleteLink.toggle);
+ return true;
+ }
+ };
+ fileEntry = {
+ el: fileEl,
+ open: function(arg) {
+ var file;
+ file = arg.file;
+ if (!file || file.isDead) {
+ return false;
+ }
+ fileEl.textContent = DeleteLink.linkText(true);
+ $.on(fileEl, 'click', DeleteLink.toggle);
+ return true;
+ }
+ };
+ return Menu.menu.addEntry({
+ el: div,
+ order: 40,
+ open: function(post) {
+ if (post.isDead) {
+ return false;
+ }
+ DeleteLink.post = post;
+ DeleteLink.nodes.menu.textContent = DeleteLink.menuText();
+ DeleteLink.cooldown.start(post);
+ return true;
+ },
+ subEntries: [postEntry, fileEntry]
+ });
+ },
+ menuText: function() {
+ var seconds;
+ if (seconds = DeleteLink.cooldown.seconds[DeleteLink.post.fullID]) {
+ return "Delete (" + seconds + ")";
+ } else {
+ return 'Delete';
+ }
+ },
+ linkText: function(fileOnly) {
+ var text;
+ text = fileOnly ? 'File' : 'Post';
+ if (DeleteLink.auto[+fileOnly][DeleteLink.post.fullID]) {
+ text = "Deleting " + (text.toLowerCase()) + "...";
+ }
+ return text;
+ },
+ toggle: function() {
+ var auto, fileOnly, post;
+ post = DeleteLink.post;
+ fileOnly = $.hasClass(this, 'delete-file');
+ auto = DeleteLink.auto[+fileOnly];
+ if (auto[post.fullID]) {
+ delete auto[post.fullID];
+ } else {
+ auto[post.fullID] = true;
+ }
+ this.textContent = DeleteLink.linkText(fileOnly);
+ if (!DeleteLink.cooldown.seconds[post.fullID]) {
+ return DeleteLink["delete"](post, fileOnly);
+ }
+ },
+ "delete": function(post, fileOnly) {
+ var form, link;
+ link = DeleteLink.nodes.links[+fileOnly];
+ delete DeleteLink.auto[+fileOnly][post.fullID];
+ if (post.fullID === DeleteLink.post.fullID) {
+ $.off(link, 'click', DeleteLink.toggle);
+ }
+ form = {
+ mode: 'usrdel',
+ onlyimgdel: fileOnly,
+ pwd: QR.persona.getPassword()
+ };
+ form[+post.ID] = 'delete';
+ return $.ajax($.id('delform').action.replace("/" + g.BOARD + "/", "/" + post.board + "/"), {
+ responseType: 'document',
+ withCredentials: true,
+ onloadend: function() {
+ return DeleteLink.load(link, post, fileOnly, this.response);
+ },
+ form: $.formData(form)
+ });
+ },
+ load: function(link, post, fileOnly, resDoc) {
+ var el, msg;
+ if (!resDoc) {
+ new Notice('warning', 'Connection error, please retry.', 20);
+ if (post.fullID === DeleteLink.post.fullID) {
+ $.on(link, 'click', DeleteLink.toggle);
+ }
+ return;
+ }
+ link.textContent = DeleteLink.linkText(fileOnly);
+ if (resDoc.title === '4chan - Banned') {
+ el = $.el('span', {innerHTML: "You can&#039;t delete posts because you are <a href=\"//www.4chan.org/banned\" target=\"_blank\">banned</a>."});
+ return new Notice('warning', el, 20);
+ } else if (msg = resDoc.getElementById('errmsg')) {
+ new Notice('warning', msg.textContent, 20);
+ if (post.fullID === DeleteLink.post.fullID) {
+ $.on(link, 'click', DeleteLink.toggle);
+ }
+ if (QR.cooldown.data && Conf['Cooldown'] && /\bwait\b/i.test(msg.textContent)) {
+ DeleteLink.cooldown.start(post, 5);
+ DeleteLink.auto[+fileOnly][post.fullID] = true;
+ return DeleteLink.nodes.links[+fileOnly].textContent = DeleteLink.linkText(fileOnly);
+ }
+ } else {
+ if (!fileOnly) {
+ QR.cooldown["delete"](post);
+ }
+ if (resDoc.title === 'Updating index...') {
+ (post.origin || post).kill(fileOnly);
+ }
+ if (post.fullID === DeleteLink.post.fullID) {
+ return link.textContent = 'Deleted';
+ }
+ }
+ },
+ cooldown: {
+ seconds: $.dict(),
+ start: function(post, seconds) {
+ if (DeleteLink.cooldown.seconds[post.fullID] != null) {
+ return;
+ }
+ if (seconds == null) {
+ seconds = QR.cooldown.secondsDeletion(post);
+ }
+ if (seconds > 0) {
+ DeleteLink.cooldown.seconds[post.fullID] = seconds;
+ return DeleteLink.cooldown.count(post);
+ }
+ },
+ count: function(post) {
+ var fileOnly, i, len, ref;
+ if (post.fullID === DeleteLink.post.fullID) {
+ DeleteLink.nodes.menu.textContent = DeleteLink.menuText();
+ }
+ if (DeleteLink.cooldown.seconds[post.fullID] > 0 && Conf['Cooldown']) {
+ DeleteLink.cooldown.seconds[post.fullID]--;
+ setTimeout(DeleteLink.cooldown.count, 1000, post);
+ } else {
+ delete DeleteLink.cooldown.seconds[post.fullID];
+ ref = [false, true];
+ for (i = 0, len = ref.length; i < len; i++) {
+ fileOnly = ref[i];
+ if (DeleteLink.auto[+fileOnly][post.fullID]) {
+ DeleteLink["delete"](post, fileOnly);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ return DeleteLink;
+
+}).call(this);
+
+DownloadLink = (function() {
+ var DownloadLink;
+
+ DownloadLink = {
+ init: function() {
+ var a, ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Menu'] && Conf['Download Link'])) {
+ return;
+ }
+ a = $.el('a', {
+ className: 'download-link',
+ textContent: 'Download file'
+ });
+ $.on(a, 'click', ImageCommon.download);
+ return Menu.menu.addEntry({
+ el: a,
+ order: 100,
+ open: function(arg) {
+ var file;
+ file = arg.file;
+ if (!file) {
+ return false;
+ }
+ a.href = file.url;
+ a.download = file.name;
+ return true;
+ }
+ });
+ }
+ };
+
+ return DownloadLink;
+
+}).call(this);
+
+Menu = (function() {
+ var Menu;
+
+ Menu = {
+ init: function() {
+ var ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Menu'])) {
+ return;
+ }
+ this.button = $.el('a', {
+ className: 'menu-button',
+ href: 'javascript:;'
+ });
+ $.extend(this.button, {innerHTML: "<i class=\"fa fa-angle-down\"></i>"});
+ this.menu = new UI.Menu('post');
+ Callbacks.Post.push({
+ name: 'Menu',
+ cb: this.node
+ });
+ return Callbacks.CatalogThread.push({
+ name: 'Menu',
+ cb: this.catalogNode
+ });
+ },
+ node: function() {
+ var button;
+ if (this.isClone) {
+ button = $('.menu-button', this.nodes.info);
+ $.rmClass(button, 'active');
+ $.rm($('.dialog', this.nodes.info));
+ Menu.makeButton(this, button);
+ return;
+ }
+ return $.add(this.nodes.info, Menu.makeButton(this));
+ },
+ catalogNode: function() {
+ return $.after(this.nodes.icons, Menu.makeButton(this.thread.OP));
+ },
+ makeButton: function(post, button) {
+ button || (button = Menu.button.cloneNode(true));
+ $.on(button, 'click', function(e) {
+ return Menu.menu.toggle(e, this, post);
+ });
+ return button;
+ }
+ };
+
+ return Menu;
+
+}).call(this);
+
+ReportLink = (function() {
+ var ReportLink;
+
+ ReportLink = {
+ init: function() {
+ var a, ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Menu'] && Conf['Report Link'])) {
+ return;
+ }
+ a = $.el('a', {
+ className: 'report-link',
+ href: 'javascript:;',
+ textContent: 'Report'
+ });
+ $.on(a, 'click', ReportLink.report);
+ return Menu.menu.addEntry({
+ el: a,
+ order: 10,
+ open: function(post) {
+ ReportLink.url = "//sys." + (location.hostname.split('.')[1]) + ".org/" + post.board + "/imgboard.php?mode=report&no=" + post;
+ if (d.cookie.indexOf('pass_enabled=1') >= 0) {
+ ReportLink.dims = 'width=350,height=275';
+ } else {
+ ReportLink.dims = 'width=400,height=550';
+ }
+ return true;
+ }
+ });
+ },
+ report: function() {
+ var dims, id, set, url;
+ url = ReportLink.url, dims = ReportLink.dims;
+ id = Date.now();
+ set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1," + dims;
+ return window.open(url, id, set);
+ }
+ };
+
+ return ReportLink;
+
+}).call(this);
+
+AntiAutoplay = (function() {
+ var AntiAutoplay;
+
+ AntiAutoplay = {
+ init: function() {
+ var audio, i, len, ref;
+ if (!Conf['Disable Autoplaying Sounds']) {
+ return;
+ }
+ $.addClass(doc, 'anti-autoplay');
+ ref = $$('audio[autoplay]', doc);
+ for (i = 0, len = ref.length; i < len; i++) {
+ audio = ref[i];
+ this.stop(audio);
+ }
+ window.addEventListener('loadstart', ((function(_this) {
+ return function(e) {
+ return _this.stop(e.target);
+ };
+ })(this)), true);
+ Callbacks.Post.push({
+ name: 'Disable Autoplaying Sounds',
+ cb: this.node
+ });
+ return $.ready((function(_this) {
+ return function() {
+ return _this.process(d.body);
+ };
+ })(this));
+ },
+ stop: function(audio) {
+ if (!audio.autoplay) {
+ return;
+ }
+ audio.pause();
+ audio.autoplay = false;
+ if (audio.controls) {
+ return;
+ }
+ audio.controls = true;
+ return $.addClass(audio, 'controls-added');
+ },
+ node: function() {
+ return AntiAutoplay.process(this.nodes.comment);
+ },
+ process: function(root) {
+ var i, iframe, j, len, len1, object, ref, ref1;
+ ref = $$('iframe[src*="youtube"][src*="autoplay=1"]', root);
+ for (i = 0, len = ref.length; i < len; i++) {
+ iframe = ref[i];
+ AntiAutoplay.processVideo(iframe, 'src');
+ }
+ ref1 = $$('object[data*="youtube"][data*="autoplay=1"]', root);
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ object = ref1[j];
+ AntiAutoplay.processVideo(object, 'data');
+ }
+ },
+ processVideo: function(el, attr) {
+ el[attr] = el[attr].replace(/\?autoplay=1&?/, '?').replace('&autoplay=1', '');
+ if (window.getComputedStyle(el).display === 'none') {
+ el.style.display = 'block';
+ }
+ return $.addClass(el, 'autoplay-removed');
+ }
+ };
+
+ return AntiAutoplay;
+
+}).call(this);
+
+Banner = (function() {
+ var Banner,
+ slice = [].slice;
+
+ Banner = {
+ init: function() {
+ if (Conf['Custom Board Titles']) {
+ this.db = new DataBoard('customTitles', null, true);
+ }
+ $.asap((function() {
+ return d.body;
+ }), function() {
+ return $.asap((function() {
+ return $('hr');
+ }), Banner.ready);
+ });
+ if (g.BOARD.ID !== 'f') {
+ return Main.ready(function() {
+ return $.queueTask(Banner.load);
+ });
+ }
+ },
+ ready: function() {
+ var banner, children;
+ banner = $(".boardBanner");
+ children = banner.children;
+ if (g.VIEW === 'thread' && Conf['Remove Thread Excerpt']) {
+ Banner.setTitle(children[1].textContent);
+ }
+ children[0].title = "Click to change";
+ $.on(children[0], 'click', Banner.cb.toggle);
+ if (Conf['Custom Board Titles']) {
+ Banner.custom(children[1]);
+ if (children[2]) {
+ return Banner.custom(children[2]);
+ }
+ }
+ },
+ load: function() {
+ var bannerCnt, img;
+ bannerCnt = $.id('bannerCnt');
+ if (!bannerCnt.firstChild) {
+ img = $.el('img', {
+ alt: '4chan',
+ src: '//s.4cdn.org/image/title/' + bannerCnt.dataset.src
+ });
+ return $.add(bannerCnt, img);
+ }
+ },
+ setTitle: function(title) {
+ if (Unread.title != null) {
+ Unread.title = title;
+ return Unread.update();
+ } else {
+ return d.title = title;
+ }
+ },
+ cb: {
+ toggle: function() {
+ var banner, i, ref;
+ if (!((ref = Banner.choices) != null ? ref.length : void 0)) {
+ Banner.choices = Conf['knownBanners'].split(',').slice();
+ }
+ i = Math.floor(Banner.choices.length * Math.random());
+ banner = Banner.choices.splice(i, 1);
+ return $('img', this.parentNode).src = "//s.4cdn.org/image/title/" + banner;
+ },
+ click: function(e) {
+ var base, br, j, len, name, ref;
+ if (!(e.ctrlKey || e.metaKey)) {
+ return;
+ }
+ if ((base = Banner.original)[name = this.className] == null) {
+ base[name] = this.cloneNode(true);
+ }
+ this.contentEditable = true;
+ ref = $$('br', this);
+ for (j = 0, len = ref.length; j < len; j++) {
+ br = ref[j];
+ $.replace(br, $.tn('\n'));
+ }
+ return this.focus();
+ },
+ keydown: function(e) {
+ e.stopPropagation();
+ if (!e.shiftKey && e.keyCode === 13) {
+ return this.blur();
+ }
+ },
+ blur: function() {
+ var br, j, len, ref;
+ ref = $$('br', this);
+ for (j = 0, len = ref.length; j < len; j++) {
+ br = ref[j];
+ $.replace(br, $.tn('\n'));
+ }
+ if (this.textContent = this.textContent.replace(/\n*$/, '')) {
+ this.contentEditable = false;
+ return Banner.db.set({
+ boardID: g.BOARD.ID,
+ threadID: this.className,
+ val: {
+ title: this.textContent,
+ orig: Banner.original[this.className].textContent
+ }
+ });
+ } else {
+ $.rmAll(this);
+ $.add(this, slice.call(Banner.original[this.className].cloneNode(true).childNodes));
+ return Banner.db["delete"]({
+ boardID: g.BOARD.ID,
+ threadID: this.className
+ });
+ }
+ }
+ },
+ original: $.dict(),
+ custom: function(child) {
+ var className, data, event, j, len, ref;
+ className = child.className;
+ child.title = "Ctrl/\u2318+click to edit board " + (className.slice(5).toLowerCase());
+ child.spellcheck = false;
+ ref = ['click', 'keydown', 'blur'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ event = ref[j];
+ $.on(child, event, Banner.cb[event]);
+ }
+ if (data = Banner.db.get({
+ boardID: g.BOARD.ID,
+ threadID: className
+ })) {
+ if (Conf['Persistent Custom Board Titles'] || data.orig === child.textContent) {
+ Banner.original[className] = child.cloneNode(true);
+ return child.textContent = data.title;
+ } else {
+ return Banner.db["delete"]({
+ boardID: g.BOARD.ID,
+ threadID: className
+ });
+ }
+ }
+ }
+ };
+
+ return Banner;
+
+}).call(this);
+
+CatalogLinks = (function() {
+ var CatalogLinks;
+
+ CatalogLinks = {
+ init: function() {
+ var el, input, selector;
+ if (g.SITE.software === 'yotsuba' && (Conf['External Catalog'] || Conf['JSON Index']) && !(Conf['JSON Index'] && g.VIEW === 'index')) {
+ selector = (function() {
+ switch (g.VIEW) {
+ case 'thread':
+ case 'archive':
+ return '.navLinks.desktop > a';
+ case 'catalog':
+ return '.navLinks > :first-child > a';
+ case 'index':
+ return '#ctrl-top > a, .cataloglink > a';
+ }
+ })();
+ $.ready(function() {
+ var base, catalogLink, catalogURL, i, len, link, link2, ref;
+ ref = $$(selector);
+ for (i = 0, len = ref.length; i < len; i++) {
+ link = ref[i];
+ switch (link.pathname.replace(/\/+/g, '/')) {
+ case "/" + g.BOARD + "/":
+ if (Conf['JSON Index']) {
+ link.textContent = 'Index';
+ }
+ link.href = CatalogLinks.index();
+ break;
+ case "/" + g.BOARD + "/catalog":
+ link.href = CatalogLinks.catalog();
+ }
+ if (g.VIEW === 'catalog' && (catalogURL = CatalogLinks.catalog()) !== (typeof (base = g.SITE.urls).catalog === "function" ? base.catalog(g.BOARD) : void 0)) {
+ catalogLink = link.parentNode.cloneNode(true);
+ link2 = catalogLink.firstElementChild;
+ link2.href = catalogURL;
+ link2.textContent = link2.hostname === location.hostname ? '4chan X Catalog' : 'External Catalog';
+ $.after(link.parentNode, [$.tn(' '), catalogLink]);
+ }
+ }
+ });
+ }
+ if (g.SITE.software === 'yotsuba' && Conf['JSON Index'] && Conf['Use 4chan X Catalog']) {
+ Callbacks.Post.push({
+ name: 'Catalog Link Rewrite',
+ cb: this.node
+ });
+ }
+ if ((this.enabled = Conf['Catalog Links'])) {
+ CatalogLinks.el = el = UI.checkbox('Header catalog links', 'Catalog Links');
+ el.id = 'toggleCatalog';
+ input = $('input', el);
+ $.on(input, 'change', this.toggle);
+ $.sync('Header catalog links', CatalogLinks.set);
+ return Header.menu.addEntry({
+ el: el,
+ order: 95
+ });
+ }
+ },
+ node: function() {
+ var a, i, len, m, ref;
+ ref = $$('a', this.nodes.comment);
+ for (i = 0, len = ref.length; i < len; i++) {
+ a = ref[i];
+ if (m = a.href.match(/^https?:\/\/(boards\.4chan(?:nel)?\.org\/[^\/]+)\/catalog(#s=.*)?/)) {
+ a.href = "//" + m[1] + "/" + (m[2] || '#catalog');
+ }
+ }
+ },
+ toggle: function() {
+ $.event('CloseMenu');
+ $.set('Header catalog links', this.checked);
+ return CatalogLinks.set(this.checked);
+ },
+ set: function(useCatalog) {
+ Conf['Header catalog links'] = useCatalog;
+ CatalogLinks.setLinks(Header.boardList);
+ CatalogLinks.setLinks(Header.bottomBoardList);
+ CatalogLinks.el.title = "Turn catalog links " + (useCatalog ? 'off' : 'on') + ".";
+ return $('input', CatalogLinks.el).checked = useCatalog;
+ },
+ setLinks: function(list) {
+ var VIEW, a, board, boardID, i, len, ref, ref1, ref2, ref3, siteID, tail, url;
+ if (!(((ref = CatalogLinks.enabled) != null ? ref : Conf['Catalog Links']) && list)) {
+ return;
+ }
+ tail = /(?:index)?(?:\.\w+)?$/;
+ ref1 = $$('a:not([data-only])', list);
+ for (i = 0, len = ref1.length; i < len; i++) {
+ a = ref1[i];
+ ref2 = a.dataset, siteID = ref2.siteID, boardID = ref2.boardID;
+ if (!(siteID && boardID)) {
+ ref3 = Site.parseURL(a), siteID = ref3.siteID, boardID = ref3.boardID, VIEW = ref3.VIEW;
+ if (!(siteID && boardID && (VIEW === 'index' || VIEW === 'catalog') && (a.dataset.indexOptions || a.href.replace(tail, '') === (Get.url(VIEW, {
+ siteID: siteID,
+ boardID: boardID
+ }) || '').replace(tail, '')))) {
+ continue;
+ }
+ $.extend(a.dataset, {
+ siteID: siteID,
+ boardID: boardID
+ });
+ }
+ board = {
+ siteID: siteID,
+ boardID: boardID
+ };
+ url = Conf['Header catalog links'] ? CatalogLinks.catalog(board) : Get.url('index', board);
+ if (url) {
+ a.href = url;
+ if (a.dataset.indexOptions && url.split('#')[0] === Get.url('index', board)) {
+ a.href += (a.hash ? '/' : '#') + a.dataset.indexOptions;
+ }
+ }
+ }
+ },
+ externalParse: function() {
+ var board, boards, excludes, i, len, line, ref, ref1, ref2, url;
+ CatalogLinks.externalList = $.dict();
+ ref = Conf['externalCatalogURLs'].split('\n');
+ for (i = 0, len = ref.length; i < len; i++) {
+ line = ref[i];
+ if (line[0] === '#') {
+ continue;
+ }
+ url = line.split(';')[0];
+ boards = Filter.parseBoards(((ref1 = line.match(/;boards:([^;]+)/)) != null ? ref1[1] : void 0) || '*');
+ excludes = Filter.parseBoards((ref2 = line.match(/;exclude:([^;]+)/)) != null ? ref2[1] : void 0) || $.dict();
+ for (board in boards) {
+ if (!(excludes[board] || excludes[board.split('/')[0] + '/*'])) {
+ CatalogLinks.externalList[board] = url;
+ }
+ }
+ }
+ },
+ external: function(arg) {
+ var boardID, external, siteID;
+ siteID = arg.siteID, boardID = arg.boardID;
+ if (!CatalogLinks.externalList) {
+ CatalogLinks.externalParse();
+ }
+ external = CatalogLinks.externalList[siteID + "/" + boardID] || CatalogLinks.externalList[siteID + "/*"];
+ if (external) {
+ return external.replace(/%board/g, boardID);
+ } else {
+ return void 0;
+ }
+ },
+ jsonIndex: function(board, hash) {
+ if (g.SITE.ID === board.siteID && g.BOARD.ID === board.boardID && g.VIEW === 'index') {
+ return hash;
+ } else {
+ return Get.url('index', board) + hash;
+ }
+ },
+ catalog: function(board) {
+ var external, nativeCatalog;
+ if (board == null) {
+ board = g.BOARD;
+ }
+ if (Conf['External Catalog'] && (external = CatalogLinks.external(board))) {
+ return external;
+ } else if (Index.enabledOn(board) && Conf['Use 4chan X Catalog']) {
+ return CatalogLinks.jsonIndex(board, '#catalog');
+ } else if ((nativeCatalog = Get.url('catalog', board))) {
+ return nativeCatalog;
+ } else {
+ return CatalogLinks.external(board);
+ }
+ },
+ index: function(board) {
+ if (board == null) {
+ board = g.BOARD;
+ }
+ if (Index.enabledOn(board)) {
+ return CatalogLinks.jsonIndex(board, '#index');
+ } else {
+ return Get.url('index', board);
+ }
+ }
+ };
+
+ return CatalogLinks;
+
+}).call(this);
+
+CustomCSS = (function() {
+ var CustomCSS;
+
+ CustomCSS = {
+ init: function() {
+ if (!Conf['Custom CSS']) {
+ return;
+ }
+ return this.addStyle();
+ },
+ addStyle: function() {
+ return this.style = $.addStyle(CSS.sub(Conf['usercss']), 'custom-css', '#fourchanx-css');
+ },
+ rmStyle: function() {
+ if (this.style) {
+ $.rm(this.style);
+ return delete this.style;
+ }
+ },
+ update: function() {
+ if (!this.style) {
+ return this.addStyle();
+ }
+ return this.style.textContent = CSS.sub(Conf['usercss']);
+ }
+ };
+
+ return CustomCSS;
+
+}).call(this);
+
+ExpandComment = (function() {
+ var ExpandComment;
+
+ ExpandComment = {
+ init: function() {
+ if (g.VIEW !== 'index' || !Conf['Comment Expansion'] || Conf['JSON Index']) {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'Comment Expansion',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var a;
+ if (a = $('.abbr > a:not([onclick])', this.nodes.comment)) {
+ return $.on(a, 'click', ExpandComment.cb);
+ }
+ },
+ callbacks: [],
+ cb: function(e) {
+ e.preventDefault();
+ return ExpandComment.expand(Get.postFromNode(this));
+ },
+ expand: function(post) {
+ var a;
+ if (post.nodes.longComment && !post.nodes.longComment.parentNode) {
+ $.replace(post.nodes.shortComment, post.nodes.longComment);
+ post.nodes.comment = post.nodes.longComment;
+ return;
+ }
+ if (!(a = $('.abbr > a', post.nodes.comment))) {
+ return;
+ }
+ a.textContent = "Post No." + post + " Loading...";
+ return $.cache(g.SITE.urls.threadJSON({
+ boardID: post.boardID,
+ threadID: post.threadID
+ }), function() {
+ return ExpandComment.parse(this, a, post);
+ });
+ },
+ contract: function(post) {
+ var a;
+ if (!post.nodes.shortComment) {
+ return;
+ }
+ a = $('.abbr > a', post.nodes.shortComment);
+ a.textContent = 'here';
+ $.replace(post.nodes.longComment, post.nodes.shortComment);
+ return post.nodes.comment = post.nodes.shortComment;
+ },
+ parse: function(req, a, post) {
+ var callback, clone, comment, href, i, j, k, len, len1, len2, postObj, posts, quote, ref, ref1, spoilerRange, status;
+ status = req.status;
+ if (status !== 200 && status !== 304) {
+ a.textContent = status ? "Error " + req.statusText + " (" + status + ")" : 'Connection Error';
+ return;
+ }
+ posts = req.response.posts;
+ if (spoilerRange = posts[0].custom_spoiler) {
+ g.SITE.Build.spoilerRange[g.BOARD] = spoilerRange;
+ }
+ for (i = 0, len = posts.length; i < len; i++) {
+ postObj = posts[i];
+ if (postObj.no === post.ID) {
+ break;
+ }
+ }
+ if (postObj.no !== post.ID) {
+ a.textContent = "Post No." + post + " not found.";
+ return;
+ }
+ comment = post.nodes.comment;
+ clone = comment.cloneNode(false);
+ clone.innerHTML = postObj.com;
+ ref = $$('.quotelink', clone);
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ quote = ref[j];
+ href = quote.getAttribute('href');
+ if (href[0] === '/') {
+ continue;
+ }
+ if (href[0] === '#') {
+ quote.href = "" + (a.pathname.split(/\/+/).splice(0, 4).join('/')) + href;
+ } else {
+ quote.href = (a.pathname.split(/\/+/).splice(0, 3).join('/')) + "/" + href;
+ }
+ }
+ post.nodes.shortComment = comment;
+ $.replace(comment, clone);
+ post.nodes.comment = post.nodes.longComment = clone;
+ post.parseComment();
+ post.parseQuotes();
+ ref1 = ExpandComment.callbacks;
+ for (k = 0, len2 = ref1.length; k < len2; k++) {
+ callback = ref1[k];
+ callback.call(post);
+ }
+ }
+ };
+
+ return ExpandComment;
+
+}).call(this);
+
+ExpandThread = (function() {
+ var ExpandThread,
+ slice = [].slice;
+
+ ExpandThread = {
+ statuses: $.dict(),
+ init: function() {
+ if (!(g.VIEW === 'index' && Conf['Thread Expansion'])) {
+ return;
+ }
+ if (Conf['JSON Index']) {
+ return $.on(d, 'IndexRefreshInternal', this.onIndexRefresh);
+ } else {
+ return Callbacks.Thread.push({
+ name: 'Expand Thread',
+ cb: function() {
+ return ExpandThread.setButton(this);
+ }
+ });
+ }
+ },
+ setButton: function(thread) {
+ var a, ref;
+ if (!(thread.nodes.root && (a = $('.summary', thread.nodes.root)))) {
+ return;
+ }
+ a.textContent = (ref = g.SITE.Build).summaryText.apply(ref, ['+'].concat(slice.call(a.textContent.match(/\d+/g))));
+ a.style.cursor = 'pointer';
+ return $.on(a, 'click', ExpandThread.cbToggle);
+ },
+ disconnect: function(refresh) {
+ var oldReq, ref, status, threadID;
+ if (g.VIEW === 'thread' || !Conf['Thread Expansion']) {
+ return;
+ }
+ ref = ExpandThread.statuses;
+ for (threadID in ref) {
+ status = ref[threadID];
+ if ((oldReq = status.req)) {
+ delete status.req;
+ oldReq.abort();
+ }
+ delete ExpandThread.statuses[threadID];
+ }
+ if (!refresh) {
+ return $.off(d, 'IndexRefreshInternal', this.onIndexRefresh);
+ }
+ },
+ onIndexRefresh: function() {
+ ExpandThread.disconnect(true);
+ return g.BOARD.threads.forEach(function(thread) {
+ return ExpandThread.setButton(thread);
+ });
+ },
+ cbToggle: function(e) {
+ if ($.modifiedClick(e)) {
+ return;
+ }
+ e.preventDefault();
+ return ExpandThread.toggle(Get.threadFromNode(this));
+ },
+ cbToggleBottom: function(e) {
+ var bottom, thread;
+ if ($.modifiedClick(e)) {
+ return;
+ }
+ e.preventDefault();
+ thread = Get.threadFromNode(this);
+ $.rm(this);
+ bottom = thread.nodes.root.getBoundingClientRect().bottom;
+ ExpandThread.toggle(thread);
+ return window.scrollBy(0, thread.nodes.root.getBoundingClientRect().bottom - bottom);
+ },
+ toggle: function(thread) {
+ var a;
+ if (!(thread.nodes.root && (a = $('.summary', thread.nodes.root)))) {
+ return;
+ }
+ if (thread.ID in ExpandThread.statuses) {
+ return ExpandThread.contract(thread, a, thread.nodes.root);
+ } else {
+ return ExpandThread.expand(thread, a);
+ }
+ },
+ expand: function(thread, a) {
+ var ref, status;
+ ExpandThread.statuses[thread] = status = {};
+ a.textContent = (ref = g.SITE.Build).summaryText.apply(ref, ['...'].concat(slice.call(a.textContent.match(/\d+/g))));
+ status.req = $.cache(g.SITE.urls.threadJSON({
+ boardID: thread.board.ID,
+ threadID: thread.ID
+ }), function() {
+ if (this !== status.req) {
+ return;
+ }
+ delete status.req;
+ return ExpandThread.parse(this, thread, a);
+ });
+ return status.numReplies = $$(g.SITE.selectors.replyOriginal, thread.nodes.root).length;
+ },
+ contract: function(thread, a, threadRoot) {
+ var filesCount, i, inlined, len, oldReq, postsCount, ref, replies, reply, status;
+ status = ExpandThread.statuses[thread];
+ delete ExpandThread.statuses[thread];
+ if ((oldReq = status.req)) {
+ delete status.req;
+ oldReq.abort();
+ if (a) {
+ a.textContent = (ref = g.SITE.Build).summaryText.apply(ref, ['+'].concat(slice.call(a.textContent.match(/\d+/g))));
+ }
+ return;
+ }
+ replies = $$('.thread > .replyContainer', threadRoot);
+ if (status.numReplies) {
+ replies = replies.slice(0, -status.numReplies);
+ }
+ postsCount = 0;
+ filesCount = 0;
+ for (i = 0, len = replies.length; i < len; i++) {
+ reply = replies[i];
+ if (Conf['Quote Inlining']) {
+ while (inlined = $('.inlined', reply)) {
+ inlined.click();
+ }
+ }
+ postsCount++;
+ if ('file' in Get.postFromRoot(reply)) {
+ filesCount++;
+ }
+ $.rm(reply);
+ }
+ if (Index.enabled) {
+ $.event('PostsRemoved', null, a.parentNode);
+ }
+ a.textContent = g.SITE.Build.summaryText('+', postsCount, filesCount);
+ return $.rm($('.summary-bottom', threadRoot));
+ },
+ parse: function(req, thread, a) {
+ var a2, filesCount, i, len, post, postData, posts, postsCount, postsRoot, ref, ref1, root;
+ if ((ref = req.status) !== 200 && ref !== 304) {
+ a.textContent = req.status ? "Error " + req.statusText + " (" + req.status + ")" : 'Connection Error';
+ return;
+ }
+ g.SITE.Build.spoilerRange[thread.board] = req.response.posts[0].custom_spoiler;
+ posts = [];
+ postsRoot = [];
+ filesCount = 0;
+ ref1 = req.response.posts;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ postData = ref1[i];
+ if (postData.no === thread.ID) {
+ continue;
+ }
+ if ((post = thread.posts.get(postData.no)) && !post.isFetchedQuote) {
+ if ('file' in post) {
+ filesCount++;
+ }
+ root = post.nodes.root;
+ postsRoot.push(root);
+ continue;
+ }
+ root = g.SITE.Build.postFromObject(postData, thread.board.ID);
+ post = new Post(root, thread, thread.board);
+ if ('file' in post) {
+ filesCount++;
+ }
+ posts.push(post);
+ postsRoot.push(root);
+ }
+ Main.callbackNodes('Post', posts);
+ $.after(a, postsRoot);
+ $.event('PostsInserted', null, a.parentNode);
+ postsCount = postsRoot.length;
+ a.textContent = g.SITE.Build.summaryText('-', postsCount, filesCount);
+ if (root) {
+ a2 = a.cloneNode(true);
+ a2.classList.add('summary-bottom');
+ $.on(a2, 'click', ExpandThread.cbToggleBottom);
+ return $.after(root, a2);
+ }
+ }
+ };
+
+ return ExpandThread;
+
+}).call(this);
+
+FileInfo = (function() {
+ var FileInfo;
+
+ FileInfo = {
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') || !Conf['File Info Formatting']) {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'File Info Formatting',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var a, i, info, j, len, len1, oldInfo, ref, ref1;
+ if (!this.file) {
+ return;
+ }
+ if (this.isClone) {
+ ref = $$('.file-info .download-button', this.file.text);
+ for (i = 0, len = ref.length; i < len; i++) {
+ a = ref[i];
+ $.on(a, 'click', ImageCommon.download);
+ }
+ ref1 = $$('.file-info .quick-filter-md5', this.file.text);
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ a = ref1[j];
+ $.on(a, 'click', Filter.quickFilterMD5);
+ }
+ return;
+ }
+ oldInfo = $.el('span', {
+ className: 'fileText-original'
+ });
+ $.prepend(this.file.link.parentNode, oldInfo);
+ $.add(oldInfo, [this.file.link.previousSibling, this.file.link, this.file.link.nextSibling]);
+ info = $.el('span', {
+ className: 'file-info'
+ });
+ FileInfo.format(Conf['fileInfo'], this, info);
+ return $.prepend(this.file.text, info);
+ },
+ format: function(formatString, post, outputNode) {
+ var a, i, j, len, len1, output, ref, ref1;
+ output = [];
+ formatString.replace(/%(.)|[^%]+/g, function(s, c) {
+ output.push($.hasOwn(FileInfo.formatters, c) ? FileInfo.formatters[c].call(post) : {innerHTML: E(s)});
+ return '';
+ });
+ $.extend(outputNode, {innerHTML: E.cat(output)});
+ ref = $$('.download-button', outputNode);
+ for (i = 0, len = ref.length; i < len; i++) {
+ a = ref[i];
+ $.on(a, 'click', ImageCommon.download);
+ }
+ ref1 = $$('.quick-filter-md5', outputNode);
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ a = ref1[j];
+ $.on(a, 'click', Filter.quickFilterMD5);
+ }
+ },
+ formatters: {
+ t: function() {
+ return {innerHTML: E(this.file.url.match(/[^/]*$/)[0])};
+ },
+ T: function() {
+ return {innerHTML: "<a href=\"" + E(this.file.url) + "\" target=\"_blank\">" + (FileInfo.formatters.t.call(this)).innerHTML + "</a>"};
+ },
+ l: function() {
+ return {innerHTML: "<a href=\"" + E(this.file.url) + "\" target=\"_blank\">" + (FileInfo.formatters.n.call(this)).innerHTML + "</a>"};
+ },
+ L: function() {
+ return {innerHTML: "<a href=\"" + E(this.file.url) + "\" target=\"_blank\">" + (FileInfo.formatters.N.call(this)).innerHTML + "</a>"};
+ },
+ n: function() {
+ var fullname, shortname;
+ fullname = this.file.name;
+ shortname = SW.yotsuba.Build.shortFilename(this.file.name, this.isReply);
+ if (fullname === shortname) {
+ return {innerHTML: E(fullname)};
+ } else {
+ return {innerHTML: "<span class=\"fnswitch\"><span class=\"fntrunc\">" + E(shortname) + "</span><span class=\"fnfull\">" + E(fullname) + "</span></span>"};
+ }
+ },
+ N: function() {
+ return {innerHTML: E(this.file.name)};
+ },
+ d: function() {
+ return {innerHTML: "<a href=\"" + E(this.file.url) + "\" download=\"" + E(this.file.name) + "\" class=\"fa fa-download download-button\"></a>"};
+ },
+ f: function() {
+ return {innerHTML: "<a href=\"javascript:;\" class=\"fa fa-times quick-filter-md5\"></a>"};
+ },
+ p: function() {
+ return {innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "")};
+ },
+ s: function() {
+ return {innerHTML: E(this.file.size)};
+ },
+ B: function() {
+ return {innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes"};
+ },
+ K: function() {
+ return {innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB"};
+ },
+ M: function() {
+ return {innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB"};
+ },
+ r: function() {
+ return {innerHTML: E(this.file.dimensions || "PDF")};
+ },
+ g: function() {
+ return {innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "")};
+ },
+ '%': function() {
+ return {innerHTML: "%"};
+ }
+ }
+ };
+
+ return FileInfo;
+
+}).call(this);
+
+Flash = (function() {
+ var Flash;
+
+ Flash = {
+ init: function() {
+ if (g.BOARD.ID === 'f' && Conf['Enable Native Flash Embedding']) {
+ return $.ready(Flash.initReady);
+ }
+ },
+ initReady: function() {
+ if ($.hasStorage) {
+ return $.global(function() {
+ if (JSON.parse(localStorage['4chan-settings'] || '{}').disableAll) {
+ return window.SWFEmbed.init();
+ }
+ });
+ } else {
+ if (g.VIEW === 'thread') {
+ $.global(function() {
+ return window.Main.tid = location.pathname.split(/\/+/)[3];
+ });
+ }
+ return $.global(function() {
+ return window.SWFEmbed.init();
+ });
+ }
+ }
+ };
+
+ return Flash;
+
+}).call(this);
+
+Fourchan = (function() {
+ var Fourchan;
+
+ Fourchan = {
+ init: function() {
+ var ref;
+ if (!(g.SITE.software === 'yotsuba' && ((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive'))) {
+ return;
+ }
+ BoardConfig.ready(this.initBoard);
+ return Main.ready(this.initReady);
+ },
+ initBoard: function() {
+ if (g.BOARD.config.code_tags) {
+ $.on(window, 'prettyprint:cb', function(e) {
+ var post, pre;
+ if (!(post = g.posts.get(e.detail.ID))) {
+ return;
+ }
+ if (!(pre = $$('.prettyprint', post.nodes.comment)[+e.detail.i])) {
+ return;
+ }
+ if (!$.hasClass(pre, 'prettyprinted')) {
+ pre.innerHTML = e.detail.html;
+ return $.addClass(pre, 'prettyprinted');
+ }
+ });
+ $.global(function() {
+ return window.addEventListener('prettyprint', function(e) {
+ return window.dispatchEvent(new CustomEvent('prettyprint:cb', {
+ detail: {
+ ID: e.detail.ID,
+ i: e.detail.i,
+ html: window.prettyPrintOne(e.detail.html)
+ }
+ }));
+ }, false);
+ });
+ Callbacks.Post.push({
+ name: 'Parse [code] tags',
+ cb: Fourchan.code
+ });
+ g.posts.forEach(function(post) {
+ if (post.callbacksExecuted) {
+ return Callbacks.Post.execute(post, ['Parse [code] tags'], true);
+ }
+ });
+ ExpandComment.callbacks.push(Fourchan.code);
+ }
+ if (g.BOARD.config.math_tags) {
+ $.global(function() {
+ return window.addEventListener('mathjax', function(e) {
+ if (window.MathJax) {
+ return window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, e.target]);
+ } else {
+ if (!document.querySelector('script[src^="//cdn.mathjax.org/"]')) {
+ window.loadMathJax();
+ window.loadMathJax = function() {};
+ }
+ if (!e.target.classList.contains('postMessage')) {
+ return document.querySelector('script[src^="//cdn.mathjax.org/"]').addEventListener('load', function() {
+ return window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, e.target]);
+ }, false);
+ }
+ }
+ }, false);
+ });
+ Callbacks.Post.push({
+ name: 'Parse [math] tags',
+ cb: Fourchan.math
+ });
+ g.posts.forEach(function(post) {
+ if (post.callbacksExecuted) {
+ return Callbacks.Post.execute(post, ['Parse [math] tags'], true);
+ }
+ });
+ return ExpandComment.callbacks.push(Fourchan.math);
+ }
+ },
+ initReady: function() {
+ return $.global(function() {
+ var j, len, node, ref;
+ window.clickable_ids = false;
+ ref = document.querySelectorAll('.posteruid, .capcode');
+ for (j = 0, len = ref.length; j < len; j++) {
+ node = ref[j];
+ node.removeEventListener('click', window.idClick, false);
+ }
+ });
+ },
+ code: function() {
+ if (this.isClone) {
+ return;
+ }
+ return $.ready((function(_this) {
+ return function() {
+ var i, j, len, pre, ref;
+ ref = $$('.prettyprint', _this.nodes.comment);
+ for (i = j = 0, len = ref.length; j < len; i = ++j) {
+ pre = ref[i];
+ if (!$.hasClass(pre, 'prettyprinted')) {
+ $.event('prettyprint', {
+ ID: _this.fullID,
+ i: i,
+ html: pre.innerHTML
+ }, window);
+ }
+ }
+ };
+ })(this));
+ },
+ math: function() {
+ var cb, j, len, wbr, wbrs;
+ if (!/\[(math|eqn)\]/.test(this.nodes.comment.textContent)) {
+ return;
+ }
+ if ((wbrs = $$('wbr', this.nodes.comment)).length) {
+ for (j = 0, len = wbrs.length; j < len; j++) {
+ wbr = wbrs[j];
+ $.rm(wbr);
+ }
+ this.nodes.comment.normalize();
+ }
+ cb = (function(_this) {
+ return function() {
+ if (!doc.contains(_this.nodes.comment)) {
+ return;
+ }
+ $.off(d, 'PostsInserted', cb);
+ return $.event('mathjax', null, _this.nodes.comment);
+ };
+ })(this);
+ $.on(d, 'PostsInserted', cb);
+ return cb();
+ }
+ };
+
+ return Fourchan;
+
+}).call(this);
+
+IDColor = (function() {
+ var IDColor;
+
+ IDColor = {
+ init: function() {
+ var ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Color User IDs'])) {
+ return;
+ }
+ this.ids = $.dict();
+ this.ids['Heaven'] = [0, 0, 0, '#fff'];
+ return Callbacks.Post.push({
+ name: 'Color User IDs',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var rgb, span, style, uid;
+ if (this.isClone || !((uid = this.info.uniqueID) && (span = this.nodes.uniqueID))) {
+ return;
+ }
+ rgb = IDColor.ids[uid] || IDColor.compute(uid);
+ style = span.style;
+ style.color = rgb[3];
+ style.backgroundColor = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
+ return $.addClass(span, 'painted');
+ },
+ compute: function(uid) {
+ var hash, rgb;
+ hash = g.SITE.uidColor ? g.SITE.uidColor(uid) : parseInt(uid, 16);
+ rgb = [(hash >> 16) & 0xFF, (hash >> 8) & 0xFF, hash & 0xFF];
+ rgb.push($.luma(rgb) > 125 ? '#000' : '#fff');
+ return this.ids[uid] = rgb;
+ }
+ };
+
+ return IDColor;
+
+}).call(this);
+
+IDHighlight = (function() {
+ var IDHighlight;
+
+ IDHighlight = {
+ init: function() {
+ var ref;
+ if ((ref = g.VIEW) !== 'index' && ref !== 'thread') {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'Highlight by User ID',
+ cb: this.node
+ });
+ },
+ uniqueID: null,
+ node: function() {
+ if (this.nodes.uniqueIDRoot) {
+ $.on(this.nodes.uniqueIDRoot, 'click', IDHighlight.click(this));
+ }
+ if (this.nodes.capcode) {
+ $.on(this.nodes.capcode, 'click', IDHighlight.click(this));
+ }
+ if (!this.isClone) {
+ return IDHighlight.set(this);
+ }
+ },
+ set: function(post) {
+ var match;
+ match = (post.info.uniqueID || post.info.capcode) === IDHighlight.uniqueID;
+ return $[match ? 'addClass' : 'rmClass'](post.nodes.post, 'highlight');
+ },
+ click: function(post) {
+ return function() {
+ var uniqueID;
+ uniqueID = post.info.uniqueID || post.info.capcode;
+ IDHighlight.uniqueID = IDHighlight.uniqueID === uniqueID ? null : uniqueID;
+ return g.posts.forEach(IDHighlight.set);
+ };
+ }
+ };
+
+ return IDHighlight;
+
+}).call(this);
+
+IDPostCount = (function() {
+ var IDPostCount;
+
+ IDPostCount = {
+ init: function() {
+ if (!(g.VIEW === 'thread' && Conf['Count Posts by ID'])) {
+ return;
+ }
+ Callbacks.Thread.push({
+ name: 'Count Posts by ID',
+ cb: function() {
+ return IDPostCount.thread = this;
+ }
+ });
+ return Callbacks.Post.push({
+ name: 'Count Posts by ID',
+ cb: this.node
+ });
+ },
+ node: function() {
+ if (this.nodes.uniqueID && this.thread === IDPostCount.thread) {
+ return $.on(this.nodes.uniqueID, 'mouseover', IDPostCount.count);
+ }
+ },
+ count: function() {
+ var n, uniqueID;
+ uniqueID = Get.postFromNode(this).info.uniqueID;
+ n = 0;
+ IDPostCount.thread.posts.forEach(function(post) {
+ if (post.info.uniqueID === uniqueID) {
+ return n++;
+ }
+ });
+ return this.title = n + " post" + (n === 1 ? '' : 's') + " by this ID";
+ }
+ };
+
+ return IDPostCount;
+
+}).call(this);
+
+Keybinds = (function() {
+ var Keybinds;
+
+ Keybinds = {
+ init: function() {
+ var hotkey, init;
+ if (!Conf['Keybinds']) {
+ return;
+ }
+ for (hotkey in Config.hotkeys) {
+ $.sync(hotkey, Keybinds.sync);
+ }
+ init = function() {
+ var i, len, node, ref;
+ $.off(d, '4chanXInitFinished', init);
+ $.on(d, 'keydown', Keybinds.keydown);
+ ref = $$('[accesskey]');
+ for (i = 0, len = ref.length; i < len; i++) {
+ node = ref[i];
+ node.removeAttribute('accesskey');
+ }
+ };
+ return $.on(d, '4chanXInitFinished', init);
+ },
+ sync: function(key, hotkey) {
+ return Conf[hotkey] = key;
+ },
+ keydown: function(e) {
+ var base, base1, catalog, i, key, len, notification, notifications, post, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, searchInput, target, thread, threadRoot;
+ if (!(key = Keybinds.keyCode(e))) {
+ return;
+ }
+ target = e.target;
+ if ((ref = target.nodeName) === 'INPUT' || ref === 'TEXTAREA') {
+ if (!(/(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test(key) && !/^Alt\+(\d|Up|Down|Left|Right)$/.test(key))) {
+ return;
+ }
+ }
+ if ((ref1 = g.VIEW) === 'index' || ref1 === 'thread') {
+ threadRoot = Nav.getThread();
+ thread = Get.threadFromRoot(threadRoot);
+ }
+ switch (key) {
+ case Conf['Toggle board list']:
+ if (!Conf['Custom Board Navigation']) {
+ return;
+ }
+ Header.toggleBoardList();
+ break;
+ case Conf['Toggle header']:
+ Header.toggleBarVisibility();
+ break;
+ case Conf['Open empty QR']:
+ if (!QR.postingIsEnabled) {
+ return;
+ }
+ Keybinds.qr();
+ break;
+ case Conf['Open QR']:
+ if (!(QR.postingIsEnabled && threadRoot)) {
+ return;
+ }
+ Keybinds.qr(threadRoot);
+ break;
+ case Conf['Open settings']:
+ Settings.open();
+ break;
+ case Conf['Close']:
+ if (Settings.dialog) {
+ Settings.close();
+ } else if ((notifications = $$('.notification')).length) {
+ for (i = 0, len = notifications.length; i < len; i++) {
+ notification = notifications[i];
+ $('.close', notification).click();
+ }
+ } else if (QR.nodes && !(QR.nodes.el.hidden || window.getComputedStyle(QR.nodes.form).display === 'none')) {
+ if (Conf['Persistent QR']) {
+ QR.hide();
+ } else {
+ QR.close();
+ }
+ } else if (Embedding.lastEmbed) {
+ Embedding.closeFloat();
+ } else {
+ return;
+ }
+ break;
+ case Conf['Spoiler tags']:
+ if (target.nodeName !== 'TEXTAREA') {
+ return;
+ }
+ Keybinds.tags('spoiler', target);
+ break;
+ case Conf['Code tags']:
+ if (target.nodeName !== 'TEXTAREA') {
+ return;
+ }
+ Keybinds.tags('code', target);
+ break;
+ case Conf['Eqn tags']:
+ if (target.nodeName !== 'TEXTAREA') {
+ return;
+ }
+ Keybinds.tags('eqn', target);
+ break;
+ case Conf['Math tags']:
+ if (target.nodeName !== 'TEXTAREA') {
+ return;
+ }
+ Keybinds.tags('math', target);
+ break;
+ case Conf['SJIS tags']:
+ if (target.nodeName !== 'TEXTAREA') {
+ return;
+ }
+ Keybinds.tags('sjis', target);
+ break;
+ case Conf['Toggle sage']:
+ if (!(QR.nodes && !QR.nodes.el.hidden)) {
+ return;
+ }
+ Keybinds.sage();
+ break;
+ case Conf['Toggle Cooldown']:
+ if (!(QR.nodes && !QR.nodes.el.hidden && $.hasClass(QR.nodes.fileSubmit, 'custom-cooldown'))) {
+ return;
+ }
+ QR.toggleCustomCooldown();
+ break;
+ case Conf['Post from URL']:
+ if (!QR.postingIsEnabled) {
+ return;
+ }
+ QR.handleUrl('');
+ break;
+ case Conf['Add new post']:
+ if (!QR.postingIsEnabled) {
+ return;
+ }
+ QR.addPost();
+ break;
+ case Conf['Submit QR']:
+ if (!(QR.nodes && !QR.nodes.el.hidden)) {
+ return;
+ }
+ if (!QR.status()) {
+ QR.submit();
+ }
+ break;
+ case Conf['Update']:
+ switch (g.VIEW) {
+ case 'thread':
+ if (!ThreadUpdater.enabled) {
+ return;
+ }
+ ThreadUpdater.update();
+ break;
+ case 'index':
+ if (!Index.enabled) {
+ return;
+ }
+ Index.update();
+ break;
+ default:
+ return;
+ }
+ break;
+ case Conf['Watch']:
+ if (!(ThreadWatcher.enabled && thread)) {
+ return;
+ }
+ ThreadWatcher.toggle(thread);
+ break;
+ case Conf['Update thread watcher']:
+ if (!ThreadWatcher.enabled) {
+ return;
+ }
+ ThreadWatcher.buttonFetchAll();
+ break;
+ case Conf['Toggle thread watcher']:
+ if (!ThreadWatcher.enabled) {
+ return;
+ }
+ ThreadWatcher.toggleWatcher();
+ break;
+ case Conf['Toggle threading']:
+ if (!QuoteThreading.ready) {
+ return;
+ }
+ QuoteThreading.toggleThreading();
+ break;
+ case Conf['Mark thread read']:
+ if (!(g.VIEW === 'index' && thread && UnreadIndex.enabled)) {
+ return;
+ }
+ UnreadIndex.markRead.call(threadRoot);
+ break;
+ case Conf['Expand image']:
+ if (!(ImageExpand.enabled && threadRoot)) {
+ return;
+ }
+ post = Get.postFromNode(Keybinds.post(threadRoot));
+ if (post.file) {
+ ImageExpand.toggle(post);
+ }
+ break;
+ case Conf['Expand images']:
+ if (!ImageExpand.enabled) {
+ return;
+ }
+ ImageExpand.cb.toggleAll();
+ break;
+ case Conf['Open Gallery']:
+ if (!Gallery.enabled) {
+ return;
+ }
+ Gallery.cb.toggle();
+ break;
+ case Conf['fappeTyme']:
+ if (!((ref2 = FappeTyme.nodes) != null ? ref2.fappe : void 0)) {
+ return;
+ }
+ FappeTyme.toggle('fappe');
+ break;
+ case Conf['werkTyme']:
+ if (!((ref3 = FappeTyme.nodes) != null ? ref3.werk : void 0)) {
+ return;
+ }
+ FappeTyme.toggle('werk');
+ break;
+ case Conf['Front page']:
+ if (Index.enabled) {
+ Index.userPageNav(1);
+ } else {
+ location.href = "/" + g.BOARD + "/";
+ }
+ break;
+ case Conf['Open front page']:
+ $.open(location.origin + "/" + g.BOARD + "/");
+ break;
+ case Conf['Next page']:
+ if (!(g.VIEW === 'index' && !(typeof (base = g.SITE).isOnePage === "function" ? base.isOnePage(g.BOARD) : void 0))) {
+ return;
+ }
+ if (Index.enabled) {
+ if ((ref4 = Conf['Index Mode']) !== 'paged' && ref4 !== 'infinite') {
+ return;
+ }
+ $('.next button', Index.pagelist).click();
+ } else {
+ if ((ref5 = $(g.SITE.selectors.nav.next)) != null) {
+ ref5.click();
+ }
+ }
+ break;
+ case Conf['Previous page']:
+ if (!(g.VIEW === 'index' && !(typeof (base1 = g.SITE).isOnePage === "function" ? base1.isOnePage(g.BOARD) : void 0))) {
+ return;
+ }
+ if (Index.enabled) {
+ if ((ref6 = Conf['Index Mode']) !== 'paged' && ref6 !== 'infinite') {
+ return;
+ }
+ $('.prev button', Index.pagelist).click();
+ } else {
+ if ((ref7 = $(g.SITE.selectors.nav.prev)) != null) {
+ ref7.click();
+ }
+ }
+ break;
+ case Conf['Search form']:
+ if (g.VIEW !== 'index') {
+ return;
+ }
+ searchInput = Index.enabled ? Index.searchInput : g.SITE.selectors.searchBox ? $(g.SITE.selectors.searchBox) : void 0;
+ if (!searchInput) {
+ return;
+ }
+ Header.scrollToIfNeeded(searchInput);
+ searchInput.focus();
+ break;
+ case Conf['Paged mode']:
+ if (!Index.enabledOn(g.BOARD)) {
+ return;
+ }
+ location.href = g.VIEW === 'index' ? '#paged' : "/" + g.BOARD + "/#paged";
+ break;
+ case Conf['Infinite scrolling mode']:
+ if (!Index.enabledOn(g.BOARD)) {
+ return;
+ }
+ location.href = g.VIEW === 'index' ? '#infinite' : "/" + g.BOARD + "/#infinite";
+ break;
+ case Conf['All pages mode']:
+ if (!Index.enabledOn(g.BOARD)) {
+ return;
+ }
+ location.href = g.VIEW === 'index' ? '#all-pages' : "/" + g.BOARD + "/#all-pages";
+ break;
+ case Conf['Open catalog']:
+ if (!(catalog = CatalogLinks.catalog())) {
+ return;
+ }
+ location.href = catalog;
+ break;
+ case Conf['Cycle sort type']:
+ if (!Index.enabled) {
+ return;
+ }
+ Index.cycleSortType();
+ break;
+ case Conf['Next thread']:
+ if (!(g.VIEW === 'index' && threadRoot)) {
+ return;
+ }
+ Nav.scroll(+1);
+ break;
+ case Conf['Previous thread']:
+ if (!(g.VIEW === 'index' && threadRoot)) {
+ return;
+ }
+ Nav.scroll(-1);
+ break;
+ case Conf['Expand thread']:
+ if (!(g.VIEW === 'index' && threadRoot)) {
+ return;
+ }
+ ExpandThread.toggle(thread);
+ Header.scrollTo(threadRoot);
+ break;
+ case Conf['Open thread']:
+ if (!(g.VIEW === 'index' && threadRoot)) {
+ return;
+ }
+ Keybinds.open(thread);
+ break;
+ case Conf['Open thread tab']:
+ if (!(g.VIEW === 'index' && threadRoot)) {
+ return;
+ }
+ Keybinds.open(thread, true);
+ break;
+ case Conf['Next reply']:
+ if (!threadRoot) {
+ return;
+ }
+ Keybinds.hl(+1, threadRoot);
+ break;
+ case Conf['Previous reply']:
+ if (!threadRoot) {
+ return;
+ }
+ Keybinds.hl(-1, threadRoot);
+ break;
+ case Conf['Deselect reply']:
+ if (!threadRoot) {
+ return;
+ }
+ Keybinds.hl(0, threadRoot);
+ break;
+ case Conf['Hide']:
+ if (!(thread && ThreadHiding.db)) {
+ return;
+ }
+ Header.scrollTo(threadRoot);
+ ThreadHiding.toggle(thread);
+ break;
+ case Conf['Quick Filter MD5']:
+ if (!threadRoot) {
+ return;
+ }
+ post = Keybinds.post(threadRoot);
+ Keybinds.hl(+1, threadRoot);
+ Filter.quickFilterMD5.call(post, e);
+ break;
+ case Conf['Previous Post Quoting You']:
+ if (!(threadRoot && QuoteYou.db)) {
+ return;
+ }
+ QuoteYou.cb.seek('preceding');
+ break;
+ case Conf['Next Post Quoting You']:
+ if (!(threadRoot && QuoteYou.db)) {
+ return;
+ }
+ QuoteYou.cb.seek('following');
+ break;
+ default:
+ return;
+ }
+ e.preventDefault();
+ return e.stopPropagation();
+ },
+ keyCode: function(e) {
+ var kc, key;
+ key = (function() {
+ switch (kc = e.keyCode) {
+ case 8:
+ return '';
+ case 13:
+ return 'Enter';
+ case 27:
+ return 'Esc';
+ case 32:
+ return 'Space';
+ case 37:
+ return 'Left';
+ case 38:
+ return 'Up';
+ case 39:
+ return 'Right';
+ case 40:
+ return 'Down';
+ case 188:
+ return 'Comma';
+ case 190:
+ return 'Period';
+ case 191:
+ return 'Slash';
+ case 59:
+ case 186:
+ return 'Semicolon';
+ default:
+ if ((48 <= kc && kc <= 57) || (65 <= kc && kc <= 90)) {
+ return String.fromCharCode(kc).toLowerCase();
+ } else if ((96 <= kc && kc <= 105)) {
+ return String.fromCharCode(kc - 48).toLowerCase();
+ } else {
+ return null;
+ }
+ }
+ })();
+ if (key) {
+ if (e.altKey) {
+ key = 'Alt+' + key;
+ }
+ if (e.ctrlKey) {
+ key = 'Ctrl+' + key;
+ }
+ if (e.metaKey) {
+ key = 'Meta+' + key;
+ }
+ if (e.shiftKey) {
+ key = 'Shift+' + key;
+ }
+ }
+ return key;
+ },
+ post: function(thread) {
+ var s;
+ s = g.SITE.selectors;
+ return $("" + s.postContainer + s.highlightable.reply + "." + g.SITE.classes.highlight, thread) || $("" + (g.SITE.isOPContainerThread ? s.thread : s.postContainer) + s.highlightable.op, thread);
+ },
+ qr: function(thread) {
+ QR.open();
+ if (thread != null) {
+ QR.quote.call(Keybinds.post(thread));
+ }
+ return QR.nodes.com.focus();
+ },
+ tags: function(tag, ta) {
+ var range, selEnd, selStart, value;
+ BoardConfig.ready(function() {
+ var config, supported;
+ config = g.BOARD.config;
+ supported = (function() {
+ switch (tag) {
+ case 'spoiler':
+ return !!config.spoilers;
+ case 'code':
+ return !!config.code_tags;
+ case 'math':
+ case 'eqn':
+ return !!config.math_tags;
+ case 'sjis':
+ return !!config.sjis_tags;
+ }
+ })();
+ if (!supported) {
+ return new Notice('warning', "[" + tag + "] tags are not supported on /" + g.BOARD + "/.", 20);
+ }
+ });
+ value = ta.value;
+ selStart = ta.selectionStart;
+ selEnd = ta.selectionEnd;
+ ta.value = value.slice(0, selStart) + ("[" + tag + "]") + value.slice(selStart, selEnd) + ("[/" + tag + "]") + value.slice(selEnd);
+ range = ("[" + tag + "]").length + selEnd;
+ ta.setSelectionRange(range, range);
+ return $.event('input', null, ta);
+ },
+ sage: function() {
+ var isSage;
+ isSage = /sage/i.test(QR.nodes.email.value);
+ return QR.nodes.email.value = isSage ? "" : "sage";
+ },
+ open: function(thread, tab) {
+ var url;
+ if (g.VIEW !== 'index') {
+ return;
+ }
+ url = Get.url('thread', thread);
+ if (tab) {
+ return $.open(url);
+ } else {
+ return location.href = url;
+ }
+ },
+ hl: function(delta, thread) {
+ var axis, height, highlight, i, len, next, postEl, replies, reply, replySelector, root;
+ replySelector = "" + g.SITE.selectors.postContainer + g.SITE.selectors.highlightable.reply;
+ highlight = g.SITE.classes.highlight;
+ postEl = $(replySelector + "." + highlight, thread);
+ if (!delta) {
+ if (postEl) {
+ $.rmClass(postEl, highlight);
+ }
+ return;
+ }
+ if (postEl) {
+ height = postEl.getBoundingClientRect().height;
+ if (Header.getTopOf(postEl) >= -height && Header.getBottomOf(postEl) >= -height) {
+ root = Get.postFromNode(postEl).nodes.root;
+ axis = delta === +1 ? 'following' : 'preceding';
+ if (!(next = $.x(axis + "-sibling::" + g.SITE.xpath.replyContainer + "[not(@hidden) and not(child::div[@class='stub'])][1]", root))) {
+ return;
+ }
+ if (!next.matches(replySelector)) {
+ next = $(replySelector, next);
+ }
+ Header.scrollToIfNeeded(next, delta === +1);
+ $.addClass(next, highlight);
+ $.rmClass(postEl, highlight);
+ return;
+ }
+ $.rmClass(postEl, highlight);
+ }
+ replies = $$(replySelector, thread);
+ if (delta === -1) {
+ replies.reverse();
+ }
+ for (i = 0, len = replies.length; i < len; i++) {
+ reply = replies[i];
+ if (delta === +1 && Header.getTopOf(reply) > 0 || delta === -1 && Header.getBottomOf(reply) > 0) {
+ $.addClass(reply, highlight);
+ return;
+ }
+ }
+ }
+ };
+
+ return Keybinds;
+
+}).call(this);
+
+ModContact = (function() {
+ var ModContact;
+
+ ModContact = {
+ init: function() {
+ var ref;
+ if (!(g.SITE.software === 'yotsuba' && ((ref = g.VIEW) === 'index' || ref === 'thread'))) {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'Mod Contact Links',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var links, moveNote, moved;
+ if (this.isClone || !$.hasOwn(ModContact.specific, this.info.capcode)) {
+ return;
+ }
+ links = $.el('span', {
+ className: 'contact-links brackets-wrap'
+ });
+ $.extend(links, ModContact.template(this.info.capcode));
+ $.after(this.nodes.capcode, links);
+ if ((moved = this.info.comment.match(/This thread was moved to >>>\/(\w+)\//)) && $.hasOwn(ModContact.moveNote, moved[1])) {
+ moveNote = $.el('div', {
+ className: 'move-note'
+ });
+ $.extend(moveNote, ModContact.moveNote[moved[1]]);
+ return $.add(this.nodes.post, moveNote);
+ }
+ },
+ template: function(capcode) {
+ return {innerHTML: "<a href=\"https://www.4chan.org/feedback\" target=\"_blank\">feedback</a>" + (ModContact.specific[capcode]()).innerHTML};
+ },
+ specific: {
+ Mod: function() {
+ return {innerHTML: " <a href=\"https://www.4chan-x.net/4chan-irc.html\" target=\"_blank\">IRC</a>"};
+ },
+ Manager: function() {
+ return ModContact.specific.Mod();
+ },
+ Developer: function() {
+ return {innerHTML: " <a href=\"https://github.com/4chan\" target=\"_blank\">github</a>"};
+ },
+ Admin: function() {
+ return {innerHTML: " <a href=\"https://twitter.com/hiroyuki_ni\" target=\"_blank\">twitter</a>"};
+ }
+ },
+ moveNote: {
+ qa: {innerHTML: "Moving a thread to /qa/ does not imply mods will read it. If you wish to contact mods, use <a href=\"https://www.4chan.org/feedback\" target=\"_blank\">feedback</a><span class=\"invisible\"> (https://www.4chan.org/feedback)</span> or <a href=\"https://www.4chan-x.net/4chan-irc.html\" target=\"_blank\">IRC</a><span class=\"invisible\"> (https://www.4chan-x.net/4chan-irc.html)</span>."}
+ }
+ };
+
+ return ModContact;
+
+}).call(this);
+
+Nav = (function() {
+ var Nav;
+
+ Nav = {
+ init: function() {
+ var append, next, prev, span;
+ switch (g.VIEW) {
+ case 'index':
+ if (!Conf['Index Navigation']) {
+ return;
+ }
+ break;
+ case 'thread':
+ if (!Conf['Reply Navigation']) {
+ return;
+ }
+ break;
+ default:
+ return;
+ }
+ span = $.el('span', {
+ id: 'navlinks'
+ });
+ prev = $.el('a', {
+ textContent: 'โ–ฒ',
+ href: 'javascript:;'
+ });
+ next = $.el('a', {
+ textContent: 'โ–ผ',
+ href: 'javascript:;'
+ });
+ $.on(prev, 'click', this.prev);
+ $.on(next, 'click', this.next);
+ $.add(span, [prev, $.tn(' '), next]);
+ append = function() {
+ $.off(d, '4chanXInitFinished', append);
+ return $.add(d.body, span);
+ };
+ return $.on(d, '4chanXInitFinished', append);
+ },
+ prev: function() {
+ if (g.VIEW === 'thread') {
+ return window.scrollTo(0, 0);
+ } else {
+ return Nav.scroll(-1);
+ }
+ },
+ next: function() {
+ if (g.VIEW === 'thread') {
+ return window.scrollTo(0, d.body.scrollHeight);
+ } else {
+ return Nav.scroll(+1);
+ }
+ },
+ getThread: function() {
+ var i, len, ref, thread, threadRoot;
+ if (g.VIEW === 'thread') {
+ return g.threads.get(g.BOARD + "." + g.THREADID).nodes.root;
+ }
+ if ($.hasClass(doc, 'catalog-mode')) {
+ return;
+ }
+ ref = $$(g.SITE.selectors.thread);
+ for (i = 0, len = ref.length; i < len; i++) {
+ threadRoot = ref[i];
+ thread = Get.threadFromRoot(threadRoot);
+ if (thread.isHidden && !thread.stub) {
+ continue;
+ }
+ if (Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height) {
+ return threadRoot;
+ }
+ }
+ },
+ scroll: function(delta) {
+ var axis, extra, next, ref, thread, top;
+ if ((ref = d.activeElement) != null) {
+ ref.blur();
+ }
+ thread = Nav.getThread();
+ if (!thread) {
+ return;
+ }
+ axis = delta === +1 ? 'following' : 'preceding';
+ if (next = $.x(axis + "-sibling::" + g.SITE.xpath.thread + "[not(@hidden)][1]", thread)) {
+ top = Header.getTopOf(thread);
+ if (delta === +1 && top < 5 || delta === -1 && top > -5) {
+ thread = next;
+ }
+ }
+ extra = Header.getTopOf(thread) + doc.clientHeight - d.body.getBoundingClientRect().bottom;
+ if (extra > 0) {
+ d.body.style.marginBottom = extra + "px";
+ }
+ Header.scrollTo(thread);
+ if (extra > 0 && !Nav.haveExtra) {
+ Nav.haveExtra = true;
+ return $.on(d, 'scroll', Nav.removeExtra);
+ }
+ },
+ removeExtra: function() {
+ var extra;
+ extra = doc.clientHeight - d.body.getBoundingClientRect().bottom;
+ if (extra > 0) {
+ return d.body.style.marginBottom = extra + "px";
+ } else {
+ d.body.style.marginBottom = '';
+ delete Nav.haveExtra;
+ return $.off(d, 'scroll', Nav.removeExtra);
+ }
+ }
+ };
+
+ return Nav;
+
+}).call(this);
+
+NormalizeURL = (function() {
+ var NormalizeURL;
+
+ NormalizeURL = {
+ init: function() {
+ var pathname;
+ if (!Conf['Normalize URL']) {
+ return;
+ }
+ pathname = location.pathname.split(/\/+/);
+ if (g.SITE.software === 'yotsuba') {
+ switch (g.VIEW) {
+ case 'thread':
+ pathname[2] = 'thread';
+ pathname = pathname.slice(0, 4);
+ break;
+ case 'index':
+ pathname = pathname.slice(0, 3);
+ }
+ }
+ pathname = pathname.join('/');
+ if (location.pathname !== pathname) {
+ return history.replaceState(history.state, '', location.protocol + "//" + location.host + pathname + location.hash);
+ }
+ }
+ };
+
+ return NormalizeURL;
+
+}).call(this);
+
+PSA = (function() {
+ var PSA,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ PSA = {
+ init: function() {
+ var announcement, el;
+ if (g.SITE.software === 'yotsuba' && g.BOARD.ID === 'qa') {
+ announcement = {
+ innerHTML: "Stay in touch with your <a href=\"https://www.4chan-x.net/qa_friends.html\" target=\"_blank\" rel=\"noopener\">/qa/ friends</a>!"
+ };
+ el = $.el('div', {
+ className: 'fcx-announcement'
+ }, announcement);
+ $.onExists(doc, '.boardBanner', function(banner) {
+ return $.after(banner, el);
+ });
+ }
+ if ('samachan.org' in Conf['siteProperties'] && indexOf.call(Conf['PSAseen'], 'samachan') < 0) {
+ el = $.el('span', {
+ innerHTML: "<a href=\"https://sushigirl.us/yakuza/res/776.html\" target=\"_blank\" rel=\"noopener\">Looking for a new home?<br>Some former Samachan users are regrouping on SushiChan.</a><br>(a message from 4chan X)"
+ });
+ return Main.ready(function() {
+ new Notice('info', el);
+ Conf['PSAseen'].push('samachan');
+ return $.set('PSAseen', Conf['PSAseen']);
+ });
+ }
+ }
+ };
+
+ return PSA;
+
+}).call(this);
+
+PSAHiding = (function() {
+ var PSAHiding,
+ slice = [].slice;
+
+ PSAHiding = {
+ init: function() {
+ if (!(Conf['Announcement Hiding'] && g.SITE.selectors.psa)) {
+ return;
+ }
+ $.addClass(doc, 'hide-announcement');
+ $.onExists(doc, g.SITE.selectors.psa, this.setup);
+ return $.ready(function() {
+ if (!$(g.SITE.selectors.psa)) {
+ return $.rmClass(doc, 'hide-announcement');
+ }
+ });
+ },
+ setup: function(psa) {
+ var btn, entry, hr, ref, ref1, ref2;
+ PSAHiding.psa = psa;
+ PSAHiding.text = (ref = psa.dataset.utc) != null ? ref : psa.innerHTML;
+ if (g.SITE.selectors.psaTop && (hr = (ref1 = $(g.SITE.selectors.psaTop)) != null ? ref1.previousElementSibling : void 0) && hr.nodeName === 'HR') {
+ PSAHiding.hr = hr;
+ }
+ PSAHiding.content = $.el('div');
+ entry = {
+ el: $.el('a', {
+ textContent: 'Show announcement',
+ className: 'show-announcement',
+ href: 'javascript:;'
+ }),
+ order: 50,
+ open: function() {
+ return psa.hidden;
+ }
+ };
+ Header.menu.addEntry(entry);
+ $.on(entry.el, 'click', PSAHiding.toggle);
+ PSAHiding.btn = btn = $.el('a', {
+ title: 'Mark announcement as read and hide.',
+ className: 'hide-announcement-button fa fa-minus-square',
+ href: 'javascript:;'
+ });
+ $.on(btn, 'click', PSAHiding.toggle);
+ if (((ref2 = psa.firstChild) != null ? ref2.tagName : void 0) === 'HR') {
+ $.after(psa.firstChild, btn);
+ } else {
+ $.prepend(psa, btn);
+ }
+ PSAHiding.sync(Conf['hiddenPSAList']);
+ $.rmClass(doc, 'hide-announcement');
+ return $.sync('hiddenPSAList', PSAHiding.sync);
+ },
+ toggle: function() {
+ var hide, set;
+ hide = $.hasClass(this, 'hide-announcement-button');
+ set = function(hiddenPSAList) {
+ if (hide) {
+ return hiddenPSAList[g.SITE.ID] = PSAHiding.text;
+ } else {
+ return delete hiddenPSAList[g.SITE.ID];
+ }
+ };
+ set(Conf['hiddenPSAList']);
+ PSAHiding.sync(Conf['hiddenPSAList']);
+ return $.get('hiddenPSAList', Conf['hiddenPSAList'], function(arg) {
+ var hiddenPSAList;
+ hiddenPSAList = arg.hiddenPSAList;
+ set(hiddenPSAList);
+ return $.set('hiddenPSAList', hiddenPSAList);
+ });
+ },
+ sync: function(hiddenPSAList) {
+ var content, psa, ref;
+ psa = PSAHiding.psa, content = PSAHiding.content;
+ psa.hidden = hiddenPSAList[g.SITE.ID] === PSAHiding.text;
+ if (psa.hidden) {
+ $.add(content, slice.call(psa.childNodes));
+ } else {
+ $.add(psa, slice.call(content.childNodes));
+ }
+ return (ref = PSAHiding.hr) != null ? ref.hidden = psa.hidden : void 0;
+ }
+ };
+
+ return PSAHiding;
+
+}).call(this);
+
+PassMessage = (function() {
+ var PassMessage;
+
+ PassMessage = {
+ init: function() {
+ var close, msg;
+ if (Conf['passMessageClosed']) {
+ return;
+ }
+ msg = $.el('div', {
+ className: 'box-outer top-box'
+ }, {innerHTML: "<div class=\"box-inner\"><div class=\"boxbar\"><h2>Trouble buying a 4chan Pass? (a message from 4chan X) <a href=\"javascript:;\" style=\"text-decoration: none; float: right; margin-right: 4px;\" title=\"Close\">ร—</a></h2></div><div class=\"boxcontent\">Check the 4chan X wiki for <a href=\"https://github.com/ccd0/4chan-x/wiki/Captcha-FAQ#alternatives\" target=\"_blank\" rel=\"noopener\">alternative solutions</a>.</div></div>"});
+ msg.style.cssText = 'padding-bottom: 0;';
+ close = $('a', msg);
+ $.on(close, 'click', function() {
+ $.rm(msg);
+ return $.set('passMessageClosed', true);
+ });
+ return $.ready(function() {
+ var hd;
+ if ((hd = $.id('hd'))) {
+ return $.after(hd, msg);
+ } else {
+ return $.prepend(d.body, msg);
+ }
+ });
+ }
+ };
+
+ return PassMessage;
+
+}).call(this);
+
+PostJumper = (function() {
+ var PostJumper;
+
+ PostJumper = {
+ init: function() {
+ var ref;
+ if (!(Conf['Unique ID and Capcode Navigation'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) {
+ return;
+ }
+ this.buttons = this.makeButtons();
+ return Callbacks.Post.push({
+ name: 'Post Jumper',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var buttons, i, len, ref;
+ if (this.isClone) {
+ ref = $$('.postJumper', this.nodes.info);
+ for (i = 0, len = ref.length; i < len; i++) {
+ buttons = ref[i];
+ PostJumper.addListeners(buttons);
+ }
+ return;
+ }
+ if (this.nodes.uniqueIDRoot) {
+ PostJumper.addButtons(this, 'uniqueID');
+ }
+ if (this.nodes.capcode) {
+ return PostJumper.addButtons(this, 'capcode');
+ }
+ },
+ addButtons: function(post, type) {
+ var buttons, value;
+ value = post.info[type];
+ buttons = PostJumper.buttons.cloneNode(true);
+ $.extend(buttons.dataset, {
+ type: type,
+ value: value
+ });
+ $.after(post.nodes[type + (type === 'capcode' ? '' : 'Root')], buttons);
+ return PostJumper.addListeners(buttons);
+ },
+ addListeners: function(buttons) {
+ $.on(buttons.firstChild, 'click', PostJumper.buttonClick);
+ return $.on(buttons.lastChild, 'click', PostJumper.buttonClick);
+ },
+ buttonClick: function() {
+ var dir, toJumper;
+ dir = $.hasClass(this, 'prev') ? -1 : 1;
+ if ((toJumper = PostJumper.find(this.parentNode, dir))) {
+ return PostJumper.scroll(this.parentNode, toJumper);
+ }
+ },
+ find: function(jumper, dir) {
+ var axis, jumper2, ref, type, value, xpath;
+ ref = jumper.dataset, type = ref.type, value = ref.value;
+ xpath = "span[contains(@class,\"postJumper\") and @data-value=\"" + value + "\" and @data-type=\"" + type + "\"]";
+ axis = dir < 0 ? 'preceding' : 'following';
+ jumper2 = jumper;
+ while ((jumper2 = $.x(axis + "::" + xpath, jumper2))) {
+ if (jumper2.getBoundingClientRect().height) {
+ return jumper2;
+ }
+ }
+ if ((jumper2 = $.x("(//" + xpath + ")[" + (dir < 0 ? 'last()' : '1') + "]"))) {
+ if (jumper2.getBoundingClientRect().height) {
+ return jumper2;
+ }
+ }
+ while ((jumper2 = $.x(axis + "::" + xpath, jumper2)) && jumper2 !== jumper) {
+ if (jumper2.getBoundingClientRect().height) {
+ return jumper2;
+ }
+ }
+ return null;
+ },
+ makeButtons: function() {
+ var charNext, charPrev, classNext, classPrev, span;
+ charPrev = '\u23EB';
+ charNext = '\u23EC';
+ classPrev = 'prev';
+ classNext = 'next';
+ span = $.el('span', {
+ className: 'postJumper'
+ });
+ $.extend(span, {innerHTML: "<a href=\"javascript:;\" class=\"" + E(classPrev) + "\">" + E(charPrev) + "</a><a href=\"javascript:;\" class=\"" + E(classNext) + "\">" + E(charNext) + "</a>"});
+ return span;
+ },
+ scroll: function(fromJumper, toJumper) {
+ var destPos, prevPos;
+ prevPos = fromJumper.getBoundingClientRect().top;
+ destPos = toJumper.getBoundingClientRect().top;
+ return window.scrollBy(0, destPos - prevPos);
+ }
+ };
+
+ return PostJumper;
+
+}).call(this);
+
+RelativeDates = (function() {
+ var RelativeDates,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ RelativeDates = {
+ INTERVAL: $.MINUTE / 2,
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || Index.enabled) {
+ this.flush();
+ $.on(d, 'visibilitychange PostsInserted', this.flush);
+ }
+ if (Conf['Relative Post Dates']) {
+ return Callbacks.Post.push({
+ name: 'Relative Post Dates',
+ cb: this.node
+ });
+ }
+ },
+ node: function() {
+ var dateEl;
+ if (!this.info.date) {
+ return;
+ }
+ dateEl = this.nodes.date;
+ if (Conf['Relative Date Title']) {
+ $.on(dateEl, 'mouseover', (function(_this) {
+ return function() {
+ return RelativeDates.hover(_this);
+ };
+ })(this));
+ return;
+ }
+ if (this.isClone) {
+ return;
+ }
+ dateEl.title = dateEl.textContent;
+ return RelativeDates.update(this);
+ },
+ relative: function(diff, now, date, abbrev) {
+ var days, months, number, rounded, unit, years;
+ unit = (number = diff / $.DAY) >= 1 ? (years = now.getFullYear() - date.getFullYear(), months = now.getMonth() - date.getMonth(), days = now.getDate() - date.getDate(), years > 1 ? (number = years - (months < 0 || months === 0 && days < 0), 'year') : years === 1 && (months > 0 || months === 0 && days >= 0) ? (number = years, 'year') : (months = months + 12 * years) > 1 ? (number = months - (days < 0), 'month') : months === 1 && days >= 0 ? (number = months, 'month') : 'day') : (number = diff / $.HOUR) >= 1 ? 'hour' : (number = diff / $.MINUTE) >= 1 ? 'minute' : (number = Math.max(0, diff) / $.SECOND, 'second');
+ rounded = Math.round(number);
+ if (abbrev) {
+ unit = unit === 'month' ? 'mo' : unit[0];
+ } else {
+ if (rounded !== 1) {
+ unit += 's';
+ }
+ }
+ if (abbrev) {
+ return "" + rounded + unit;
+ } else {
+ return rounded + " " + unit + " ago";
+ }
+ },
+ stale: [],
+ flush: function() {
+ var data, i, len, now, ref;
+ if (d.hidden) {
+ return;
+ }
+ now = new Date();
+ ref = RelativeDates.stale;
+ for (i = 0, len = ref.length; i < len; i++) {
+ data = ref[i];
+ RelativeDates.update(data, now);
+ }
+ RelativeDates.stale = [];
+ clearTimeout(RelativeDates.timeout);
+ return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL);
+ },
+ hover: function(post) {
+ var date, diff, now;
+ date = post.info.date;
+ now = new Date();
+ diff = now - date;
+ return post.nodes.date.title = RelativeDates.relative(diff, now, date);
+ },
+ update: function(data, now) {
+ var abbrev, date, diff, i, isPost, len, ref, relative, singlePost;
+ isPost = data instanceof Post;
+ if (isPost) {
+ date = data.info.date;
+ abbrev = false;
+ } else {
+ date = new Date(+data.dataset.utc);
+ abbrev = !!data.dataset.abbrev;
+ }
+ now || (now = new Date());
+ diff = now - date;
+ relative = RelativeDates.relative(diff, now, date, abbrev);
+ if (isPost) {
+ ref = [data].concat(data.clones);
+ for (i = 0, len = ref.length; i < len; i++) {
+ singlePost = ref[i];
+ singlePost.nodes.date.firstChild.textContent = relative;
+ }
+ } else {
+ data.firstChild.textContent = relative;
+ }
+ return RelativeDates.setOwnTimeout(diff, data);
+ },
+ setOwnTimeout: function(diff, data) {
+ var delay;
+ delay = diff < $.MINUTE ? $.SECOND - (diff + $.SECOND / 2) % $.SECOND : diff < $.HOUR ? $.MINUTE - (diff + $.MINUTE / 2) % $.MINUTE : diff < $.DAY ? $.HOUR - (diff + $.HOUR / 2) % $.HOUR : $.DAY - (diff + $.DAY / 2) % $.DAY;
+ return setTimeout(RelativeDates.markStale, delay, data);
+ },
+ markStale: function(data) {
+ if (indexOf.call(RelativeDates.stale, data) >= 0) {
+ return;
+ }
+ if (data instanceof Post && !g.posts.get(data.fullID)) {
+ return;
+ }
+ if (data instanceof Element && !doc.contains(data)) {
+ return;
+ }
+ return RelativeDates.stale.push(data);
+ }
+ };
+
+ return RelativeDates;
+
+}).call(this);
+
+RemoveSpoilers = (function() {
+ var RemoveSpoilers,
+ slice = [].slice;
+
+ RemoveSpoilers = {
+ init: function() {
+ if (Conf['Reveal Spoilers']) {
+ $.addClass(doc, 'reveal-spoilers');
+ }
+ if (!Conf['Remove Spoilers']) {
+ return;
+ }
+ Callbacks.Post.push({
+ name: 'Reveal Spoilers',
+ cb: this.node
+ });
+ if (g.VIEW === 'archive') {
+ return $.ready(function() {
+ return RemoveSpoilers.unspoiler($.id('arc-list'));
+ });
+ }
+ },
+ node: function() {
+ return RemoveSpoilers.unspoiler(this.nodes.comment);
+ },
+ unspoiler: function(el) {
+ var i, len, span, spoiler, spoilers;
+ spoilers = $$(g.SITE.selectors.spoiler, el);
+ for (i = 0, len = spoilers.length; i < len; i++) {
+ spoiler = spoilers[i];
+ span = $.el('span', {
+ className: 'removed-spoiler'
+ });
+ $.replace(spoiler, span);
+ $.add(span, slice.call(spoiler.childNodes));
+ }
+ }
+ };
+
+ return RemoveSpoilers;
+
+}).call(this);
+
+Report = (function() {
+ var Report;
+
+ Report = {
+ init: function() {
+ var match;
+ if (!(match = location.search.match(/\bno=(\d+)/))) {
+ return;
+ }
+ Captcha.replace.init();
+ this.postID = +match[1];
+ return $.ready(this.ready);
+ },
+ ready: function() {
+ $.addStyle(CSS.report);
+ if (Conf['Archive Report']) {
+ Report.archive();
+ }
+ new MutationObserver(function() {
+ Report.fit('iframe[src^="https://www.google.com/recaptcha/api2/frame"]');
+ return Report.fit('body');
+ }).observe(d.body, {
+ childList: true,
+ attributes: true,
+ subtree: true
+ });
+ return Report.fit('body');
+ },
+ fit: function(selector) {
+ var dy, el;
+ if (!((el = $(selector, doc)) && getComputedStyle(el).visibility !== 'hidden')) {
+ return;
+ }
+ dy = el.getBoundingClientRect().bottom - doc.clientHeight + 8;
+ if (dy > 0) {
+ return window.resizeBy(0, dy);
+ }
+ },
+ archive: function() {
+ var enabled, fieldset, form, match, message, reason, submit, types, urls;
+ if (!(urls = Redirect.report(g.BOARD.ID)).length) {
+ return;
+ }
+ form = $('form');
+ types = $.id('reportTypes');
+ message = $('h3');
+ fieldset = $.el('fieldset', {
+ id: 'archive-report',
+ hidden: true
+ }, {innerHTML: "<legend><label><input id=\"archive-report-enabled\" type=\"checkbox\">Report illegal content to archives</label></legend><label for=\"archive-report-reason\">Details</label><textarea id=\"archive-report-reason\" disabled>Illegal content</textarea><button id=\"archive-report-submit\" hidden>Submit</button>"});
+ enabled = $('#archive-report-enabled', fieldset);
+ reason = $('#archive-report-reason', fieldset);
+ submit = $('#archive-report-submit', fieldset);
+ $.on(enabled, 'change', function() {
+ return reason.disabled = !this.checked;
+ });
+ if (form && types) {
+ fieldset.hidden = !$('[value="31"]', types).checked;
+ $.on(types, 'change', function(e) {
+ fieldset.hidden = e.target.value !== '31';
+ return Report.fit('body');
+ });
+ $.after(types, fieldset);
+ Report.fit('body');
+ $.one(form, 'submit', function(e) {
+ if (!fieldset.hidden && enabled.checked) {
+ e.preventDefault();
+ return Report.archiveSubmit(urls, reason.value, (function(_this) {
+ return function(results) {
+ _this.action = '#archiveresults=' + encodeURIComponent(JSON.stringify(results));
+ return _this.submit();
+ };
+ })(this));
+ }
+ });
+ } else if (message) {
+ fieldset.hidden = /Report submitted!/.test(message.textContent);
+ $.on(enabled, 'change', function() {
+ return submit.hidden = !this.checked;
+ });
+ $.after(message, fieldset);
+ $.on(submit, 'click', function() {
+ return Report.archiveSubmit(urls, reason.value, Report.archiveResults);
+ });
+ }
+ if ((match = location.hash.match(/^#archiveresults=(.*)$/))) {
+ try {
+ return Report.archiveResults(JSON.parse(decodeURIComponent(match[1])));
+ } catch (error) {}
+ }
+ },
+ archiveSubmit: function(urls, reason, cb) {
+ var fn, form, i, len, name, ref, results, url;
+ form = $.formData({
+ board: g.BOARD.ID,
+ num: Report.postID,
+ reason: reason
+ });
+ results = [];
+ fn = function(name, url) {
+ return $.ajax(url, {
+ onloadend: function() {
+ results.push([
+ name, this.response || {
+ error: ''
+ }
+ ]);
+ if (results.length === urls.length) {
+ return cb(results);
+ }
+ },
+ form: form
+ });
+ };
+ for (i = 0, len = urls.length; i < len; i++) {
+ ref = urls[i], name = ref[0], url = ref[1];
+ fn(name, url);
+ }
+ },
+ archiveResults: function(results) {
+ var fieldset, i, len, line, name, ref, response;
+ fieldset = $.id('archive-report');
+ for (i = 0, len = results.length; i < len; i++) {
+ ref = results[i], name = ref[0], response = ref[1];
+ line = $.el('h3', {
+ className: 'archive-report-response'
+ });
+ if ('success' in response) {
+ $.addClass(line, 'archive-report-success');
+ line.textContent = name + ": " + response.success;
+ } else {
+ $.addClass(line, 'archive-report-error');
+ line.textContent = name + ": " + (response.error || 'Error reporting post.');
+ }
+ if (fieldset) {
+ $.before(fieldset, line);
+ } else {
+ $.add(d.body, line);
+ }
+ }
+ }
+ };
+
+ return Report;
+
+}).call(this);
+
+ThreadLinks = (function() {
+ var ThreadLinks;
+
+ ThreadLinks = {
+ init: function() {
+ if (!(g.VIEW === 'index' && Conf['Open Threads in New Tab'])) {
+ return;
+ }
+ Callbacks.Post.push({
+ name: 'Thread Links',
+ cb: this.node
+ });
+ return Callbacks.CatalogThread.push({
+ name: 'Thread Links',
+ cb: this.catalogNode
+ });
+ },
+ node: function() {
+ if (this.isReply || this.isClone) {
+ return;
+ }
+ return ThreadLinks.process(this.nodes.reply);
+ },
+ catalogNode: function() {
+ return ThreadLinks.process(this.nodes.thumb.parentNode);
+ },
+ process: function(link) {
+ return link.target = '_blank';
+ }
+ };
+
+ return ThreadLinks;
+
+}).call(this);
+
+Time = (function() {
+ var Time;
+
+ Time = {
+ init: function() {
+ var ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Time Formatting'])) {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'Time Formatting',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var textContent;
+ if (!this.info.date || this.isClone) {
+ return;
+ }
+ textContent = this.nodes.date.textContent;
+ return this.nodes.date.textContent = textContent.match(/^\s*/)[0] + Time.format(Conf['time'], this.info.date) + textContent.match(/\s*$/)[0];
+ },
+ format: function(formatString, date) {
+ return formatString.replace(/%(.)/g, function(s, c) {
+ if ($.hasOwn(Time.formatters, c)) {
+ return Time.formatters[c].call(date);
+ } else {
+ return s;
+ }
+ });
+ },
+ day: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+ month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+ localeFormat: function(date, options, defaultValue) {
+ if (Conf['timeLocale']) {
+ try {
+ return Intl.DateTimeFormat(Conf['timeLocale'], options).format(date);
+ } catch (error) {}
+ }
+ return defaultValue;
+ },
+ localeFormatPart: function(date, options, part, defaultValue) {
+ var parts;
+ if (Conf['timeLocale']) {
+ try {
+ parts = Intl.DateTimeFormat(Conf['timeLocale'], options).formatToParts(date);
+ return parts.map(function(x) {
+ if (x.type === part) {
+ return x.value;
+ } else {
+ return '';
+ }
+ }).join('');
+ } catch (error) {}
+ }
+ return defaultValue;
+ },
+ zeroPad: function(n) {
+ if (n < 10) {
+ return "0" + n;
+ } else {
+ return n;
+ }
+ },
+ formatters: {
+ a: function() {
+ return Time.localeFormat(this, {
+ weekday: 'short'
+ }, Time.day[this.getDay()].slice(0, 3));
+ },
+ A: function() {
+ return Time.localeFormat(this, {
+ weekday: 'long'
+ }, Time.day[this.getDay()]);
+ },
+ b: function() {
+ return Time.localeFormat(this, {
+ month: 'short'
+ }, Time.month[this.getMonth()].slice(0, 3));
+ },
+ B: function() {
+ return Time.localeFormat(this, {
+ month: 'long'
+ }, Time.month[this.getMonth()]);
+ },
+ d: function() {
+ return Time.zeroPad(this.getDate());
+ },
+ e: function() {
+ return this.getDate();
+ },
+ H: function() {
+ return Time.zeroPad(this.getHours());
+ },
+ I: function() {
+ return Time.zeroPad(this.getHours() % 12 || 12);
+ },
+ k: function() {
+ return this.getHours();
+ },
+ l: function() {
+ return this.getHours() % 12 || 12;
+ },
+ m: function() {
+ return Time.zeroPad(this.getMonth() + 1);
+ },
+ M: function() {
+ return Time.zeroPad(this.getMinutes());
+ },
+ p: function() {
+ return Time.localeFormatPart(this, {
+ hour: 'numeric',
+ hour12: true
+ }, 'dayperiod', (this.getHours() < 12 ? 'AM' : 'PM'));
+ },
+ P: function() {
+ return Time.formatters.p.call(this).toLowerCase();
+ },
+ S: function() {
+ return Time.zeroPad(this.getSeconds());
+ },
+ y: function() {
+ return this.getFullYear().toString().slice(2);
+ },
+ Y: function() {
+ return this.getFullYear();
+ },
+ '%': function() {
+ return '%';
+ }
+ }
+ };
+
+ return Time;
+
+}).call(this);
+
+Tinyboard = (function() {
+ var Tinyboard;
+
+ Tinyboard = {
+ init: function() {
+ if (g.SITE.software !== 'tinyboard') {
+ return;
+ }
+ if (g.VIEW === 'thread') {
+ return Main.ready(function() {
+ return $.global(function() {
+ var base, boardID, form, originalNoko, ref, ref1, ref2, threadID;
+ ref = document.currentScript.dataset, boardID = ref.boardID, threadID = ref.threadID;
+ threadID = +threadID;
+ form = document.querySelector('form[name="post"]');
+ window.$(document).ajaxComplete(function(event, request, settings) {
+ var detail, noko, postID, redirect, ref1, ref2;
+ if (settings.url !== form.action) {
+ return;
+ }
+ if (!(postID = +((ref1 = request.responseJSON) != null ? ref1.id : void 0))) {
+ return;
+ }
+ detail = {
+ boardID: boardID,
+ threadID: threadID,
+ postID: postID
+ };
+ try {
+ ref2 = request.responseJSON, redirect = ref2.redirect, noko = ref2.noko;
+ if (redirect && (typeof originalNoko !== "undefined" && originalNoko !== null) && !originalNoko && !noko) {
+ detail.redirect = redirect;
+ }
+ } catch (error) {}
+ event = new CustomEvent('QRPostSuccessful', {
+ bubbles: true,
+ detail: detail
+ });
+ return document.dispatchEvent(event);
+ });
+ originalNoko = (ref1 = window.tb_settings) != null ? (ref2 = ref1.ajax) != null ? ref2.always_noko_replies : void 0 : void 0;
+ return ((base = (window.tb_settings || (window.tb_settings = {}))).ajax || (base.ajax = {})).always_noko_replies = true;
+ }, {
+ boardID: g.BOARD.ID,
+ threadID: g.THREADID
+ });
+ });
+ }
+ }
+ };
+
+ return Tinyboard;
+
+}).call(this);
+
+Favicon = (function() {
+ var Favicon;
+
+ Favicon = {
+ init: function() {
+ return $.asap((function() {
+ return d.head && (Favicon.el = $('link[rel="shortcut icon"]', d.head));
+ }), Favicon.initAsap);
+ },
+ set: function(status) {
+ Favicon.status = status;
+ if (Favicon.el) {
+ Favicon.el.href = Favicon[status];
+ return $.add(d.head, Favicon.el);
+ }
+ },
+ initAsap: function() {
+ var href;
+ Favicon.el.type = 'image/x-icon';
+ href = Favicon.el.href;
+ Favicon.isSFW = /ws\.ico$/.test(href);
+ Favicon["default"] = href;
+ Favicon["switch"]();
+ if (Favicon.status) {
+ return Favicon.set(Favicon.status);
+ }
+ },
+ "switch": function() {
+ var f, i, items, t;
+ items = {
+ ferongr: ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///9zBQC/AADpDAP/gID/q6voCwJJTwpOAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxUlEQVR42q1TOwrCQBB9s0FRtJI0WoqFtSLYegoP4gVSeJsUHsHSI3iFeIqRXXgwrhlXwYHHhLwPTB7B36abBCV+0pA4DUBQUNZYQptGtW3jtoKyxgoe0yrBCoyZfL/5ioQ3URZOXW9I341l3oo+NXEZiW4CEuIzvPECopED4OaZ3RNmeAm4u+a8Jr5f17VyVoL8fr8qcltzwlyyj2iqcgPOQ9ExkHAITgD75bYBe0A5S4H/P9htuWMF3QXoQpwaKeT+lnsC6JE5I6aq6fEAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8AcH4AtswA2PJ55fKi6fIA1/FtpPADAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxElEQVQ4y2NgoBq4/vE/HJOsBiRQUIfA2AzBqQYqUfn00/9FLz+BaQxDCKqBmX7jExijKEDSDJPHrnnbGQhGV4RmOFwdVkNwhQMheYwQxhaIi7b9Z9A3gWAQm2BUoQOgRhgA8o7j1ozLC4LCyAZcx6kZI5qg4kLKqggDFFWxJySsUQVzlb4pwgAJaTRvokcVNgOqOv8zcHBCsL07DgNg8YsczzA5MxtUL+DMD8g0slxI/H8GQ/P/DJKyeKIRpglXZsIiBwBhP5O+VbI/JgAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8oeQBJ3ABV/wHM/7Lu/+ZU/gAqUP3dAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAx0lEQVQ4y2NgoBYI+cfwH4ZJVgMS0KhEYGyG4FQDkzjzf9P/d/+fgWl0QwiqgSkI/c8IxsgKkDXD5LFq9rwDweiK0A2HqcNqCK5wICSPEcLYAtH+AMN/IXMIBrEJRie6OEgjDAC5x3FqxuUFNiEUA67j1IweTTBxBQ1puAG86jgSEraogskJWSBcwCGF5k30qMJmgMFEhv/MXBAs5oLDAFj8IsczTE7UEeECbhU8+QGZRpaTi2b4L2zF8J9TGk80wjThykzY5AAW/2O1C2mIbgAAAABJRU5ErkJggg=='],
+ 'xat-': ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAG1BMVEX+AACLkZFub2yfaF3zZGIAAAD/AAD/iYr/zs8IPcF6AAAABXRSTlMAeprJ7xzg6IEAAABZSURBVAjXY2DABKGBSkqioQwMrGmpxsZhaQEMDGFpIa5pqSCRtPDSNJBIaGh5eShQDYOye0V7iREKAyQFYoiCFAcyILQDGcGmEEZYkGoqiMHKysAQEICwGwAAjBmBqhYlagAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAPFBMVEUAAACEgoBva2ilamDxcG7IaWYgFBNOSEf//f0PDQwBAAA7LCwAAAD/AAD+hIX+m5z+zc5HAADPAAAGAADl032uAAAADHRSTlMAzNv0/vz+6v3+7ALrmfyXAAAAaUlEQVQY042PyxKAIAhFAc1eV7T6/3/N8VXOtAgWwBm4ANEPA8AswpySXHvvYZLlpBNrh9pDtcSqAQ1BUTVIjNUQY5icmwfglmXNgE0d6QBF9GigrU0A9LoM53U1kFzk6SBQuWfD/vHqDUCpBmVKTTM4AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAIVBMVEUAAACRjop4dXVpZ2tdcI9dfKdisfMAAAAumMN9xv+s2/+PADT2AAAAB3RSTlMAepGdv83v3HIc4QAAAFxJREFUCNdjYMAE5YXKRuLlDAzsHe2uIRUdBQwMFR1l6R3tIJGOyukdIJHy8lkry4FqGEwzV62aFozMUAFJOQEZ4iDFhQwI7UBGaTiEUVFs3g5isLMzMBQUIOwGAJRlIu9hk08QAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAMFBMVEUAAACAgYVlc4ljsu4AAAAAAAAAAAAumMODyP6b1P6e1f/g8v89msgSIiwNFxwbPU3tQYj5AAAABnRSTlMAxej+9VTmD9ciAAAAZElEQVQI12NgwARpiUKKYmkMDGzlZUpK6eUJDAzp5clm5WUgkfKMtnKQSFpa54o0oBoGJYvZO88+gjJu7wMyhIBS2SCGGFDxaxADpP32NjAjSe0bSFd6epIaWISNjYEhJRVhNwAGlyJpYtcvcAAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAHlBMVEUfJSCRi5Frbm9dn19082KR/30AAABmzDOq/5vZ/9Gt/vt2AAAABnRSTlMAe5rJ7/4vxEp4AAAAWUlEQVQI12NgwARpiUpKYmkMDGzlZcbG6eUJDAzp5Slu5WUgkfLUsHKQSFpaRGsaUA2DsmvnjBAjFAZICsQQAylOZEBoBzKSzSCM9CS1MhCDjY2BISEBYTcAtgAcKSK2vuIAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAM1BMVEUAAACBj39tfm1qj2RepFlu2VQAAQAAAAAAAABmyzOX/oSr/pus/pzk/98PGgtatC4CBAI1ENblAAAACHRSTlMA09/p9v77ig0SBcQAAABnSURBVBjTjY9LDsAgCEQRsR2xWu9/2hK/adJFYQG8wABEPwyAYzNnSatjjPAiviWLhPCqI1R7HBrQdCmGBrEETTmnUAq/QMm5dODHyAQOXXR1zLUGsIEI7lonMGfeHQTq9xw4P159AIxSBSC53km7AAAAAElFTkSuQmCC'],
+ Mayhem: ['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'],
+ '4chanJS': ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAD1BMVEUBAAAAAAD/AABnZ2f///8nFk05AAAAAXRSTlMAQObYZgAAAEFJREFUeNqNjgEKACAMAjvX/98cAkkxgmSgO8Bt/Ai4ApJ6KKhzF3OiEMDASrGB/QWgPEHsUpN+Ng9xAETMYhDrWmeHAMcmvycWAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAD1BMVEUAAAAAAAD/AABmZmYA/wBD99DBAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAE9JREFUCNdljcsRACEIQ5MOiNKAdGAJ9N/Uiu7nsMzABHgB4B8ygFoZA2hhVWavhhGeURPJU9q45+17hGbfGxa82Ndex3hEM44SJGD2/b4AzDgGlHbl388AAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAD1BMVEUBAAAAAAAul8NnZ2f////82iC9AAAAAXRSTlMAQObYZgAAAEFJREFUeNqNjgEKACAMAjvX/98cAkkxgmSgO8Bt/Ai4ApJ6KKhzF3OiEMDASrGB/QWgPEHsUpN+Ng9xAETMYhDrWmeHAMcmvycWAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAD1BMVEUAAAAAAAAul8NnZ2f/AAD7B+mqAAAAAXRSTlMAQObYZgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAE9JREFUCNdljcsRACEIQ5MOiNKAdGAJ9N/Uiu7nsMzABHgB4B8ygFoZA2hhVWavhhGeURPJU9q45+17hGbfGxa82Ndex3hEM44SJGD2/b4AzDgGlHbl388AAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAElBMVEUBAAAAAABmzDNlyjJnZ2f///+6o7dfAAAAAXRSTlMAQObYZgAAAERJREFUeF6NjkEKADEIA51o///lJZfQxUsHITogWi8AvwZJuxmYa25xDooBLEwOWFTYAsYVhdorLZt9Ng9xCUTCUCQ2H3F4ANrZ2WNiAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAD1BMVEUAAAAAAABmzDNmZmb/AAC8/wCMAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAE9JREFUCNdljcsRACEIQ5MOiNKAdGAJ9N/Uiu7nsMzABHgB4B8ygFoZA2hhVWavhhGeURPJU9q45+17hGbfGxa82Ndex3hEM44SJGD2/b4AzDgGlHbl388AAAAASUVORK5CYII='],
+ Original: ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEX/////AAD///8AAABBZmS3AAAAAXRSTlMAQObYZgAAAExJREFUeF4tyrENgDAMAMFXKuQswQLBG3mOlBnFS1gwDfIYLpEivvjq2MlqjmYvYg5jWEzCwtDSQlwcXKCVLrpFbvLvvSf9uZJ2HusDtJAY7Tkn1oYAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAhElEQVR42q1RwQnAMAjMu5M4guAKXa4j5dUROo5tipSDcrFChUONd0di2m/hEGVOHDyIPufgwAFASDkpoSzmBrkJ2UMyR9LsJ3rvrqo3Rt1YMIMhhNnOxLMnoMFBxHyJAr2IOBFzA8U+6pLBdmEJTA0aMVjpDd6Loks0s5HZNwYx8tfZCZ0kll7ORffZAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEX///8ul8P///8AAACaqgkzAAAAAXRSTlMAQObYZgAAAExJREFUeF4tyrENgDAMAMFXKuQswQLBG3mOlBnFS1gwDfIYLpEivvjq2MlqjmYvYg5jWEzCwtDSQlwcXKCVLrpFbvLvvSf9uZJ2HusDtJAY7Tkn1oYAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAALVBMVEUAAAAAAAAAAAAAAAABBQcHFx4KISoNLToaVW4oKCgul8M4ODg7OzvBwcH///8uS/CdAAAAA3RSTlMAx9dmesIgAAAAV0lEQVR42m2NWw6AIBAD1eILZO5/XI0UAgm7H9tOsu0yGWAQSOoFijHOxOANGqm/LczpOaXs4gISrPZ+gc2+hO5w2xdwgOjBFUIF+sEJrhUl9JFr+badFwR+BfqlmGUJAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEX///9mzDP///8AAACT0n1lAAAAAXRSTlMAQObYZgAAAExJREFUeF4tyrENgDAMAMFXKuQswQLBG3mOlBnFS1gwDfIYLpEivvjq2MlqjmYvYg5jWEzCwtDSQlwcXKCVLrpFbvLvvSf9uZJ2HusDtJAY7Tkn1oYAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAALVBMVEUAAAAAAAAAAAAAAAAECAIQIAgWLAsePA8oKCg4ODg6dB07OztmzDPBwcH///+rsf3XAAAAA3RSTlMAx9dmesIgAAAAV0lEQVR42m2NWw6AIBAD1eIDhbn/cTVSCCTsfmw7ybbLZIBBIKkXKKU0E4M3aKT+tjCn5xiziwuIsNr7BTb7ErrDZV/AAaIHdwgV6AcnuFaU0Eeu5dt2XiUyBjCQ2bIrAAAAAElFTkSuQmCC'],
+ 'Metro': ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAC/AABrZQDiAAAAAXRSTlMAQObYZgAAABJJREFUCB1jZGBgrMNAQEEc4gCSfAX5bRw/NQAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAAAAAAAAAC/AAD///8dAAApAABsAAAHAAA4AACQAAAsAABMCpCvAAAAA3RSTlMAPse+s4iwAAAAMklEQVQI12NggAFmY2MDECaNAQZCilAzVJyg5oS4GqAxUtygjIp2KGOKJ5SxepcB3BUAcdYRqxAtgFoAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAAA1/GhpCidAAAAAXRSTlMAQObYZgAAABJJREFUCB1jZGBgrMNAQEEc4gCSfAX5bRw/NQAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAAAAAAAAAAA1/H///8AISUALzQAeokACAkAQEcAorYAMTcE9WFNAAAAA3RSTlMAPse+s4iwAAAAMklEQVQI12NggAFmY2MDECaNAQZCilAzVJyg5oS4GqAxUtygjIp2KGOKJ5SxepcB3BUAcdYRqxAtgFoAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAABV/wErM5hwAAAAAXRSTlMAQObYZgAAABJJREFUCB1jZGBgrMNAQEEc4gCSfAX5bRw/NQAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAAAAAAAAABV/wH///8NKAASOAAwkQADCgAZTABAwQATOwC5e3VGAAAAA3RSTlMAPse+s4iwAAAAMklEQVQI12NggAFmY2MDECaNAQZCilAzVJyg5oS4GqAxUtygjIp2KGOKJ5SxepcB3BUAcdYRqxAtgFoAAAAASUVORK5CYII=']
+ };
+ items = $.getOwn(items, Conf['favicon']);
+ f = Favicon;
+ t = 'data:image/png;base64,';
+ i = 0;
+ while (items[i]) {
+ items[i] = t + items[i++];
+ }
+ f.unreadDead = items[0], f.unreadDeadY = items[1], f.unreadSFW = items[2], f.unreadSFWY = items[3], f.unreadNSFW = items[4], f.unreadNSFWY = items[5];
+ return f.update();
+ },
+ update: function() {
+ if (this.isSFW) {
+ this.unread = this.unreadSFW;
+ return this.unreadY = this.unreadSFWY;
+ } else {
+ this.unread = this.unreadNSFW;
+ return this.unreadY = this.unreadNSFWY;
+ }
+ },
+ SFW: '//s.4cdn.org/image/favicon-ws.ico',
+ NSFW: '//s.4cdn.org/image/favicon.ico',
+ dead: 'data:image/gif;base64,R0lGODlhEAAQAKECAAAAAP8AAP///////yH5BAEKAAIALAAAAAAQABAAAAIvlI+pq+D9DAgUoFkPDlbs7lFZKIJOJJ3MyraoB14jFpOcVMpzrnF3OKlZYsMWowAAOw==',
+ logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAgMAAAC+UIlYAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAGlJREFUWMPtlkEKADEIA/tJP9lXLttQto2yHxgDHozTi0ToGK2WKZZ+HAQQMZc+xBwI4EZ+wAC2IfPuSIDOZJrSZQEAX9eVJhhwIuUYAnQe8rhAEMAZlTI2MID9f5Clyh0JeE1V1ZEAvB4qDfwuJTSGRAAAAABJRU5ErkJggg=='
+ };
+
+ return Favicon;
+
+}).call(this);
+
+MarkNewIPs = (function() {
+ var MarkNewIPs;
+
+ MarkNewIPs = {
+ init: function() {
+ if (!(g.SITE.software === 'yotsuba' && g.VIEW === 'thread' && Conf['Mark New IPs'])) {
+ return;
+ }
+ return Callbacks.Thread.push({
+ name: 'Mark New IPs',
+ cb: this.node
+ });
+ },
+ node: function() {
+ MarkNewIPs.ipCount = this.ipCount;
+ MarkNewIPs.postCount = this.posts.keys.length;
+ return $.on(d, 'ThreadUpdate', MarkNewIPs.onUpdate);
+ },
+ onUpdate: function(e) {
+ var deletedPosts, fullID, i, ipCount, j, k, len, len1, newPosts, postCount, ref;
+ ref = e.detail, ipCount = ref.ipCount, postCount = ref.postCount, newPosts = ref.newPosts, deletedPosts = ref.deletedPosts;
+ if (ipCount == null) {
+ return;
+ }
+ switch (ipCount - MarkNewIPs.ipCount) {
+ case postCount - MarkNewIPs.postCount + deletedPosts.length:
+ i = MarkNewIPs.ipCount;
+ for (j = 0, len = newPosts.length; j < len; j++) {
+ fullID = newPosts[j];
+ MarkNewIPs.markNew(g.posts.get(fullID), ++i);
+ }
+ break;
+ case -deletedPosts.length:
+ for (k = 0, len1 = newPosts.length; k < len1; k++) {
+ fullID = newPosts[k];
+ MarkNewIPs.markOld(g.posts.get(fullID));
+ }
+ }
+ MarkNewIPs.ipCount = ipCount;
+ return MarkNewIPs.postCount = postCount;
+ },
+ markNew: function(post, ipCount) {
+ var counter, suffix;
+ suffix = (Math.floor(ipCount / 10)) % 10 === 1 ? 'th' : ['st', 'nd', 'rd'][ipCount % 10 - 1] || 'th';
+ counter = $.el('span', {
+ className: 'ip-counter',
+ textContent: "(" + ipCount + ")"
+ });
+ post.nodes.nameBlock.title = "This is the " + ipCount + suffix + " IP in the thread.";
+ $.add(post.nodes.nameBlock, [$.tn(' '), counter]);
+ return $.addClass(post.nodes.root, 'new-ip');
+ },
+ markOld: function(post) {
+ post.nodes.nameBlock.title = 'Not the first post from this IP.';
+ return $.addClass(post.nodes.root, 'old-ip');
+ }
+ };
+
+ return MarkNewIPs;
+
+}).call(this);
+
+ReplyPruning = (function() {
+ var ReplyPruning;
+
+ ReplyPruning = {
+ init: function() {
+ var el, label;
+ if (!(g.VIEW === 'thread' && Conf['Reply Pruning'])) {
+ return;
+ }
+ this.container = $.frag();
+ this.summary = $.el('span', {
+ hidden: true,
+ className: 'summary'
+ });
+ this.summary.style.cursor = 'pointer';
+ $.on(this.summary, 'click', (function(_this) {
+ return function() {
+ _this.inputs.enabled.checked = !_this.inputs.enabled.checked;
+ return $.event('change', null, _this.inputs.enabled);
+ };
+ })(this));
+ label = UI.checkbox('Prune Replies', 'Show Last', Conf['Prune All Threads']);
+ el = $.el('span', {
+ title: 'Maximum number of replies to show.'
+ }, {innerHTML: " <input type=\"number\" name=\"Max Replies\" min=\"0\" step=\"1\" value=\"" + E(Conf["Max Replies"]) + "\" class=\"field\">"});
+ $.prepend(el, label);
+ this.inputs = {
+ enabled: label.firstElementChild,
+ replies: el.lastElementChild
+ };
+ this.setEnabled.call(this.inputs.enabled);
+ $.on(this.inputs.enabled, 'change', this.setEnabled);
+ $.on(this.inputs.replies, 'change', $.cb.value);
+ Header.menu.addEntry({
+ el: el,
+ order: 190
+ });
+ return Callbacks.Thread.push({
+ name: 'Reply Pruning',
+ cb: this.node
+ });
+ },
+ position: 0,
+ hidden: 0,
+ hiddenFiles: 0,
+ total: 0,
+ totalFiles: 0,
+ setEnabled: function() {
+ var other;
+ other = QuoteThreading.input;
+ if (this.checked && (other != null ? other.checked : void 0)) {
+ other.checked = false;
+ $.event('change', null, other);
+ }
+ return ReplyPruning.active = this.checked;
+ },
+ showIfHidden: function(id) {
+ if (ReplyPruning.container && $("#" + id, ReplyPruning.container)) {
+ ReplyPruning.inputs.enabled.checked = false;
+ return $.event('change', null, ReplyPruning.inputs.enabled);
+ }
+ },
+ node: function() {
+ var ref;
+ ReplyPruning.thread = this;
+ if (this.isSticky) {
+ ReplyPruning.active = ReplyPruning.inputs.enabled.checked = true;
+ if (QuoteThreading.input) {
+ Conf['Thread Quotes'] = QuoteThreading.input.checked = false;
+ }
+ }
+ this.posts.forEach(function(post) {
+ if (post.isReply) {
+ ReplyPruning.total++;
+ if (post.file) {
+ return ReplyPruning.totalFiles++;
+ }
+ }
+ });
+ if (ReplyPruning.active && /^#p\d+$/.test(location.hash) && (1 <= (ref = this.posts.keys.indexOf(location.hash.slice(2))) && ref < 1 + Math.max(ReplyPruning.total - +Conf["Max Replies"], 0))) {
+ ReplyPruning.active = ReplyPruning.inputs.enabled.checked = false;
+ }
+ $.after(this.OP.nodes.root, ReplyPruning.summary);
+ $.on(ReplyPruning.inputs.enabled, 'change', ReplyPruning.update);
+ $.on(ReplyPruning.inputs.replies, 'change', ReplyPruning.update);
+ $.on(d, 'ThreadUpdate', ReplyPruning.updateCount);
+ $.on(d, 'ThreadUpdate', ReplyPruning.update);
+ return ReplyPruning.update();
+ },
+ updateCount: function(e) {
+ var fullID, i, len, ref;
+ if (e.detail[404]) {
+ return;
+ }
+ ref = e.detail.newPosts;
+ for (i = 0, len = ref.length; i < len; i++) {
+ fullID = ref[i];
+ ReplyPruning.total++;
+ if (g.posts.get(fullID).file) {
+ ReplyPruning.totalFiles++;
+ }
+ }
+ },
+ update: function() {
+ var boardTop, frag, hidden1, hidden2, node, oldPos, post, posts;
+ hidden1 = ReplyPruning.hidden;
+ hidden2 = ReplyPruning.active ? Math.max(ReplyPruning.total - +Conf["Max Replies"], 0) : 0;
+ oldPos = d.body.clientHeight - window.scrollY;
+ posts = ReplyPruning.thread.posts;
+ if (ReplyPruning.hidden < hidden2) {
+ while (ReplyPruning.hidden < hidden2 && ReplyPruning.position < posts.keys.length) {
+ post = posts.get(posts.keys[ReplyPruning.position++]);
+ if (post.isReply && !post.isFetchedQuote) {
+ while ((node = ReplyPruning.summary.nextSibling) && node !== post.nodes.root) {
+ $.add(ReplyPruning.container, node);
+ }
+ $.add(ReplyPruning.container, post.nodes.root);
+ ReplyPruning.hidden++;
+ if (post.file) {
+ ReplyPruning.hiddenFiles++;
+ }
+ }
+ }
+ } else if (ReplyPruning.hidden > hidden2) {
+ frag = $.frag();
+ while (ReplyPruning.hidden > hidden2 && ReplyPruning.position > 0) {
+ post = posts.get(posts.keys[--ReplyPruning.position]);
+ if (post.isReply && !post.isFetchedQuote) {
+ while ((node = ReplyPruning.container.lastChild) && node !== post.nodes.root) {
+ $.prepend(frag, node);
+ }
+ $.prepend(frag, post.nodes.root);
+ ReplyPruning.hidden--;
+ if (post.file) {
+ ReplyPruning.hiddenFiles--;
+ }
+ }
+ }
+ $.after(ReplyPruning.summary, frag);
+ $.event('PostsInserted', null, ReplyPruning.summary.parentNode);
+ }
+ ReplyPruning.summary.textContent = ReplyPruning.active ? g.SITE.Build.summaryText('+', ReplyPruning.hidden, ReplyPruning.hiddenFiles) : g.SITE.Build.summaryText('-', ReplyPruning.total, ReplyPruning.totalFiles);
+ ReplyPruning.summary.hidden = ReplyPruning.total <= +Conf["Max Replies"];
+ if (hidden1 !== hidden2 && (boardTop = Header.getTopOf($('.board'))) < 0) {
+ return window.scrollBy(0, Math.max(d.body.clientHeight - oldPos, window.scrollY + boardTop) - window.scrollY);
+ }
+ }
+ };
+
+ return ReplyPruning;
+
+}).call(this);
+
+ThreadStats = (function() {
+ var ThreadStats;
+
+ ThreadStats = {
+ postCount: 0,
+ fileCount: 0,
+ postIndex: 0,
+ init: function() {
+ var base, sc, statsHTML, statsTitle;
+ if (g.VIEW !== 'thread' || !Conf['Thread Stats']) {
+ return;
+ }
+ if (Conf['Page Count in Stats']) {
+ this[(typeof (base = g.SITE).isPrunedByAge === "function" ? base.isPrunedByAge(g.BOARD) : void 0) ? 'showPurgePos' : 'showPage'] = true;
+ }
+ statsHTML = {innerHTML: "<span id=\"post-count\">?</span> / <span id=\"file-count\">?</span>" + ((Conf["IP Count in Stats"] && g.SITE.hasIPCount) ? " / <span id=\"ip-count\">?</span>" : "") + ((Conf["Page Count in Stats"]) ? " / <span id=\"page-count\">?</span>" : "")};
+ statsTitle = 'Posts / Files';
+ if (Conf['IP Count in Stats'] && g.SITE.hasIPCount) {
+ statsTitle += ' / IPs';
+ }
+ if (Conf['Page Count in Stats']) {
+ statsTitle += (this.showPurgePos ? ' / Purge Position' : ' / Page');
+ }
+ if (Conf['Updater and Stats in Header']) {
+ this.dialog = sc = $.el('span', {
+ id: 'thread-stats',
+ title: statsTitle
+ });
+ $.extend(sc, statsHTML);
+ Header.addShortcut('stats', sc, 200);
+ } else {
+ this.dialog = sc = UI.dialog('thread-stats', {innerHTML: "<div class=\"move\" title=\"" + E(statsTitle) + "\">" + (statsHTML).innerHTML + "</div>"});
+ $.addClass(doc, 'float');
+ $.ready(function() {
+ return $.add(d.body, sc);
+ });
+ }
+ this.postCountEl = $('#post-count', sc);
+ this.fileCountEl = $('#file-count', sc);
+ this.ipCountEl = $('#ip-count', sc);
+ this.pageCountEl = $('#page-count', sc);
+ if (this.pageCountEl) {
+ $.on(this.pageCountEl, 'click', ThreadStats.fetchPage);
+ }
+ return Callbacks.Thread.push({
+ name: 'Thread Stats',
+ cb: this.node
+ });
+ },
+ node: function() {
+ ThreadStats.thread = this;
+ ThreadStats.count();
+ ThreadStats.update();
+ ThreadStats.fetchPage();
+ $.on(d, 'PostsInserted', function() {
+ return $.queueTask(ThreadStats.onPostsInserted);
+ });
+ return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate);
+ },
+ count: function() {
+ var i, j, n, post, posts, ref, ref1;
+ posts = ThreadStats.thread.posts;
+ n = posts.keys.length;
+ for (i = j = ref = ThreadStats.postIndex, ref1 = n; j < ref1; i = j += 1) {
+ post = posts.get(posts.keys[i]);
+ if (!post.isFetchedQuote) {
+ ThreadStats.postCount++;
+ ThreadStats.fileCount += post.files.length;
+ }
+ }
+ return ThreadStats.postIndex = n;
+ },
+ onUpdate: function(e) {
+ var fileCount, postCount, ref;
+ if (e.detail[404]) {
+ return;
+ }
+ ref = e.detail, postCount = ref.postCount, fileCount = ref.fileCount;
+ $.extend(ThreadStats, {
+ postCount: postCount,
+ fileCount: fileCount
+ });
+ ThreadStats.postIndex = ThreadStats.thread.posts.keys.length;
+ ThreadStats.update();
+ if (ThreadStats.showPage && ThreadStats.pageCountEl.textContent !== '1') {
+ return ThreadStats.fetchPage();
+ }
+ },
+ onPostsInserted: function() {
+ if (!(ThreadStats.thread.posts.keys.length > ThreadStats.postIndex)) {
+ return;
+ }
+ ThreadStats.count();
+ ThreadStats.update();
+ if (ThreadStats.showPage && ThreadStats.pageCountEl.textContent !== '1') {
+ return ThreadStats.fetchPage();
+ }
+ },
+ update: function() {
+ var fileCountEl, ipCountEl, postCountEl, ref, thread;
+ thread = ThreadStats.thread, postCountEl = ThreadStats.postCountEl, fileCountEl = ThreadStats.fileCountEl, ipCountEl = ThreadStats.ipCountEl;
+ postCountEl.textContent = ThreadStats.postCount;
+ fileCountEl.textContent = ThreadStats.fileCount;
+ if (ipCountEl != null) {
+ ipCountEl.textContent = (ref = thread.ipCount) != null ? ref : '?';
+ }
+ postCountEl.classList.toggle('warning', thread.postLimit && !thread.isSticky);
+ return fileCountEl.classList.toggle('warning', thread.fileLimit && !thread.isSticky);
+ },
+ fetchPage: function() {
+ if (!ThreadStats.pageCountEl) {
+ return;
+ }
+ clearTimeout(ThreadStats.timeout);
+ if (ThreadStats.thread.isDead) {
+ ThreadStats.pageCountEl.textContent = 'Dead';
+ $.addClass(ThreadStats.pageCountEl, 'warning');
+ return;
+ }
+ ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE);
+ return $.whenModified(g.SITE.urls.threadsListJSON(ThreadStats.thread), 'ThreadStats', ThreadStats.onThreadsLoad);
+ },
+ onThreadsLoad: function() {
+ var i, j, k, l, len, len1, len2, len3, len4, m, nThreads, o, page, pageNum, purgePos, ref, ref1, ref2, ref3, ref4, thread;
+ if (this.status === 200) {
+ if (ThreadStats.showPurgePos) {
+ purgePos = 1;
+ ref = this.response;
+ for (j = 0, len = ref.length; j < len; j++) {
+ page = ref[j];
+ ref1 = page.threads;
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ thread = ref1[k];
+ if (thread.no < ThreadStats.thread.ID) {
+ purgePos++;
+ }
+ }
+ }
+ ThreadStats.pageCountEl.textContent = purgePos;
+ return ThreadStats.pageCountEl.classList.toggle('warning', purgePos === 1);
+ } else {
+ i = nThreads = 0;
+ ref2 = this.response;
+ for (l = 0, len2 = ref2.length; l < len2; l++) {
+ page = ref2[l];
+ nThreads += page.threads.length;
+ }
+ ref3 = this.response;
+ for (pageNum = m = 0, len3 = ref3.length; m < len3; pageNum = ++m) {
+ page = ref3[pageNum];
+ ref4 = page.threads;
+ for (o = 0, len4 = ref4.length; o < len4; o++) {
+ thread = ref4[o];
+ if (thread.no === ThreadStats.thread.ID) {
+ ThreadStats.pageCountEl.textContent = pageNum + 1;
+ ThreadStats.pageCountEl.classList.toggle('warning', i >= nThreads - this.response[0].threads.length);
+ ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND);
+ ThreadStats.retry();
+ return;
+ }
+ i++;
+ }
+ }
+ }
+ } else if (this.status === 304) {
+ return ThreadStats.retry();
+ }
+ },
+ retry: function() {
+ if (!(ThreadStats.showPage && ThreadStats.pageCountEl.textContent !== '1' && !g.SITE.threadModTimeIgnoresSage && ThreadStats.thread.posts.get(ThreadStats.thread.lastPost).info.date > ThreadStats.lastPageUpdate)) {
+ return;
+ }
+ clearTimeout(ThreadStats.timeout);
+ return ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 5 * $.SECOND);
+ }
+ };
+
+ return ThreadStats;
+
+}).call(this);
+
+ThreadUpdater = (function() {
+ var ThreadUpdater,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ ThreadUpdater = {
+ init: function() {
+ var conf, el, input, name, ref, sc, subEntries, updateLink;
+ if (g.VIEW !== 'thread' || !Conf['Thread Updater']) {
+ return;
+ }
+ this.enabled = true;
+ this.audio = $.el('audio');
+ if ($.engine !== 'gecko') {
+ this.audio.src = this.beep;
+ }
+ if (Conf['Updater and Stats in Header']) {
+ this.dialog = sc = $.el('span', {
+ id: 'updater'
+ });
+ $.extend(sc, {innerHTML: "<span id=\"update-status\" class=\"empty\"></span><span id=\"update-timer\" class=\"empty\" title=\"Update now\"></span>"});
+ Header.addShortcut('updater', sc, 100);
+ } else {
+ this.dialog = sc = UI.dialog('updater', {innerHTML: "<div class=\"move\"></div><span id=\"update-status\" class=\"empty\"></span><span id=\"update-timer\" class=\"empty\" title=\"Update now\"></span>"});
+ $.addClass(doc, 'float');
+ $.ready(function() {
+ return $.add(d.body, sc);
+ });
+ }
+ this.checkPostCount = 0;
+ this.timer = $('#update-timer', sc);
+ this.status = $('#update-status', sc);
+ $.on(this.timer, 'click', this.update);
+ $.on(this.status, 'click', this.update);
+ updateLink = $.el('span', {
+ className: 'brackets-wrap updatelink'
+ });
+ $.extend(updateLink, {innerHTML: "<a href=\"javascript:;\">Update</a>"});
+ Main.ready(function() {
+ var navLinksBot;
+ if ((navLinksBot = $('.navLinksBot'))) {
+ return $.add(navLinksBot, [$.tn(' '), updateLink]);
+ }
+ });
+ $.on(updateLink.firstElementChild, 'click', this.update);
+ subEntries = [];
+ ref = Config.updater.checkbox;
+ for (name in ref) {
+ conf = ref[name];
+ el = UI.checkbox(name, name);
+ el.title = conf[1];
+ input = el.firstElementChild;
+ $.on(input, 'change', $.cb.checked);
+ if (input.name === 'Scroll BG') {
+ $.on(input, 'change', this.cb.scrollBG);
+ this.cb.scrollBG();
+ } else if (input.name === 'Auto Update') {
+ $.on(input, 'change', this.setInterval);
+ }
+ subEntries.push({
+ el: el
+ });
+ }
+ this.settings = $.el('span', {innerHTML: "<a href=\"javascript:;\">Interval</a>"});
+ $.on(this.settings, 'click', this.intervalShortcut);
+ subEntries.push({
+ el: this.settings
+ });
+ Header.menu.addEntry(this.entry = {
+ el: $.el('span', {
+ textContent: 'Updater'
+ }),
+ order: 110,
+ subEntries: subEntries
+ });
+ return Callbacks.Thread.push({
+ name: 'Thread Updater',
+ cb: this.node
+ });
+ },
+ node: function() {
+ ThreadUpdater.thread = this;
+ ThreadUpdater.root = this.nodes.root;
+ ThreadUpdater.outdateCount = 0;
+ ThreadUpdater.postIDs = [];
+ ThreadUpdater.fileIDs = [];
+ this.posts.forEach(function(post) {
+ ThreadUpdater.postIDs.push(post.ID);
+ if (post.file) {
+ return ThreadUpdater.fileIDs.push(post.ID);
+ }
+ });
+ ThreadUpdater.cb.interval.call($.el('input', {
+ value: Conf['Interval']
+ }));
+ $.on(d, 'QRPostSuccessful', ThreadUpdater.cb.checkpost);
+ $.on(d, 'visibilitychange', ThreadUpdater.cb.visibility);
+ return ThreadUpdater.setInterval();
+ },
+
+ /*
+ http://freesound.org/people/pierrecartoons1979/sounds/90112/
+ cc-by-nc-3.0
+ */
+ beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA',
+ playBeep: function() {
+ var audio;
+ audio = ThreadUpdater.audio;
+ audio.src || (audio.src = ThreadUpdater.beep);
+ if (audio.paused) {
+ return audio.play();
+ } else {
+ return $.one(audio, 'ended', ThreadUpdater.playBeep);
+ }
+ },
+ cb: {
+ checkpost: function(e) {
+ if (e.detail.threadID !== ThreadUpdater.thread.ID) {
+ return;
+ }
+ ThreadUpdater.postID = e.detail.postID;
+ ThreadUpdater.checkPostCount = 0;
+ ThreadUpdater.outdateCount = 0;
+ return ThreadUpdater.setInterval();
+ },
+ visibility: function() {
+ if (d.hidden) {
+ return;
+ }
+ ThreadUpdater.outdateCount = 0;
+ if (ThreadUpdater.seconds > ThreadUpdater.interval) {
+ return ThreadUpdater.setInterval();
+ }
+ },
+ scrollBG: function() {
+ return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() {
+ return true;
+ } : function() {
+ return !d.hidden;
+ };
+ },
+ interval: function(e) {
+ var val;
+ val = parseInt(this.value, 10);
+ if (val < 1) {
+ val = 1;
+ }
+ ThreadUpdater.interval = this.value = val;
+ if (e) {
+ return $.cb.value.call(this);
+ }
+ },
+ load: function() {
+ if (this !== ThreadUpdater.req) {
+ return;
+ }
+ switch (this.status) {
+ case 200:
+ ThreadUpdater.parse(this);
+ if (ThreadUpdater.thread.isArchived) {
+ return ThreadUpdater.kill();
+ } else {
+ return ThreadUpdater.setInterval();
+ }
+ break;
+ case 404:
+ return $.ajax(g.SITE.urls.catalogJSON({
+ boardID: ThreadUpdater.thread.board.ID
+ }), {
+ onloadend: function() {
+ var confirmed, i, k, len, len1, page, ref, ref1, thread;
+ if (this.status === 200) {
+ confirmed = true;
+ ref = this.response;
+ for (i = 0, len = ref.length; i < len; i++) {
+ page = ref[i];
+ ref1 = page.threads;
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ thread = ref1[k];
+ if (thread.no === ThreadUpdater.thread.ID) {
+ confirmed = false;
+ break;
+ }
+ }
+ }
+ } else {
+ confirmed = false;
+ }
+ if (confirmed) {
+ return ThreadUpdater.kill();
+ } else {
+ return ThreadUpdater.error(this);
+ }
+ }
+ });
+ default:
+ return ThreadUpdater.error(this);
+ }
+ }
+ },
+ kill: function() {
+ ThreadUpdater.thread.kill();
+ ThreadUpdater.setInterval();
+ return $.event('ThreadUpdate', {
+ 404: true,
+ threadID: ThreadUpdater.thread.fullID
+ });
+ },
+ error: function(req) {
+ if (req.status === 304) {
+ ThreadUpdater.set('status', '');
+ }
+ ThreadUpdater.setInterval();
+ if (!req.status) {
+ return ThreadUpdater.set('status', 'Connection Error', 'warning');
+ } else if (req.status !== 304) {
+ return ThreadUpdater.set('status', req.statusText + " (" + req.status + ")", 'warning');
+ }
+ },
+ setInterval: function() {
+ var cur, interval, j, limit;
+ clearTimeout(ThreadUpdater.timeoutID);
+ if (ThreadUpdater.thread.isDead) {
+ ThreadUpdater.set('status', (ThreadUpdater.thread.isArchived ? 'Archived' : '404'), 'warning');
+ ThreadUpdater.set('timer', '');
+ return;
+ }
+ if (ThreadUpdater.postID && ThreadUpdater.checkPostCount < 5) {
+ ThreadUpdater.set('timer', '...', 'loading');
+ ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND);
+ return;
+ }
+ if (!Conf['Auto Update']) {
+ ThreadUpdater.set('timer', 'Update');
+ return;
+ }
+ interval = ThreadUpdater.interval;
+ if (Conf['Optional Increase']) {
+ limit = d.hidden ? 10 : 5;
+ j = Math.min(ThreadUpdater.outdateCount, limit);
+ cur = (Math.floor(interval * 0.1) || 1) * j * j;
+ ThreadUpdater.seconds = $.minmax(cur, interval, 300);
+ } else {
+ ThreadUpdater.seconds = interval;
+ }
+ return ThreadUpdater.timeout();
+ },
+ intervalShortcut: function() {
+ var settings;
+ Settings.open('Advanced');
+ settings = $.id('fourchanx-settings');
+ return $('input[name=Interval]', settings).focus();
+ },
+ set: function(name, text, klass) {
+ var el, node;
+ el = ThreadUpdater[name];
+ if (node = el.firstChild) {
+ node.data = text;
+ } else {
+ el.textContent = text;
+ }
+ return el.className = klass != null ? klass : (text === '' ? 'empty' : '');
+ },
+ timeout: function() {
+ if (ThreadUpdater.seconds) {
+ ThreadUpdater.set('timer', ThreadUpdater.seconds);
+ ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000);
+ } else {
+ ThreadUpdater.outdateCount++;
+ ThreadUpdater.update();
+ }
+ return ThreadUpdater.seconds--;
+ },
+ update: function() {
+ var oldReq;
+ clearTimeout(ThreadUpdater.timeoutID);
+ ThreadUpdater.set('timer', '...', 'loading');
+ if ((oldReq = ThreadUpdater.req)) {
+ delete ThreadUpdater.req;
+ oldReq.abort();
+ }
+ return ThreadUpdater.req = $.whenModified(g.SITE.urls.threadJSON({
+ boardID: ThreadUpdater.thread.board.ID,
+ threadID: ThreadUpdater.thread.ID
+ }), 'ThreadUpdater', ThreadUpdater.cb.load, {
+ timeout: $.MINUTE
+ });
+ },
+ updateThreadStatus: function(type, status) {
+ var change, hasChanged;
+ if (!(hasChanged = ThreadUpdater.thread["is" + type] !== status)) {
+ return;
+ }
+ ThreadUpdater.thread.setStatus(type, status);
+ if (type === 'Closed' && ThreadUpdater.thread.isArchived) {
+ return;
+ }
+ change = type === 'Sticky' ? status ? 'now a sticky' : 'not a sticky anymore' : status ? 'now closed' : 'not closed anymore';
+ return new Notice('info', "The thread is " + change + ".", 30);
+ },
+ parse: function(req) {
+ var ID, OP, board, deletedFiles, deletedPosts, files, firstPost, i, index, ipCountEl, k, l, lastPost, len, len1, len2, len3, m, newPosts, node, post, postObject, postObjects, posts, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, scroll, thread, unreadCount, unreadQYCount;
+ postObjects = req.response.posts;
+ OP = postObjects[0];
+ thread = ThreadUpdater.thread;
+ board = thread.board;
+ ref = ThreadUpdater.postIDs, lastPost = ref[ref.length - 1];
+ if (postObjects[postObjects.length - 1].no < lastPost && new Date(req.getResponseHeader('Last-Modified')) - thread.posts.get(lastPost).info.date < 30 * $.SECOND) {
+ return;
+ }
+ g.SITE.Build.spoilerRange[board] = OP.custom_spoiler;
+ thread.setStatus('Archived', !!OP.archived);
+ ThreadUpdater.updateThreadStatus('Sticky', !!OP.sticky);
+ ThreadUpdater.updateThreadStatus('Closed', !!OP.closed);
+ thread.postLimit = !!OP.bumplimit;
+ thread.fileLimit = !!OP.imagelimit;
+ if (OP.unique_ips != null) {
+ thread.ipCount = OP.unique_ips;
+ }
+ posts = [];
+ index = [];
+ files = [];
+ newPosts = [];
+ for (i = 0, len = postObjects.length; i < len; i++) {
+ postObject = postObjects[i];
+ ID = postObject.no;
+ index.push(ID);
+ if (postObject.fsize) {
+ files.push(ID);
+ }
+ if (ID <= lastPost) {
+ continue;
+ }
+ if ((post = thread.posts.get(ID)) && !post.isFetchedQuote) {
+ post.resurrect();
+ continue;
+ }
+ newPosts.push(board + "." + ID);
+ node = g.SITE.Build.postFromObject(postObject, board.ID);
+ posts.push(new Post(node, thread, board));
+ if (ThreadUpdater.postID === ID) {
+ delete ThreadUpdater.postID;
+ }
+ }
+ deletedPosts = [];
+ ref1 = ThreadUpdater.postIDs;
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ ID = ref1[k];
+ if (!(indexOf.call(index, ID) < 0)) {
+ continue;
+ }
+ thread.posts.get(ID).kill();
+ deletedPosts.push(board + "." + ID);
+ }
+ ThreadUpdater.postIDs = index;
+ deletedFiles = [];
+ ref2 = ThreadUpdater.fileIDs;
+ for (l = 0, len2 = ref2.length; l < len2; l++) {
+ ID = ref2[l];
+ if (!(!(indexOf.call(files, ID) >= 0 || (ref3 = board + "." + ID, indexOf.call(deletedPosts, ref3) >= 0)))) {
+ continue;
+ }
+ thread.posts.get(ID).kill(true);
+ deletedFiles.push(board + "." + ID);
+ }
+ ThreadUpdater.fileIDs = files;
+ if (!posts.length) {
+ ThreadUpdater.set('status', '');
+ } else {
+ ThreadUpdater.set('status', "+" + posts.length, 'new');
+ ThreadUpdater.outdateCount = 0;
+ unreadCount = (ref4 = Unread.posts) != null ? ref4.size : void 0;
+ unreadQYCount = (ref5 = Unread.postsQuotingYou) != null ? ref5.size : void 0;
+ Main.callbackNodes('Post', posts);
+ if (d.hidden || !d.hasFocus()) {
+ if (Conf['Beep Quoting You'] && ((ref6 = Unread.postsQuotingYou) != null ? ref6.size : void 0) > unreadQYCount) {
+ ThreadUpdater.playBeep();
+ if (Conf['Beep']) {
+ ThreadUpdater.playBeep();
+ }
+ } else if (Conf['Beep'] && ((ref7 = Unread.posts) != null ? ref7.size : void 0) > 0 && unreadCount === 0) {
+ ThreadUpdater.playBeep();
+ }
+ }
+ scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25;
+ firstPost = null;
+ for (m = 0, len3 = posts.length; m < len3; m++) {
+ post = posts[m];
+ if (!QuoteThreading.insert(post)) {
+ firstPost || (firstPost = post.nodes.root);
+ $.add(ThreadUpdater.root, post.nodes.root);
+ }
+ }
+ $.event('PostsInserted', null, ThreadUpdater.root);
+ if (scroll) {
+ if (Conf['Bottom Scroll']) {
+ window.scrollTo(0, d.body.clientHeight);
+ } else {
+ if (firstPost) {
+ Header.scrollTo(firstPost);
+ }
+ }
+ }
+ }
+ if ((OP.unique_ips != null) && (ipCountEl = $.id('unique-ips'))) {
+ ipCountEl.textContent = OP.unique_ips;
+ ipCountEl.previousSibling.textContent = ipCountEl.previousSibling.textContent.replace(/\b(?:is|are)\b/, OP.unique_ips === 1 ? 'is' : 'are');
+ ipCountEl.nextSibling.textContent = ipCountEl.nextSibling.textContent.replace(/\bposters?\b/, OP.unique_ips === 1 ? 'poster' : 'posters');
+ }
+ return $.event('ThreadUpdate', {
+ 404: false,
+ threadID: thread.fullID,
+ newPosts: newPosts,
+ deletedPosts: deletedPosts,
+ deletedFiles: deletedFiles,
+ postCount: OP.replies + 1,
+ fileCount: OP.images + !!OP.fsize,
+ ipCount: OP.unique_ips
+ });
+ }
+ };
+
+ return ThreadUpdater;
+
+}).call(this);
+
+ThreadWatcher = (function() {
+ var ThreadWatcher,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
+ slice = [].slice;
+
+ ThreadWatcher = {
+ init: function() {
+ var ref, sc;
+ if (!(this.enabled = Conf['Thread Watcher'])) {
+ return;
+ }
+ this.shortcut = sc = $.el('a', {
+ id: 'watcher-link',
+ textContent: 'Watcher',
+ title: 'Thread Watcher',
+ href: 'javascript:;',
+ className: 'fa fa-eye'
+ });
+ this.db = new DataBoard('watchedThreads', this.refresh, true);
+ this.dbLM = new DataBoard('watcherLastModified', null, true);
+ this.dialog = UI.dialog('thread-watcher', {innerHTML: "<div class=\"move\">Thread Watcher <a class=\"refresh fa fa-refresh\" title=\"Check threads\" href=\"javascript:;\"></a><span id=\"watcher-status\"></span><a class=\"menu-button\" href=\"javascript:;\"><i class=\"fa fa-angle-down\"></i></a><a class=\"close\" href=\"javascript:;\">ร—</a></div><div id=\"watched-threads\"></div>"});
+ this.status = $('#watcher-status', this.dialog);
+ this.list = this.dialog.lastElementChild;
+ this.refreshButton = $('.refresh', this.dialog);
+ this.closeButton = $('.move > .close', this.dialog);
+ this.unreaddb = Unread.db || UnreadIndex.db || new DataBoard('lastReadPosts');
+ this.unreadEnabled = Conf['Remember Last Read Post'];
+ $.on(d, 'QRPostSuccessful', this.cb.post);
+ $.on(sc, 'click', this.toggleWatcher);
+ $.on(this.refreshButton, 'click', this.buttonFetchAll);
+ $.on(this.closeButton, 'click', this.toggleWatcher);
+ this.menu.addHeaderMenuEntry();
+ $.onExists(doc, 'body', this.addDialog);
+ switch (g.VIEW) {
+ case 'index':
+ $.on(d, 'IndexUpdate', this.cb.onIndexUpdate);
+ break;
+ case 'thread':
+ $.on(d, 'ThreadUpdate', this.cb.onThreadRefresh);
+ }
+ if (Conf['Fixed Thread Watcher']) {
+ $.addClass(doc, 'fixed-watcher');
+ }
+ if (!Conf['Persistent Thread Watcher']) {
+ $.addClass(ThreadWatcher.shortcut, 'disabled');
+ this.dialog.hidden = true;
+ }
+ Header.addShortcut('watcher', sc, 510);
+ ThreadWatcher.initLastModified();
+ ThreadWatcher.fetchAuto();
+ $.on(window, 'visibilitychange focus', function() {
+ return $.queueTask(ThreadWatcher.fetchAuto);
+ });
+ if (Conf['Menu'] && Index.enabled) {
+ Menu.menu.addEntry({
+ el: $.el('a', {
+ href: 'javascript:;',
+ className: 'has-shortcut-text'
+ }, {innerHTML: "<span></span><span class=\"shortcut-text\">Alt+click</span>"}),
+ order: 6,
+ open: function(arg) {
+ var thread;
+ thread = arg.thread;
+ if (Conf['Index Mode'] !== 'catalog') {
+ return false;
+ }
+ this.el.firstElementChild.textContent = ThreadWatcher.isWatched(thread) ? 'Unwatch' : 'Watch';
+ if (this.cb) {
+ $.off(this.el, 'click', this.cb);
+ }
+ this.cb = function() {
+ $.event('CloseMenu');
+ return ThreadWatcher.toggle(thread);
+ };
+ $.on(this.el, 'click', this.cb);
+ return true;
+ }
+ });
+ }
+ if ((ref = g.VIEW) !== 'index' && ref !== 'thread') {
+ return;
+ }
+ Callbacks.Post.push({
+ name: 'Thread Watcher',
+ cb: this.node
+ });
+ return Callbacks.CatalogThread.push({
+ name: 'Thread Watcher',
+ cb: this.catalogNode
+ });
+ },
+ isWatched: function(thread) {
+ var ref;
+ return !!((ref = ThreadWatcher.db) != null ? ref.get({
+ boardID: thread.board.ID,
+ threadID: thread.ID
+ }) : void 0);
+ },
+ isWatchedRaw: function(boardID, threadID) {
+ var ref;
+ return !!((ref = ThreadWatcher.db) != null ? ref.get({
+ boardID: boardID,
+ threadID: threadID
+ }) : void 0);
+ },
+ setToggler: function(toggler, isWatched) {
+ toggler.classList.toggle('watched', isWatched);
+ return toggler.title = (isWatched ? 'Unwatch' : 'Watch') + " Thread";
+ },
+ node: function() {
+ var boardID, data, siteID, threadID, toggler;
+ if (this.isReply) {
+ return;
+ }
+ if (this.isClone) {
+ toggler = $('.watch-thread-link', this.nodes.info);
+ } else {
+ toggler = $.el('a', {
+ href: 'javascript:;',
+ className: 'watch-thread-link'
+ });
+ $.before($('input', this.nodes.info), toggler);
+ }
+ siteID = g.SITE.ID;
+ boardID = this.board.ID;
+ threadID = this.thread.ID;
+ data = ThreadWatcher.db.get({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID
+ });
+ ThreadWatcher.setToggler(toggler, !!data);
+ $.on(toggler, 'click', ThreadWatcher.cb.toggle);
+ if (data && (data.excerpt == null)) {
+ return $.queueTask((function(_this) {
+ return function() {
+ return ThreadWatcher.update(siteID, boardID, threadID, {
+ excerpt: Get.threadExcerpt(_this.thread)
+ });
+ };
+ })(this));
+ }
+ },
+ catalogNode: function() {
+ if (ThreadWatcher.isWatched(this.thread)) {
+ $.addClass(this.nodes.root, 'watched');
+ }
+ return $.on(this.nodes.root, 'mousedown click', (function(_this) {
+ return function(e) {
+ if (!(e.button === 0 && e.altKey)) {
+ return;
+ }
+ if (e.type === 'click') {
+ ThreadWatcher.toggle(_this.thread);
+ }
+ return e.preventDefault();
+ };
+ })(this));
+ },
+ addDialog: function() {
+ if (!Main.isThisPageLegit()) {
+ return;
+ }
+ ThreadWatcher.build();
+ return $.prepend(d.body, ThreadWatcher.dialog);
+ },
+ toggleWatcher: function() {
+ $.toggleClass(ThreadWatcher.shortcut, 'disabled');
+ return ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden;
+ },
+ cb: {
+ openAll: function() {
+ var a, j, len1, ref;
+ if ($.hasClass(this, 'disabled')) {
+ return;
+ }
+ ref = $$('a.watcher-link', ThreadWatcher.list);
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ a = ref[j];
+ $.open(a.href);
+ }
+ return $.event('CloseMenu');
+ },
+ openUnread: function() {
+ var a, j, len1, ref;
+ if ($.hasClass(this, 'disabled')) {
+ return;
+ }
+ ref = $$('.replies-unread > a.watcher-link', ThreadWatcher.list);
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ a = ref[j];
+ $.open(a.href);
+ }
+ return $.event('CloseMenu');
+ },
+ openDeads: function() {
+ var a, j, len1, ref;
+ if ($.hasClass(this, 'disabled')) {
+ return;
+ }
+ ref = $$('.dead-thread > a.watcher-link', ThreadWatcher.list);
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ a = ref[j];
+ $.open(a.href);
+ }
+ return $.event('CloseMenu');
+ },
+ pruneDeads: function() {
+ var boardID, data, j, len1, ref, ref1, siteID, threadID;
+ if ($.hasClass(this, 'disabled')) {
+ return;
+ }
+ ref = ThreadWatcher.getAll();
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ ref1 = ref[j], siteID = ref1.siteID, boardID = ref1.boardID, threadID = ref1.threadID, data = ref1.data;
+ if (data.isDead) {
+ ThreadWatcher.db["delete"]({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID
+ });
+ }
+ }
+ ThreadWatcher.refresh();
+ return $.event('CloseMenu');
+ },
+ dismiss: function() {
+ var boardID, data, j, len1, ref, ref1, siteID, threadID;
+ ref = ThreadWatcher.getAll();
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ ref1 = ref[j], siteID = ref1.siteID, boardID = ref1.boardID, threadID = ref1.threadID, data = ref1.data;
+ if (data.quotingYou) {
+ ThreadWatcher.update(siteID, boardID, threadID, {
+ dismiss: data.quotingYou || 0
+ });
+ }
+ }
+ return $.event('CloseMenu');
+ },
+ toggle: function() {
+ var thread;
+ thread = Get.postFromNode(this).thread;
+ return ThreadWatcher.toggle(thread);
+ },
+ rm: function() {
+ var boardID, ref, siteID, threadID;
+ siteID = this.parentNode.dataset.siteID;
+ ref = this.parentNode.dataset.fullID.split('.'), boardID = ref[0], threadID = ref[1];
+ return ThreadWatcher.rm(siteID, boardID, +threadID);
+ },
+ post: function(e) {
+ var boardID, cb, postID, ref, threadID;
+ ref = e.detail, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID;
+ cb = PostRedirect.delay();
+ if (postID === threadID) {
+ if (Conf['Auto Watch']) {
+ return ThreadWatcher.addRaw(boardID, threadID, {}, cb);
+ }
+ } else if (Conf['Auto Watch Reply']) {
+ return ThreadWatcher.add(g.threads.get(boardID + '.' + threadID) || new Thread(threadID, g.boards[boardID] || new Board(boardID)), cb);
+ }
+ },
+ onIndexUpdate: function(e) {
+ var boardID, data, db, nKilled, ref, ref1, siteID, threadID;
+ db = ThreadWatcher.db;
+ siteID = g.SITE.ID;
+ boardID = g.BOARD.ID;
+ nKilled = 0;
+ ref = db.data[siteID].boards[boardID];
+ for (threadID in ref) {
+ data = ref[threadID];
+ if (!(!(data != null ? data.isDead : void 0) && (ref1 = boardID + "." + threadID, indexOf.call(e.detail.threads, ref1) < 0))) {
+ continue;
+ }
+ if (!e.detail.threads.some(function(fullID) {
+ return +fullID.split('.')[1] > threadID;
+ })) {
+ continue;
+ }
+ if (Conf['Auto Prune'] || !(data && typeof data === 'object')) {
+ db["delete"]({
+ boardID: boardID,
+ threadID: threadID
+ });
+ nKilled++;
+ } else {
+ ThreadWatcher.fetchStatus({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID,
+ data: data
+ });
+ }
+ }
+ if (nKilled) {
+ return ThreadWatcher.refresh();
+ }
+ },
+ onThreadRefresh: function(e) {
+ var thread;
+ thread = g.threads.get(e.detail.threadID);
+ if (!(e.detail[404] && ThreadWatcher.isWatched(thread))) {
+ return;
+ }
+ return ThreadWatcher.add(thread);
+ }
+ },
+ requests: [],
+ fetched: 0,
+ fetch: function(url, arg, args, cb) {
+ var ajax, force, onloadend, ref, req, siteID;
+ siteID = arg.siteID, force = arg.force;
+ if (ThreadWatcher.requests.length === 0) {
+ ThreadWatcher.status.textContent = '...';
+ $.addClass(ThreadWatcher.refreshButton, 'fa-spin');
+ }
+ onloadend = function() {
+ if (this.finished) {
+ return;
+ }
+ this.finished = true;
+ ThreadWatcher.fetched++;
+ if (ThreadWatcher.fetched === ThreadWatcher.requests.length) {
+ ThreadWatcher.clearRequests();
+ } else {
+ ThreadWatcher.status.textContent = (Math.round(ThreadWatcher.fetched / ThreadWatcher.requests.length * 100)) + "%";
+ }
+ return cb.apply(this, args);
+ };
+ ajax = siteID === g.SITE.ID ? $.ajax : CrossOrigin.ajax;
+ if (force) {
+ if ((ref = $.lastModified.ThreadWatcher) != null) {
+ delete ref[url];
+ }
+ }
+ req = $.whenModified(url, 'ThreadWatcher', onloadend, {
+ timeout: $.MINUTE,
+ ajax: ajax
+ });
+ return ThreadWatcher.requests.push(req);
+ },
+ clearRequests: function() {
+ ThreadWatcher.requests = [];
+ ThreadWatcher.fetched = 0;
+ ThreadWatcher.status.textContent = '';
+ return $.rmClass(ThreadWatcher.refreshButton, 'fa-spin');
+ },
+ abort: function() {
+ var j, len1, ref, req;
+ delete ThreadWatcher.syncing;
+ ref = ThreadWatcher.requests;
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ req = ref[j];
+ if (!(!req.finished)) {
+ continue;
+ }
+ req.finished = true;
+ req.abort();
+ }
+ return ThreadWatcher.clearRequests();
+ },
+ initLastModified: function() {
+ var base, boardID, boards, data, date, lm, ref, ref1, siteID, url;
+ lm = ((base = $.lastModified)['ThreadWatcher'] || (base['ThreadWatcher'] = $.dict()));
+ ref = ThreadWatcher.dbLM.data;
+ for (siteID in ref) {
+ boards = ref[siteID];
+ ref1 = boards.boards;
+ for (boardID in ref1) {
+ data = ref1[boardID];
+ if (ThreadWatcher.db.get({
+ siteID: siteID,
+ boardID: boardID
+ })) {
+ for (url in data) {
+ date = data[url];
+ lm[url] = date;
+ }
+ } else {
+ ThreadWatcher.dbLM["delete"]({
+ siteID: siteID,
+ boardID: boardID
+ });
+ }
+ }
+ }
+ },
+ fetchAuto: function() {
+ var db, interval, now, ref;
+ clearTimeout(ThreadWatcher.timeout);
+ if (!Conf['Auto Update Thread Watcher']) {
+ return;
+ }
+ db = ThreadWatcher.db;
+ interval = Conf['Show Page'] || (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) ? 5 * $.MINUTE : 2 * $.HOUR;
+ now = Date.now();
+ if (!((now - interval < (ref = db.data.lastChecked || 0) && ref <= now) || d.hidden || !d.hasFocus())) {
+ ThreadWatcher.fetchAllStatus(interval);
+ }
+ return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval);
+ },
+ buttonFetchAll: function() {
+ if (ThreadWatcher.syncing || ThreadWatcher.requests.length) {
+ return ThreadWatcher.abort();
+ } else {
+ return ThreadWatcher.fetchAllStatus();
+ }
+ },
+ fetchAllStatus: function(interval) {
+ var dbi, dbs, j, len1, n, results;
+ if (interval == null) {
+ interval = 0;
+ }
+ ThreadWatcher.status.textContent = '...';
+ $.addClass(ThreadWatcher.refreshButton, 'fa-spin');
+ ThreadWatcher.syncing = true;
+ dbs = [ThreadWatcher.db, ThreadWatcher.unreaddb, QuoteYou.db].filter(function(x) {
+ return x;
+ });
+ n = 0;
+ results = [];
+ for (j = 0, len1 = dbs.length; j < len1; j++) {
+ dbi = dbs[j];
+ results.push(dbi.forceSync(function() {
+ var board, boards, db, deep, k, len2, now, ref, ref1;
+ if ((++n) === dbs.length) {
+ if (!ThreadWatcher.syncing) {
+ return;
+ }
+ delete ThreadWatcher.syncing;
+ if (!((0 <= (ref = Date.now() - (ThreadWatcher.db.data.lastChecked || 0)) && ref < interval))) {
+ db = ThreadWatcher.db;
+ now = Date.now();
+ deep = !((now - 2 * $.HOUR < (ref1 = db.data.lastChecked2 || 0) && ref1 <= now));
+ boards = ThreadWatcher.getAll(true);
+ for (k = 0, len2 = boards.length; k < len2; k++) {
+ board = boards[k];
+ ThreadWatcher.fetchBoard(board, deep);
+ }
+ db.setLastChecked();
+ if (deep) {
+ db.setLastChecked('lastChecked2');
+ }
+ }
+ if (ThreadWatcher.fetched === ThreadWatcher.requests.length) {
+ return ThreadWatcher.clearRequests();
+ }
+ }
+ }));
+ }
+ return results;
+ },
+ fetchBoard: function(board, deep) {
+ var base, boardID, data, force, j, len1, ref, site, siteID, thread, url, urlF;
+ if (!board.some(function(thread) {
+ return !thread.data.isDead;
+ })) {
+ return;
+ }
+ force = false;
+ for (j = 0, len1 = board.length; j < len1; j++) {
+ thread = board[j];
+ data = thread.data;
+ if (!data.isDead && data.last !== -1) {
+ if (Conf['Show Page'] && (data.page == null)) {
+ force = true;
+ }
+ if (data.modified == null) {
+ force = thread.force = true;
+ }
+ }
+ }
+ ref = board[0], siteID = ref.siteID, boardID = ref.boardID;
+ site = g.sites[siteID];
+ if (!site) {
+ return;
+ }
+ urlF = deep && site.threadModTimeIgnoresSage ? 'catalogJSON' : 'threadsListJSON';
+ url = typeof (base = site.urls)[urlF] === "function" ? base[urlF]({
+ siteID: siteID,
+ boardID: boardID
+ }) : void 0;
+ if (!url) {
+ return;
+ }
+ return ThreadWatcher.fetch(url, {
+ siteID: siteID,
+ force: force
+ }, [board, url], ThreadWatcher.parseBoard);
+ },
+ parseBoard: function(board, url) {
+ var base, boardID, data, i, index, item, j, k, l, lastPage, len1, len2, len3, len4, lmDate, m, modified, nThreads, oldest, page, pageLength, ref, ref1, ref2, ref3, ref4, replies, siteID, thread, threadID, threads;
+ if (this.status !== 200) {
+ return;
+ }
+ ref = board[0], siteID = ref.siteID, boardID = ref.boardID;
+ lmDate = this.getResponseHeader('Last-Modified');
+ ThreadWatcher.dbLM.extend({
+ siteID: siteID,
+ boardID: boardID,
+ val: $.item(url, lmDate)
+ });
+ threads = $.dict();
+ pageLength = 0;
+ nThreads = 0;
+ oldest = null;
+ try {
+ pageLength = ((ref1 = this.response[0]) != null ? ref1.threads.length : void 0) || 0;
+ ref2 = this.response;
+ for (i = j = 0, len1 = ref2.length; j < len1; i = ++j) {
+ page = ref2[i];
+ ref3 = page.threads;
+ for (k = 0, len2 = ref3.length; k < len2; k++) {
+ item = ref3[k];
+ threads[item.no] = {
+ page: i + 1,
+ index: nThreads,
+ modified: item.last_modified,
+ replies: item.replies
+ };
+ nThreads++;
+ if ((oldest == null) || item.no < oldest) {
+ oldest = item.no;
+ }
+ }
+ }
+ } catch (error) {
+ for (l = 0, len3 = board.length; l < len3; l++) {
+ thread = board[l];
+ ThreadWatcher.fetchStatus(thread);
+ }
+ }
+ for (m = 0, len4 = board.length; m < len4; m++) {
+ thread = board[m];
+ threadID = thread.threadID, data = thread.data;
+ if (threads[threadID]) {
+ ref4 = threads[threadID], page = ref4.page, index = ref4.index, modified = ref4.modified, replies = ref4.replies;
+ if (Conf['Show Page']) {
+ lastPage = (typeof (base = g.sites[siteID]).isPrunedByAge === "function" ? base.isPrunedByAge({
+ siteID: siteID,
+ boardID: boardID
+ }) : void 0) ? threadID === oldest : index >= nThreads - pageLength;
+ ThreadWatcher.update(siteID, boardID, threadID, {
+ page: page,
+ lastPage: lastPage
+ });
+ }
+ if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) {
+ if (modified !== data.modified || ((replies != null) && replies !== data.replies)) {
+ (thread.newData || (thread.newData = {})).modified = modified;
+ ThreadWatcher.fetchStatus(thread);
+ }
+ }
+ } else {
+ ThreadWatcher.fetchStatus(thread);
+ }
+ }
+ },
+ fetchStatus: function(thread) {
+ var base, boardID, data, force, ref, siteID, threadID, url;
+ siteID = thread.siteID, boardID = thread.boardID, threadID = thread.threadID, data = thread.data, force = thread.force;
+ url = (ref = g.sites[siteID]) != null ? typeof (base = ref.urls).threadJSON === "function" ? base.threadJSON({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID
+ }) : void 0 : void 0;
+ if (!url) {
+ return;
+ }
+ if (data.isDead && !force) {
+ return;
+ }
+ if (data.last === -1) {
+ return;
+ }
+ return ThreadWatcher.fetch(url, {
+ siteID: siteID,
+ force: force
+ }, [thread], ThreadWatcher.parseStatus);
+ },
+ parseStatus: function(thread, isArchiveURL) {
+ var archiveURL, base, boardID, data, force, isArchived, isDead, j, last, lastReadPost, len1, match, newData, postObj, quotesYou, quotingYou, ref, ref1, ref2, ref3, regexp, replies, site, siteID, threadID, unread, youOP;
+ siteID = thread.siteID, boardID = thread.boardID, threadID = thread.threadID, data = thread.data, newData = thread.newData, force = thread.force;
+ site = g.sites[siteID];
+ if (this.status === 200 && this.response) {
+ last = this.response.posts[this.response.posts.length - 1].no;
+ replies = this.response.posts.length - 1;
+ isDead = isArchived = !!(this.response.posts[0].archived || isArchiveURL);
+ if (isDead && Conf['Auto Prune']) {
+ ThreadWatcher.rm(siteID, boardID, threadID);
+ return;
+ }
+ if (last === data.last && isDead === data.isDead && isArchived === data.isArchived) {
+ return;
+ }
+ lastReadPost = ThreadWatcher.unreaddb.get({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID,
+ defaultValue: 0
+ });
+ unread = data.unread || 0;
+ quotingYou = data.quotingYou || 0;
+ youOP = !!((ref = QuoteYou.db) != null ? ref.get({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID,
+ postID: threadID
+ }) : void 0);
+ ref1 = this.response.posts;
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ postObj = ref1[j];
+ if (!(postObj.no > (data.last || 0) && postObj.no > lastReadPost)) {
+ continue;
+ }
+ if ((ref2 = QuoteYou.db) != null ? ref2.get({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID,
+ postID: postObj.no
+ }) : void 0) {
+ continue;
+ }
+ quotesYou = false;
+ if (!Conf['Require OP Quote Link'] && youOP) {
+ quotesYou = true;
+ } else if (QuoteYou.db && postObj.com) {
+ regexp = site.regexp.quotelinkHTML;
+ regexp.lastIndex = 0;
+ while ((match = regexp.exec(postObj.com))) {
+ if (QuoteYou.db.get({
+ siteID: siteID,
+ boardID: match[1] ? encodeURIComponent(match[1]) : boardID,
+ threadID: match[2] || threadID,
+ postID: match[3] || match[2] || threadID
+ })) {
+ quotesYou = true;
+ break;
+ }
+ }
+ }
+ if (!unread || (!quotingYou && quotesYou)) {
+ if (Filter.isHidden(site.Build.parseJSON(postObj, {
+ siteID: siteID,
+ boardID: boardID
+ }))) {
+ continue;
+ }
+ }
+ unread++;
+ if (quotesYou) {
+ quotingYou = postObj.no;
+ }
+ }
+ newData || (newData = {});
+ $.extend(newData, {
+ last: last,
+ replies: replies,
+ isDead: isDead,
+ isArchived: isArchived,
+ unread: unread,
+ quotingYou: quotingYou
+ });
+ return ThreadWatcher.update(siteID, boardID, threadID, newData);
+ } else if (this.status === 404) {
+ archiveURL = (ref3 = g.sites[siteID]) != null ? typeof (base = ref3.urls).archivedThreadJSON === "function" ? base.archivedThreadJSON({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID
+ }) : void 0 : void 0;
+ if (!isArchiveURL && archiveURL) {
+ return ThreadWatcher.fetch(archiveURL, {
+ siteID: siteID,
+ force: force
+ }, [thread, true], ThreadWatcher.parseStatus);
+ } else if (site.mayLackJSON && (data.last == null)) {
+ return ThreadWatcher.update(siteID, boardID, threadID, {
+ last: -1
+ });
+ } else {
+ return ThreadWatcher.update(siteID, boardID, threadID, {
+ isDead: true
+ });
+ }
+ }
+ },
+ getAll: function(groupByBoard) {
+ var all, boardID, boards, cont, data, ref, ref1, siteID, threadID, threads;
+ all = [];
+ ref = ThreadWatcher.db.data;
+ for (siteID in ref) {
+ boards = ref[siteID];
+ ref1 = boards.boards;
+ for (boardID in ref1) {
+ threads = ref1[boardID];
+ if (Conf['Current Board'] && (siteID !== g.SITE.ID || boardID !== g.BOARD.ID)) {
+ continue;
+ }
+ if (groupByBoard) {
+ all.push((cont = []));
+ }
+ for (threadID in threads) {
+ data = threads[threadID];
+ if (data && typeof data === 'object') {
+ (groupByBoard ? cont : all).push({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID,
+ data: data
+ });
+ }
+ }
+ }
+ }
+ return all;
+ },
+ makeLine: function(siteID, boardID, threadID, data) {
+ var count, div, excerpt, fullID, isArchived, link, page, ref, title, x;
+ x = $.el('a', {
+ className: 'fa fa-times',
+ href: 'javascript:;'
+ });
+ $.on(x, 'click', ThreadWatcher.cb.rm);
+ excerpt = data.excerpt, isArchived = data.isArchived;
+ excerpt || (excerpt = "/" + boardID + "/ - No." + threadID);
+ if (Conf['Show Site Prefix']) {
+ excerpt = ThreadWatcher.prefixes[siteID] + excerpt;
+ }
+ link = $.el('a', {
+ href: ((ref = g.sites[siteID]) != null ? ref.urls.thread({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID
+ }, isArchived) : void 0) || '',
+ title: excerpt,
+ className: 'watcher-link'
+ });
+ if (Conf['Show Page'] && (data.page != null)) {
+ page = $.el('span', {
+ textContent: "[" + data.page + "]",
+ className: 'watcher-page'
+ });
+ $.add(link, page);
+ }
+ if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count'] && (data.unread != null)) {
+ count = $.el('span', {
+ textContent: "(" + data.unread + ")",
+ className: 'watcher-unread'
+ });
+ $.add(link, count);
+ }
+ title = $.el('span', {
+ textContent: excerpt,
+ className: 'watcher-title'
+ });
+ $.add(link, title);
+ div = $.el('div');
+ fullID = boardID + "." + threadID;
+ div.dataset.fullID = fullID;
+ div.dataset.siteID = siteID;
+ if (g.VIEW === 'thread' && fullID === (g.BOARD + "." + g.THREADID)) {
+ $.addClass(div, 'current');
+ }
+ if (data.isDead) {
+ $.addClass(div, 'dead-thread');
+ }
+ if (Conf['Show Page']) {
+ if (data.lastPage) {
+ $.addClass(div, 'last-page');
+ }
+ if (data.page != null) {
+ div.dataset.page = data.page;
+ }
+ }
+ if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) {
+ if (data.unread === 0) {
+ $.addClass(div, 'replies-read');
+ }
+ if (data.unread) {
+ $.addClass(div, 'replies-unread');
+ }
+ if ((data.quotingYou || 0) > (data.dismiss || 0)) {
+ $.addClass(div, 'replies-quoting-you');
+ }
+ }
+ $.add(div, [x, $.tn(' '), link]);
+ return div;
+ },
+ setPrefixes: function(threads) {
+ var conflicts, conflicts2, j, k, len, len1, len2, prefix, prefixes, siteID, siteID2;
+ prefixes = $.dict();
+ for (j = 0, len1 = threads.length; j < len1; j++) {
+ siteID = threads[j].siteID;
+ if (siteID in prefixes) {
+ continue;
+ }
+ len = 0;
+ prefix = '';
+ conflicts = Object.keys(prefixes);
+ while (conflicts.length > 0) {
+ len++;
+ prefix = siteID.slice(0, len);
+ conflicts2 = [];
+ for (k = 0, len2 = conflicts.length; k < len2; k++) {
+ siteID2 = conflicts[k];
+ if (siteID2.slice(0, len) === prefix) {
+ conflicts2.push(siteID2);
+ } else if (prefixes[siteID2].length < len) {
+ prefixes[siteID2] = siteID2.slice(0, len);
+ }
+ }
+ conflicts = conflicts2;
+ }
+ prefixes[siteID] = prefix;
+ }
+ return ThreadWatcher.prefixes = prefixes;
+ },
+ build: function() {
+ var boardID, data, j, len1, list, nodes, ref, siteID, thread, threadID, threads;
+ nodes = [];
+ threads = ThreadWatcher.getAll();
+ ThreadWatcher.setPrefixes(threads);
+ for (j = 0, len1 = threads.length; j < len1; j++) {
+ ref = threads[j], siteID = ref.siteID, boardID = ref.boardID, threadID = ref.threadID, data = ref.data;
+ if ((data.excerpt == null) && siteID === g.SITE.ID && (thread = g.threads.get(boardID + "." + threadID)) && thread.OP) {
+ ThreadWatcher.db.extend({
+ boardID: boardID,
+ threadID: threadID,
+ val: {
+ excerpt: Get.threadExcerpt(thread)
+ }
+ });
+ }
+ nodes.push(ThreadWatcher.makeLine(siteID, boardID, threadID, data));
+ }
+ list = ThreadWatcher.list;
+ $.rmAll(list);
+ $.add(list, nodes);
+ return ThreadWatcher.refreshIcon();
+ },
+ refresh: function() {
+ ThreadWatcher.build();
+ g.threads.forEach(function(thread) {
+ var isWatched, j, len1, post, ref, toggler;
+ isWatched = ThreadWatcher.isWatched(thread);
+ if (thread.OP) {
+ ref = [thread.OP].concat(slice.call(thread.OP.clones));
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ post = ref[j];
+ if ((toggler = $('.watch-thread-link', post.nodes.info))) {
+ ThreadWatcher.setToggler(toggler, isWatched);
+ }
+ }
+ }
+ if (thread.catalogView) {
+ return thread.catalogView.nodes.root.classList.toggle('watched', isWatched);
+ }
+ });
+ if (Conf['Pin Watched Threads']) {
+ return $.event('SortIndex', {
+ deferred: Conf['Index Mode'] !== 'catalog'
+ });
+ }
+ },
+ refreshIcon: function() {
+ var className, j, len1, ref;
+ ref = ['replies-unread', 'replies-quoting-you'];
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ className = ref[j];
+ ThreadWatcher.shortcut.classList.toggle(className, !!$("." + className, ThreadWatcher.dialog));
+ }
+ },
+ update: function(siteID, boardID, threadID, newData) {
+ var data, j, key, len1, line, n, newLine, ref, ref1, val;
+ if (!(data = (ref = ThreadWatcher.db) != null ? ref.get({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID
+ }) : void 0)) {
+ return;
+ }
+ if (newData.isDead && Conf['Auto Prune']) {
+ ThreadWatcher.rm(siteID, boardID, threadID);
+ return;
+ }
+ if (newData.isDead || newData.last === -1) {
+ ref1 = ['isArchived', 'page', 'lastPage', 'unread', 'quotingyou'];
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ key = ref1[j];
+ if (!(key in newData)) {
+ newData[key] = void 0;
+ }
+ }
+ }
+ if ((newData.last != null) && newData.last < data.last) {
+ newData.modified = void 0;
+ }
+ n = 0;
+ for (key in newData) {
+ val = newData[key];
+ if (data[key] !== val) {
+ n++;
+ }
+ }
+ if (!n) {
+ return;
+ }
+ ThreadWatcher.db.extend({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID,
+ val: newData
+ });
+ if ((line = $("#watched-threads > [data-site-i-d='" + siteID + "'][data-full-i-d='" + boardID + "." + threadID + "']", ThreadWatcher.dialog))) {
+ newLine = ThreadWatcher.makeLine(siteID, boardID, threadID, data);
+ $.replace(line, newLine);
+ return ThreadWatcher.refreshIcon();
+ } else {
+ return ThreadWatcher.refresh();
+ }
+ },
+ set404: function(boardID, threadID, cb) {
+ var data, ref;
+ if (!(data = (ref = ThreadWatcher.db) != null ? ref.get({
+ boardID: boardID,
+ threadID: threadID
+ }) : void 0)) {
+ return cb();
+ }
+ if (Conf['Auto Prune']) {
+ ThreadWatcher.db["delete"]({
+ boardID: boardID,
+ threadID: threadID
+ });
+ return cb();
+ }
+ if (data.isDead && !((data.isArchived != null) || (data.page != null) || (data.lastPage != null) || (data.unread != null) || (data.quotingYou != null))) {
+ return cb();
+ }
+ return ThreadWatcher.db.extend({
+ boardID: boardID,
+ threadID: threadID,
+ val: {
+ isDead: true,
+ isArchived: void 0,
+ page: void 0,
+ lastPage: void 0,
+ unread: void 0,
+ quotingYou: void 0
+ }
+ }, cb);
+ },
+ toggle: function(thread) {
+ var boardID, siteID, threadID;
+ siteID = g.SITE.ID;
+ boardID = thread.board.ID;
+ threadID = thread.ID;
+ if (ThreadWatcher.db.get({
+ boardID: boardID,
+ threadID: threadID
+ })) {
+ return ThreadWatcher.rm(siteID, boardID, threadID);
+ } else {
+ return ThreadWatcher.add(thread);
+ }
+ },
+ add: function(thread, cb) {
+ var boardID, data, siteID, threadID;
+ data = {};
+ siteID = g.SITE.ID;
+ boardID = thread.board.ID;
+ threadID = thread.ID;
+ if (thread.isDead) {
+ if (Conf['Auto Prune'] && ThreadWatcher.db.get({
+ boardID: boardID,
+ threadID: threadID
+ })) {
+ ThreadWatcher.rm(siteID, boardID, threadID, cb);
+ return;
+ }
+ data.isDead = true;
+ }
+ if (thread.OP) {
+ data.excerpt = Get.threadExcerpt(thread);
+ }
+ return ThreadWatcher.addRaw(boardID, threadID, data, cb);
+ },
+ addRaw: function(boardID, threadID, data, cb) {
+ var oldData, thread;
+ oldData = ThreadWatcher.db.get({
+ boardID: boardID,
+ threadID: threadID,
+ defaultValue: $.dict()
+ });
+ delete oldData.last;
+ delete oldData.modified;
+ $.extend(oldData, data);
+ ThreadWatcher.db.set({
+ boardID: boardID,
+ threadID: threadID,
+ val: oldData
+ }, cb);
+ ThreadWatcher.refresh();
+ thread = {
+ siteID: g.SITE.ID,
+ boardID: boardID,
+ threadID: threadID,
+ data: data,
+ force: true
+ };
+ if (Conf['Show Page'] && !data.isDead) {
+ return ThreadWatcher.fetchBoard([thread]);
+ } else if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) {
+ return ThreadWatcher.fetchStatus(thread);
+ }
+ },
+ rm: function(siteID, boardID, threadID, cb) {
+ ThreadWatcher.db["delete"]({
+ siteID: siteID,
+ boardID: boardID,
+ threadID: threadID
+ }, cb);
+ return ThreadWatcher.refresh();
+ },
+ menu: {
+ init: function() {
+ var menu;
+ if (!Conf['Thread Watcher']) {
+ return;
+ }
+ menu = this.menu = new UI.Menu('thread watcher');
+ $.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) {
+ return menu.toggle(e, this, ThreadWatcher);
+ });
+ return this.addMenuEntries();
+ },
+ addHeaderMenuEntry: function() {
+ var entryEl;
+ if (g.VIEW !== 'thread') {
+ return;
+ }
+ entryEl = $.el('a', {
+ href: 'javascript:;'
+ });
+ Header.menu.addEntry({
+ el: entryEl,
+ order: 60,
+ open: function() {
+ var addClass, ref, rmClass, text;
+ ref = !!ThreadWatcher.db.get({
+ boardID: g.BOARD.ID,
+ threadID: g.THREADID
+ }) ? ['unwatch-thread', 'watch-thread', 'Unwatch thread'] : ['watch-thread', 'unwatch-thread', 'Watch thread'], addClass = ref[0], rmClass = ref[1], text = ref[2];
+ $.addClass(entryEl, addClass);
+ $.rmClass(entryEl, rmClass);
+ entryEl.textContent = text;
+ return true;
+ }
+ });
+ return $.on(entryEl, 'click', function() {
+ return ThreadWatcher.toggle(g.threads.get(g.BOARD + "." + g.THREADID));
+ });
+ },
+ addMenuEntries: function() {
+ var cb, conf, entries, entry, j, len1, name, open, ref, ref1, text, title;
+ entries = [];
+ entries.push({
+ text: 'Open all threads',
+ cb: ThreadWatcher.cb.openAll,
+ open: function() {
+ this.el.classList.toggle('disabled', !ThreadWatcher.list.firstElementChild);
+ return true;
+ }
+ });
+ entries.push({
+ text: 'Open unread threads',
+ cb: ThreadWatcher.cb.openUnread,
+ open: function() {
+ this.el.classList.toggle('disabled', !$('.replies-unread', ThreadWatcher.list));
+ return true;
+ }
+ });
+ entries.push({
+ text: 'Open dead threads',
+ cb: ThreadWatcher.cb.openDeads,
+ open: function() {
+ this.el.classList.toggle('disabled', !$('.dead-thread', ThreadWatcher.list));
+ return true;
+ }
+ });
+ entries.push({
+ text: 'Prune dead threads',
+ cb: ThreadWatcher.cb.pruneDeads,
+ open: function() {
+ this.el.classList.toggle('disabled', !$('.dead-thread', ThreadWatcher.list));
+ return true;
+ }
+ });
+ entries.push({
+ text: 'Dismiss posts quoting you',
+ title: 'Unhighlight the thread watcher icon and threads until there are new replies quoting you.',
+ cb: ThreadWatcher.cb.dismiss,
+ open: function() {
+ this.el.classList.toggle('disabled', !$.hasClass(ThreadWatcher.shortcut, 'replies-quoting-you'));
+ return true;
+ }
+ });
+ for (j = 0, len1 = entries.length; j < len1; j++) {
+ ref = entries[j], text = ref.text, title = ref.title, cb = ref.cb, open = ref.open;
+ entry = {
+ el: $.el('a', {
+ textContent: text,
+ href: 'javascript:;'
+ })
+ };
+ if (title) {
+ entry.el.title = title;
+ }
+ $.on(entry.el, 'click', cb);
+ entry.open = open.bind(entry);
+ this.menu.addEntry(entry);
+ }
+ ref1 = Config.threadWatcher;
+ for (name in ref1) {
+ conf = ref1[name];
+ this.addCheckbox(name, conf[1]);
+ }
+ },
+ addCheckbox: function(name, desc) {
+ var entry, input;
+ entry = {
+ type: 'thread watcher',
+ el: UI.checkbox(name, name.replace(' Thread Watcher', ''))
+ };
+ entry.el.title = desc;
+ input = entry.el.firstElementChild;
+ if (name === 'Show Unread Count' && !ThreadWatcher.unreadEnabled) {
+ input.disabled = true;
+ $.addClass(entry.el, 'disabled');
+ entry.el.title += '\n[Remember Last Read Post is disabled.]';
+ }
+ $.on(input, 'change', $.cb.checked);
+ if (name === 'Current Board' || name === 'Show Page' || name === 'Show Unread Count' || name === 'Show Site Prefix') {
+ $.on(input, 'change', ThreadWatcher.refresh);
+ }
+ if (name === 'Show Page' || name === 'Show Unread Count' || name === 'Auto Update Thread Watcher') {
+ $.on(input, 'change', ThreadWatcher.fetchAuto);
+ }
+ return this.menu.addEntry(entry);
+ }
+ }
+ };
+
+ return ThreadWatcher;
+
+}).call(this);
+
+Unread = (function() {
+ var Unread;
+
+ Unread = {
+ init: function() {
+ if (!(g.VIEW === 'thread' && (Conf['Unread Count'] || Conf['Unread Favicon'] || Conf['Unread Line'] || Conf['Remember Last Read Post'] || Conf['Desktop Notifications'] || Conf['Quote Threading']))) {
+ return;
+ }
+ if (Conf['Remember Last Read Post']) {
+ $.sync('Remember Last Read Post', function(enabled) {
+ return Conf['Remember Last Read Post'] = enabled;
+ });
+ this.db = new DataBoard('lastReadPosts', this.sync);
+ }
+ this.hr = $.el('hr', {
+ id: 'unread-line',
+ className: 'unread-line'
+ });
+ this.posts = new Set();
+ this.postsQuotingYou = new Set();
+ this.order = new RandomAccessList();
+ this.position = null;
+ Callbacks.Thread.push({
+ name: 'Unread',
+ cb: this.node
+ });
+ return Callbacks.Post.push({
+ name: 'Unread',
+ cb: this.addPost
+ });
+ },
+ node: function() {
+ var ID, j, len, ref, ref1, resetLink;
+ Unread.thread = this;
+ Unread.title = d.title;
+ Unread.lastReadPost = ((ref = Unread.db) != null ? ref.get({
+ boardID: this.board.ID,
+ threadID: this.ID
+ }) : void 0) || 0;
+ Unread.readCount = 0;
+ ref1 = this.posts.keys;
+ for (j = 0, len = ref1.length; j < len; j++) {
+ ID = ref1[j];
+ if (+ID <= Unread.lastReadPost) {
+ Unread.readCount++;
+ }
+ }
+ $.one(d, '4chanXInitFinished', Unread.ready);
+ $.on(d, 'PostsInserted', Unread.onUpdate);
+ $.on(d, 'ThreadUpdate', function(e) {
+ if (e.detail[404]) {
+ return Unread.update();
+ }
+ });
+ resetLink = $.el('a', {
+ href: 'javascript:;',
+ className: 'unread-reset',
+ textContent: 'Mark all unread'
+ });
+ $.on(resetLink, 'click', Unread.reset);
+ return Header.menu.addEntry({
+ el: resetLink,
+ order: 70
+ });
+ },
+ ready: function() {
+ if (Conf['Remember Last Read Post'] && Conf['Scroll to Last Read Post']) {
+ Unread.scroll();
+ }
+ Unread.setLine(true);
+ Unread.read();
+ Unread.update();
+ $.on(d, 'scroll visibilitychange', Unread.read);
+ if (Conf['Unread Line']) {
+ return $.on(d, 'visibilitychange', Unread.setLine);
+ }
+ },
+ positionPrev: function() {
+ if (Unread.position) {
+ return Unread.position.prev;
+ } else {
+ return Unread.order.last;
+ }
+ },
+ scroll: function() {
+ var bottom, hash, position;
+ if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) {
+ return;
+ }
+ position = Unread.positionPrev();
+ while (position) {
+ bottom = position.data.nodes.bottom;
+ if (!bottom.getBoundingClientRect().height) {
+ position = position.prev;
+ } else {
+ Header.scrollToIfNeeded(bottom, true);
+ break;
+ }
+ }
+ },
+ reset: function() {
+ if (Unread.lastReadPost == null) {
+ return;
+ }
+ Unread.posts = new Set();
+ Unread.postsQuotingYou = new Set();
+ Unread.order = new RandomAccessList();
+ Unread.position = null;
+ Unread.lastReadPost = 0;
+ Unread.readCount = 0;
+ Unread.thread.posts.forEach(function(post) {
+ return Unread.addPost.call(post);
+ });
+ $.forceSync('Remember Last Read Post');
+ if (Conf['Remember Last Read Post'] && (!Unread.thread.isDead || Unread.thread.isArchived)) {
+ Unread.db.set({
+ boardID: Unread.thread.board.ID,
+ threadID: Unread.thread.ID,
+ val: 0
+ });
+ }
+ Unread.updatePosition();
+ Unread.setLine();
+ return Unread.update();
+ },
+ sync: function() {
+ var ID, i, j, lastReadPost, postIDs, ref, ref1;
+ if (Unread.lastReadPost == null) {
+ return;
+ }
+ lastReadPost = Unread.db.get({
+ boardID: Unread.thread.board.ID,
+ threadID: Unread.thread.ID,
+ defaultValue: 0
+ });
+ if (!(Unread.lastReadPost < lastReadPost)) {
+ return;
+ }
+ Unread.lastReadPost = lastReadPost;
+ postIDs = Unread.thread.posts.keys;
+ for (i = j = ref = Unread.readCount, ref1 = postIDs.length; j < ref1; i = j += 1) {
+ ID = +postIDs[i];
+ if (!Unread.thread.posts.get(ID).isFetchedQuote) {
+ if (ID > Unread.lastReadPost) {
+ break;
+ }
+ Unread.posts["delete"](ID);
+ Unread.postsQuotingYou["delete"](ID);
+ }
+ Unread.readCount++;
+ }
+ Unread.updatePosition();
+ Unread.setLine();
+ return Unread.update();
+ },
+ addPost: function() {
+ if (this.isFetchedQuote || this.isClone) {
+ return;
+ }
+ Unread.order.push(this);
+ if (this.ID <= Unread.lastReadPost || this.isHidden || QuoteYou.isYou(this)) {
+ return;
+ }
+ Unread.posts.add((Unread.posts.last = this.ID));
+ Unread.addPostQuotingYou(this);
+ return Unread.position != null ? Unread.position : Unread.position = Unread.order[this.ID];
+ },
+ addPostQuotingYou: function(post) {
+ var j, len, quotelink, ref, ref1;
+ ref = post.nodes.quotelinks;
+ for (j = 0, len = ref.length; j < len; j++) {
+ quotelink = ref[j];
+ if (!((ref1 = QuoteYou.db) != null ? ref1.get(Get.postDataFromLink(quotelink)) : void 0)) {
+ continue;
+ }
+ Unread.postsQuotingYou.add((Unread.postsQuotingYou.last = post.ID));
+ Unread.openNotification(post);
+ return;
+ }
+ },
+ openNotification: function(post, predicate) {
+ var notif;
+ if (predicate == null) {
+ predicate = ' replied to you';
+ }
+ if (!Header.areNotificationsEnabled) {
+ return;
+ }
+ notif = new Notification("" + post.info.nameBlock + predicate, {
+ body: post.commentDisplay(),
+ icon: Favicon.logo
+ });
+ notif.onclick = function() {
+ Header.scrollToIfNeeded(post.nodes.bottom, true);
+ return window.focus();
+ };
+ return notif.onshow = function() {
+ return setTimeout(function() {
+ return notif.close();
+ }, 7 * $.SECOND);
+ };
+ },
+ onUpdate: function() {
+ return $.queueTask(function() {
+ Unread.setLine();
+ Unread.read();
+ return Unread.update();
+ });
+ },
+ readSinglePost: function(post) {
+ var ID;
+ ID = post.ID;
+ if (!Unread.posts.has(ID)) {
+ return;
+ }
+ Unread.posts["delete"](ID);
+ Unread.postsQuotingYou["delete"](ID);
+ Unread.updatePosition();
+ Unread.saveLastReadPost();
+ return Unread.update();
+ },
+ read: $.debounce(100, function(e) {
+ var ID, bottom, count, data, ref;
+ if (!Unread.posts.size && Unread.readCount !== Unread.thread.posts.keys.length) {
+ Unread.saveLastReadPost();
+ }
+ if (d.hidden || !Unread.posts.size) {
+ return;
+ }
+ count = 0;
+ while (Unread.position) {
+ ref = Unread.position, ID = ref.ID, data = ref.data;
+ bottom = data.nodes.bottom;
+ if (!(!bottom.getBoundingClientRect().height || Header.getBottomOf(bottom) > -1)) {
+ break;
+ }
+ count++;
+ Unread.posts["delete"](ID);
+ Unread.postsQuotingYou["delete"](ID);
+ Unread.position = Unread.position.next;
+ }
+ if (!count) {
+ return;
+ }
+ Unread.updatePosition();
+ Unread.saveLastReadPost();
+ if (e) {
+ return Unread.update();
+ }
+ }),
+ updatePosition: function() {
+ while (Unread.position && !Unread.posts.has(Unread.position.ID)) {
+ Unread.position = Unread.position.next;
+ }
+ },
+ saveLastReadPost: $.debounce(2 * $.SECOND, function() {
+ var ID, i, j, postIDs, ref, ref1;
+ $.forceSync('Remember Last Read Post');
+ if (!(Conf['Remember Last Read Post'] && Unread.db)) {
+ return;
+ }
+ postIDs = Unread.thread.posts.keys;
+ for (i = j = ref = Unread.readCount, ref1 = postIDs.length; j < ref1; i = j += 1) {
+ ID = +postIDs[i];
+ if (!Unread.thread.posts.get(ID).isFetchedQuote) {
+ if (Unread.posts.has(ID)) {
+ break;
+ }
+ Unread.lastReadPost = ID;
+ }
+ Unread.readCount++;
+ }
+ if (Unread.thread.isDead && !Unread.thread.isArchived) {
+ return;
+ }
+ return Unread.db.set({
+ boardID: Unread.thread.board.ID,
+ threadID: Unread.thread.ID,
+ val: Unread.lastReadPost
+ });
+ }),
+ setLine: function(force) {
+ var node, oldPosition, ref;
+ if (!Conf['Unread Line']) {
+ return;
+ }
+ if (Unread.hr.hidden || d.hidden || (force === true)) {
+ oldPosition = Unread.linePosition;
+ if ((Unread.linePosition = Unread.positionPrev())) {
+ if (Unread.linePosition !== oldPosition) {
+ node = Unread.linePosition.data.nodes.bottom;
+ if (((ref = node.nextSibling) != null ? ref.tagName : void 0) === 'BR') {
+ node = node.nextSibling;
+ }
+ $.after(node, Unread.hr);
+ }
+ } else {
+ $.rm(Unread.hr);
+ }
+ }
+ return Unread.hr.hidden = Unread.linePosition === Unread.order.last;
+ },
+ update: function() {
+ var count, countQuotingYou, isDead, titleCount, titleDead, titleQuotingYou;
+ count = Unread.posts.size;
+ countQuotingYou = Unread.postsQuotingYou.size;
+ if (Conf['Unread Count']) {
+ titleQuotingYou = Conf['Quoted Title'] && countQuotingYou ? '(!) ' : '';
+ titleCount = count || !Conf['Hide Unread Count at (0)'] ? "(" + count + ") " : '';
+ titleDead = Unread.thread.isDead ? Unread.title.replace('-', (Unread.thread.isArchived ? '- Archived -' : '- 404 -')) : Unread.title;
+ d.title = "" + titleQuotingYou + titleCount + titleDead;
+ }
+ Unread.saveThreadWatcherCount();
+ if (Conf['Unread Favicon'] && g.SITE.software === 'yotsuba') {
+ isDead = Unread.thread.isDead;
+ return Favicon.set((countQuotingYou ? (isDead ? 'unreadDeadY' : 'unreadY') : count ? (isDead ? 'unreadDead' : 'unread') : (isDead ? 'dead' : 'default')));
+ }
+ },
+ saveThreadWatcherCount: $.debounce(2 * $.SECOND, function() {
+ var i, j, posts, quotingYou, ref;
+ $.forceSync('Remember Last Read Post');
+ if (Conf['Remember Last Read Post'] && (!Unread.thread.isDead || Unread.thread.isArchived)) {
+ quotingYou = !Conf['Require OP Quote Link'] && QuoteYou.isYou(Unread.thread.OP) ? Unread.posts : Unread.postsQuotingYou;
+ if (!quotingYou.size) {
+ quotingYou.last = 0;
+ } else if (!quotingYou.has(quotingYou.last)) {
+ quotingYou.last = 0;
+ posts = Unread.thread.posts.keys;
+ for (i = j = ref = posts.length - 1; j >= 0; i = j += -1) {
+ if (quotingYou.has(+posts[i])) {
+ quotingYou.last = posts[i];
+ break;
+ }
+ }
+ }
+ return ThreadWatcher.update(g.SITE.ID, Unread.thread.board.ID, Unread.thread.ID, {
+ last: Unread.thread.lastPost,
+ isDead: Unread.thread.isDead,
+ isArchived: Unread.thread.isArchived,
+ unread: Unread.posts.size,
+ quotingYou: quotingYou.last || 0
+ });
+ }
+ })
+ };
+
+ return Unread;
+
+}).call(this);
+
+UnreadIndex = (function() {
+ var UnreadIndex;
+
+ UnreadIndex = {
+ lastReadPost: $.dict(),
+ hr: $.dict(),
+ markReadLink: $.dict(),
+ init: function() {
+ if (!(g.VIEW === 'index' && Conf['Remember Last Read Post'] && Conf['Unread Line in Index'])) {
+ return;
+ }
+ this.enabled = true;
+ this.db = new DataBoard('lastReadPosts', this.sync);
+ Callbacks.Thread.push({
+ name: 'Unread Line in Index',
+ cb: this.node
+ });
+ $.on(d, 'IndexRefreshInternal', this.onIndexRefresh);
+ return $.on(d, 'PostsInserted PostsRemoved', this.onPostsInserted);
+ },
+ node: function() {
+ UnreadIndex.lastReadPost[this.fullID] = UnreadIndex.db.get({
+ boardID: this.board.ID,
+ threadID: this.ID
+ }) || 0;
+ if (!Index.enabled) {
+ return UnreadIndex.update(this);
+ }
+ },
+ onIndexRefresh: function(e) {
+ var i, len, ref, results, thread, threadID;
+ if (e.detail.isCatalog) {
+ return;
+ }
+ ref = e.detail.threadIDs;
+ results = [];
+ for (i = 0, len = ref.length; i < len; i++) {
+ threadID = ref[i];
+ thread = g.threads.get(threadID);
+ results.push(UnreadIndex.update(thread));
+ }
+ return results;
+ },
+ onPostsInserted: function(e) {
+ var ref, ref1, thread, wasVisible;
+ if (e.target === Index.root) {
+ return;
+ }
+ thread = Get.threadFromNode(e.target);
+ if (!thread || thread.nodes.root !== e.target) {
+ return;
+ }
+ wasVisible = !!((ref = UnreadIndex.hr[thread.fullID]) != null ? ref.parentNode : void 0);
+ UnreadIndex.update(thread);
+ if (Conf['Scroll to Last Read Post'] && e.type === 'PostsInserted' && !wasVisible && !!((ref1 = UnreadIndex.hr[thread.fullID]) != null ? ref1.parentNode : void 0)) {
+ return Header.scrollToIfNeeded(UnreadIndex.hr[thread.fullID], true);
+ }
+ },
+ sync: function() {
+ return g.threads.forEach(function(thread) {
+ var lastReadPost, ref;
+ lastReadPost = UnreadIndex.db.get({
+ boardID: thread.board.ID,
+ threadID: thread.ID
+ }) || 0;
+ if (lastReadPost !== UnreadIndex.lastReadPost[thread.fullID]) {
+ UnreadIndex.lastReadPost[thread.fullID] = lastReadPost;
+ if ((ref = thread.nodes.root) != null ? ref.parentNode : void 0) {
+ return UnreadIndex.update(thread);
+ }
+ }
+ });
+ },
+ update: function(thread) {
+ var divider, firstUnread, hasUnread, hr, lastReadPost, link, repliesRead, repliesShown;
+ lastReadPost = UnreadIndex.lastReadPost[thread.fullID];
+ repliesShown = 0;
+ repliesRead = 0;
+ firstUnread = null;
+ thread.posts.forEach(function(post) {
+ if (post.isReply && thread.nodes.root.contains(post.nodes.root)) {
+ repliesShown++;
+ if (post.ID <= lastReadPost) {
+ return repliesRead++;
+ } else if ((!firstUnread || post.ID < firstUnread.ID) && !post.isHidden && !QuoteYou.isYou(post)) {
+ return firstUnread = post;
+ }
+ }
+ });
+ hr = UnreadIndex.hr[thread.fullID];
+ if (firstUnread && (repliesRead || (lastReadPost === thread.OP.ID && (!$(g.SITE.selectors.summary, thread.nodes.root) || thread.ID in ExpandThread.statuses)))) {
+ if (!hr) {
+ hr = UnreadIndex.hr[thread.fullID] = $.el('hr', {
+ className: 'unread-line'
+ });
+ }
+ $.before(firstUnread.nodes.root, hr);
+ } else {
+ $.rm(hr);
+ }
+ hasUnread = repliesShown ? firstUnread || !repliesRead : Index.enabled ? thread.lastPost > lastReadPost : thread.OP.ID > lastReadPost;
+ thread.nodes.root.classList.toggle('unread-thread', hasUnread);
+ link = UnreadIndex.markReadLink[thread.fullID];
+ if (!link) {
+ link = UnreadIndex.markReadLink[thread.fullID] = $.el('a', {
+ className: 'unread-mark-read brackets-wrap',
+ href: 'javascript:;',
+ textContent: 'Mark Read'
+ });
+ $.on(link, 'click', UnreadIndex.markRead);
+ }
+ if ((divider = $(g.SITE.selectors.threadDivider, thread.nodes.root))) {
+ return $.before(divider, link);
+ } else {
+ return $.add(thread.nodes.root, link);
+ }
+ },
+ markRead: function() {
+ var thread;
+ thread = Get.threadFromNode(this);
+ UnreadIndex.lastReadPost[thread.fullID] = thread.lastPost;
+ UnreadIndex.db.set({
+ boardID: thread.board.ID,
+ threadID: thread.ID,
+ val: thread.lastPost
+ });
+ $.rm(UnreadIndex.hr[thread.fullID]);
+ thread.nodes.root.classList.remove('unread-thread');
+ return ThreadWatcher.update(g.SITE.ID, thread.board.ID, thread.ID, {
+ last: thread.lastPost,
+ unread: 0,
+ quotingYou: 0
+ });
+ }
+ };
+
+ return UnreadIndex;
+
+}).call(this);
+
+Captcha = {};
+
+(function() {
+ Captcha.cache = {
+ init: function() {
+ $.on(d, 'SaveCaptcha', (function(_this) {
+ return function(e) {
+ return _this.saveAPI(e.detail);
+ };
+ })(this));
+ return $.on(d, 'NoCaptcha', (function(_this) {
+ return function(e) {
+ return _this.noCaptcha(e.detail);
+ };
+ })(this));
+ },
+ captchas: [],
+ getCount: function() {
+ return this.captchas.length;
+ },
+ neededRaw: function() {
+ return !(this.haveCookie() || this.captchas.length || QR.req || this.submitCB) && (QR.posts.length > 1 || Conf['Auto-load captcha'] || !QR.posts[0].isOnlyQuotes() || QR.posts[0].file);
+ },
+ needed: function() {
+ return this.neededRaw() && $.event('LoadCaptcha');
+ },
+ prerequest: function() {
+ if (!Conf['Prerequest Captcha']) {
+ return;
+ }
+ return $.queueTask((function(_this) {
+ return function() {
+ var isReply;
+ if (!_this.prerequested && _this.neededRaw() && !$.event('LoadCaptcha') && !QR.captcha.occupied() && QR.cooldown.seconds <= 60 && QR.selected === QR.posts[QR.posts.length - 1] && !QR.selected.isOnlyQuotes()) {
+ isReply = QR.selected.thread !== 'new';
+ if (!$.event('RequestCaptcha', {
+ isReply: isReply
+ })) {
+ _this.prerequested = true;
+ _this.submitCB = function(captcha) {
+ if (captcha) {
+ return _this.save(captcha);
+ }
+ };
+ return _this.updateCount();
+ }
+ }
+ };
+ })(this));
+ },
+ haveCookie: function() {
+ return /\b_ct=/.test(d.cookie) && QR.posts[0].thread !== 'new';
+ },
+ getOne: function() {
+ var captcha;
+ delete this.prerequested;
+ this.clear();
+ if ((captcha = this.captchas.shift())) {
+ this.count();
+ return captcha;
+ } else {
+ return null;
+ }
+ },
+ request: function(isReply) {
+ if (!this.submitCB) {
+ if ($.event('RequestCaptcha', {
+ isReply: isReply
+ })) {
+ return;
+ }
+ }
+ return (function(_this) {
+ return function(cb) {
+ _this.submitCB = cb;
+ return _this.updateCount();
+ };
+ })(this);
+ },
+ abort: function() {
+ if (this.submitCB) {
+ delete this.submitCB;
+ $.event('AbortCaptcha');
+ return this.updateCount();
+ }
+ },
+ saveAPI: function(captcha) {
+ var cb;
+ if ((cb = this.submitCB)) {
+ delete this.submitCB;
+ cb(captcha);
+ return this.updateCount();
+ } else {
+ return this.save(captcha);
+ }
+ },
+ noCaptcha: function(detail) {
+ var cb;
+ if ((cb = this.submitCB)) {
+ if (!this.haveCookie() || (detail != null ? detail.error : void 0)) {
+ QR.error((detail != null ? detail.error : void 0) || 'Failed to retrieve captcha.');
+ QR.captcha.setup(d.activeElement === QR.nodes.status);
+ }
+ delete this.submitCB;
+ cb();
+ return this.updateCount();
+ }
+ },
+ save: function(captcha) {
+ var cb;
+ if ((cb = this.submitCB)) {
+ this.abort();
+ cb(captcha);
+ return;
+ }
+ this.captchas.push(captcha);
+ this.captchas.sort(function(a, b) {
+ return a.timeout - b.timeout;
+ });
+ return this.count();
+ },
+ clear: function() {
+ var captcha, i, j, len, now, ref;
+ if (this.captchas.length) {
+ now = Date.now();
+ ref = this.captchas;
+ for (i = j = 0, len = ref.length; j < len; i = ++j) {
+ captcha = ref[i];
+ if (captcha.timeout > now) {
+ break;
+ }
+ }
+ if (i) {
+ this.captchas = this.captchas.slice(i);
+ return this.count();
+ }
+ }
+ },
+ count: function() {
+ clearTimeout(this.timer);
+ if (this.captchas.length) {
+ this.timer = setTimeout(this.clear.bind(this), this.captchas[0].timeout - Date.now());
+ }
+ return this.updateCount();
+ },
+ updateCount: function() {
+ return $.event('CaptchaCount', this.captchas.length);
+ }
+ };
+
+}).call(this);
+
+(function() {
+ Captcha.replace = {
+ init: function() {
+ var ref;
+ if (!(g.SITE.software === 'yotsuba' && d.cookie.indexOf('pass_enabled=1') < 0)) {
+ return;
+ }
+ if (Conf['Force Noscript Captcha'] && Main.jsEnabled) {
+ $.ready(Captcha.replace.noscript);
+ return;
+ }
+ if (Conf['captchaLanguage'].trim()) {
+ if ((ref = location.hostname) === 'boards.4chan.org' || ref === 'boards.4channel.org') {
+ return $.onExists(doc, '#captchaFormPart', function(node) {
+ return $.onExists(node, 'iframe[src^="https://www.google.com/recaptcha/"]', Captcha.replace.iframe);
+ });
+ } else {
+ return $.onExists(doc, 'iframe[src^="https://www.google.com/recaptcha/"]', Captcha.replace.iframe);
+ }
+ }
+ },
+ noscript: function() {
+ var insert, noscript, original, span, toggle;
+ if (!((original = $('#g-recaptcha')) && (noscript = $('noscript', original.parentNode)))) {
+ return;
+ }
+ span = $.el('span', {
+ id: 'captcha-forced-noscript'
+ });
+ $.replace(noscript, span);
+ $.rm(original);
+ insert = function() {
+ span.innerHTML = noscript.textContent;
+ return Captcha.replace.iframe($('iframe[src^="https://www.google.com/recaptcha/"]', span));
+ };
+ if ((toggle = $('#togglePostFormLink a, #form-link'))) {
+ return $.on(toggle, 'click', insert);
+ } else {
+ return insert();
+ }
+ },
+ iframe: function(iframe) {
+ var lang, src;
+ if ((lang = Conf['captchaLanguage'].trim())) {
+ src = /[?&]hl=/.test(iframe.src) ? iframe.src.replace(/([?&]hl=)[^&]*/, '$1' + encodeURIComponent(lang)) : iframe.src + ("&hl=" + (encodeURIComponent(lang)));
+ if (iframe.src !== src) {
+ iframe.src = src;
+ }
+ }
+ }
+ };
+
+}).call(this);
+
+(function() {
+ Captcha.t = {
+ init: function() {
+ var root;
+ if (d.cookie.indexOf('pass_enabled=1') >= 0) {
+ return;
+ }
+ if (!(this.isEnabled = !!$('#t-root') || !$.id('postForm'))) {
+ return;
+ }
+ root = $.el('div', {
+ className: 'captcha-root'
+ });
+ this.nodes = {
+ root: root
+ };
+ $.addClass(QR.nodes.el, 'has-captcha', 'captcha-t');
+ return $.after(QR.nodes.com.parentNode, root);
+ },
+ moreNeeded: function() {},
+ getThread: function() {
+ var boardID, threadID;
+ boardID = g.BOARD.ID;
+ if (QR.posts[0].thread === 'new') {
+ threadID = '0';
+ } else {
+ threadID = '' + QR.posts[0].thread;
+ }
+ return {
+ boardID: boardID,
+ threadID: threadID
+ };
+ },
+ setup: function(focus) {
+ if (!this.isEnabled) {
+ return;
+ }
+ if (!this.nodes.container) {
+ this.nodes.container = $.el('div', {
+ className: 'captcha-container'
+ });
+ $.prepend(this.nodes.root, this.nodes.container);
+ Captcha.t.currentThread = Captcha.t.getThread();
+ $.global(function() {
+ var el;
+ el = document.querySelector('#qr .captcha-container');
+ window.TCaptcha.init(el, this.boardID, +this.threadID);
+ return window.TCaptcha.setErrorCb(function(err) {
+ return window.dispatchEvent(new CustomEvent('CreateNotification', {
+ detail: {
+ type: 'warning',
+ content: '' + err
+ }
+ }));
+ });
+ }, Captcha.t.currentThread);
+ }
+ if (focus) {
+ return $('#t-resp').focus();
+ }
+ },
+ destroy: function() {
+ if (!(this.isEnabled && this.nodes.container)) {
+ return;
+ }
+ $.global(function() {
+ return window.TCaptcha.destroy();
+ });
+ $.rm(this.nodes.container);
+ return delete this.nodes.container;
+ },
+ updateThread: function() {
+ var boardID, newThread, ref, threadID;
+ if (!this.isEnabled) {
+ return;
+ }
+ ref = Captcha.t.currentThread || {}, boardID = ref.boardID, threadID = ref.threadID;
+ newThread = Captcha.t.getThread();
+ if (!(newThread.boardID === boardID && newThread.threadID === threadID)) {
+ Captcha.t.destroy();
+ return Captcha.t.setup();
+ }
+ },
+ getOne: function() {
+ var el, i, key, len, ref, response;
+ response = {};
+ if (this.nodes.container) {
+ ref = ['t-response', 't-challenge'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ key = ref[i];
+ response[key] = $("[name='" + key + "']", this.nodes.container).value;
+ }
+ }
+ if (!response['t-response'] && !((el = $('#t-msg')) && /Verification not required/i.test(el.textContent))) {
+ response = null;
+ }
+ return response;
+ },
+ setUsed: function() {
+ if (!this.isEnabled) {
+ return;
+ }
+ if (this.nodes.container) {
+ return $.global(function() {
+ return window.TCaptcha.clearChallenge();
+ });
+ }
+ },
+ occupied: function() {
+ return !!this.nodes.container;
+ }
+ };
+
+}).call(this);
+
+(function() {
+ var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Captcha.v2 = {
+ lifetime: 2 * $.MINUTE,
+ init: function() {
+ var counter, root;
+ if (d.cookie.indexOf('pass_enabled=1') >= 0) {
+ return;
+ }
+ if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript') || !$.id('postForm'))) {
+ return;
+ }
+ if ((this.noscript = Conf['Force Noscript Captcha'] || !Main.jsEnabled)) {
+ $.addClass(QR.nodes.el, 'noscript-captcha');
+ }
+ Captcha.cache.init();
+ $.on(d, 'CaptchaCount', this.count.bind(this));
+ root = $.el('div', {
+ className: 'captcha-root'
+ });
+ $.extend(root, {innerHTML: "<div class=\"captcha-counter\"><a href=\"javascript:;\"></a></div>"});
+ counter = $('.captcha-counter > a', root);
+ this.nodes = {
+ root: root,
+ counter: counter
+ };
+ this.count();
+ $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v2');
+ $.after(QR.nodes.com.parentNode, root);
+ $.on(counter, 'click', this.toggle.bind(this));
+ $.on(counter, 'keydown', (function(_this) {
+ return function(e) {
+ if (Keybinds.keyCode(e) !== 'Space') {
+ return;
+ }
+ _this.toggle();
+ e.preventDefault();
+ return e.stopPropagation();
+ };
+ })(this));
+ return $.on(window, 'captcha:success', (function(_this) {
+ return function() {
+ return $.queueTask(function() {
+ return _this.save(false);
+ });
+ };
+ })(this));
+ },
+ timeouts: {},
+ prevNeeded: 0,
+ noscriptURL: function() {
+ var lang, url;
+ url = 'https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc';
+ if ((lang = Conf['captchaLanguage'].trim())) {
+ url += "&hl=" + (encodeURIComponent(lang));
+ }
+ return url;
+ },
+ moreNeeded: function() {
+ return $.queueTask((function(_this) {
+ return function() {
+ var needed;
+ needed = Captcha.cache.needed();
+ if (needed && !_this.prevNeeded) {
+ _this.setup(QR.cooldown.auto && d.activeElement === QR.nodes.status);
+ }
+ return _this.prevNeeded = needed;
+ };
+ })(this));
+ },
+ toggle: function() {
+ if (this.nodes.container && !this.timeouts.destroy) {
+ return this.destroy();
+ } else {
+ return this.setup(true, true);
+ }
+ },
+ setup: function(focus, force) {
+ if (!(this.isEnabled && (Captcha.cache.needed() || force))) {
+ return;
+ }
+ if (focus) {
+ $.addClass(QR.nodes.el, 'focus');
+ this.nodes.counter.focus();
+ }
+ if (this.timeouts.destroy) {
+ clearTimeout(this.timeouts.destroy);
+ delete this.timeouts.destroy;
+ return this.reload();
+ }
+ if (this.nodes.container) {
+ $.queueTask((function(_this) {
+ return function() {
+ var iframe;
+ if (_this.nodes.container && d.activeElement === _this.nodes.counter && (iframe = $('iframe[src^="https://www.google.com/recaptcha/"]', _this.nodes.container))) {
+ iframe.focus();
+ return QR.focus();
+ }
+ };
+ })(this));
+ return;
+ }
+ this.nodes.container = $.el('div', {
+ className: 'captcha-container'
+ });
+ $.prepend(this.nodes.root, this.nodes.container);
+ new MutationObserver(this.afterSetup.bind(this)).observe(this.nodes.container, {
+ childList: true,
+ subtree: true
+ });
+ if (this.noscript) {
+ return this.setupNoscript();
+ } else {
+ return this.setupJS();
+ }
+ },
+ setupNoscript: function() {
+ var div, iframe, textarea;
+ iframe = $.el('iframe', {
+ id: 'qr-captcha-iframe',
+ scrolling: 'no',
+ src: this.noscriptURL()
+ });
+ div = $.el('div');
+ textarea = $.el('textarea');
+ $.add(div, textarea);
+ return $.add(this.nodes.container, [iframe, div]);
+ },
+ setupJS: function() {
+ return $.global(function() {
+ var cbNative, render, script;
+ render = function() {
+ var classList, container;
+ classList = document.documentElement.classList;
+ container = document.querySelector('#qr .captcha-container');
+ return container.dataset.widgetID = window.grecaptcha.render(container, {
+ sitekey: '6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc',
+ theme: classList.contains('tomorrow') || classList.contains('spooky') || classList.contains('dark-captcha') ? 'dark' : 'light',
+ callback: function(response) {
+ return window.dispatchEvent(new CustomEvent('captcha:success', {
+ detail: response
+ }));
+ }
+ });
+ };
+ if (window.grecaptcha) {
+ return render();
+ } else {
+ cbNative = window.onRecaptchaLoaded;
+ window.onRecaptchaLoaded = function() {
+ render();
+ return cbNative();
+ };
+ if (!document.head.querySelector('script[src^="https://www.google.com/recaptcha/api.js"]')) {
+ script = document.createElement('script');
+ script.src = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoaded&render=explicit';
+ return document.head.appendChild(script);
+ }
+ }
+ });
+ },
+ afterSetup: function(mutations) {
+ var i, iframe, j, len, len1, mutation, node, ref, textarea;
+ for (i = 0, len = mutations.length; i < len; i++) {
+ mutation = mutations[i];
+ ref = mutation.addedNodes;
+ for (j = 0, len1 = ref.length; j < len1; j++) {
+ node = ref[j];
+ if ((iframe = $.x('./descendant-or-self::iframe[starts-with(@src, "https://www.google.com/recaptcha/")]', node))) {
+ this.setupIFrame(iframe);
+ }
+ if ((textarea = $.x('./descendant-or-self::textarea', node))) {
+ this.setupTextArea(textarea);
+ }
+ }
+ }
+ },
+ setupIFrame: function(iframe) {
+ var ref, ref1;
+ if (!doc.contains(iframe)) {
+ return;
+ }
+ Captcha.replace.iframe(iframe);
+ $.addClass(QR.nodes.el, 'captcha-open');
+ this.fixQRPosition();
+ $.on(iframe, 'load', this.fixQRPosition);
+ if (d.activeElement === this.nodes.counter) {
+ iframe.focus();
+ }
+ if (((ref = $.engine) === 'blink' || ref === 'edge') && (ref1 = iframe.parentNode, indexOf.call($$('#qr .captcha-container > div > div:first-of-type'), ref1) >= 0)) {
+ return $.on(iframe.parentNode, 'scroll', function() {
+ return this.scrollTop = 0;
+ });
+ }
+ },
+ fixQRPosition: function() {
+ if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) {
+ QR.nodes.el.style.top = '';
+ return QR.nodes.el.style.bottom = '0px';
+ }
+ },
+ setupTextArea: function(textarea) {
+ return $.one(textarea, 'input', (function(_this) {
+ return function() {
+ return _this.save(true);
+ };
+ })(this));
+ },
+ destroy: function() {
+ if (!this.isEnabled) {
+ return;
+ }
+ delete this.timeouts.destroy;
+ $.rmClass(QR.nodes.el, 'captcha-open');
+ if (this.nodes.container) {
+ $.global(function() {
+ var container;
+ container = document.querySelector('#qr .captcha-container');
+ return window.grecaptcha.reset(container.dataset.widgetID);
+ });
+ $.rm(this.nodes.container);
+ return delete this.nodes.container;
+ }
+ },
+ getOne: function(isReply) {
+ return Captcha.cache.getOne(isReply);
+ },
+ save: function(pasted, token) {
+ var base, focus, ref;
+ Captcha.cache.save({
+ response: token || $('textarea', this.nodes.container).value,
+ timeout: Date.now() + this.lifetime
+ });
+ focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && /https?:\/\/www\.google\.com\/recaptcha\//.test(d.activeElement.src);
+ if (Captcha.cache.needed()) {
+ if (focus) {
+ if (QR.cooldown.auto || Conf['Post on Captcha Completion']) {
+ this.nodes.counter.focus();
+ } else {
+ QR.nodes.status.focus();
+ }
+ }
+ this.reload();
+ } else {
+ if (pasted) {
+ this.destroy();
+ } else {
+ if ((base = this.timeouts).destroy == null) {
+ base.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND);
+ }
+ }
+ if (focus) {
+ QR.nodes.status.focus();
+ }
+ }
+ if (Conf['Post on Captcha Completion'] && !QR.cooldown.auto) {
+ return QR.submit();
+ }
+ },
+ count: function() {
+ var count, loading;
+ count = Captcha.cache.getCount();
+ loading = Captcha.cache.submitCB ? '...' : '';
+ this.nodes.counter.textContent = "Captchas: " + count + loading;
+ return this.moreNeeded();
+ },
+ reload: function() {
+ if ($('iframe[src^="https://www.google.com/recaptcha/api/fallback?"]', this.nodes.container)) {
+ this.destroy();
+ return this.setup(false, true);
+ } else {
+ return $.global(function() {
+ var container;
+ container = document.querySelector('#qr .captcha-container');
+ return window.grecaptcha.reset(container.dataset.widgetID);
+ });
+ }
+ },
+ occupied: function() {
+ return !!this.nodes.container && !this.timeouts.destroy;
+ }
+ };
+
+}).call(this);
+
+PassLink = (function() {
+ var PassLink;
+
+ PassLink = {
+ init: function() {
+ if (!(g.SITE.software === 'yotsuba' && Conf['Pass Link'])) {
+ return;
+ }
+ return Main.ready(this.ready);
+ },
+ ready: function() {
+ var passLink, styleSelector;
+ if (!(styleSelector = $.id('styleSelector'))) {
+ return;
+ }
+ passLink = $.el('span', {
+ className: 'brackets-wrap pass-link-container'
+ });
+ $.extend(passLink, {innerHTML: "<a href=\"javascript:;\">4chan Pass</a>"});
+ $.on(passLink.firstElementChild, 'click', function() {
+ return window.open("//sys." + (location.hostname.split('.')[1]) + ".org/auth", Date.now(), 'width=500,height=280,toolbar=0');
+ });
+ return $.before(styleSelector.previousSibling, [passLink, $.tn('\u00A0\u00A0')]);
+ }
+ };
+
+ return PassLink;
+
+}).call(this);
+
+PostRedirect = (function() {
+ var PostRedirect;
+
+ PostRedirect = {
+ init: function() {
+ return $.on(d, 'QRPostSuccessful', (function(_this) {
+ return function(e) {
+ if (!e.detail.redirect) {
+ return;
+ }
+ _this.event = e;
+ _this.delays = 0;
+ return $.queueTask(function() {
+ if (e === _this.event && _this.delays === 0) {
+ return location.href = e.detail.redirect;
+ }
+ });
+ };
+ })(this));
+ },
+ delays: 0,
+ delay: function() {
+ var e;
+ if (!this.event) {
+ return null;
+ }
+ e = this.event;
+ this.delays++;
+ return (function(_this) {
+ return function() {
+ if (e !== _this.event) {
+ return;
+ }
+ _this.delays--;
+ if (_this.delays === 0) {
+ return location.href = e.detail.redirect;
+ }
+ };
+ })(this);
+ }
+ };
+
+ return PostRedirect;
+
+}).call(this);
+
+PostSuccessful = (function() {
+ var PostSuccessful;
+
+ PostSuccessful = {
+ init: function() {
+ if (!Conf['Remember Your Posts']) {
+ return;
+ }
+ return $.ready(this.ready);
+ },
+ ready: function() {
+ var _, db, postID, ref, threadID;
+ if (d.title !== 'Post successful!') {
+ return;
+ }
+ ref = $('h1').nextSibling.textContent.match(/thread:(\d+),no:(\d+)/), _ = ref[0], threadID = ref[1], postID = ref[2];
+ postID = +postID;
+ threadID = +threadID || postID;
+ db = new DataBoard('yourPosts');
+ return db.set({
+ boardID: g.BOARD.ID,
+ threadID: threadID,
+ postID: postID,
+ val: true
+ });
+ }
+ };
+
+ return PostSuccessful;
+
+}).call(this);
+
+QR = (function() {
+ var QR,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
+ slice = [].slice;
+
+ QR = {
+ mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash', 'video/webm'],
+ validExtension: /\.(jpe?g|png|gif|pdf|swf|webm)$/i,
+ typeFromExtension: {
+ 'jpg': 'image/jpeg',
+ 'jpeg': 'image/jpeg',
+ 'png': 'image/png',
+ 'gif': 'image/gif',
+ 'pdf': 'application/pdf',
+ 'swf': 'application/vnd.adobe.flash.movie',
+ 'webm': 'video/webm'
+ },
+ extensionFromType: {
+ 'image/jpeg': 'jpg',
+ 'image/png': 'png',
+ 'image/gif': 'gif',
+ 'application/pdf': 'pdf',
+ 'application/vnd.adobe.flash.movie': 'swf',
+ 'application/x-shockwave-flash': 'swf',
+ 'video/webm': 'webm'
+ },
+ init: function() {
+ var sc;
+ if (!Conf['Quick Reply']) {
+ return;
+ }
+ this.posts = [];
+ $.on(d, '4chanXInitFinished', function() {
+ return BoardConfig.ready(QR.initReady);
+ });
+ Callbacks.Post.push({
+ name: 'Quick Reply',
+ cb: this.node
+ });
+ this.shortcut = sc = $.el('a', {
+ className: 'fa fa-comment-o disabled',
+ textContent: 'QR',
+ title: 'Quick Reply',
+ href: 'javascript:;'
+ });
+ $.on(sc, 'click', function() {
+ if (!QR.postingIsEnabled) {
+ return;
+ }
+ if (Conf['Persistent QR'] || !QR.nodes || QR.nodes.el.hidden) {
+ QR.open();
+ return QR.nodes.com.focus();
+ } else {
+ return QR.close();
+ }
+ });
+ return Header.addShortcut('qr', sc, 540);
+ },
+ initReady: function() {
+ var captchaVersion, config, link, linkBot, navLinksBot, origToggle, prop;
+ captchaVersion = $('#g-recaptcha, #captcha-forced-noscript') ? 'v2' : 't';
+ QR.captcha = Captcha[captchaVersion];
+ QR.postingIsEnabled = true;
+ config = g.BOARD.config;
+ prop = function(key, def) {
+ var ref;
+ return +((ref = config[key]) != null ? ref : def);
+ };
+ QR.min_width = prop('min_image_width', 1);
+ QR.min_height = prop('min_image_height', 1);
+ QR.max_width = QR.max_height = 10000;
+ QR.max_size = prop('max_filesize', 4194304);
+ QR.max_size_video = prop('max_webm_filesize', QR.max_size);
+ QR.max_comment = prop('max_comment_chars', 2000);
+ QR.max_width_video = QR.max_height_video = 2048;
+ QR.max_duration_video = prop('max_webm_duration', 120);
+ QR.forcedAnon = !!config.forced_anon;
+ QR.spoiler = !!config.spoilers;
+ if ((origToggle = $.id('togglePostFormLink'))) {
+ link = $.el('h1', {
+ className: "qr-link-container"
+ });
+ $.extend(link, {innerHTML: "<a href=\"javascript:;\" class=\"qr-link\">" + ((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + "</a>"});
+ QR.link = link.firstElementChild;
+ $.on(link.firstChild, 'click', function() {
+ QR.open();
+ return QR.nodes.com.focus();
+ });
+ $.before(origToggle, link);
+ origToggle.firstElementChild.textContent = 'Original Form';
+ }
+ if (g.VIEW === 'thread') {
+ linkBot = $.el('div', {
+ className: "brackets-wrap qr-link-container-bottom"
+ });
+ $.extend(linkBot, {innerHTML: "<a href=\"javascript:;\" class=\"qr-link-bottom\">Reply to Thread</a>"});
+ $.on(linkBot.firstElementChild, 'click', function() {
+ QR.open();
+ return QR.nodes.com.focus();
+ });
+ if ((navLinksBot = $('.navLinksBot'))) {
+ $.prepend(navLinksBot, linkBot);
+ }
+ }
+ $.on(d, 'QRGetFile', QR.getFile);
+ $.on(d, 'QRDrawFile', QR.drawFile);
+ $.on(d, 'QRSetFile', QR.setFile);
+ $.on(d, 'paste', QR.paste);
+ $.on(d, 'dragover', QR.dragOver);
+ $.on(d, 'drop', QR.dropFile);
+ $.on(d, 'dragstart dragend', QR.drag);
+ $.on(d, 'IndexRefreshInternal', QR.generatePostableThreadsList);
+ $.on(d, 'ThreadUpdate', QR.statusCheck);
+ if (!Conf['Persistent QR']) {
+ return;
+ }
+ QR.open();
+ if (Conf['Auto Hide QR']) {
+ return QR.hide();
+ }
+ },
+ statusCheck: function() {
+ var thread;
+ if (!QR.nodes) {
+ return;
+ }
+ thread = QR.posts[0].thread;
+ if (thread !== 'new' && g.threads.get(g.BOARD + "." + thread).isDead) {
+ return QR.abort();
+ } else {
+ return QR.status();
+ }
+ },
+ node: function() {
+ $.on(this.nodes.quote, 'click', QR.quote);
+ if (this.isFetchedQuote) {
+ return QR.generatePostableThreadsList();
+ }
+ },
+ open: function() {
+ var err;
+ if (QR.nodes) {
+ if (QR.nodes.el.hidden) {
+ QR.captcha.setup();
+ }
+ QR.nodes.el.hidden = false;
+ QR.unhide();
+ } else {
+ try {
+ QR.dialog();
+ } catch (error) {
+ err = error;
+ delete QR.nodes;
+ Main.handleErrors({
+ message: 'Quick Reply dialog creation crashed.',
+ error: err
+ });
+ return;
+ }
+ }
+ return $.rmClass(QR.shortcut, 'disabled');
+ },
+ close: function() {
+ var j, len, post, ref;
+ if (QR.req) {
+ QR.abort();
+ return;
+ }
+ QR.nodes.el.hidden = true;
+ QR.cleanNotifications();
+ QR.blur();
+ $.rmClass(QR.nodes.el, 'dump');
+ $.addClass(QR.shortcut, 'disabled');
+ new QR.post(true);
+ ref = QR.posts.splice(0, QR.posts.length - 1);
+ for (j = 0, len = ref.length; j < len; j++) {
+ post = ref[j];
+ post["delete"]();
+ }
+ QR.cooldown.auto = false;
+ QR.status();
+ return QR.captcha.destroy();
+ },
+ focus: function() {
+ return $.queueTask(function() {
+ if (!QR.inBubble()) {
+ QR.hasFocus = d.activeElement && QR.nodes.el.contains(d.activeElement);
+ return QR.nodes.el.classList.toggle('focus', QR.hasFocus);
+ }
+ });
+ },
+ inBubble: function() {
+ var bubbles, ref;
+ bubbles = $$('iframe[src^="https://www.google.com/recaptcha/api2/frame"]');
+ return (ref = d.activeElement, indexOf.call(bubbles, ref) >= 0) || bubbles.some(function(el) {
+ return getComputedStyle(el).visibility !== 'hidden' && el.getBoundingClientRect().bottom > 0;
+ });
+ },
+ hide: function() {
+ QR.blur();
+ $.addClass(QR.nodes.el, 'autohide');
+ return QR.nodes.autohide.checked = true;
+ },
+ unhide: function() {
+ $.rmClass(QR.nodes.el, 'autohide');
+ return QR.nodes.autohide.checked = false;
+ },
+ toggleHide: function() {
+ if (this.checked) {
+ return QR.hide();
+ } else {
+ return QR.unhide();
+ }
+ },
+ blur: function() {
+ if (QR.nodes.el.contains(d.activeElement)) {
+ return d.activeElement.blur();
+ }
+ },
+ toggleSJIS: function(e) {
+ e.preventDefault();
+ Conf['sjisPreview'] = !Conf['sjisPreview'];
+ $.set('sjisPreview', Conf['sjisPreview']);
+ return QR.nodes.el.classList.toggle('sjis-preview', Conf['sjisPreview']);
+ },
+ texPreviewShow: function() {
+ if ($.hasClass(QR.nodes.el, 'tex-preview')) {
+ return QR.texPreviewHide();
+ }
+ $.addClass(QR.nodes.el, 'tex-preview');
+ QR.nodes.texPreview.textContent = QR.nodes.com.value;
+ return $.event('mathjax', null, QR.nodes.texPreview);
+ },
+ texPreviewHide: function() {
+ return $.rmClass(QR.nodes.el, 'tex-preview');
+ },
+ addPost: function() {
+ var wasOpen;
+ wasOpen = QR.nodes && !QR.nodes.el.hidden;
+ QR.open();
+ if (wasOpen) {
+ $.addClass(QR.nodes.el, 'dump');
+ new QR.post(true);
+ }
+ return QR.nodes.com.focus();
+ },
+ setCustomCooldown: function(enabled) {
+ Conf['customCooldownEnabled'] = enabled;
+ QR.cooldown.customCooldown = enabled;
+ return QR.nodes.customCooldown.classList.toggle('disabled', !enabled);
+ },
+ toggleCustomCooldown: function() {
+ var enabled;
+ enabled = $.hasClass(QR.nodes.customCooldown, 'disabled');
+ QR.setCustomCooldown(enabled);
+ return $.set('customCooldownEnabled', enabled);
+ },
+ error: function(err, focusOverride) {
+ var el, notice, notif;
+ QR.open();
+ if (typeof err === 'string') {
+ el = $.tn(err);
+ } else {
+ el = err;
+ el.removeAttribute('style');
+ }
+ notice = new Notice('warning', el);
+ QR.notifications.push(notice);
+ if (!Header.areNotificationsEnabled) {
+ if (d.hidden && !QR.cooldown.auto) {
+ return alert(el.textContent);
+ }
+ } else if (d.hidden || !(focusOverride || d.hasFocus())) {
+ notif = new Notification(el.textContent, {
+ body: el.textContent,
+ icon: Favicon.logo
+ });
+ notif.onclick = function() {
+ return window.focus();
+ };
+ if ($.engine !== 'gecko') {
+ notif.onclose = function() {
+ return notice.close();
+ };
+ return notif.onshow = function() {
+ return setTimeout(function() {
+ notif.onclose = null;
+ return notif.close();
+ }, 7 * $.SECOND);
+ };
+ }
+ }
+ },
+ connectionError: function() {
+ return $.el('span', {innerHTML: "Connection error while posting. [<a href=\"https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions#connection-errors\" target=\"_blank\">More info</a>]"});
+ },
+ notifications: [],
+ cleanNotifications: function() {
+ var j, len, notification, ref;
+ ref = QR.notifications;
+ for (j = 0, len = ref.length; j < len; j++) {
+ notification = ref[j];
+ notification.close();
+ }
+ return QR.notifications = [];
+ },
+ status: function() {
+ var disabled, status, thread, value;
+ if (!QR.nodes) {
+ return;
+ }
+ thread = QR.posts[0].thread;
+ if (thread !== 'new' && g.threads.get(g.BOARD + "." + thread).isDead) {
+ value = 'Dead';
+ disabled = true;
+ QR.cooldown.auto = false;
+ }
+ value = QR.req ? QR.req.progress : QR.cooldown.seconds || value;
+ status = QR.nodes.status;
+ status.value = !value ? 'Submit' : QR.cooldown.auto ? "Auto " + value : value;
+ return status.disabled = disabled || false;
+ },
+ openPost: function() {
+ var index;
+ QR.open();
+ if (QR.selected.isLocked) {
+ index = QR.posts.indexOf(QR.selected);
+ (QR.posts[index + 1] || new QR.post()).select();
+ $.addClass(QR.nodes.el, 'dump');
+ return QR.cooldown.auto = true;
+ }
+ },
+ quote: function(e) {
+ var ancestor, base, caretPos, com, frag, i, insideCode, j, k, l, len, len1, len2, len3, n, node, o, post, postRange, range, ref, ref1, ref2, ref3, ref4, ref5, ref6, root, sel, text, thread, wasOnlyQuotes;
+ if (e != null) {
+ e.preventDefault();
+ }
+ if (!QR.postingIsEnabled) {
+ return;
+ }
+ sel = d.getSelection();
+ post = Get.postFromNode(this);
+ root = post.nodes.root;
+ postRange = new Range();
+ postRange.selectNode(root);
+ text = post.board.ID === g.BOARD.ID ? ">>" + post + "\n" : ">>>/" + post.board + "/" + post + "\n";
+ for (i = j = 0, ref = sel.rangeCount; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
+ range = sel.getRangeAt(i);
+ if (range.compareBoundaryPoints(Range.START_TO_START, postRange) < 0) {
+ range.setStartBefore(root);
+ }
+ if (range.compareBoundaryPoints(Range.END_TO_END, postRange) > 0) {
+ range.setEndAfter(root);
+ }
+ if (!range.toString().trim()) {
+ continue;
+ }
+ frag = range.cloneContents();
+ ancestor = range.commonAncestorContainer;
+ if ($.x('ancestor-or-self::*[self::s or contains(@class,"removed-spoiler")]', ancestor)) {
+ $.prepend(frag, $.tn('[spoiler]'));
+ $.add(frag, $.tn('[/spoiler]'));
+ }
+ if (insideCode = $.x('ancestor-or-self::pre[contains(@class,"prettyprint")]', ancestor)) {
+ $.prepend(frag, $.tn('[code]'));
+ $.add(frag, $.tn('[/code]'));
+ }
+ ref1 = $$((insideCode ? 'br' : '.prettyprint br'), frag);
+ for (k = 0, len = ref1.length; k < len; k++) {
+ node = ref1[k];
+ $.replace(node, $.tn('\n'));
+ }
+ ref2 = $$('br', frag);
+ for (l = 0, len1 = ref2.length; l < len1; l++) {
+ node = ref2[l];
+ if (node !== frag.lastChild) {
+ $.replace(node, $.tn('\n>'));
+ }
+ }
+ if (typeof (base = g.SITE).insertTags === "function") {
+ base.insertTags(frag);
+ }
+ ref3 = $$('.linkify[data-original]', frag);
+ for (n = 0, len2 = ref3.length; n < len2; n++) {
+ node = ref3[n];
+ $.replace(node, $.tn(node.dataset.original));
+ }
+ ref4 = $$('.embedder', frag);
+ for (o = 0, len3 = ref4.length; o < len3; o++) {
+ node = ref4[o];
+ if (((ref5 = node.previousSibling) != null ? ref5.nodeValue : void 0) === ' ') {
+ $.rm(node.previousSibling);
+ }
+ $.rm(node);
+ }
+ text += ">" + (frag.textContent.trim()) + "\n";
+ }
+ QR.openPost();
+ ref6 = QR.nodes, com = ref6.com, thread = ref6.thread;
+ if (!com.value) {
+ thread.value = Get.threadFromNode(this);
+ }
+ wasOnlyQuotes = QR.selected.isOnlyQuotes();
+ caretPos = com.selectionStart;
+ com.value = com.value.slice(0, caretPos) + text + com.value.slice(com.selectionEnd);
+ range = caretPos + text.length;
+ com.setSelectionRange(range, range);
+ com.focus();
+ if (wasOnlyQuotes) {
+ QR.selected.quotedText = com.value;
+ }
+ QR.selected.save(com);
+ return QR.selected.save(thread);
+ },
+ characterCount: function() {
+ var count, counter;
+ counter = QR.nodes.charCount;
+ count = QR.nodes.com.value.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length;
+ counter.textContent = count;
+ counter.hidden = count < QR.max_comment / 2;
+ return (count > QR.max_comment ? $.addClass : $.rmClass)(counter, 'warning');
+ },
+ getFile: function() {
+ var ref;
+ return $.event('QRFile', (ref = QR.selected) != null ? ref.file : void 0);
+ },
+ drawFile: function(e) {
+ var el, file, isVideo, ref;
+ file = (ref = QR.selected) != null ? ref.file : void 0;
+ if (!(file && /^(image|video)\//.test(file.type))) {
+ return;
+ }
+ isVideo = /^video\//.test(file);
+ el = $.el((isVideo ? 'video' : 'img'));
+ $.on(el, 'error', function() {
+ return QR.openError();
+ });
+ $.on(el, (isVideo ? 'loadeddata' : 'load'), function() {
+ e.target.getContext('2d').drawImage(el, 0, 0);
+ URL.revokeObjectURL(el.src);
+ return $.event('QRImageDrawn', null, e.target);
+ });
+ return el.src = URL.createObjectURL(file);
+ },
+ openError: function() {
+ var div;
+ div = $.el('div');
+ $.extend(div, {innerHTML: "Could not open file. [<a href=\"https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions#error-reading-metadata\" target=\"_blank\">More info</a>]"});
+ return QR.error(div);
+ },
+ setFile: function(e) {
+ var file, name, ref, source;
+ ref = e.detail, file = ref.file, name = ref.name, source = ref.source;
+ if (name != null) {
+ file.name = name;
+ }
+ if (source != null) {
+ file.source = source;
+ }
+ QR.open();
+ return QR.handleFiles([file]);
+ },
+ drag: function(e) {
+ var toggle;
+ toggle = e.type === 'dragstart' ? $.off : $.on;
+ toggle(d, 'dragover', QR.dragOver);
+ return toggle(d, 'drop', QR.dropFile);
+ },
+ dragOver: function(e) {
+ e.preventDefault();
+ return e.dataTransfer.dropEffect = 'copy';
+ },
+ dropFile: function(e) {
+ if (!e.dataTransfer.files.length) {
+ return;
+ }
+ e.preventDefault();
+ QR.open();
+ return QR.handleFiles(e.dataTransfer.files);
+ },
+ paste: function(e) {
+ var blob, file, file2, item, j, len, ref, score, score2, type;
+ if (!e.clipboardData.items) {
+ return;
+ }
+ file = null;
+ score = -1;
+ ref = e.clipboardData.items;
+ for (j = 0, len = ref.length; j < len; j++) {
+ item = ref[j];
+ if (!(item.kind === 'file' && (file2 = item.getAsFile()))) {
+ continue;
+ }
+ score2 = 2 * (file2.size <= QR.max_size) + (file2.type === 'image/png');
+ if (score2 > score) {
+ file = file2;
+ score = score2;
+ }
+ }
+ if (file) {
+ type = file.type;
+ blob = new Blob([file], {
+ type: type
+ });
+ blob.name = Conf['pastedname'] + "." + ($.getOwn(QR.extensionFromType, type) || 'jpg');
+ QR.open();
+ QR.handleFiles([blob]);
+ $.addClass(QR.nodes.el, 'dump');
+ }
+ },
+ pasteFF: function() {
+ var arr, blob, bstr, i, images, img, j, k, len, m, pasteArea, ref, src;
+ pasteArea = QR.nodes.pasteArea;
+ if (!pasteArea.childNodes.length) {
+ return;
+ }
+ images = $$('img', pasteArea);
+ $.rmAll(pasteArea);
+ for (j = 0, len = images.length; j < len; j++) {
+ img = images[j];
+ src = img.src;
+ if (m = src.match(/data:(image\/(\w+));base64,(.+)/)) {
+ bstr = atob(m[3]);
+ arr = new Uint8Array(bstr.length);
+ for (i = k = 0, ref = bstr.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
+ arr[i] = bstr.charCodeAt(i);
+ }
+ blob = new Blob([arr], {
+ type: m[1]
+ });
+ blob.name = Conf['pastedname'] + "." + m[2];
+ QR.handleFiles([blob]);
+ } else if (/^https?:\/\//.test(src)) {
+ QR.handleUrl(src);
+ }
+ }
+ },
+ handleUrl: function(urlDefault) {
+ QR.open();
+ QR.selected.preventAutoPost();
+ return CrossOrigin.permission(function() {
+ var url;
+ url = prompt('Enter a URL:', urlDefault);
+ if (url === null) {
+ return;
+ }
+ QR.nodes.fileButton.focus();
+ return CrossOrigin.file(url, function(blob) {
+ if (blob && !/^text\//.test(blob.type)) {
+ return QR.handleFiles([blob]);
+ } else {
+ return QR.error("Can't load file.");
+ }
+ });
+ });
+ },
+ handleFiles: function(files) {
+ var file, j, len;
+ if (this !== QR) {
+ files = slice.call(this.files);
+ this.value = null;
+ }
+ if (!files.length) {
+ return;
+ }
+ QR.cleanNotifications();
+ for (j = 0, len = files.length; j < len; j++) {
+ file = files[j];
+ QR.handleFile(file, files.length);
+ }
+ if (files.length !== 1) {
+ $.addClass(QR.nodes.el, 'dump');
+ }
+ if (d.activeElement === QR.nodes.fileButton && $.hasClass(QR.nodes.fileSubmit, 'has-file')) {
+ return QR.nodes.filename.focus();
+ }
+ },
+ handleFile: function(file, nfiles) {
+ var isText, post;
+ isText = /^text\//.test(file.type);
+ if (nfiles === 1) {
+ post = QR.selected;
+ } else {
+ post = QR.posts[QR.posts.length - 1];
+ if ((isText ? post.com || post.pasting : post.file)) {
+ post = new QR.post();
+ }
+ }
+ return post[isText ? 'pasteText' : 'setFile'](file);
+ },
+ openFileInput: function() {
+ if (QR.nodes.fileButton.disabled) {
+ return;
+ }
+ QR.nodes.fileInput.click();
+ return QR.nodes.fileButton.focus();
+ },
+ generatePostableThreadsList: function() {
+ var j, len, list, options, ref, thread, val;
+ if (!QR.nodes) {
+ return;
+ }
+ list = QR.nodes.thread;
+ options = [list.firstElementChild];
+ ref = g.BOARD.threads.keys;
+ for (j = 0, len = ref.length; j < len; j++) {
+ thread = ref[j];
+ options.push($.el('option', {
+ value: thread,
+ textContent: "Thread " + thread
+ }));
+ }
+ val = list.value;
+ $.rmAll(list);
+ $.add(list, options);
+ list.value = val;
+ if (list.value === val) {
+ return;
+ }
+ list.value = g.VIEW === 'thread' ? g.THREADID : 'new';
+ return (g.VIEW === 'thread' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread');
+ },
+ dialog: function() {
+ var classList, config, dialog, event, i, items, name, node, nodes, save, setNode;
+ QR.nodes = nodes = {
+ el: dialog = UI.dialog('qr', {innerHTML: "<div class=\"move\"><label><input type=\"checkbox\" id=\"autohide\" title=\"Auto-hide\">Quick Reply</label><a href=\"javascript:;\" class=\"close\" title=\"Close\">ร—</a><select data-name=\"thread\" title=\"Create a new thread / Reply\"><option value=\"new\">New thread</option></select></div><form><div class=\"persona\"><button type=\"button\" id=\"sjis-toggle\" title=\"Toggle Mona font\">โˆ€</button><button type=\"button\" id=\"tex-preview-button\" title=\"Preview TeX\">T<sub>E</sub>X</button><input name=\"name\" data-name=\"name\" list=\"list-name\" placeholder=\"Name\" class=\"field\" size=\"1\"><input name=\"email\" data-name=\"email\" list=\"list-email\" placeholder=\"Options\" class=\"field\" size=\"1\"><input name=\"sub\" data-name=\"sub\" list=\"list-sub\" placeholder=\"Subject\" class=\"field\" size=\"1\"></div><div class=\"textarea\"><textarea data-name=\"com\" placeholder=\"Comment\" class=\"field\"></textarea><span id=\"char-count\"></span><div id=\"tex-preview\"></div></div><div id=\"dump-list-container\"><div id=\"dump-list\"></div><a id=\"add-post\" href=\"javascript:;\" title=\"Add a post\">+</a></div><div class=\"oekaki\" hidden><input type=\"button\" id=\"qr-draw-button\" value=\"Draw\"><label><span>Width:</span><input name=\"oekaki-width\" value=\"400\" type=\"number\" class=\"field\" size=\"1\"></label><label><span>Height:</span><input name=\"oekaki-height\" value=\"400\" type=\"number\" class=\"field\" size=\"1\"></label><span class=\"oekaki-bg\" title=\"Background Color\"><input name=\"oekaki-bg\" type=\"checkbox\" checked><input name=\"oekaki-bgcolor\" type=\"color\" value=\"#ffffff\"></span></div><div id=\"file-n-submit\"><input type=\"button\" id=\"qr-file-button\" value=\"Files\"><span id=\"qr-filename-container\" class=\"field\"><span id=\"qr-no-file\">No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><label id=\"qr-spoiler-label\"><input type=\"checkbox\" id=\"qr-file-spoiler\" title=\"Spoiler image\"><a class=\"checkbox-letter\">S</a></label><a id=\"qr-oekaki-button\" title=\"Edit in Tegaki\"><i class=\"fa fa-edit\"></i></a><a href=\"javascript:;\" id=\"qr-filerm\" title=\"Remove file\"><i class=\"fa fa-times-circle\"></i></a><a id=\"url-button\" title=\"Post from URL\"><i class=\"fa fa-link\"></i></a><a hidden id=\"paste-area\" title=\"Select to paste images\" class=\"fa fa-clipboard\" tabindex=\"-1\" contentEditable=\"true\"></a><a id=\"custom-cooldown-button\" title=\"Toggle custom cooldown\" class=\"disabled\"><i class=\"fa fa-clock-o\"></i></a><a id=\"dump-button\" title=\"Dump list\"><i class=\"fa fa-plus-square\"></i></a></span><input type=\"submit\"></div><select data-default=\"4\" name=\"filetag\"><option value=\"0\">Hentai</option><option value=\"6\">Porn</option><option value=\"1\">Japanese</option><option value=\"2\">Anime</option><option value=\"3\">Game</option><option value=\"5\">Loop</option><option value=\"4\" selected>Other</option></select><input type=\"file\" multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist> "})
+ };
+ setNode = function(name, query) {
+ return nodes[name] = $(query, dialog);
+ };
+ setNode('move', '.move');
+ setNode('autohide', '#autohide');
+ setNode('close', '.close');
+ setNode('thread', 'select');
+ setNode('form', 'form');
+ setNode('sjisToggle', '#sjis-toggle');
+ setNode('texButton', '#tex-preview-button');
+ setNode('name', '[data-name=name]');
+ setNode('email', '[data-name=email]');
+ setNode('sub', '[data-name=sub]');
+ setNode('com', '[data-name=com]');
+ setNode('charCount', '#char-count');
+ setNode('texPreview', '#tex-preview');
+ setNode('dumpList', '#dump-list');
+ setNode('addPost', '#add-post');
+ setNode('oekaki', '.oekaki');
+ setNode('drawButton', '#qr-draw-button');
+ setNode('fileSubmit', '#file-n-submit');
+ setNode('fileButton', '#qr-file-button');
+ setNode('noFile', '#qr-no-file');
+ setNode('filename', '#qr-filename');
+ setNode('spoiler', '#qr-file-spoiler');
+ setNode('oekakiButton', '#qr-oekaki-button');
+ setNode('fileRM', '#qr-filerm');
+ setNode('urlButton', '#url-button');
+ setNode('pasteArea', '#paste-area');
+ setNode('customCooldown', '#custom-cooldown-button');
+ setNode('dumpButton', '#dump-button');
+ setNode('status', '[type=submit]');
+ setNode('flashTag', '[name=filetag]');
+ setNode('fileInput', '[type=file]');
+ config = g.BOARD.config;
+ classList = QR.nodes.el.classList;
+ classList.toggle('forced-anon', QR.forcedAnon);
+ classList.toggle('has-spoiler', QR.spoiler);
+ classList.toggle('has-sjis', !!config.sjis_tags);
+ classList.toggle('has-math', !!config.math_tags);
+ classList.toggle('sjis-preview', !!config.sjis_tags && Conf['sjisPreview']);
+ classList.toggle('show-new-thread-option', Conf['Show New Thread Option in Threads']);
+ if (parseInt(Conf['customCooldown'], 10) > 0) {
+ $.addClass(QR.nodes.fileSubmit, 'custom-cooldown');
+ $.get('customCooldownEnabled', Conf['customCooldownEnabled'], function(arg) {
+ var customCooldownEnabled;
+ customCooldownEnabled = arg.customCooldownEnabled;
+ QR.setCustomCooldown(customCooldownEnabled);
+ return $.sync('customCooldownEnabled', QR.setCustomCooldown);
+ });
+ }
+ QR.flagsInput();
+ $.on(nodes.autohide, 'change', QR.toggleHide);
+ $.on(nodes.close, 'click', QR.close);
+ $.on(nodes.status, 'click', QR.submit);
+ $.on(nodes.form, 'submit', QR.submit);
+ $.on(nodes.sjisToggle, 'click', QR.toggleSJIS);
+ $.on(nodes.texButton, 'mousedown', QR.texPreviewShow);
+ $.on(nodes.texButton, 'mouseup', QR.texPreviewHide);
+ $.on(nodes.addPost, 'click', function() {
+ return new QR.post(true);
+ });
+ $.on(nodes.drawButton, 'click', QR.oekaki.draw);
+ $.on(nodes.fileButton, 'click', QR.openFileInput);
+ $.on(nodes.noFile, 'click', QR.openFileInput);
+ $.on(nodes.filename, 'focus', function() {
+ return $.addClass(this.parentNode, 'focus');
+ });
+ $.on(nodes.filename, 'blur', function() {
+ return $.rmClass(this.parentNode, 'focus');
+ });
+ $.on(nodes.spoiler, 'change', function() {
+ return QR.selected.nodes.spoiler.click();
+ });
+ $.on(nodes.oekakiButton, 'click', QR.oekaki.button);
+ $.on(nodes.fileRM, 'click', function() {
+ return QR.selected.rmFile();
+ });
+ $.on(nodes.urlButton, 'click', function() {
+ return QR.handleUrl('');
+ });
+ $.on(nodes.customCooldown, 'click', QR.toggleCustomCooldown);
+ $.on(nodes.dumpButton, 'click', function() {
+ return nodes.el.classList.toggle('dump');
+ });
+ $.on(nodes.fileInput, 'change', QR.handleFiles);
+ window.addEventListener('focus', QR.focus, true);
+ window.addEventListener('blur', QR.focus, true);
+ $.on(d, 'click', QR.focus);
+ if ($.engine === 'gecko' && !window.DataTransferItemList) {
+ nodes.pasteArea.hidden = false;
+ }
+ new MutationObserver(QR.pasteFF).observe(nodes.pasteArea, {
+ childList: true
+ });
+ items = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'];
+ i = 0;
+ save = function() {
+ return QR.selected.save(this);
+ };
+ while (name = items[i++]) {
+ if (!(node = nodes[name])) {
+ continue;
+ }
+ event = node.nodeName === 'SELECT' ? 'change' : 'input';
+ $.on(nodes[name], event, save);
+ }
+ if ($.engine === 'gecko' && Conf['Remember QR Size']) {
+ $.get('QR Size', '', function(item) {
+ return nodes.com.style.cssText = item['QR Size'];
+ });
+ $.on(nodes.com, 'mouseup', function(e) {
+ if (e.button !== 0) {
+ return;
+ }
+ return $.set('QR Size', this.style.cssText);
+ });
+ }
+ QR.generatePostableThreadsList();
+ QR.persona.load();
+ new QR.post(true);
+ QR.status();
+ QR.cooldown.setup();
+ QR.captcha.init();
+ $.add(d.body, dialog);
+ QR.captcha.setup();
+ QR.oekaki.setup();
+ return $.event('QRDialogCreation', null, dialog);
+ },
+ flags: function() {
+ var addFlag, ref, select, textContent, value;
+ select = $.el('select', {
+ name: 'flag',
+ className: 'flagSelector'
+ });
+ addFlag = function(value, textContent) {
+ return $.add(select, $.el('option', {
+ value: value,
+ textContent: textContent
+ }));
+ };
+ addFlag('0', (g.BOARD.config.country_flags ? 'Geographic Location' : 'None'));
+ ref = g.BOARD.config.board_flags;
+ for (value in ref) {
+ textContent = ref[value];
+ addFlag(value, textContent);
+ }
+ return select;
+ },
+ flagsInput: function() {
+ var flag, nodes;
+ nodes = QR.nodes;
+ if (!nodes) {
+ return;
+ }
+ if (nodes.flag) {
+ $.rm(nodes.flag);
+ delete nodes.flag;
+ }
+ if (g.BOARD.config.board_flags) {
+ flag = QR.flags();
+ flag.dataset.name = 'flag';
+ flag.dataset["default"] = '0';
+ nodes.flag = flag;
+ return $.add(nodes.form, flag);
+ }
+ },
+ submit: function(e) {
+ var captcha, cb, err, filetag, force, formData, options, post, ref, thread, threadID;
+ if (e != null) {
+ e.preventDefault();
+ }
+ force = e != null ? e.shiftKey : void 0;
+ if (QR.req) {
+ QR.abort();
+ return;
+ }
+ $.forceSync('cooldowns');
+ if (QR.cooldown.seconds) {
+ if (force) {
+ QR.cooldown.clear();
+ } else {
+ QR.cooldown.auto = !QR.cooldown.auto;
+ QR.status();
+ return;
+ }
+ }
+ post = QR.posts[0];
+ delete post.quotedText;
+ post.forceSave();
+ threadID = post.thread;
+ thread = g.BOARD.threads.get(threadID);
+ if (g.BOARD.ID === 'f' && threadID === 'new') {
+ filetag = QR.nodes.flashTag.value;
+ }
+ if (threadID === 'new') {
+ threadID = null;
+ if (!!g.BOARD.config.require_subject && !post.sub) {
+ err = 'New threads require a subject.';
+ } else if (!(!!g.BOARD.config.text_only || post.file)) {
+ err = 'No file selected.';
+ }
+ } else if (g.BOARD.threads.get(threadID).isClosed) {
+ err = 'You can\'t reply to this thread anymore.';
+ } else if (!(post.com || post.file)) {
+ err = 'No comment or file.';
+ } else if (post.file && thread.fileLimit) {
+ err = 'Max limit of image replies has been reached.';
+ }
+ if (g.BOARD.ID === 'r9k' && !((ref = post.com) != null ? ref.match(/[a-z-]/i) : void 0)) {
+ err || (err = 'Original comment required.');
+ }
+ if (QR.captcha.isEnabled && !(QR.captcha === Captcha.v2 && /\b_ct=/.test(d.cookie) && threadID) && !(err && !force)) {
+ captcha = QR.captcha.getOne(!!threadID);
+ if (QR.captcha === Captcha.v2) {
+ captcha || (captcha = Captcha.cache.request(!!threadID));
+ }
+ if (!captcha) {
+ err = 'No valid captcha.';
+ QR.captcha.setup(!QR.cooldown.auto || d.activeElement === QR.nodes.status);
+ }
+ }
+ QR.cleanNotifications();
+ if (err && !force) {
+ QR.cooldown.auto = false;
+ QR.status();
+ QR.error(err);
+ return;
+ }
+ QR.cooldown.auto = QR.posts.length > 1;
+ post.lock();
+ formData = {
+ resto: threadID,
+ name: !QR.forcedAnon ? post.name : void 0,
+ email: post.email,
+ sub: !(QR.forcedAnon || threadID) ? post.sub : void 0,
+ com: post.com,
+ upfile: post.file,
+ filetag: filetag,
+ spoiler: post.spoiler,
+ flag: post.flag,
+ mode: 'regist',
+ pwd: QR.persona.getPassword()
+ };
+ options = {
+ responseType: 'document',
+ withCredentials: true,
+ onloadend: QR.response,
+ form: $.formData(formData)
+ };
+ if (Conf['Show Upload Progress']) {
+ options.onprogress = function(e) {
+ var ref1;
+ if (this !== ((ref1 = QR.req) != null ? ref1.upload : void 0)) {
+ return;
+ }
+ if (e.loaded < e.total) {
+ QR.req.progress = (Math.round(e.loaded / e.total * 100)) + "%";
+ } else {
+ QR.req.isUploadFinished = true;
+ QR.req.progress = '...';
+ }
+ return QR.status();
+ };
+ }
+ cb = function(response) {
+ var key, val;
+ if (response != null) {
+ QR.currentCaptcha = response;
+ if (QR.captcha === Captcha.v2) {
+ if (response.challenge != null) {
+ options.form.append('recaptcha_challenge_field', response.challenge);
+ options.form.append('recaptcha_response_field', response.response);
+ } else {
+ options.form.append('g-recaptcha-response', response.response);
+ }
+ } else {
+ for (key in response) {
+ val = response[key];
+ options.form.append(key, val);
+ }
+ }
+ }
+ QR.req = $.ajax("https://sys." + (location.hostname.split('.')[1]) + ".org/" + g.BOARD + "/post", options);
+ return QR.req.progress = '...';
+ };
+ if (typeof captcha === 'function') {
+ QR.req = {
+ progress: '...',
+ abort: function() {
+ if (QR.captcha === Captcha.v2) {
+ Captcha.cache.abort();
+ }
+ return cb = null;
+ }
+ };
+ captcha(function(response) {
+ if (QR.captcha === Captcha.v2 && Captcha.cache.haveCookie()) {
+ if (typeof cb === "function") {
+ cb();
+ }
+ if (response) {
+ return Captcha.cache.save(response);
+ }
+ } else if (response) {
+ return typeof cb === "function" ? cb(response) : void 0;
+ } else {
+ delete QR.req;
+ post.unlock();
+ QR.cooldown.auto = !!Captcha.cache.getCount();
+ return QR.status();
+ }
+ });
+ } else {
+ cb(captcha);
+ }
+ return QR.status();
+ },
+ response: function() {
+ var URL, _, base, connErr, err, h1, isReply, j, lastPostToThread, len, m, mi, open, post, postID, postsCount, ref, ref1, ref2, ref3, seconds, threadID;
+ if (this !== QR.req) {
+ return;
+ }
+ delete QR.req;
+ post = QR.posts[0];
+ post.unlock();
+ if ((err = (ref = this.response) != null ? ref.getElementById('errmsg') : void 0)) {
+ if ((ref1 = $('a', err)) != null) {
+ ref1.target = '_blank';
+ }
+ } else if ((connErr = !this.response || this.response.title !== 'Post successful!')) {
+ err = QR.connectionError();
+ if (QR.captcha === Captcha.v2 && QR.currentCaptcha) {
+ Captcha.cache.save(QR.currentCaptcha);
+ }
+ } else if (this.status !== 200) {
+ err = "Error " + this.statusText + " (" + this.status + ")";
+ }
+ if (!connErr) {
+ if (typeof (base = QR.captcha).setUsed === "function") {
+ base.setUsed();
+ }
+ }
+ delete QR.currentCaptcha;
+ if (err) {
+ QR.errorCount = (QR.errorCount || 0) + 1;
+ if (/captcha|verification/i.test(err.textContent) || connErr) {
+ if (/mistyped/i.test(err.textContent)) {
+ err = 'You mistyped the CAPTCHA, or the CAPTCHA malfunctioned.';
+ } else if (/expired/i.test(err.textContent)) {
+ err = 'This CAPTCHA is no longer valid because it has expired.';
+ }
+ if (QR.errorCount >= 5) {
+ QR.cooldown.auto = false;
+ } else {
+ QR.cooldown.auto = QR.captcha.isEnabled || connErr;
+ QR.cooldown.addDelay(post, 2);
+ }
+ } else if (err.textContent && (m = err.textContent.match(/\d+\s+(?:minute|second)/gi)) && !/duplicate|hour/i.test(err.textContent)) {
+ QR.cooldown.auto = !/have\s+been\s+muted/i.test(err.textContent);
+ seconds = 0;
+ for (j = 0, len = m.length; j < len; j++) {
+ mi = m[j];
+ seconds += (/minute/i.test(mi) ? 60 : 1) * (+mi.match(/\d+/)[0]);
+ }
+ if (/muted/i.test(err.textContent)) {
+ QR.cooldown.addMute(seconds);
+ } else {
+ QR.cooldown.addDelay(post, seconds);
+ }
+ } else {
+ QR.cooldown.auto = false;
+ }
+ QR.captcha.setup(QR.cooldown.auto && ((ref2 = d.activeElement) === QR.nodes.status || ref2 === d.body));
+ QR.status();
+ QR.error(err);
+ return;
+ }
+ delete QR.errorCount;
+ h1 = $('h1', this.response);
+ ref3 = h1.nextSibling.textContent.match(/thread:(\d+),no:(\d+)/), _ = ref3[0], threadID = ref3[1], postID = ref3[2];
+ postID = +postID;
+ threadID = +threadID || postID;
+ isReply = threadID !== postID;
+ $.event('QRPostSuccessful', {
+ boardID: g.BOARD.ID,
+ threadID: threadID,
+ postID: postID
+ });
+ $.event('QRPostSuccessful_', {
+ boardID: g.BOARD.ID,
+ threadID: threadID,
+ postID: postID
+ });
+ postsCount = QR.posts.length - 1;
+ QR.cooldown.auto = postsCount && isReply;
+ lastPostToThread = !((function() {
+ var k, len1, p, ref4;
+ ref4 = QR.posts.slice(1);
+ for (k = 0, len1 = ref4.length; k < len1; k++) {
+ p = ref4[k];
+ if (p.thread === post.thread) {
+ return true;
+ }
+ }
+ })());
+ if (postsCount) {
+ post.rm();
+ QR.captcha.setup(d.activeElement === QR.nodes.status);
+ } else if (Conf['Persistent QR']) {
+ post.rm();
+ if (Conf['Auto Hide QR']) {
+ QR.hide();
+ } else {
+ QR.blur();
+ }
+ } else {
+ QR.close();
+ }
+ QR.cleanNotifications();
+ if (Conf['Posting Success Notifications']) {
+ QR.notifications.push(new Notice('success', h1.textContent, 5));
+ }
+ QR.cooldown.add(threadID, postID);
+ URL = threadID === postID ? window.location.origin + "/" + g.BOARD + "/thread/" + threadID : threadID !== g.THREADID && lastPostToThread && Conf['Open Post in New Tab'] ? window.location.origin + "/" + g.BOARD + "/thread/" + threadID + "#p" + postID : void 0;
+ if (URL) {
+ open = Conf['Open Post in New Tab'] || postsCount ? function() {
+ return $.open(URL);
+ } : function() {
+ return location.href = URL;
+ };
+ if (threadID === postID) {
+ QR.waitForThread(URL, open);
+ } else {
+ open();
+ }
+ }
+ return QR.status();
+ },
+ waitForThread: function(url, cb) {
+ var attempts, check;
+ attempts = 0;
+ check = function() {
+ return $.ajax(url, {
+ onloadend: function() {
+ attempts++;
+ if (attempts >= 6 || this.status === 200) {
+ return cb();
+ } else {
+ return setTimeout(check, attempts * $.SECOND);
+ }
+ },
+ responseType: 'text',
+ type: 'HEAD'
+ });
+ };
+ return check();
+ },
+ abort: function() {
+ var oldReq;
+ if ((oldReq = QR.req) && !QR.req.isUploadFinished) {
+ delete QR.req;
+ oldReq.abort();
+ if (QR.captcha === Captcha.v2 && QR.currentCaptcha) {
+ Captcha.cache.save(QR.currentCaptcha);
+ }
+ delete QR.currentCaptcha;
+ QR.posts[0].unlock();
+ QR.cooldown.auto = false;
+ QR.notifications.push(new Notice('info', 'QR upload aborted.', 5));
+ }
+ return QR.status();
+ }
+ };
+
+ return QR;
+
+}).call(this);
+
+(function() {
+ QR.cooldown = {
+ seconds: 0,
+ delays: {
+ deletion: 60
+ },
+ init: function() {
+ if (!Conf['Quick Reply']) {
+ return;
+ }
+ this.data = Conf['cooldowns'];
+ this.changes = $.dict();
+ return $.sync('cooldowns', this.sync);
+ },
+ setup: function() {
+ var delay, ref, type;
+ $.extend(QR.cooldown.delays, g.BOARD.cooldowns());
+ QR.cooldown.maxDelay = 0;
+ ref = QR.cooldown.delays;
+ for (type in ref) {
+ delay = ref[type];
+ if (type !== 'thread' && type !== 'thread_global') {
+ QR.cooldown.maxDelay = Math.max(QR.cooldown.maxDelay, delay);
+ }
+ }
+ QR.cooldown.isSetup = true;
+ return QR.cooldown.start();
+ },
+ start: function() {
+ var data;
+ data = QR.cooldown.data;
+ if (!(Conf['Cooldown'] && QR.cooldown.isSetup && !QR.cooldown.isCounting && Object.keys(data[g.BOARD.ID] || {}).length + Object.keys(data.global || {}).length > 0)) {
+ return;
+ }
+ QR.cooldown.isCounting = true;
+ return QR.cooldown.count();
+ },
+ sync: function(data) {
+ QR.cooldown.data = data || $.dict();
+ return QR.cooldown.start();
+ },
+ add: function(threadID, postID) {
+ var boardID, start;
+ if (!Conf['Cooldown']) {
+ return;
+ }
+ start = Date.now();
+ boardID = g.BOARD.ID;
+ QR.cooldown.set(boardID, start, {
+ threadID: threadID,
+ postID: postID
+ });
+ if (threadID === postID) {
+ QR.cooldown.set('global', start, {
+ boardID: boardID,
+ threadID: threadID,
+ postID: postID
+ });
+ }
+ QR.cooldown.save();
+ return QR.cooldown.start();
+ },
+ addDelay: function(post, delay) {
+ var cooldown;
+ if (!Conf['Cooldown']) {
+ return;
+ }
+ cooldown = QR.cooldown.categorize(post);
+ cooldown.delay = delay;
+ QR.cooldown.set(g.BOARD.ID, Date.now(), cooldown);
+ QR.cooldown.save();
+ return QR.cooldown.start();
+ },
+ addMute: function(delay) {
+ if (!Conf['Cooldown']) {
+ return;
+ }
+ QR.cooldown.set(g.BOARD.ID, Date.now(), {
+ type: 'mute',
+ delay: delay
+ });
+ QR.cooldown.save();
+ return QR.cooldown.start();
+ },
+ "delete": function(post) {
+ var base, cooldown, cooldowns, id, name;
+ if (!QR.cooldown.data) {
+ return;
+ }
+ cooldowns = ((base = QR.cooldown.data)[name = post.board.ID] || (base[name] = $.dict()));
+ for (id in cooldowns) {
+ cooldown = cooldowns[id];
+ if ((cooldown.delay == null) && cooldown.threadID === post.thread.ID && cooldown.postID === post.ID) {
+ QR.cooldown.set(post.board.ID, id, null);
+ }
+ }
+ return QR.cooldown.save();
+ },
+ secondsDeletion: function(post) {
+ var cooldown, cooldowns, seconds, start;
+ if (!(QR.cooldown.data && Conf['Cooldown'])) {
+ return 0;
+ }
+ cooldowns = QR.cooldown.data[post.board.ID] || $.dict();
+ for (start in cooldowns) {
+ cooldown = cooldowns[start];
+ if ((cooldown.delay == null) && cooldown.threadID === post.thread.ID && cooldown.postID === post.ID) {
+ seconds = QR.cooldown.delays.deletion - Math.floor((Date.now() - start) / $.SECOND);
+ return Math.max(seconds, 0);
+ }
+ }
+ return 0;
+ },
+ categorize: function(post) {
+ if (post.thread === 'new') {
+ return {
+ type: 'thread'
+ };
+ } else {
+ return {
+ type: !!post.file ? 'image' : 'reply',
+ threadID: +post.thread
+ };
+ }
+ },
+ mergeChange: function(data, scope, id, value) {
+ if (value) {
+ return (data[scope] || (data[scope] = $.dict()))[id] = value;
+ } else if (scope in data) {
+ delete data[scope][id];
+ if (Object.keys(data[scope]).length === 0) {
+ return delete data[scope];
+ }
+ }
+ },
+ set: function(scope, id, value) {
+ var base;
+ QR.cooldown.mergeChange(QR.cooldown.data, scope, id, value);
+ return ((base = QR.cooldown.changes)[scope] || (base[scope] = $.dict()))[id] = value;
+ },
+ save: function() {
+ var changes;
+ changes = QR.cooldown.changes;
+ if (!Object.keys(changes).length) {
+ return;
+ }
+ return $.get('cooldowns', $.dict(), function(arg) {
+ var cooldowns, id, ref, scope, value;
+ cooldowns = arg.cooldowns;
+ for (scope in QR.cooldown.changes) {
+ ref = QR.cooldown.changes[scope];
+ for (id in ref) {
+ value = ref[id];
+ QR.cooldown.mergeChange(cooldowns, scope, id, value);
+ }
+ QR.cooldown.data = cooldowns;
+ }
+ return $.set('cooldowns', cooldowns, function() {
+ return QR.cooldown.changes = $.dict();
+ });
+ });
+ },
+ clear: function() {
+ QR.cooldown.data = $.dict();
+ QR.cooldown.changes = $.dict();
+ QR.cooldown.auto = false;
+ QR.cooldown.update();
+ return $.queueTask($["delete"], 'cooldowns');
+ },
+ update: function() {
+ var base, cooldown, cooldowns, elapsed, i, len, maxDelay, nCooldowns, now, ref, ref1, save, scope, seconds, start, suffix, threadID, type, update;
+ if (!QR.cooldown.isCounting) {
+ return;
+ }
+ save = false;
+ nCooldowns = 0;
+ now = Date.now();
+ ref = QR.cooldown.categorize(QR.posts[0]), type = ref.type, threadID = ref.threadID;
+ seconds = 0;
+ if (Conf['Cooldown']) {
+ ref1 = [g.BOARD.ID, 'global'];
+ for (i = 0, len = ref1.length; i < len; i++) {
+ scope = ref1[i];
+ cooldowns = ((base = QR.cooldown.data)[scope] || (base[scope] = $.dict()));
+ for (start in cooldowns) {
+ cooldown = cooldowns[start];
+ start = +start;
+ elapsed = Math.floor((now - start) / $.SECOND);
+ if (elapsed < 0) {
+ QR.cooldown.set(scope, start, null);
+ save = true;
+ continue;
+ }
+ if (cooldown.delay != null) {
+ if (cooldown.delay <= elapsed) {
+ QR.cooldown.set(scope, start, null);
+ save = true;
+ } else if ((cooldown.type === type && cooldown.threadID === threadID) || cooldown.type === 'mute') {
+ seconds = Math.max(seconds, cooldown.delay - elapsed);
+ }
+ continue;
+ }
+ maxDelay = cooldown.threadID !== cooldown.postID ? QR.cooldown.maxDelay : QR.cooldown.delays[scope === 'global' ? 'thread_global' : 'thread'];
+ if (QR.cooldown.customCooldown) {
+ maxDelay = Math.max(maxDelay, parseInt(Conf['customCooldown'], 10));
+ }
+ if (maxDelay <= elapsed) {
+ QR.cooldown.set(scope, start, null);
+ save = true;
+ continue;
+ }
+ if ((type === 'thread') === (cooldown.threadID === cooldown.postID) && cooldown.boardID !== g.BOARD.ID) {
+ suffix = scope === 'global' ? '_global' : '';
+ seconds = Math.max(seconds, QR.cooldown.delays[type + suffix] - elapsed);
+ if (QR.cooldown.customCooldown) {
+ seconds = Math.max(seconds, parseInt(Conf['customCooldown'], 10) - elapsed);
+ }
+ }
+ }
+ nCooldowns += Object.keys(cooldowns).length;
+ }
+ }
+ if (save) {
+ QR.cooldown.save;
+ }
+ if (nCooldowns) {
+ clearTimeout(QR.cooldown.timeout);
+ QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND);
+ } else {
+ delete QR.cooldown.isCounting;
+ }
+ update = seconds !== QR.cooldown.seconds;
+ QR.cooldown.seconds = seconds;
+ if (update) {
+ return QR.status();
+ }
+ },
+ count: function() {
+ QR.cooldown.update();
+ if (QR.cooldown.seconds === 0 && QR.cooldown.auto && !QR.req) {
+ return QR.submit();
+ }
+ }
+ };
+
+}).call(this);
+
+(function() {
+ QR.oekaki = {
+ menu: {
+ init: function() {
+ var a, ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Menu'] && Conf['Edit Link'] && Conf['Quick Reply'])) {
+ return;
+ }
+ a = $.el('a', {
+ className: 'edit-link',
+ href: 'javascript:;',
+ textContent: 'Edit image'
+ });
+ $.on(a, 'click', this.editFile);
+ return Menu.menu.addEntry({
+ el: a,
+ order: 90,
+ open: function(post) {
+ var file;
+ QR.oekaki.menu.post = post;
+ file = post.file;
+ return QR.postingIsEnabled && !!file && (file.isImage || file.isVideo);
+ }
+ });
+ },
+ editFile: function() {
+ var currentTime, isVideo, post, ref;
+ post = QR.oekaki.menu.post;
+ QR.quote.call(post.nodes.post);
+ isVideo = post.file.isVideo;
+ currentTime = ((ref = post.file.fullImage) != null ? ref.currentTime : void 0) || 0;
+ return CrossOrigin.file(post.file.url, function(blob) {
+ var video;
+ if (!blob) {
+ return QR.error("Can't load file.");
+ } else if (isVideo) {
+ video = $.el('video');
+ $.on(video, 'loadedmetadata', function() {
+ $.on(video, 'seeked', function() {
+ var canvas;
+ canvas = $.el('canvas', {
+ width: video.videoWidth,
+ height: video.videoHeight
+ });
+ canvas.getContext('2d').drawImage(video, 0, 0);
+ return canvas.toBlob(function(snapshot) {
+ snapshot.name = post.file.name.replace(/\.\w+$/, '') + '.png';
+ QR.handleFiles([snapshot]);
+ return QR.oekaki.edit();
+ });
+ });
+ return video.currentTime = currentTime;
+ });
+ $.on(video, 'error', function() {
+ return QR.openError();
+ });
+ return video.src = URL.createObjectURL(blob);
+ } else {
+ blob.name = post.file.name;
+ QR.handleFiles([blob]);
+ return QR.oekaki.edit();
+ }
+ });
+ }
+ },
+ setup: function() {
+ return $.global(function() {
+ var FCX;
+ FCX = window.FCX;
+ FCX.oekakiCB = function() {
+ return window.Tegaki.flatten().toBlob(function(file) {
+ var source;
+ source = "oekaki-" + (Date.now());
+ FCX.oekakiLatest = source;
+ return document.dispatchEvent(new CustomEvent('QRSetFile', {
+ bubbles: true,
+ detail: {
+ file: file,
+ name: FCX.oekakiName,
+ source: source
+ }
+ }));
+ });
+ };
+ if (window.Tegaki) {
+ return document.querySelector('#qr .oekaki').hidden = false;
+ }
+ });
+ },
+ load: function(cb) {
+ var n, onload, script, style;
+ if ($('script[src^="//s.4cdn.org/js/tegaki"]', d.head)) {
+ return cb();
+ } else {
+ style = $.el('link', {
+ rel: 'stylesheet',
+ href: "//s.4cdn.org/css/tegaki." + (Date.now()) + ".css"
+ });
+ script = $.el('script', {
+ src: "//s.4cdn.org/js/tegaki.min." + (Date.now()) + ".js"
+ });
+ n = 0;
+ onload = function() {
+ if (++n === 2) {
+ return cb();
+ }
+ };
+ $.on(style, 'load', onload);
+ $.on(script, 'load', onload);
+ return $.add(d.head, [style, script]);
+ }
+ },
+ draw: function() {
+ return $.global(function() {
+ var FCX, Tegaki;
+ Tegaki = window.Tegaki, FCX = window.FCX;
+ if (Tegaki.bg) {
+ Tegaki.destroy();
+ }
+ FCX.oekakiName = 'tegaki.png';
+ return Tegaki.open({
+ onDone: FCX.oekakiCB,
+ onCancel: function() {
+ return Tegaki.bgColor = '#ffffff';
+ },
+ width: +document.querySelector('#qr [name=oekaki-width]').value,
+ height: +document.querySelector('#qr [name=oekaki-height]').value,
+ bgColor: document.querySelector('#qr [name=oekaki-bg]').checked ? document.querySelector('#qr [name=oekaki-bgcolor]').value : 'transparent'
+ });
+ });
+ },
+ button: function() {
+ if (QR.selected.file) {
+ return QR.oekaki.edit();
+ } else {
+ return QR.oekaki.toggle();
+ }
+ },
+ edit: function() {
+ return QR.oekaki.load(function() {
+ return $.global(function() {
+ var FCX, Tegaki, cb, error, name, source;
+ Tegaki = window.Tegaki, FCX = window.FCX;
+ name = document.getElementById('qr-filename').value.replace(/\.\w+$/, '') + '.png';
+ source = document.getElementById('file-n-submit').dataset.source;
+ error = function(content) {
+ return document.dispatchEvent(new CustomEvent('CreateNotification', {
+ bubbles: true,
+ detail: {
+ type: 'warning',
+ content: content,
+ lifetime: 20
+ }
+ }));
+ };
+ cb = function(e) {
+ var canvas, selected;
+ if (e) {
+ this.removeEventListener('QRMetadata', cb, false);
+ }
+ selected = document.getElementById('selected');
+ if (!(selected != null ? selected.dataset.type : void 0)) {
+ return error('No file to edit.');
+ }
+ if (!/^(image|video)\//.test(selected.dataset.type)) {
+ return error('Not an image.');
+ }
+ if (!selected.dataset.height) {
+ return error('Metadata not available.');
+ }
+ if (selected.dataset.height === 'loading') {
+ selected.addEventListener('QRMetadata', cb, false);
+ return;
+ }
+ if (Tegaki.bg) {
+ Tegaki.destroy();
+ }
+ FCX.oekakiName = name;
+ Tegaki.open({
+ onDone: FCX.oekakiCB,
+ onCancel: function() {
+ return Tegaki.bgColor = '#ffffff';
+ },
+ width: +selected.dataset.width,
+ height: +selected.dataset.height,
+ bgColor: 'transparent'
+ });
+ canvas = document.createElement('canvas');
+ canvas.width = canvas.naturalWidth = +selected.dataset.width;
+ canvas.height = canvas.naturalHeight = +selected.dataset.height;
+ canvas.hidden = true;
+ document.body.appendChild(canvas);
+ canvas.addEventListener('QRImageDrawn', function() {
+ this.remove();
+ return Tegaki.onOpenImageLoaded.call(this);
+ }, false);
+ return canvas.dispatchEvent(new CustomEvent('QRDrawFile', {
+ bubbles: true
+ }));
+ };
+ if (Tegaki.bg && Tegaki.onDoneCb === FCX.oekakiCB && source === FCX.oekakiLatest) {
+ FCX.oekakiName = name;
+ return Tegaki.resume();
+ } else {
+ return cb();
+ }
+ });
+ });
+ },
+ toggle: function() {
+ return QR.oekaki.load(function() {
+ return QR.nodes.oekaki.hidden = !QR.nodes.oekaki.hidden;
+ });
+ }
+ };
+
+}).call(this);
+
+(function() {
+ var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ QR.persona = {
+ always: {},
+ types: {
+ name: [],
+ email: [],
+ sub: []
+ },
+ init: function() {
+ var i, item, len, ref;
+ if (!(Conf['Quick Reply'] || (Conf['Menu'] && Conf['Delete Link']))) {
+ return;
+ }
+ ref = Conf['QR.personas'].split('\n');
+ for (i = 0, len = ref.length; i < len; i++) {
+ item = ref[i];
+ QR.persona.parseItem(item.trim());
+ }
+ },
+ parseItem: function(item) {
+ var boards, match, ref, ref1, ref2, type, val;
+ if (item[0] === '#') {
+ return;
+ }
+ if (!(match = item.match(/(name|options|email|subject|password):"(.*)"/i))) {
+ return;
+ }
+ ref = match, match = ref[0], type = ref[1], val = ref[2];
+ item = item.replace(match, '');
+ boards = ((ref1 = item.match(/boards:([^;]+)/i)) != null ? ref1[1].toLowerCase() : void 0) || 'global';
+ if (boards !== 'global' && (ref2 = g.BOARD.ID, indexOf.call(boards.split(','), ref2) < 0)) {
+ return;
+ }
+ if (type === 'password') {
+ QR.persona.pwd = val;
+ return;
+ }
+ if (type === 'options') {
+ type = 'email';
+ }
+ if (type === 'subject') {
+ type = 'sub';
+ }
+ if (/always/i.test(item)) {
+ QR.persona.always[type] = val;
+ }
+ if (indexOf.call(QR.persona.types[type], val) < 0) {
+ return QR.persona.types[type].push(val);
+ }
+ },
+ load: function() {
+ var arr, i, len, list, ref, type, val;
+ ref = QR.persona.types;
+ for (type in ref) {
+ arr = ref[type];
+ list = $("#list-" + type, QR.nodes.el);
+ for (i = 0, len = arr.length; i < len; i++) {
+ val = arr[i];
+ if (val) {
+ $.add(list, $.el('option', {
+ textContent: val
+ }));
+ }
+ }
+ }
+ },
+ getPassword: function() {
+ var m;
+ if (QR.persona.pwd != null) {
+ return QR.persona.pwd;
+ } else if ((m = d.cookie.match(/4chan_pass=([^;]+)/))) {
+ return decodeURIComponent(m[1]);
+ } else {
+ return '';
+ }
+ },
+ get: function(cb) {
+ return $.get('QR.persona', {}, function(arg) {
+ var persona;
+ persona = arg['QR.persona'];
+ return cb(persona);
+ });
+ },
+ set: function(post) {
+ return $.get('QR.persona', {}, function(arg) {
+ var persona;
+ persona = arg['QR.persona'];
+ persona = {
+ name: post.name,
+ flag: post.flag
+ };
+ return $.set('QR.persona', persona);
+ });
+ }
+ };
+
+}).call(this);
+
+(function() {
+ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
+ slice = [].slice;
+
+ QR.post = (function() {
+ function _Class(select) {
+ this.select = bind(this.select, this);
+ var el, event, i, j, label, len, len1, prev, ref, ref1;
+ el = $.el('a', {
+ className: 'qr-preview',
+ draggable: true,
+ href: 'javascript:;'
+ });
+ $.extend(el, {innerHTML: "<a class=\"remove fa fa-times-circle\" title=\"Remove\"></a><label class=\"qr-preview-spoiler\"><input type=\"checkbox\"> Spoiler</label><span></span>"});
+ this.nodes = {
+ el: el,
+ rm: el.firstChild,
+ spoiler: $('.qr-preview-spoiler input', el),
+ span: el.lastChild
+ };
+ $.on(el, 'click', this.select);
+ $.on(this.nodes.rm, 'click', (function(_this) {
+ return function(e) {
+ e.stopPropagation();
+ return _this.rm();
+ };
+ })(this));
+ $.on(this.nodes.spoiler, 'change', (function(_this) {
+ return function(e) {
+ _this.spoiler = e.target.checked;
+ if (_this === QR.selected) {
+ QR.nodes.spoiler.checked = _this.spoiler;
+ }
+ return _this.preventAutoPost();
+ };
+ })(this));
+ ref = $$('label', el);
+ for (i = 0, len = ref.length; i < len; i++) {
+ label = ref[i];
+ $.on(label, 'click', function(e) {
+ return e.stopPropagation();
+ });
+ }
+ $.add(QR.nodes.dumpList, el);
+ ref1 = ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'];
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ event = ref1[j];
+ $.on(el, event.toLowerCase(), this[event]);
+ }
+ this.thread = g.VIEW === 'thread' ? g.THREADID : 'new';
+ prev = QR.posts[QR.posts.length - 1];
+ QR.posts.push(this);
+ this.nodes.spoiler.checked = this.spoiler = prev && Conf['Remember Spoiler'] ? prev.spoiler : false;
+ QR.persona.get((function(_this) {
+ return function(persona) {
+ _this.name = 'name' in QR.persona.always ? QR.persona.always.name : prev ? prev.name : persona.name;
+ _this.email = 'email' in QR.persona.always ? QR.persona.always.email : '';
+ _this.sub = 'sub' in QR.persona.always ? QR.persona.always.sub : '';
+ if (QR.nodes.flag) {
+ _this.flag = prev ? prev.flag : persona.flag && persona.flag in g.BOARD.config.board_flags ? persona.flag : void 0;
+ }
+ if (QR.selected === _this) {
+ return _this.load();
+ }
+ };
+ })(this));
+ if (select) {
+ this.select();
+ }
+ this.unlock();
+ QR.captcha.moreNeeded();
+ }
+
+ _Class.prototype.rm = function() {
+ var base, index;
+ this["delete"]();
+ index = QR.posts.indexOf(this);
+ if (QR.posts.length === 1) {
+ new QR.post(true);
+ $.rmClass(QR.nodes.el, 'dump');
+ } else if (this === QR.selected) {
+ (QR.posts[index - 1] || QR.posts[index + 1]).select();
+ }
+ QR.posts.splice(index, 1);
+ QR.status();
+ return typeof (base = QR.captcha).updateThread === "function" ? base.updateThread() : void 0;
+ };
+
+ _Class.prototype["delete"] = function() {
+ $.rm(this.nodes.el);
+ URL.revokeObjectURL(this.URL);
+ return this.dismissErrors();
+ };
+
+ _Class.prototype.lock = function(lock) {
+ var i, len, name, node, ref;
+ if (lock == null) {
+ lock = true;
+ }
+ this.isLocked = lock;
+ if (this !== QR.selected) {
+ return;
+ }
+ ref = ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ name = ref[i];
+ if (node = QR.nodes[name]) {
+ node.disabled = lock;
+ }
+ }
+ this.nodes.rm.style.visibility = lock ? 'hidden' : '';
+ this.nodes.spoiler.disabled = lock;
+ return this.nodes.el.draggable = !lock;
+ };
+
+ _Class.prototype.unlock = function() {
+ return this.lock(false);
+ };
+
+ _Class.prototype.select = function() {
+ var rectEl, rectList;
+ if (QR.selected) {
+ QR.selected.nodes.el.removeAttribute('id');
+ QR.selected.forceSave();
+ }
+ QR.selected = this;
+ this.lock(this.isLocked);
+ this.nodes.el.id = 'selected';
+ rectEl = this.nodes.el.getBoundingClientRect();
+ rectList = this.nodes.el.parentNode.getBoundingClientRect();
+ this.nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width / 2 - rectList.left - rectList.width / 2;
+ return this.load();
+ };
+
+ _Class.prototype.load = function() {
+ var i, len, name, node, ref;
+ ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ name = ref[i];
+ if (!(node = QR.nodes[name])) {
+ continue;
+ }
+ node.value = this[name] || node.dataset["default"] || '';
+ }
+ (this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread');
+ this.showFileData();
+ return QR.characterCount();
+ };
+
+ _Class.prototype.save = function(input, forced) {
+ var base, name, prev;
+ if (input.type === 'checkbox') {
+ this.spoiler = input.checked;
+ return;
+ }
+ name = input.dataset.name;
+ if (name !== 'thread' && name !== 'name' && name !== 'email' && name !== 'sub' && name !== 'com' && name !== 'filename' && name !== 'flag') {
+ return;
+ }
+ prev = this[name] || input.dataset["default"] || null;
+ this[name] = input.value || input.dataset["default"] || null;
+ switch (name) {
+ case 'thread':
+ (this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread');
+ QR.status();
+ if (typeof (base = QR.captcha).updateThread === "function") {
+ base.updateThread();
+ }
+ break;
+ case 'com':
+ this.updateComment();
+ break;
+ case 'filename':
+ if (!this.file) {
+ return;
+ }
+ this.saveFilename();
+ this.updateFilename();
+ break;
+ case 'name':
+ case 'flag':
+ if (this[name] !== prev) {
+ QR.persona.set(this);
+ }
+ }
+ if (!forced) {
+ return this.preventAutoPost();
+ }
+ };
+
+ _Class.prototype.forceSave = function() {
+ var i, len, name, node, ref;
+ if (this !== QR.selected) {
+ return;
+ }
+ ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ name = ref[i];
+ if (!(node = QR.nodes[name])) {
+ continue;
+ }
+ this.save(node, true);
+ }
+ };
+
+ _Class.prototype.preventAutoPost = function() {
+ if (QR.cooldown.auto && this === QR.posts[0]) {
+ QR.cooldown.update();
+ if (QR.cooldown.seconds <= 5) {
+ return QR.cooldown.auto = false;
+ }
+ }
+ };
+
+ _Class.prototype.setComment = function(com) {
+ this.com = com || null;
+ if (this === QR.selected) {
+ QR.nodes.com.value = this.com;
+ }
+ return this.updateComment();
+ };
+
+ _Class.prototype.updateComment = function() {
+ if (this === QR.selected) {
+ QR.characterCount();
+ }
+ this.nodes.span.textContent = this.com;
+ QR.captcha.moreNeeded();
+ if (QR.captcha === Captcha.v2) {
+ return Captcha.cache.prerequest();
+ }
+ };
+
+ _Class.prototype.isOnlyQuotes = function() {
+ return (this.com || '').trim() === (this.quotedText || '').trim();
+ };
+
+ _Class.rmErrored = function(e) {
+ var error, errors, i, j, len, post, ref;
+ e.stopPropagation();
+ ref = QR.posts;
+ for (i = ref.length - 1; i >= 0; i += -1) {
+ post = ref[i];
+ if (errors = post.errors) {
+ for (j = 0, len = errors.length; j < len; j++) {
+ error = errors[j];
+ if (!(doc.contains(error))) {
+ continue;
+ }
+ post.rm();
+ break;
+ }
+ }
+ }
+ };
+
+ _Class.prototype.error = function(className, message, link) {
+ var div, ref, rm, rmAll;
+ div = $.el('div', {
+ className: className
+ });
+ $.extend(div, {innerHTML: E(message) + ((link) ? " [<a href=\"" + E(link) + "\" target=\"_blank\">More info</a>]" : "") + "<br>[<a href=\"javascript:;\">delete post</a>] [<a href=\"javascript:;\">delete all</a>]"});
+ (this.errors || (this.errors = [])).push(div);
+ ref = $$('a', div), rm = ref[0], rmAll = ref[1];
+ $.on(div, 'click', (function(_this) {
+ return function() {
+ if (indexOf.call(QR.posts, _this) >= 0) {
+ return _this.select();
+ }
+ };
+ })(this));
+ $.on(rm, 'click', (function(_this) {
+ return function(e) {
+ e.stopPropagation();
+ if (indexOf.call(QR.posts, _this) >= 0) {
+ return _this.rm();
+ }
+ };
+ })(this));
+ $.on(rmAll, 'click', QR.post.rmErrored);
+ return QR.error(div, true);
+ };
+
+ _Class.prototype.fileError = function(message, link) {
+ return this.error('file-error', this.filename + ": " + message, link);
+ };
+
+ _Class.prototype.dismissErrors = function(test) {
+ var error, i, len, ref;
+ if (test == null) {
+ test = function() {
+ return true;
+ };
+ }
+ if (this.errors) {
+ ref = this.errors;
+ for (i = 0, len = ref.length; i < len; i++) {
+ error = ref[i];
+ if (doc.contains(error) && test(error)) {
+ error.parentNode.previousElementSibling.click();
+ }
+ }
+ }
+ };
+
+ _Class.prototype.setFile = function(file1) {
+ var ext, ref;
+ this.file = file1;
+ if (Conf['Randomize Filename'] && g.BOARD.ID !== 'f') {
+ this.filename = "" + (Date.now() - Math.floor(Math.random() * 365 * $.DAY));
+ if (ext = this.file.name.match(QR.validExtension)) {
+ this.filename += ext[0];
+ }
+ } else {
+ this.filename = this.file.name;
+ }
+ this.filesize = $.bytesToString(this.file.size);
+ this.checkSize();
+ $.addClass(this.nodes.el, 'has-file');
+ QR.captcha.moreNeeded();
+ URL.revokeObjectURL(this.URL);
+ this.saveFilename();
+ if (this === QR.selected) {
+ this.showFileData();
+ } else {
+ this.updateFilename();
+ }
+ this.rmMetadata();
+ this.nodes.el.dataset.type = this.file.type;
+ this.nodes.el.style.backgroundImage = '';
+ if (ref = this.file.type, indexOf.call(QR.mimeTypes, ref) < 0) {
+ this.fileError('Unsupported file type.');
+ } else if (/^(image|video)\//.test(this.file.type)) {
+ this.readFile();
+ }
+ return this.preventAutoPost();
+ };
+
+ _Class.prototype.checkSize = function() {
+ var max;
+ max = QR.max_size;
+ if (/^video\//.test(this.file.type)) {
+ max = Math.min(max, QR.max_size_video);
+ }
+ if (this.file.size > max) {
+ return this.fileError("File too large (file: " + this.filesize + ", max: " + ($.bytesToString(max)) + ").");
+ }
+ };
+
+ _Class.prototype.readFile = function() {
+ var el, event, isVideo, onerror, onload;
+ isVideo = /^video\//.test(this.file.type);
+ el = $.el(isVideo ? 'video' : 'img');
+ if (isVideo && !el.canPlayType(this.file.type)) {
+ return;
+ }
+ event = isVideo ? 'loadeddata' : 'load';
+ onload = (function(_this) {
+ return function() {
+ $.off(el, event, onload);
+ $.off(el, 'error', onerror);
+ _this.checkDimensions(el);
+ _this.setThumbnail(el);
+ return $.event('QRMetadata', null, _this.nodes.el);
+ };
+ })(this);
+ onerror = (function(_this) {
+ return function() {
+ $.off(el, event, onload);
+ $.off(el, 'error', onerror);
+ _this.fileError("Corrupt " + (isVideo ? 'video' : 'image') + " or error reading metadata.", 'https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions#error-reading-metadata');
+ URL.revokeObjectURL(el.src);
+ _this.nodes.el.removeAttribute('data-height');
+ return $.event('QRMetadata', null, _this.nodes.el);
+ };
+ })(this);
+ this.nodes.el.dataset.height = 'loading';
+ $.on(el, event, onload);
+ $.on(el, 'error', onerror);
+ return el.src = URL.createObjectURL(this.file);
+ };
+
+ _Class.prototype.checkDimensions = function(el) {
+ var duration, height, max_height, max_width, videoHeight, videoWidth, width;
+ if (el.tagName === 'IMG') {
+ height = el.height, width = el.width;
+ this.nodes.el.dataset.height = height;
+ this.nodes.el.dataset.width = width;
+ if (height > QR.max_height || width > QR.max_width) {
+ this.fileError("Image too large (image: " + height + "x" + width + "px, max: " + QR.max_height + "x" + QR.max_width + "px)");
+ }
+ if (height < QR.min_height || width < QR.min_width) {
+ return this.fileError("Image too small (image: " + height + "x" + width + "px, min: " + QR.min_height + "x" + QR.min_width + "px)");
+ }
+ } else {
+ videoHeight = el.videoHeight, videoWidth = el.videoWidth, duration = el.duration;
+ this.nodes.el.dataset.height = videoHeight;
+ this.nodes.el.dataset.width = videoWidth;
+ this.nodes.el.dataset.duration = duration;
+ max_height = Math.min(QR.max_height, QR.max_height_video);
+ max_width = Math.min(QR.max_width, QR.max_width_video);
+ if (videoHeight > max_height || videoWidth > max_width) {
+ this.fileError("Video too large (video: " + videoHeight + "x" + videoWidth + "px, max: " + max_height + "x" + max_width + "px)");
+ }
+ if (videoHeight < QR.min_height || videoWidth < QR.min_width) {
+ this.fileError("Video too small (video: " + videoHeight + "x" + videoWidth + "px, min: " + QR.min_height + "x" + QR.min_width + "px)");
+ }
+ if (!isFinite(duration)) {
+ this.fileError('Video lacks duration metadata (try remuxing)');
+ } else if (duration > QR.max_duration_video) {
+ this.fileError("Video too long (video: " + duration + "s, max: " + QR.max_duration_video + "s)");
+ }
+ if (BoardConfig.noAudio(g.BOARD.ID) && $.hasAudio(el)) {
+ return this.fileError('Audio not allowed');
+ }
+ }
+ };
+
+ _Class.prototype.setThumbnail = function(el) {
+ var cv, height, isVideo, s, width;
+ isVideo = el.tagName === 'VIDEO';
+ s = 90 * 2 * window.devicePixelRatio;
+ if (this.file.type === 'image/gif') {
+ s *= 3;
+ }
+ if (isVideo) {
+ height = el.videoHeight;
+ width = el.videoWidth;
+ } else {
+ height = el.height, width = el.width;
+ if (height < s || width < s) {
+ this.URL = el.src;
+ this.nodes.el.style.backgroundImage = "url(" + this.URL + ")";
+ return;
+ }
+ }
+ if (height <= width) {
+ width = s / height * width;
+ height = s;
+ } else {
+ height = s / width * height;
+ width = s;
+ }
+ cv = $.el('canvas');
+ cv.height = height;
+ cv.width = width;
+ cv.getContext('2d').drawImage(el, 0, 0, width, height);
+ URL.revokeObjectURL(el.src);
+ return cv.toBlob((function(_this) {
+ return function(blob) {
+ _this.URL = URL.createObjectURL(blob);
+ return _this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")";
+ };
+ })(this));
+ };
+
+ _Class.prototype.rmFile = function() {
+ if (this.isLocked) {
+ return;
+ }
+ delete this.file;
+ delete this.filename;
+ delete this.filesize;
+ this.nodes.el.removeAttribute('title');
+ QR.nodes.filename.removeAttribute('title');
+ this.rmMetadata();
+ this.nodes.el.style.backgroundImage = '';
+ $.rmClass(this.nodes.el, 'has-file');
+ this.showFileData();
+ URL.revokeObjectURL(this.URL);
+ this.dismissErrors(function(error) {
+ return $.hasClass(error, 'file-error');
+ });
+ return this.preventAutoPost();
+ };
+
+ _Class.prototype.rmMetadata = function() {
+ var attr, i, len, ref;
+ ref = ['type', 'height', 'width', 'duration'];
+ for (i = 0, len = ref.length; i < len; i++) {
+ attr = ref[i];
+ this.nodes.el.removeAttribute("data-" + attr);
+ }
+ };
+
+ _Class.prototype.saveFilename = function() {
+ this.file.newName = (this.filename || '').replace(/[\/\\]/g, '-');
+ if (!QR.validExtension.test(this.filename)) {
+ return this.file.newName += "." + ($.getOwn(QR.extensionFromType, this.file.type) || 'jpg');
+ }
+ };
+
+ _Class.prototype.updateFilename = function() {
+ var long;
+ long = this.filename + " (" + this.filesize + ")";
+ this.nodes.el.title = long;
+ if (this !== QR.selected) {
+ return;
+ }
+ return QR.nodes.filename.title = long;
+ };
+
+ _Class.prototype.showFileData = function() {
+ var ref;
+ if (this.file) {
+ this.updateFilename();
+ QR.nodes.filename.value = this.filename;
+ $.addClass(QR.nodes.oekaki, 'has-file');
+ $.addClass(QR.nodes.fileSubmit, 'has-file');
+ } else {
+ $.rmClass(QR.nodes.oekaki, 'has-file');
+ $.rmClass(QR.nodes.fileSubmit, 'has-file');
+ }
+ if (((ref = this.file) != null ? ref.source : void 0) != null) {
+ QR.nodes.fileSubmit.dataset.source = this.file.source;
+ } else {
+ QR.nodes.fileSubmit.removeAttribute('data-source');
+ }
+ return QR.nodes.spoiler.checked = this.spoiler;
+ };
+
+ _Class.prototype.pasteText = function(file) {
+ var reader;
+ this.pasting = true;
+ this.preventAutoPost();
+ reader = new FileReader();
+ reader.onload = (function(_this) {
+ return function(e) {
+ var result;
+ result = e.target.result;
+ _this.setComment((_this.com ? _this.com + "\n" + result : result));
+ return delete _this.pasting;
+ };
+ })(this);
+ return reader.readAsText(file);
+ };
+
+ _Class.prototype.dragStart = function(e) {
+ var left, ref, top;
+ ref = this.getBoundingClientRect(), left = ref.left, top = ref.top;
+ e.dataTransfer.setDragImage(this, e.clientX - left, e.clientY - top);
+ return $.addClass(this, 'drag');
+ };
+
+ _Class.prototype.dragEnd = function() {
+ return $.rmClass(this, 'drag');
+ };
+
+ _Class.prototype.dragEnter = function() {
+ return $.addClass(this, 'over');
+ };
+
+ _Class.prototype.dragLeave = function() {
+ return $.rmClass(this, 'over');
+ };
+
+ _Class.prototype.dragOver = function(e) {
+ e.preventDefault();
+ return e.dataTransfer.dropEffect = 'move';
+ };
+
+ _Class.prototype.drop = function() {
+ var base, el, index, newIndex, oldIndex, post;
+ $.rmClass(this, 'over');
+ if (!this.draggable) {
+ return;
+ }
+ el = $('.drag', this.parentNode);
+ index = function(el) {
+ return slice.call(el.parentNode.children).indexOf(el);
+ };
+ oldIndex = index(el);
+ newIndex = index(this);
+ if (QR.posts[oldIndex].isLocked || QR.posts[newIndex].isLocked) {
+ return;
+ }
+ (oldIndex < newIndex ? $.after : $.before)(this, el);
+ post = QR.posts.splice(oldIndex, 1)[0];
+ QR.posts.splice(newIndex, 0, post);
+ QR.status();
+ return typeof (base = QR.captcha).updateThread === "function" ? base.updateThread() : void 0;
+ };
+
+ return _Class;
+
+ })();
+
+}).call(this);
+
+QuoteBacklink = (function() {
+ var QuoteBacklink;
+
+ QuoteBacklink = {
+ containers: $.dict(),
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread') || !Conf['Quote Backlinks']) {
+ return;
+ }
+ if ((this.bottomBacklinks = Conf['Bottom Backlinks'])) {
+ $.addClass(doc, 'bottom-backlinks');
+ }
+ Callbacks.Post.push({
+ name: 'Quote Backlinking Part 1',
+ cb: this.firstNode
+ });
+ return Callbacks.Post.push({
+ name: 'Quote Backlinking Part 2',
+ cb: this.secondNode
+ });
+ },
+ firstNode: function() {
+ var a, clone, container, containers, hash, i, j, k, len, len1, len2, link, markYours, nodes, post, quote, ref, ref1;
+ if (this.isClone || !this.quotes.length || this.isRebuilt) {
+ return;
+ }
+ markYours = Conf['Mark Quotes of You'] && QuoteYou.isYou(this);
+ a = $.el('a', {
+ href: g.SITE.Build.postURL(this.board.ID, this.thread.ID, this.ID),
+ className: this.isHidden ? 'filtered backlink' : 'backlink',
+ textContent: Conf['backlink'].replace(/%(?:id|%)/g, (function(_this) {
+ return function(x) {
+ return {
+ '%id': _this.ID,
+ '%%': '%'
+ }[x];
+ };
+ })(this))
+ });
+ if (markYours) {
+ $.add(a, QuoteYou.mark.cloneNode(true));
+ }
+ ref = this.quotes;
+ for (i = 0, len = ref.length; i < len; i++) {
+ quote = ref[i];
+ containers = [QuoteBacklink.getContainer(quote)];
+ if ((post = g.posts.get(quote)) && post.nodes.backlinkContainer) {
+ ref1 = post.clones;
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ clone = ref1[j];
+ containers.push(clone.nodes.backlinkContainer);
+ }
+ }
+ for (k = 0, len2 = containers.length; k < len2; k++) {
+ container = containers[k];
+ link = a.cloneNode(true);
+ nodes = container.firstChild ? [$.tn(' '), link] : [link];
+ if (Conf['Quote Previewing']) {
+ $.on(link, 'mouseover', QuotePreview.mouseover);
+ }
+ if (Conf['Quote Inlining']) {
+ $.on(link, 'click', QuoteInline.toggle);
+ if (Conf['Quote Hash Navigation']) {
+ hash = QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'));
+ nodes.push(hash);
+ }
+ }
+ $.add(container, nodes);
+ }
+ }
+ },
+ secondNode: function() {
+ var container;
+ if (this.isClone && (this.origin.isReply || Conf['OP Backlinks'])) {
+ this.nodes.backlinkContainer = $('.container', this.nodes.post);
+ return;
+ }
+ if (!(this.isReply || Conf['OP Backlinks'])) {
+ return;
+ }
+ container = QuoteBacklink.getContainer(this.fullID);
+ this.nodes.backlinkContainer = container;
+ if (QuoteBacklink.bottomBacklinks) {
+ return $.add(this.nodes.post, container);
+ } else {
+ return $.add(this.nodes.info, container);
+ }
+ },
+ getContainer: function(id) {
+ var base;
+ return (base = this.containers)[id] || (base[id] = $.el('span', {
+ className: 'container'
+ }));
+ }
+ };
+
+ return QuoteBacklink;
+
+}).call(this);
+
+QuoteCT = (function() {
+ var QuoteCT;
+
+ QuoteCT = {
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread') || !Conf['Mark Cross-thread Quotes']) {
+ return;
+ }
+ if (Conf['Comment Expansion']) {
+ ExpandComment.callbacks.push(this.node);
+ }
+ this.mark = $.el('span', {
+ textContent: '\u00A0(Cross-thread)',
+ className: 'qmark-ct'
+ });
+ return Callbacks.Post.push({
+ name: 'Mark Cross-thread Quotes',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var board, boardID, i, len, quotelink, ref, ref1, ref2, thread, threadID;
+ if (this.isClone && this.thread === this.context.thread) {
+ return;
+ }
+ ref = this.context, board = ref.board, thread = ref.thread;
+ ref1 = this.nodes.quotelinks;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ quotelink = ref1[i];
+ ref2 = Get.postDataFromLink(quotelink), boardID = ref2.boardID, threadID = ref2.threadID;
+ if (!threadID) {
+ continue;
+ }
+ if (this.isClone) {
+ $.rm($('.qmark-ct', quotelink));
+ }
+ if (boardID === board.ID && threadID !== thread.ID) {
+ $.add(quotelink, QuoteCT.mark.cloneNode(true));
+ }
+ }
+ }
+ };
+
+ return QuoteCT;
+
+}).call(this);
+
+QuoteInline = (function() {
+ var QuoteInline,
+ slice = [].slice;
+
+ QuoteInline = {
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread') || !Conf['Quote Inlining']) {
+ return;
+ }
+ if (Conf['Comment Expansion']) {
+ ExpandComment.callbacks.push(this.node);
+ }
+ return Callbacks.Post.push({
+ name: 'Quote Inlining',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var i, isClone, len, link, process, ref;
+ process = QuoteInline.process;
+ isClone = this.isClone;
+ ref = this.nodes.quotelinks.concat(slice.call(this.nodes.backlinks), this.nodes.archivelinks);
+ for (i = 0, len = ref.length; i < len; i++) {
+ link = ref[i];
+ process(link, isClone);
+ }
+ },
+ process: function(link, clone) {
+ if (Conf['Quote Hash Navigation']) {
+ if (!clone) {
+ $.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
+ }
+ }
+ return $.on(link, 'click', QuoteInline.toggle);
+ },
+ qiQuote: function(link, hidden) {
+ var name;
+ name = "hashlink";
+ if (hidden) {
+ name += " filtered";
+ }
+ return $.el('a', {
+ className: name,
+ textContent: '#',
+ href: link.href
+ });
+ },
+ toggle: function(e) {
+ var boardID, context, postID, quoter, ref, ref1, threadID;
+ if ($.modifiedClick(e)) {
+ return;
+ }
+ ref = Get.postDataFromLink(this), boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID;
+ if (Conf['Inline Cross-thread Quotes Only'] && g.VIEW === 'thread' && ((ref1 = g.posts.get(boardID + "." + postID)) != null ? ref1.nodes.root.offsetParent : void 0)) {
+ return;
+ }
+ if ($.hasClass(doc, 'catalog-mode')) {
+ return;
+ }
+ e.preventDefault();
+ quoter = Get.postFromNode(this);
+ context = quoter.context;
+ if ($.hasClass(this, 'inlined')) {
+ QuoteInline.rm(this, boardID, threadID, postID, context);
+ } else {
+ if ($.x("ancestor::div[@data-full-i-d='" + boardID + "." + postID + "']", this)) {
+ return;
+ }
+ QuoteInline.add(this, boardID, threadID, postID, context, quoter);
+ }
+ return this.classList.toggle('inlined');
+ },
+ findRoot: function(quotelink, isBacklink) {
+ if (isBacklink) {
+ return $.x('ancestor::*[parent::*[contains(@class,"post")]][1]', quotelink);
+ } else {
+ return $.x('ancestor-or-self::*[parent::blockquote][1]', quotelink);
+ }
+ },
+ add: function(quotelink, boardID, threadID, postID, context, quoter) {
+ var inline, isBacklink, post, qroot, root;
+ isBacklink = $.hasClass(quotelink, 'backlink');
+ inline = $.el('div', {
+ className: 'inline'
+ });
+ inline.dataset.fullID = boardID + "." + postID;
+ root = QuoteInline.findRoot(quotelink, isBacklink);
+ $.after(root, inline);
+ qroot = $.x('ancestor::*[contains(@class,"postContainer")][1]', root);
+ $.addClass(qroot, 'hasInline');
+ new Fetcher(boardID, threadID, postID, inline, quoter);
+ if (!((post = g.posts.get(boardID + "." + postID)) && context.thread === post.thread)) {
+ return;
+ }
+ if (isBacklink && Conf['Forward Hiding']) {
+ $.addClass(post.nodes.root, 'forwarded');
+ post.forwarded++ || (post.forwarded = 1);
+ }
+ if (!Unread.posts) {
+ return;
+ }
+ return Unread.readSinglePost(post);
+ },
+ rm: function(quotelink, boardID, threadID, postID, context) {
+ var el, inlined, isBacklink, parentNode, post, qroot, ref, root;
+ isBacklink = $.hasClass(quotelink, 'backlink');
+ root = QuoteInline.findRoot(quotelink, isBacklink);
+ root = $.x("following-sibling::div[@data-full-i-d='" + boardID + "." + postID + "'][1]", root);
+ qroot = $.x('ancestor::*[contains(@class,"postContainer")][1]', root);
+ parentNode = root.parentNode;
+ $.rm(root);
+ $.event('PostsRemoved', null, parentNode);
+ if (!$('.inline', qroot)) {
+ $.rmClass(qroot, 'hasInline');
+ }
+ if (!(el = root.firstElementChild)) {
+ return;
+ }
+ post = g.posts.get(boardID + "." + postID);
+ post.rmClone(el.dataset.clone);
+ if (Conf['Forward Hiding'] && isBacklink && context.thread === g.threads.get(boardID + "." + threadID) && !--post.forwarded) {
+ delete post.forwarded;
+ $.rmClass(post.nodes.root, 'forwarded');
+ }
+ while (inlined = $('.inlined', el)) {
+ ref = Get.postDataFromLink(inlined), boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID;
+ QuoteInline.rm(inlined, boardID, threadID, postID, context);
+ $.rmClass(inlined, 'inlined');
+ }
+ }
+ };
+
+ return QuoteInline;
+
+}).call(this);
+
+QuoteOP = (function() {
+ var QuoteOP,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ QuoteOP = {
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread') || !Conf['Mark OP Quotes']) {
+ return;
+ }
+ if (Conf['Comment Expansion']) {
+ ExpandComment.callbacks.push(this.node);
+ }
+ this.mark = $.el('span', {
+ textContent: '\u00A0(OP)',
+ className: 'qmark-op'
+ });
+ return Callbacks.Post.push({
+ name: 'Mark OP Quotes',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var boardID, fullID, i, postID, quotelink, quotelinks, quotes, ref, ref1;
+ if (this.isClone && this.thread === this.context.thread) {
+ return;
+ }
+ if (!(quotes = this.quotes).length) {
+ return;
+ }
+ quotelinks = this.nodes.quotelinks;
+ if (this.isClone && (ref = this.thread.fullID, indexOf.call(quotes, ref) >= 0)) {
+ i = 0;
+ while (quotelink = quotelinks[i++]) {
+ $.rm($('.qmark-op', quotelink));
+ }
+ }
+ fullID = this.context.thread.fullID;
+ if (indexOf.call(quotes, fullID) < 0) {
+ return;
+ }
+ i = 0;
+ while (quotelink = quotelinks[i++]) {
+ ref1 = Get.postDataFromLink(quotelink), boardID = ref1.boardID, postID = ref1.postID;
+ if ((boardID + "." + postID) === fullID) {
+ $.add(quotelink, QuoteOP.mark.cloneNode(true));
+ }
+ }
+ }
+ };
+
+ return QuoteOP;
+
+}).call(this);
+
+QuotePreview = (function() {
+ var QuotePreview,
+ slice = [].slice;
+
+ QuotePreview = {
+ init: function() {
+ var ref;
+ if (!Conf['Quote Previewing']) {
+ return;
+ }
+ if (g.VIEW === 'archive') {
+ $.on(d, 'mouseover', function(e) {
+ if (e.target.nodeName === 'A' && $.hasClass(e.target, 'quotelink')) {
+ return QuotePreview.mouseover.call(e.target, e);
+ }
+ });
+ }
+ if ((ref = g.VIEW) !== 'index' && ref !== 'thread') {
+ return;
+ }
+ if (Conf['Comment Expansion']) {
+ ExpandComment.callbacks.push(this.node);
+ }
+ return Callbacks.Post.push({
+ name: 'Quote Previewing',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var i, len, link, ref;
+ ref = this.nodes.quotelinks.concat(slice.call(this.nodes.backlinks), this.nodes.archivelinks);
+ for (i = 0, len = ref.length; i < len; i++) {
+ link = ref[i];
+ $.on(link, 'mouseover', QuotePreview.mouseover);
+ }
+ },
+ mouseover: function(e) {
+ var boardID, i, len, origin, post, postID, posts, qp, ref, threadID;
+ if (($.hasClass(this, 'inlined') && !$.hasClass(doc, 'catalog-mode')) || !d.contains(this)) {
+ return;
+ }
+ ref = Get.postDataFromLink(this), boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID;
+ qp = $.el('div', {
+ id: 'qp',
+ className: 'dialog'
+ });
+ $.add(Header.hover, qp);
+ new Fetcher(boardID, threadID, postID, qp, Get.postFromNode(this));
+ UI.hover({
+ root: this,
+ el: qp,
+ latestEvent: e,
+ endEvents: 'mouseout click',
+ cb: QuotePreview.mouseout
+ });
+ if (Conf['Quote Highlighting'] && (origin = g.posts.get(boardID + "." + postID))) {
+ posts = [origin].concat(origin.clones);
+ posts.pop();
+ for (i = 0, len = posts.length; i < len; i++) {
+ post = posts[i];
+ $.addClass(post.nodes.post, 'qphl');
+ }
+ }
+ },
+ mouseout: function() {
+ var clone, i, len, post, ref, root;
+ if (!(root = this.el.firstElementChild)) {
+ return;
+ }
+ $.event('PostsRemoved', null, Header.hover);
+ clone = Get.postFromRoot(root);
+ post = clone.origin;
+ post.rmClone(root.dataset.clone);
+ if (!Conf['Quote Highlighting']) {
+ return;
+ }
+ ref = [post].concat(post.clones);
+ for (i = 0, len = ref.length; i < len; i++) {
+ post = ref[i];
+ $.rmClass(post.nodes.post, 'qphl');
+ }
+ }
+ };
+
+ return QuotePreview;
+
+}).call(this);
+
+QuoteStrikeThrough = (function() {
+ var QuoteStrikeThrough;
+
+ QuoteStrikeThrough = {
+ init: function() {
+ var ref;
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && (Conf['Reply Hiding Buttons'] || (Conf['Menu'] && Conf['Reply Hiding Link']) || Conf['Filter']))) {
+ return;
+ }
+ return Callbacks.Post.push({
+ name: 'Strike-through Quotes',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var boardID, i, len, postID, quotelink, ref, ref1, ref2;
+ if (this.isClone) {
+ return;
+ }
+ ref = this.nodes.quotelinks;
+ for (i = 0, len = ref.length; i < len; i++) {
+ quotelink = ref[i];
+ ref1 = Get.postDataFromLink(quotelink), boardID = ref1.boardID, postID = ref1.postID;
+ if ((ref2 = g.posts.get(boardID + "." + postID)) != null ? ref2.isHidden : void 0) {
+ $.addClass(quotelink, 'filtered');
+ }
+ }
+ }
+ };
+
+ return QuoteStrikeThrough;
+
+}).call(this);
+
+QuoteThreading =
+/*
+ <3 aeosynth
+ */
+
+(function() {
+ var QuoteThreading;
+
+ QuoteThreading = {
+ init: function() {
+ if (!(Conf['Quote Threading'] && g.VIEW === 'thread')) {
+ return;
+ }
+ this.controls = $.el('label', {innerHTML: "<input id=\"threadingControl\" name=\"Thread Quotes\" type=\"checkbox\"> Threading"});
+ this.threadNewLink = $.el('span', {
+ className: 'brackets-wrap threadnewlink',
+ hidden: true
+ });
+ $.extend(this.threadNewLink, {innerHTML: "<a href=\"javascript:;\">Thread New Posts</a>"});
+ this.input = $('input', this.controls);
+ this.input.checked = Conf['Thread Quotes'];
+ $.on(this.input, 'change', this.setEnabled);
+ $.on(this.input, 'change', this.rethread);
+ $.on(this.threadNewLink.firstElementChild, 'click', this.rethread);
+ $.on(d, '4chanXInitFinished', (function(_this) {
+ return function() {
+ return _this.ready = true;
+ };
+ })(this));
+ Header.menu.addEntry(this.entry = {
+ el: this.controls,
+ order: 99
+ });
+ Callbacks.Thread.push({
+ name: 'Quote Threading',
+ cb: this.setThread
+ });
+ return Callbacks.Post.push({
+ name: 'Quote Threading',
+ cb: this.node
+ });
+ },
+ parent: $.dict(),
+ children: $.dict(),
+ inserted: $.dict(),
+ toggleThreading: function() {
+ return this.setThreadingState(!Conf['Thread Quotes']);
+ },
+ setThreadingState: function(enabled) {
+ this.input.checked = enabled;
+ this.setEnabled.call(this.input);
+ return this.rethread.call(this.input);
+ },
+ setEnabled: function() {
+ var other, ref;
+ if (this.checked) {
+ $.set('Prune All Threads', false);
+ other = (ref = ReplyPruning.inputs) != null ? ref.enabled : void 0;
+ if (other != null ? other.checked : void 0) {
+ other.checked = false;
+ $.event('change', null, other);
+ }
+ }
+ return $.cb.checked.call(this);
+ },
+ setThread: function() {
+ QuoteThreading.thread = this;
+ return $.asap((function() {
+ return !Conf['Thread Updater'] || $('.navLinksBot > .updatelink');
+ }), function() {
+ var navLinksBot;
+ if ((navLinksBot = $('.navLinksBot'))) {
+ return $.add(navLinksBot, [$.tn(' '), QuoteThreading.threadNewLink]);
+ }
+ });
+ },
+ node: function() {
+ var ancestor, j, lastParent, len, parent, parents, quote, ref;
+ if (this.isFetchedQuote || this.isClone || !this.isReply) {
+ return;
+ }
+ parents = new Set();
+ lastParent = null;
+ ref = this.quotes;
+ for (j = 0, len = ref.length; j < len; j++) {
+ quote = ref[j];
+ if (parent = g.posts.get(quote)) {
+ if (!parent.isFetchedQuote && parent.isReply && parent.ID < this.ID) {
+ parents.add(parent.ID);
+ if (!lastParent || parent.ID > lastParent.ID) {
+ lastParent = parent;
+ }
+ }
+ }
+ }
+ if (!lastParent) {
+ return;
+ }
+ ancestor = lastParent;
+ while (ancestor = QuoteThreading.parent[ancestor.fullID]) {
+ parents["delete"](ancestor.ID);
+ }
+ if (parents.size === 1) {
+ return QuoteThreading.parent[this.fullID] = lastParent;
+ }
+ },
+ descendants: function(post) {
+ var child, children, j, len, posts;
+ posts = [post];
+ if (children = QuoteThreading.children[post.fullID]) {
+ for (j = 0, len = children.length; j < len; j++) {
+ child = children[j];
+ posts = posts.concat(QuoteThreading.descendants(child));
+ }
+ }
+ return posts;
+ },
+ insert: function(post) {
+ var base, child, children, descendants, i, j, k, l, len, name, next, nodes, order, parent, prev, prev2, threadContainer, x;
+ if (!(Conf['Thread Quotes'] && (parent = QuoteThreading.parent[post.fullID]) && !QuoteThreading.inserted[post.fullID])) {
+ return false;
+ }
+ descendants = QuoteThreading.descendants(post);
+ if (!Unread.posts.has(parent.ID)) {
+ if ((function() {
+ var j, len, x;
+ for (j = 0, len = descendants.length; j < len; j++) {
+ x = descendants[j];
+ if (Unread.posts.has(x.ID)) {
+ return true;
+ }
+ }
+ })()) {
+ QuoteThreading.threadNewLink.hidden = false;
+ return false;
+ }
+ }
+ order = Unread.order;
+ children = ((base = QuoteThreading.children)[name = parent.fullID] || (base[name] = []));
+ threadContainer = parent.nodes.threadContainer || $.el('div', {
+ className: 'threadContainer'
+ });
+ nodes = [post.nodes.root];
+ if (post.nodes.threadContainer) {
+ nodes.push(post.nodes.threadContainer);
+ }
+ i = children.length;
+ for (j = children.length - 1; j >= 0; j += -1) {
+ child = children[j];
+ if (child.ID >= post.ID) {
+ i--;
+ }
+ }
+ if (i !== children.length) {
+ next = children[i];
+ for (k = 0, len = descendants.length; k < len; k++) {
+ x = descendants[k];
+ order.before(order[next.ID], order[x.ID]);
+ }
+ children.splice(i, 0, post);
+ $.before(next.nodes.root, nodes);
+ } else {
+ prev = parent;
+ while ((prev2 = QuoteThreading.children[prev.fullID]) && prev2.length) {
+ prev = prev2[prev2.length - 1];
+ }
+ for (l = descendants.length - 1; l >= 0; l += -1) {
+ x = descendants[l];
+ order.after(order[prev.ID], order[x.ID]);
+ }
+ children.push(post);
+ $.add(threadContainer, nodes);
+ }
+ QuoteThreading.inserted[post.fullID] = true;
+ if (!parent.nodes.threadContainer) {
+ parent.nodes.threadContainer = threadContainer;
+ $.addClass(parent.nodes.root, 'threadOP');
+ $.after(parent.nodes.root, threadContainer);
+ }
+ return true;
+ },
+ rethread: function() {
+ var nodes, posts, thread;
+ if (!QuoteThreading.ready) {
+ return;
+ }
+ thread = QuoteThreading.thread;
+ posts = thread.posts;
+ QuoteThreading.threadNewLink.hidden = true;
+ if (Conf['Thread Quotes']) {
+ posts.forEach(QuoteThreading.insert);
+ } else {
+ nodes = [];
+ Unread.order = new RandomAccessList();
+ QuoteThreading.inserted = $.dict();
+ posts.forEach(function(post) {
+ if (post.isFetchedQuote) {
+ return;
+ }
+ Unread.order.push(post);
+ if (post.isReply) {
+ nodes.push(post.nodes.root);
+ }
+ if (QuoteThreading.children[post.fullID]) {
+ delete QuoteThreading.children[post.fullID];
+ $.rmClass(post.nodes.root, 'threadOP');
+ $.rm(post.nodes.threadContainer);
+ return delete post.nodes.threadContainer;
+ }
+ });
+ $.add(thread.nodes.root, nodes);
+ }
+ Unread.position = Unread.order.first;
+ Unread.updatePosition();
+ Unread.setLine(true);
+ Unread.read();
+ return Unread.update();
+ }
+ };
+
+ return QuoteThreading;
+
+}).call(this);
+
+QuoteYou = (function() {
+ var QuoteYou;
+
+ QuoteYou = {
+ init: function() {
+ var ref;
+ if (!Conf['Remember Your Posts']) {
+ return;
+ }
+ this.db = new DataBoard('yourPosts');
+ $.sync('Remember Your Posts', function(enabled) {
+ return Conf['Remember Your Posts'] = enabled;
+ });
+ $.on(d, 'QRPostSuccessful', function(e) {
+ var cb;
+ cb = PostRedirect.delay();
+ return $.get('Remember Your Posts', Conf['Remember Your Posts'], function(items) {
+ var boardID, postID, ref, threadID;
+ if (!items['Remember Your Posts']) {
+ return;
+ }
+ ref = e.detail, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID;
+ return QuoteYou.db.set({
+ boardID: boardID,
+ threadID: threadID,
+ postID: postID,
+ val: true
+ }, cb);
+ });
+ });
+ if ((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') {
+ return;
+ }
+ if (Conf['Highlight Own Posts']) {
+ $.addClass(doc, 'highlight-own');
+ }
+ if (Conf['Highlight Posts Quoting You']) {
+ $.addClass(doc, 'highlight-you');
+ }
+ if (Conf['Comment Expansion']) {
+ ExpandComment.callbacks.push(this.node);
+ }
+ this.mark = $.el('span', {
+ textContent: '\u00A0(You)',
+ className: 'qmark-you'
+ });
+ Callbacks.Post.push({
+ name: 'Mark Quotes of You',
+ cb: this.node
+ });
+ return QuoteYou.menu.init();
+ },
+ isYou: function(post) {
+ var ref;
+ return !!((ref = QuoteYou.db) != null ? ref.get({
+ boardID: post.boardID,
+ threadID: post.threadID,
+ postID: post.ID
+ }) : void 0);
+ },
+ node: function() {
+ var i, len, quotelink, ref;
+ if (this.isClone) {
+ return;
+ }
+ if (QuoteYou.isYou(this)) {
+ $.addClass(this.nodes.root, 'yourPost');
+ }
+ if (!this.quotes.length) {
+ return;
+ }
+ ref = this.nodes.quotelinks;
+ for (i = 0, len = ref.length; i < len; i++) {
+ quotelink = ref[i];
+ if (!(QuoteYou.db.get(Get.postDataFromLink(quotelink)))) {
+ continue;
+ }
+ if (Conf['Mark Quotes of You']) {
+ $.add(quotelink, QuoteYou.mark.cloneNode(true));
+ }
+ $.addClass(quotelink, 'you');
+ $.addClass(this.nodes.root, 'quotesYou');
+ }
+ },
+ menu: {
+ init: function() {
+ var input, label, ref;
+ label = $.el('label', {
+ className: 'toggle-you'
+ }, {innerHTML: "<input type=\"checkbox\"> You"});
+ input = $('input', label);
+ $.on(input, 'change', QuoteYou.menu.toggle);
+ return (ref = Menu.menu) != null ? ref.addEntry({
+ el: label,
+ order: 80,
+ open: function(post) {
+ QuoteYou.menu.post = post.origin || post;
+ input.checked = QuoteYou.isYou(post);
+ return true;
+ }
+ }) : void 0;
+ },
+ toggle: function() {
+ var clone, data, i, j, len, len1, post, quotelink, quoter, ref, ref1;
+ post = QuoteYou.menu.post;
+ data = {
+ boardID: post.board.ID,
+ threadID: post.thread.ID,
+ postID: post.ID,
+ val: true
+ };
+ if (this.checked) {
+ QuoteYou.db.set(data);
+ } else {
+ QuoteYou.db["delete"](data);
+ }
+ ref = [post].concat(post.clones);
+ for (i = 0, len = ref.length; i < len; i++) {
+ clone = ref[i];
+ clone.nodes.root.classList.toggle('yourPost', this.checked);
+ }
+ ref1 = Get.allQuotelinksLinkingTo(post);
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ quotelink = ref1[j];
+ if (this.checked) {
+ if (Conf['Mark Quotes of You']) {
+ $.add(quotelink, QuoteYou.mark.cloneNode(true));
+ }
+ } else {
+ $.rm($('.qmark-you', quotelink));
+ }
+ quotelink.classList.toggle('you', this.checked);
+ if ($.hasClass(quotelink, 'quotelink')) {
+ quoter = Get.postFromNode(quotelink).nodes.root;
+ quoter.classList.toggle('quotesYou', !!$('.quotelink.you', quoter));
+ }
+ }
+ }
+ },
+ cb: {
+ seek: function(type) {
+ var highlight, highlighted, post, posts, result, str;
+ highlight = g.SITE.classes.highlight;
+ if ((highlighted = $("." + highlight))) {
+ $.rmClass(highlighted, highlight);
+ }
+ if (!(QuoteYou.lastRead && doc.contains(QuoteYou.lastRead) && $.hasClass(QuoteYou.lastRead, 'quotesYou'))) {
+ if (!(post = QuoteYou.lastRead = $('.quotesYou'))) {
+ new Notice('warning', 'No posts are currently quoting you, loser.', 20);
+ return;
+ }
+ if (QuoteYou.cb.scroll(post)) {
+ return;
+ }
+ } else {
+ post = QuoteYou.lastRead;
+ }
+ str = type + "::div[contains(@class,'quotesYou')]";
+ while ((post = (result = $.X(str, post)).snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0))) {
+ if (QuoteYou.cb.scroll(post)) {
+ return;
+ }
+ }
+ posts = $$('.quotesYou');
+ return QuoteYou.cb.scroll(posts[type === 'following' ? 0 : posts.length - 1]);
+ },
+ scroll: function(root) {
+ var node, post, sel;
+ post = Get.postFromRoot(root);
+ if (!post.nodes.post.getBoundingClientRect().height) {
+ return false;
+ } else {
+ QuoteYou.lastRead = root;
+ location.href = Get.url('post', post);
+ Header.scrollTo(post.nodes.post);
+ if (post.isReply) {
+ sel = "" + g.SITE.selectors.postContainer + g.SITE.selectors.highlightable.reply;
+ node = post.nodes.root;
+ if (!node.matches(sel)) {
+ node = $(sel, node);
+ }
+ $.addClass(node, g.SITE.classes.highlight);
+ }
+ return true;
+ }
+ }
+ }
+ };
+
+ return QuoteYou;
+
+}).call(this);
+
+Quotify = (function() {
+ var Quotify,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
+ slice = [].slice;
+
+ Quotify = {
+ init: function() {
+ var ref;
+ if (((ref = g.VIEW) !== 'index' && ref !== 'thread') || !Conf['Resurrect Quotes']) {
+ return;
+ }
+ $.addClass(doc, 'resurrect-quotes');
+ if (Conf['Comment Expansion']) {
+ ExpandComment.callbacks.push(this.node);
+ }
+ return Callbacks.Post.push({
+ name: 'Resurrect Quotes',
+ cb: this.node
+ });
+ },
+ node: function() {
+ var deadlink, i, j, len, len1, link, ref, ref1;
+ if (this.isClone) {
+ this.nodes.archivelinks = $$('a.linkify.quotelink', this.nodes.comment);
+ return;
+ }
+ ref = $$('a.linkify', this.nodes.comment);
+ for (i = 0, len = ref.length; i < len; i++) {
+ link = ref[i];
+ Quotify.parseArchivelink.call(this, link);
+ }
+ ref1 = $$('.deadlink', this.nodes.comment);
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ deadlink = ref1[j];
+ Quotify.parseDeadlink.call(this, deadlink);
+ }
+ },
+ parseArchivelink: function(link) {
+ var boardID, m, postID, ref, threadID;
+ if (!(m = link.pathname.match(/^\/([^\/]+)\/thread\/S?(\d+)\/?$/))) {
+ return;
+ }
+ if ((ref = link.hostname) === 'boards.4chan.org' || ref === 'boards.4channel.org') {
+ return;
+ }
+ boardID = m[1];
+ threadID = m[2];
+ postID = link.hash.match(/^#[pq]?(\d+)$|$/)[1] || threadID;
+ if (Redirect.to('post', {
+ boardID: boardID,
+ postID: postID
+ })) {
+ $.addClass(link, 'quotelink');
+ $.extend(link.dataset, {
+ boardID: boardID,
+ threadID: threadID,
+ postID: postID
+ });
+ return this.nodes.archivelinks.push(link);
+ }
+ },
+ parseDeadlink: function(deadlink) {
+ var a, boardID, fetchable, m, post, postID, quote, quoteID, redirect, ref;
+ if ($.hasClass(deadlink.parentNode, 'prettyprint')) {
+ Quotify.fixDeadlink(deadlink);
+ return;
+ }
+ quote = deadlink.textContent;
+ if (!(postID = (ref = quote.match(/\d+$/)) != null ? ref[0] : void 0)) {
+ return;
+ }
+ if (postID[0] === '0') {
+ Quotify.fixDeadlink(deadlink);
+ return;
+ }
+ boardID = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : this.board.ID;
+ quoteID = boardID + "." + postID;
+ if (post = g.posts.get(quoteID)) {
+ if (!post.isDead) {
+ a = $.el('a', {
+ href: g.SITE.Build.postURL(boardID, post.thread.ID, postID),
+ className: 'quotelink',
+ textContent: quote
+ });
+ } else {
+ a = $.el('a', {
+ href: g.SITE.Build.postURL(boardID, post.thread.ID, postID),
+ className: 'quotelink deadlink',
+ textContent: quote
+ });
+ $.add(a, Post.deadMark.cloneNode(true));
+ $.extend(a.dataset, {
+ boardID: boardID,
+ threadID: post.thread.ID,
+ postID: postID
+ });
+ }
+ } else {
+ redirect = Redirect.to('thread', {
+ boardID: boardID,
+ threadID: 0,
+ postID: postID
+ });
+ fetchable = Redirect.to('post', {
+ boardID: boardID,
+ postID: postID
+ });
+ if (redirect || fetchable) {
+ a = $.el('a', {
+ href: redirect || 'javascript:;',
+ className: 'deadlink',
+ textContent: quote
+ });
+ $.add(a, Post.deadMark.cloneNode(true));
+ if (fetchable) {
+ $.addClass(a, 'quotelink');
+ $.extend(a.dataset, {
+ boardID: boardID,
+ postID: postID
+ });
+ }
+ }
+ }
+ if (indexOf.call(this.quotes, quoteID) < 0) {
+ this.quotes.push(quoteID);
+ }
+ if (!a) {
+ $.add(deadlink, Post.deadMark.cloneNode(true));
+ return;
+ }
+ $.replace(deadlink, a);
+ if ($.hasClass(a, 'quotelink')) {
+ return this.nodes.quotelinks.push(a);
+ }
+ },
+ fixDeadlink: function(deadlink) {
+ var el, green;
+ if (!(el = deadlink.previousSibling) || el.nodeName === 'BR') {
+ green = $.el('span', {
+ className: 'quote'
+ });
+ $.before(deadlink, green);
+ $.add(green, deadlink);
+ }
+ return $.replace(deadlink, slice.call(deadlink.childNodes));
+ }
+ };
+
+ return Quotify;
+
+}).call(this);
+
+Main = (function() {
+ var Main,
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Main = {
+ init: function() {
+ var db, flatten, i, items, j, k, key, len, mountedCB, ref, ref1, ref2, w;
+ try {
+ w = window;
+ if ($.platform === 'crx') {
+ w = w.wrappedJSObject || w;
+ }
+ if ('4chan X antidup' in w) {
+ return;
+ }
+ w['4chan X antidup'] = true;
+ } catch (error1) {}
+ try {
+ if (window.frameElement && ((ref = window.frameElement.src) === '' || ref === 'about:blank')) {
+ return;
+ }
+ } catch (error1) {}
+ if (doc && $.hasClass(doc, 'fourchan-x')) {
+ return;
+ }
+ $.asap(docSet, function() {
+ $.addClass(doc, 'fourchan-x', 'seaweedchan');
+ if ($.engine) {
+ return $.addClass(doc, "ua-" + $.engine);
+ }
+ });
+ $.on(d, '4chanXInitFinished', function() {
+ if (Main.expectInitFinished) {
+ return delete Main.expectInitFinished;
+ } else {
+ new Notice('error', 'Error: Multiple copies of 4chan X are enabled.');
+ return $.addClass(doc, 'tainted');
+ }
+ });
+ mountedCB = function() {
+ var cb, j, len, ref1, results;
+ d.removeEventListener('mounted', mountedCB, true);
+ Main.isMounted = true;
+ ref1 = Main.mountedCBs;
+ results = [];
+ for (j = 0, len = ref1.length; j < len; j++) {
+ cb = ref1[j];
+ try {
+ results.push(cb());
+ } catch (error1) {}
+ }
+ return results;
+ };
+ d.addEventListener('mounted', mountedCB, true);
+ flatten = function(parent, obj) {
+ var key, val;
+ if (obj instanceof Array) {
+ Conf[parent] = $.dict.clone(obj[0]);
+ } else if (typeof obj === 'object') {
+ for (key in obj) {
+ val = obj[key];
+ flatten(key, val);
+ }
+ } else {
+ Conf[parent] = obj;
+ }
+ };
+ if ((ref1 = location.hostname) === 'boards.4chan.org' || ref1 === 'boards.4channel.org') {
+ $.global(function() {
+ var fromCharCode0;
+ fromCharCode0 = String.fromCharCode;
+ return String.fromCharCode = function() {
+ if (document.body) {
+ String.fromCharCode = fromCharCode0;
+ } else if (document.currentScript && !document.currentScript.src) {
+ throw Error();
+ }
+ return fromCharCode0.apply(this, arguments);
+ };
+ });
+ $.asap(docSet, function() {
+ return $.onExists(doc, 'iframe[srcdoc]', $.rm);
+ });
+ }
+ flatten(null, Config);
+ ref2 = DataBoard.keys;
+ for (j = 0, len = ref2.length; j < len; j++) {
+ db = ref2[j];
+ Conf[db] = $.dict();
+ }
+ Conf['customTitles'] = $.dict.clone({
+ '4chan.org': {
+ boards: {
+ 'qa': {
+ 'boardTitle': {
+ orig: '/qa/ - Question & Answer',
+ title: '/qa/ - 2D/Random'
+ }
+ }
+ }
+ }
+ });
+ Conf['boardConfig'] = {
+ boards: $.dict()
+ };
+ Conf['archives'] = Redirect.archives;
+ Conf['selectedArchives'] = $.dict();
+ Conf['cooldowns'] = $.dict();
+ Conf['Index Sort'] = $.dict();
+ for (i = k = 0; k < 2; i = ++k) {
+ Conf["Last Long Reply Thresholds " + i] = $.dict();
+ }
+ Conf['siteProperties'] = $.dict();
+ Conf['Except Archives from Encryption'] = false;
+ Conf['JSON Navigation'] = true;
+ Conf['Oekaki Links'] = true;
+ Conf['Show Name and Subject'] = false;
+ Conf['QR Shortcut'] = true;
+ Conf['Bottom QR Link'] = true;
+ Conf['Toggleable Thread Watcher'] = true;
+ Conf['siteSoftware'] = '';
+ Conf['Use Faster Image Host'] = 'true';
+ Conf['Captcha Fixes'] = true;
+ Conf['captchaServiceDomain'] = '';
+ Conf['captchaServiceKey'] = $.dict();
+ if (/\.4chan(?:nel)?\.org$/.test(location.hostname) && !SW.yotsuba.regexp.pass.test(location.href) && !$$('script:not([src])', d).filter(function(s) {
+ return /this\[/.test(s.textContent);
+ }).length) {
+ ($.getSync || $.get)({
+ 'jsWhitelist': Conf['jsWhitelist']
+ }, function(arg) {
+ var jsWhitelist;
+ jsWhitelist = arg.jsWhitelist;
+ return $.addCSP("script-src " + (jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim()));
+ });
+ }
+ items = $.dict();
+ for (key in Conf) {
+ items[key] = void 0;
+ }
+ items['previousversion'] = void 0;
+ return ($.getSync || $.get)(items, function(items) {
+ var ref3;
+ if (!$.perProtocolSettings && /\.4chan(?:nel)?\.org$/.test(location.hostname) && ((ref3 = items['Redirect to HTTPS']) != null ? ref3 : Conf['Redirect to HTTPS']) && location.protocol !== 'https:') {
+ location.replace('https://' + location.host + location.pathname + location.search + location.hash);
+ return;
+ }
+ return $.asap(docSet, function() {
+ var ref4, val;
+ if ($.cantSet) {
+
+ } else if (items.previousversion == null) {
+ Main.isFirstRun = true;
+ Main.ready(function() {
+ $.set('previousversion', g.VERSION);
+ return Settings.open();
+ });
+ } else if (items.previousversion !== g.VERSION) {
+ Main.upgrade(items);
+ }
+ for (key in Conf) {
+ val = Conf[key];
+ Conf[key] = (ref4 = items[key]) != null ? ref4 : val;
+ }
+ return Site.init(Main.initFeatures);
+ });
+ });
+ },
+ upgrade: function(items) {
+ var changes, previousversion;
+ previousversion = items.previousversion;
+ changes = Settings.upgrade(items, previousversion);
+ items.previousversion = changes.previousversion = g.VERSION;
+ return $.set(changes, function() {
+ var el, ref;
+ if ((ref = items['Show Updated Notifications']) != null ? ref : true) {
+ el = $.el('span', {innerHTML: "4chan X has been updated to <a href=\"https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md\" target=\"_blank\">version " + E(g.VERSION) + "</a>."});
+ return new Notice('info', el, 15);
+ }
+ });
+ },
+ parseURL: function(site, url) {
+ var pathname, r, ref;
+ if (site == null) {
+ site = g.SITE;
+ }
+ if (url == null) {
+ url = location;
+ }
+ r = {};
+ if (!site) {
+ return r;
+ }
+ r.siteID = site.ID;
+ if (typeof site.isBoardlessPage === "function" ? site.isBoardlessPage(url) : void 0) {
+ return r;
+ }
+ pathname = url.pathname.split(/\/+/);
+ r.boardID = pathname[1];
+ if (site.isFileURL(url)) {
+ r.VIEW = 'file';
+ } else if (typeof site.isAuxiliaryPage === "function" ? site.isAuxiliaryPage(url) : void 0) {
+
+ } else if ((ref = pathname[2]) === 'thread' || ref === 'res') {
+ r.VIEW = 'thread';
+ r.threadID = r.THREADID = +pathname[3].replace(/\.\w+$/, '');
+ } else if (pathname[2] === 'archive' && pathname[3] === 'res') {
+ r.VIEW = 'thread';
+ r.threadID = r.THREADID = +pathname[4].replace(/\.\w+$/, '');
+ r.threadArchived = true;
+ } else if (/^(?:catalog|archive)(?:\.\w+)?$/.test(pathname[2])) {
+ r.VIEW = pathname[2].replace(/\.\w+$/, '');
+ } else if (/^(?:index|\d*)(?:\.\w+)?$/.test(pathname[2])) {
+ r.VIEW = 'index';
+ }
+ return r;
+ },
+ initFeatures: function() {
+ var base, err, feature, j, len, name, ref, ref1;
+ $.global(function() {
+ document.documentElement.classList.add('js-enabled');
+ return window.FCX = {};
+ });
+ Main.jsEnabled = $.hasClass(doc, 'js-enabled');
+ if (typeof $.ajaxPageInit === "function") {
+ $.ajaxPageInit();
+ }
+ $.extend(g, Main.parseURL());
+ if (g.boardID) {
+ g.BOARD = new Board(g.boardID);
+ }
+ if (!g.VIEW) {
+ if (typeof (base = g.SITE).initAuxiliary === "function") {
+ base.initAuxiliary();
+ }
+ return;
+ }
+ if (g.VIEW === 'file') {
+ $.asap((function() {
+ return d.readyState !== 'loading';
+ }), function() {
+ var base1, pathname, video;
+ if (g.SITE.software === 'yotsuba' && Conf['404 Redirect'] && (typeof (base1 = g.SITE).is404 === "function" ? base1.is404() : void 0)) {
+ pathname = location.pathname.split(/\/+/);
+ return Redirect.navigate('file', {
+ boardID: g.BOARD.ID,
+ filename: pathname[pathname.length - 1]
+ });
+ } else if (video = $('video')) {
+ if (Conf['Volume in New Tab']) {
+ Volume.setup(video);
+ }
+ if (Conf['Loop in New Tab']) {
+ video.loop = true;
+ video.controls = false;
+ video.play();
+ return ImageCommon.addControls(video);
+ }
+ }
+ });
+ return;
+ }
+ g.threads = new SimpleDict();
+ g.posts = new SimpleDict();
+ $.onExists(doc, 'body', Main.initStyle);
+ ref = Main.features;
+ for (j = 0, len = ref.length; j < len; j++) {
+ ref1 = ref[j], name = ref1[0], feature = ref1[1];
+ if (g.SITE.disabledFeatures && indexOf.call(g.SITE.disabledFeatures, name) >= 0) {
+ continue;
+ }
+ try {
+ feature.init();
+ } catch (error1) {
+ err = error1;
+ Main.handleErrors({
+ message: "\"" + name + "\" initialization crashed.",
+ error: err
+ });
+ }
+ }
+ return $.ready(Main.initReady);
+ },
+ initStyle: function() {
+ var keyboard, ref;
+ if (!Main.isThisPageLegit()) {
+ return;
+ }
+ if ((ref = $('link[href*=mobile]', d.head)) != null) {
+ ref.disabled = true;
+ }
+ doc.dataset.host = location.host;
+ $.addClass(doc, "sw-" + g.SITE.software);
+ $.addClass(doc, g.VIEW === 'thread' ? 'thread-view' : g.VIEW);
+ $.onExists(doc, '.ad-cnt, .adg-rects > .desktop', function(ad) {
+ return $.onExists(ad, 'img, iframe', function() {
+ return $.addClass(doc, 'ads-loaded');
+ });
+ });
+ if (Conf['Autohiding Scrollbar']) {
+ $.addClass(doc, 'autohiding-scrollbar');
+ }
+ $.ready(function() {
+ if (d.body.clientHeight > doc.clientHeight && (window.innerWidth === doc.clientWidth) !== Conf['Autohiding Scrollbar']) {
+ Conf['Autohiding Scrollbar'] = !Conf['Autohiding Scrollbar'];
+ $.set('Autohiding Scrollbar', Conf['Autohiding Scrollbar']);
+ return $.toggleClass(doc, 'autohiding-scrollbar');
+ }
+ });
+ $.addStyle(CSS.sub(CSS.boards), 'fourchanx-css');
+ Main.bgColorStyle = $.el('style', {
+ id: 'fourchanx-bgcolor-css'
+ });
+ keyboard = false;
+ $.on(d, 'mousedown', function() {
+ return keyboard = false;
+ });
+ $.on(d, 'keydown', function(e) {
+ if (e.keyCode === 9) {
+ return keyboard = true;
+ }
+ });
+ window.addEventListener('focus', (function() {
+ return doc.classList.toggle('keyboard-focus', keyboard);
+ }), true);
+ return Main.setClass();
+ },
+ setClass: function() {
+ var j, knownStyles, len, mainStyleSheet, ref, ref1, setStyle, style, styleSheet, styleSheets;
+ knownStyles = ['yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'photon', 'tomorrow', 'spooky'];
+ if (g.SITE.software === 'yotsuba' && g.VIEW === 'catalog') {
+ if ((mainStyleSheet = $.id('base-css'))) {
+ style = (ref = mainStyleSheet.href.match(/catalog_(\w+)/)) != null ? ref[1].replace('_new', '').replace(/_+/g, '-') : void 0;
+ if (indexOf.call(knownStyles, style) >= 0) {
+ $.addClass(doc, style);
+ return;
+ }
+ }
+ }
+ style = mainStyleSheet = styleSheets = null;
+ setStyle = function() {
+ var bgColor, css, div, j, len, rgb, s, styleSheet;
+ if (g.SITE.software === 'yotsuba') {
+ $.rmClass(doc, style);
+ style = null;
+ for (j = 0, len = styleSheets.length; j < len; j++) {
+ styleSheet = styleSheets[j];
+ if (styleSheet.href === (mainStyleSheet != null ? mainStyleSheet.href : void 0)) {
+ style = styleSheet.title.toLowerCase().replace('new', '').trim().replace(/\s+/g, '-');
+ if (style === '_special') {
+ style = styleSheet.href.match(/[a-z]*(?=[^\/]*$)/)[0];
+ }
+ if (indexOf.call(knownStyles, style) < 0) {
+ style = null;
+ }
+ break;
+ }
+ }
+ if (style) {
+ $.addClass(doc, style);
+ $.rm(Main.bgColorStyle);
+ return;
+ }
+ }
+ div = g.SITE.bgColoredEl();
+ div.style.position = 'absolute';
+ div.style.visibility = 'hidden';
+ $.add(d.body, div);
+ bgColor = window.getComputedStyle(div).backgroundColor;
+ $.rm(div);
+ rgb = bgColor.match(/[\d.]+/g);
+ if (!/^rgb\(/.test(bgColor)) {
+ s = window.getComputedStyle(d.body);
+ bgColor = s.backgroundColor + " " + s.backgroundImage + " " + s.backgroundRepeat + " " + s.backgroundPosition;
+ }
+ css = ".dialog, .suboption-list > div:last-of-type, :root.catalog-hover-expand .catalog-container:hover > .post {\n background: " + bgColor + ";\n}\n.unread-mark-read {\n background-color: rgba(" + (rgb.slice(0, 3).join(', ')) + ", " + (0.5 * (rgb[3] || 1)) + ");\n}";
+ if ($.luma(rgb) < 100) {
+ css += ".watch-thread-link {\n background-image: url(\"data:image/svg+xml,<svg viewBox='0 0 26 26' preserveAspectRatio='true' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(200,200,200)' d='M24.132,7.971c-2.203-2.205-5.916-2.098-8.25,0.235L15.5,8.588l-0.382-0.382c-2.334-2.333-6.047-2.44-8.25-0.235c-2.204,2.203-2.098,5.916,0.235,8.249l8.396,8.396l8.396-8.396C26.229,13.887,26.336,10.174,24.132,7.971z'/></svg>\");\n}";
+ }
+ Main.bgColorStyle.textContent = css;
+ return $.after($.id('fourchanx-css'), Main.bgColorStyle);
+ };
+ $.onExists(d.head, g.SITE.selectors.styleSheet, function(el) {
+ mainStyleSheet = el;
+ if (g.SITE.software === 'yotsuba') {
+ styleSheets = $$('link[rel="alternate stylesheet"]', d.head);
+ }
+ new MutationObserver(setStyle).observe(mainStyleSheet, {
+ attributes: true,
+ attributeFilter: ['href']
+ });
+ $.on(mainStyleSheet, 'load', setStyle);
+ return setStyle();
+ });
+ if (!mainStyleSheet) {
+ ref1 = $$('link[rel="stylesheet"]', d.head);
+ for (j = 0, len = ref1.length; j < len; j++) {
+ styleSheet = ref1[j];
+ $.on(styleSheet, 'load', setStyle);
+ }
+ return setStyle();
+ }
+ },
+ initReady: function() {
+ var base, base1, msg;
+ if (typeof (base = g.SITE).is404 === "function" ? base.is404() : void 0) {
+ if (g.VIEW === 'thread') {
+ ThreadWatcher.set404(g.BOARD.ID, g.THREADID, function() {
+ if (Conf['404 Redirect']) {
+ return Redirect.navigate('thread', {
+ boardID: g.BOARD.ID,
+ threadID: g.THREADID,
+ postID: +location.hash.match(/\d+/)
+ }, "/" + g.BOARD + "/");
+ }
+ });
+ }
+ return;
+ }
+ if (typeof (base1 = g.SITE).isIncomplete === "function" ? base1.isIncomplete() : void 0) {
+ msg = $.el('div', {innerHTML: "The page didn&#039;t load completely.<br>Some features may not work unless you <a href=\"javascript:;\">reload</a>."});
+ $.on($('a', msg), 'click', function() {
+ return location.reload();
+ });
+ new Notice('warning', msg);
+ }
+ if (g.VIEW === 'catalog') {
+ return Main.initCatalog();
+ } else if (!Index.enabled) {
+ if (g.SITE.awaitBoard) {
+ return g.SITE.awaitBoard(Main.initThread);
+ } else {
+ return Main.initThread();
+ }
+ } else {
+ Main.expectInitFinished = true;
+ return $.event('4chanXInitFinished');
+ }
+ },
+ initThread: function() {
+ var base, base1, board, errors, posts, ref, s, threads;
+ s = g.SITE.selectors;
+ if ((board = $(((ref = s.boardFor) != null ? ref[g.VIEW] : void 0) || s.board))) {
+ threads = [];
+ posts = [];
+ errors = [];
+ try {
+ if (typeof (base = g.SITE).preParsingFixes === "function") {
+ base.preParsingFixes(board);
+ }
+ } catch (error1) {}
+ Main.addThreadsObserver = new MutationObserver(Main.addThreads);
+ Main.addPostsObserver = new MutationObserver(Main.addPosts);
+ Main.addThreadsObserver.observe(board, {
+ childList: true
+ });
+ Main.parseThreads($$(s.thread, board), threads, posts, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ if (g.VIEW === 'thread') {
+ if (g.threadArchived) {
+ threads[0].isArchived = true;
+ threads[0].kill();
+ }
+ if (typeof (base1 = g.SITE).parseThreadMetadata === "function") {
+ base1.parseThreadMetadata(threads[0]);
+ }
+ }
+ Main.callbackNodes('Thread', threads);
+ return Main.callbackNodesDB('Post', posts, function() {
+ var j, len, post;
+ for (j = 0, len = posts.length; j < len; j++) {
+ post = posts[j];
+ QuoteThreading.insert(post);
+ }
+ Main.expectInitFinished = true;
+ return $.event('4chanXInitFinished');
+ });
+ } else {
+ Main.expectInitFinished = true;
+ return $.event('4chanXInitFinished');
+ }
+ },
+ parseThreads: function(threadRoots, threads, posts, errors) {
+ var boardID, boardObj, j, len, postRoots, ref, thread, threadID, threadRoot;
+ for (j = 0, len = threadRoots.length; j < len; j++) {
+ threadRoot = threadRoots[j];
+ boardObj = (boardID = threadRoot.dataset.board) ? (boardID = encodeURIComponent(boardID), g.boards[boardID] || new Board(boardID)) : g.BOARD;
+ threadID = +threadRoot.id.match(/\d*$/)[0];
+ if (!threadID || ((ref = boardObj.threads.get(threadID)) != null ? ref.nodes.root : void 0)) {
+ return;
+ }
+ thread = new Thread(threadID, boardObj);
+ thread.nodes.root = threadRoot;
+ threads.push(thread);
+ postRoots = $$(g.SITE.selectors.postContainer, threadRoot);
+ if (g.SITE.isOPContainerThread) {
+ postRoots.unshift(threadRoot);
+ }
+ Main.parsePosts(postRoots, thread, posts, errors);
+ Main.addPostsObserver.observe(threadRoot, {
+ childList: true
+ });
+ }
+ },
+ parsePosts: function(postRoots, thread, posts, errors) {
+ var err, j, len, postRoot;
+ for (j = 0, len = postRoots.length; j < len; j++) {
+ postRoot = postRoots[j];
+ if (!(postRoot.dataset.fullID && g.posts.get(postRoot.dataset.fullID)) && $(g.SITE.selectors.comment, postRoot)) {
+ try {
+ posts.push(new Post(postRoot, thread, thread.board));
+ } catch (error1) {
+ err = error1;
+ errors.push({
+ message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.",
+ error: err,
+ html: postRoot.outerHTML
+ });
+ }
+ }
+ }
+ },
+ addThreads: function(records) {
+ var errors, j, k, len, len1, node, posts, record, ref, threadRoots, threads;
+ threadRoots = [];
+ for (j = 0, len = records.length; j < len; j++) {
+ record = records[j];
+ ref = record.addedNodes;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ node = ref[k];
+ if (node.nodeType === Node.ELEMENT_NODE && node.matches(g.SITE.selectors.thread)) {
+ threadRoots.push(node);
+ }
+ }
+ }
+ if (!threadRoots.length) {
+ return;
+ }
+ threads = [];
+ posts = [];
+ errors = [];
+ Main.parseThreads(threadRoots, threads, posts, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ Main.callbackNodes('Thread', threads);
+ return Main.callbackNodesDB('Post', posts, function() {
+ return $.event('PostsInserted', null, records[0].target);
+ });
+ },
+ addPosts: function(records) {
+ var anyRemoved, el, errors, j, k, l, len, len1, len2, n, node, postRoots, posts, record, ref, ref1, ref2, thread, threads, threadsRM;
+ threads = [];
+ threadsRM = [];
+ posts = [];
+ errors = [];
+ for (j = 0, len = records.length; j < len; j++) {
+ record = records[j];
+ thread = Get.threadFromRoot(record.target);
+ postRoots = [];
+ ref = record.addedNodes;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ node = ref[k];
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ if (node.matches(g.SITE.selectors.postContainer) || (node = $(g.SITE.selectors.postContainer, node))) {
+ postRoots.push(node);
+ }
+ }
+ }
+ n = posts.length;
+ Main.parsePosts(postRoots, thread, posts, errors);
+ if (posts.length > n && indexOf.call(threads, thread) < 0) {
+ threads.push(thread);
+ }
+ anyRemoved = false;
+ ref1 = record.removedNodes;
+ for (l = 0, len2 = ref1.length; l < len2; l++) {
+ el = ref1[l];
+ if (((ref2 = Get.postFromRoot(el)) != null ? ref2.nodes.root : void 0) === el && !doc.contains(el)) {
+ anyRemoved = true;
+ break;
+ }
+ }
+ if (anyRemoved && indexOf.call(threadsRM, thread) < 0) {
+ threadsRM.push(thread);
+ }
+ }
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ return Main.callbackNodesDB('Post', posts, function() {
+ var len3, len4, m, o;
+ for (m = 0, len3 = threads.length; m < len3; m++) {
+ thread = threads[m];
+ $.event('PostsInserted', null, thread.nodes.root);
+ }
+ for (o = 0, len4 = threadsRM.length; o < len4; o++) {
+ thread = threadsRM[o];
+ $.event('PostsRemoved', null, thread.nodes.root);
+ }
+ });
+ },
+ initCatalog: function() {
+ var board, errors, s, threads;
+ s = g.SITE.selectors.catalog;
+ if (s && (board = $(s.board))) {
+ threads = [];
+ errors = [];
+ Main.addCatalogThreadsObserver = new MutationObserver(Main.addCatalogThreads);
+ Main.addCatalogThreadsObserver.observe(board, {
+ childList: true
+ });
+ Main.parseCatalogThreads($$(s.thread, board), threads, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ Main.callbackNodes('CatalogThreadNative', threads);
+ }
+ Main.expectInitFinished = true;
+ return $.event('4chanXInitFinished');
+ },
+ parseCatalogThreads: function(threadRoots, threads, errors) {
+ var err, j, len, ref, thread, threadRoot;
+ for (j = 0, len = threadRoots.length; j < len; j++) {
+ threadRoot = threadRoots[j];
+ try {
+ thread = new CatalogThreadNative(threadRoot);
+ if (((ref = thread.thread.catalogViewNative) != null ? ref.nodes.root : void 0) !== threadRoot) {
+ thread.thread.catalogViewNative = thread;
+ threads.push(thread);
+ }
+ } catch (error1) {
+ err = error1;
+ errors.push({
+ message: "Parsing of Catalog Thread No." + ((threadRoot.dataset.id || threadRoot.id).match(/\d+/)) + " failed. Thread will be skipped.",
+ error: err,
+ html: threadRoot.outerHTML
+ });
+ }
+ }
+ },
+ addCatalogThreads: function(records) {
+ var errors, j, k, len, len1, node, record, ref, threadRoots, threads;
+ threadRoots = [];
+ for (j = 0, len = records.length; j < len; j++) {
+ record = records[j];
+ ref = record.addedNodes;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ node = ref[k];
+ if (node.nodeType === Node.ELEMENT_NODE && node.matches(g.SITE.selectors.catalog.thread)) {
+ threadRoots.push(node);
+ }
+ }
+ }
+ if (!threadRoots.length) {
+ return;
+ }
+ threads = [];
+ errors = [];
+ Main.parseCatalogThreads(threadRoots, threads, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ return Main.callbackNodes('CatalogThreadNative', threads);
+ },
+ callbackNodes: function(klass, nodes) {
+ var cb, i, node;
+ i = 0;
+ cb = Callbacks[klass];
+ while (node = nodes[i++]) {
+ cb.execute(node);
+ }
+ },
+ callbackNodesDB: function(klass, nodes, cb) {
+ var cbs, fn, i, softTask;
+ i = 0;
+ cbs = Callbacks[klass];
+ fn = function() {
+ var node;
+ if (!(node = nodes[i])) {
+ return false;
+ }
+ cbs.execute(node);
+ return ++i % 25;
+ };
+ softTask = function() {
+ while (fn()) {
+ continue;
+ }
+ if (!nodes[i]) {
+ if (cb) {
+ cb();
+ }
+ return;
+ }
+ return setTimeout(softTask, 0);
+ };
+ return softTask();
+ },
+ handleErrors: function(errors) {
+ var div, enabled, error, j, len, logs, msg;
+ if (d.body && $.hasClass(d.body, 'fourchan_x') && !$.hasClass(doc, 'tainted')) {
+ new Notice('error', 'Error: Multiple copies of 4chan X are enabled.');
+ $.addClass(doc, 'tainted');
+ }
+ if (g.SITE.testNativeExtension && !$.hasClass(doc, 'tainted')) {
+ enabled = g.SITE.testNativeExtension().enabled;
+ if (enabled) {
+ $.addClass(doc, 'tainted');
+ if (Conf['Disable Native Extension'] && !Main.isFirstRun) {
+ msg = $.el('div', {innerHTML: "Failed to disable the native extension. You may need to <a href=\"https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions#blocking-native-extension\" target=\"_blank\">block it</a>."});
+ new Notice('error', msg);
+ }
+ }
+ }
+ if (!(errors instanceof Array)) {
+ error = errors;
+ } else if (errors.length === 1) {
+ error = errors[0];
+ }
+ if (error) {
+ new Notice('error', Main.parseError(error, Main.reportLink([error])), 15);
+ return;
+ }
+ div = $.el('div', {innerHTML: E(errors.length) + " errors occurred." + (Main.reportLink(errors)).innerHTML + " [<a href=\"javascript:;\">show</a>]"});
+ $.on(div.lastElementChild, 'click', function() {
+ var ref;
+ return ref = this.textContent === 'show' ? ['hide', false] : ['show', true], this.textContent = ref[0], logs.hidden = ref[1], ref;
+ });
+ logs = $.el('div', {
+ hidden: true
+ });
+ for (j = 0, len = errors.length; j < len; j++) {
+ error = errors[j];
+ $.add(logs, Main.parseError(error));
+ }
+ return new Notice('error', [div, logs], 30);
+ },
+ parseError: function(data, reportLink) {
+ var context, error, lines, message, ref, ref1;
+ c.error(data.message, data.error.stack);
+ message = $.el('div', {innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "")});
+ error = $.el('div', {
+ textContent: (data.error.name || 'Error') + ": " + (data.error.message || 'see console for details')
+ });
+ lines = ((ref = data.error.stack) != null ? (ref1 = ref.match(/\d+(?=:\d+\)?$)/mg)) != null ? ref1.join().replace(/^/, ' at ') : void 0 : void 0) || '';
+ context = $.el('div', {
+ textContent: "(4chan X ccd0 v" + g.VERSION + " " + $.platform + " on " + $.engine + lines + ")"
+ });
+ return [message, error, context];
+ },
+ reportLink: function(errors) {
+ var addDetails, data, details, info, title, url;
+ data = errors[0];
+ title = data.message;
+ if (errors.length > 1) {
+ title += " (+" + (errors.length - 1) + " other errors)";
+ }
+ details = '';
+ addDetails = function(text) {
+ if (!(encodeURIComponent(title + details + text + '\n').length > 8110)) {
+ return details += text + '\n';
+ }
+ };
+ addDetails("[Please describe the steps needed to reproduce this error.]\n\nScript: 4chan X ccd0 v" + g.VERSION + " " + $.platform + "\nURL: " + location.href + "\nUser agent: " + navigator.userAgent);
+ if ($.platform === 'userscript' && (info = typeof GM !== "undefined" && GM !== null ? GM.info : (typeof GM_info !== "undefined" && GM_info !== null ? GM_info : void 0))) {
+ addDetails("Userscript manager: " + info.scriptHandler + " " + info.version);
+ }
+ addDetails('\n' + data.error);
+ if (data.error.stack) {
+ addDetails(data.error.stack.replace(data.error.toString(), '').trim());
+ }
+ if (data.html) {
+ addDetails('\n`' + data.html + '`');
+ }
+ details = details.replace(/file:\/{3}.+\//g, '');
+ url = 'https://gitreports.com/issue/ccd0/4chan-x?issue_title=%title&details=%details'.replace('%title', encodeURIComponent(title)).replace('%details', encodeURIComponent(details));
+ return {innerHTML: "<span class=\"report-error\"> [<a href=\"" + E(url) + "\" target=\"_blank\">report</a>]</span>"};
+ },
+ isThisPageLegit: function() {
+ if (!('thisPageIsLegit' in Main)) {
+ Main.thisPageIsLegit = g.SITE.isThisPageLegit ? g.SITE.isThisPageLegit() : !/^[45]\d\d\b/.test(document.title) && !/\.(?:json|rss)$/.test(location.pathname);
+ }
+ return Main.thisPageIsLegit;
+ },
+ ready: function(cb) {
+ return $.ready(function() {
+ if (Main.isThisPageLegit()) {
+ return cb();
+ }
+ });
+ },
+ mounted: function(cb) {
+ if (Main.isMounted) {
+ return cb();
+ } else {
+ return Main.mountedCBs.push(cb);
+ }
+ },
+ mountedCBs: [],
+ features: [['Polyfill', Polyfill], ['Board Configuration', BoardConfig], ['Normalize URL', NormalizeURL], ['Delay Redirect on Post', PostRedirect], ['Captcha Configuration', Captcha.replace], ['Image Host Rewriting', ImageHost], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Tinyboard Glue', Tinyboard], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['Count Posts by ID', IDPostCount], ['Custom CSS', CustomCSS], ['Thread Links', ThreadLinks], ['Linkify', Linkify], ['Reveal Spoilers', RemoveSpoilers], ['Resurrect Quotes', Quotify], ['Filter', Filter], ['Thread Hiding Buttons', ThreadHiding], ['Reply Hiding Buttons', PostHiding], ['Recursive', Recursive], ['Strike-through Quotes', QuoteStrikeThrough], ['Quick Reply Personas', QR.persona], ['Quick Reply', QR], ['Cooldown', QR.cooldown], ['Post Jumper', PostJumper], ['Pass Link', PassLink], ['Menu', Menu], ['Index Generator (Menu)', Index.menu], ['Report Link', ReportLink], ['Copy Text Link', CopyTextLink], ['Thread Hiding (Menu)', ThreadHiding.menu], ['Reply Hiding (Menu)', PostHiding.menu], ['Delete Link', DeleteLink], ['Filter (Menu)', Filter.menu], ['Edit Link', QR.oekaki.menu], ['Download Link', DownloadLink], ['Archive Link', ArchiveLink], ['Quote Inlining', QuoteInline], ['Quote Previewing', QuotePreview], ['Quote Backlinks', QuoteBacklink], ['Mark Quotes of You', QuoteYou], ['Mark OP Quotes', QuoteOP], ['Mark Cross-thread Quotes', QuoteCT], ['Anonymize', Anonymize], ['Time Formatting', Time], ['Relative Post Dates', RelativeDates], ['File Info Formatting', FileInfo], ['Fappe Tyme', FappeTyme], ['Gallery', Gallery], ['Gallery (menu)', Gallery.menu], ['Sauce', Sauce], ['Image Expansion', ImageExpand], ['Image Expansion (Menu)', ImageExpand.menu], ['Reveal Spoiler Thumbnails', RevealSpoilers], ['Image Loading', ImageLoader], ['Image Hover', ImageHover], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['Comment Expansion', ExpandComment], ['Thread Expansion', ExpandThread], ['Favicon', Favicon], ['Unread', Unread], ['Unread Line in Index', UnreadIndex], ['Quote Threading', QuoteThreading], ['Thread Stats', ThreadStats], ['Thread Updater', ThreadUpdater], ['Thread Watcher', ThreadWatcher], ['Thread Watcher (Menu)', ThreadWatcher.menu], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Announcements', PSA], ['Flash Features', Flash], ['Reply Pruning', ReplyPruning], ['Mod Contact Links', ModContact]]
+ };
+
+ return Main;
+
+}).call(this);
+
+Main.init();
+
+})();
diff --git a/.config/qutebrowser/plugins/redirect.py b/.config/qutebrowser/plugins/redirect.py
new file mode 100644
index 0000000..15b6756
--- /dev/null
+++ b/.config/qutebrowser/plugins/redirect.py
@@ -0,0 +1,41 @@
+import qutebrowser.api.interceptor
+import random
+
+
+redirects = {
+ "imgur.com": [
+ "imgur.alt.tyil.nl",
+ ],
+ "www.youtube.com": [
+ "youtube.alt.tyil.nl",
+ ],
+ "youtube.com": [
+ "youtube.alt.tyil.nl",
+ ],
+ "twitter.com": [
+ "twitter.alt.tyil.nl",
+ ],
+ "reddit.com": [
+ "reddit.alt.tyil.nl",
+ ],
+ "www.reddit.com": [
+ "reddit.alt.tyil.nl",
+ ]
+}
+
+
+def redirect(request: qutebrowser.api.interceptor.Request):
+ source = request.request_url.host()
+
+ if source not in redirects:
+ return
+
+ destination = random.choice(redirects[request.request_url.host()])
+
+ print(f"Redirecting {source} to {destination}")
+
+ request.request_url.setHost(destination)
+ request.redirect(request.request_url)
+
+
+qutebrowser.api.interceptor.register(redirect)
diff --git a/.config/qutebrowser/quickmarks b/.config/qutebrowser/quickmarks
new file mode 100644
index 0000000..623722a
--- /dev/null
+++ b/.config/qutebrowser/quickmarks
@@ -0,0 +1,2 @@
+blog https://www.tyil.nl/posts/
+youtube https://youtube.alt.tyil.nl/feed/popular
diff --git a/.config/shell/env b/.config/shell/env
index e4ac331..fe1e814 100644
--- a/.config/shell/env
+++ b/.config/shell/env
@@ -34,12 +34,14 @@ fi
export DMENU_OPTS='-i -fn "Liberation Mono:pixelsize=13" -nb "#000" -nf "#fff" -sb "#4c679a" -l 15 -dim 0.5 -o 0.9'
# Set XDG directories
-export XDG_DATA_HOME="${HOME}/.local/share"
-export XDG_CONFIG_HOME="${HOME}/.config"
-export XDG_DATA_DIRS="/usr/local/share/:/usr/share/"
+export XDG_CACHE_HOME="$HOME/.cache"
+export XDG_CONFIG_HOME="$HOME/.config"
+export XDG_DATA_HOME="$HOME/.local/share"
+export XDG_STATE_HOME="$HOME/.local/state"
+
export XDG_CONFIG_DIRS="/etc/xdg/"
-export XDG_CACHE_HOME="${HOME}/.cache"
-export XDG_TEMPLATES_DIR="${HOME}/.local/templates"
+export XDG_DATA_DIRS="/usr/local/share/:/usr/share/"
+export XDG_TEMPLATES_DIR="$HOME/.local/templates"
#export XDG_RUNTIME_DIR=""
# export gpg-agent
diff --git a/.config/shell/functions.d/venv b/.config/shell/functions.d/venv
new file mode 100644
index 0000000..c23a02d
--- /dev/null
+++ b/.config/shell/functions.d/venv
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+venv() {
+ if ! command -V "venv_$1" | grep -q " function "
+ then
+ cat <<EOF
+Usage:
+ venv on
+ venv off
+
+Manage the python venv for a given project or directory.
+
+Commands:
+ on Enable the venv for \$PWD
+ off Deactivate the current venv
+EOF
+
+ return 1
+ fi
+
+ "venv_$1"
+}
+
+venv_on() {
+ export PYTHON_VENV_DIR="$PWD/.venv"
+ export VIRTUAL_ENV_DISABLE_PROMPT=1
+
+ if [ ! -d "$PYTHON_VENV_DIR" ]
+ then
+ printf "Initializing venv directory at %s\n" "$PYTHON_VENV_DIR"
+ python3 -m venv "$PYTHON_VENV_DIR"
+ fi
+
+ . "$PYTHON_VENV_DIR/bin/activate"
+}
+
+venv_off() {
+ if [ -z "$PYTHON_VENV_DIR" ]
+ then
+ printf "No venv active?\n"
+ return 1
+ fi
+
+ deactivate
+
+ unset PYTHON_VENV_DIR
+ unset VIRTUAL_ENV_DISABLE_PROMPT
+}
diff --git a/.config/shell/sources b/.config/shell/sources
index a9b11b9..62a7f7b 100644
--- a/.config/shell/sources
+++ b/.config/shell/sources
@@ -2,42 +2,18 @@
# Author: Patrick Spek <p.spek@tyil.nl>
# License: BSD 3-clause license
#
-# Apparently tmux likes to unset your PATH variable. As a workaround the PATH
-# will be unset completely at the start of this script, then reset using this
-# script. This was the only viable method for me to keep my PATH clean
-[ "${DEBUG_DOTFILES}" ] && echo "Unsetting PATH"
-unset PATH
-
[ "${DEBUG_DOTFILES}" ] && echo "Setting PATH"
# User-local bin dir
-PATH="${HOME}/.local/bin"
-
-# User-level pkgsrc bin dir
-PATH="${PATH}:/home/tyil/.pkgsrc/bin"
-PATH="${PATH}:/home/tyil/.pkgsrc/sbin"
-
-# Personal scripts and wrappers
-PATH="${PATH}:${HOME}/.config/shell/wrappers.d"
-
-# Language specific package manager bin dirs
-PATH="${PATH}:${HOME}/.local/share/perl6/site/bin"
-PATH="${PATH}:${HOME}/.local/share/perl6/vendor/bin"
-PATH="${PATH}:${HOME}/.local/share/perl6"
-PATH="${PATH}:${HOME}/.pkgsrc/share/perl6/site/bin"
-PATH="${PATH}:${HOME}/.pkgsrc/share/perl6/vendor/bin"
-PATH="${PATH}:${HOME}/.pkgsrc/share/perl6"
-PATH="${PATH}:${HOME}/.cabal/bin"
-
-# System bin dirs
-PATH="${PATH}:/bin"
-PATH="${PATH}:/sbin"
-PATH="${PATH}:/usr/bin"
-PATH="${PATH}:/usr/sbin"
-PATH="${PATH}:/usr/local/bin"
-PATH="${PATH}:/usr/local/sbin"
-PATH="${PATH}:/usr/games/bin"
-PATH="${PATH}:/opt/bin"
+PATH="$HOME/.config/shell/wrappers.d:$PATH"
+PATH="$HOME/.local/bin:$PATH"
+
+# snaps were a terrible idea and anyone in favour of them should be ashamed
+# of themselves
+if [ -d "/snap/bin" ]
+then
+ PATH="$PATH:/snap/bin"
+fi
# Raku stuff
if [ -d "$HOME/.raku/bin" ]
@@ -51,6 +27,13 @@ then
PATH="$PATH:/usr/local/share/perl6/vendor/bin"
fi
+# Python packages
+if command -v python >/dev/null 2>&1
+then
+ PATH="$PATH:$(python -c 'import site; print(":".join(map(lambda x: x + "/usr/bin", site.getsitepackages())))')"
+ PATH="$PATH:$(python -c 'import site; print(":".join(map(lambda x: x + "/usr/sbin", site.getsitepackages())))')"
+fi
+
# Load color definitions if they exist
if [ -f "$HOME/.config/shell/colors/$SHORTSHELL" ]
then
diff --git a/.config/shell/vendor/bash/feature-auto-completion b/.config/shell/vendor/bash/feature-auto-completion
new file mode 160000
+Subproject a18a07b34b37377bb523afa78899259f21d575a
diff --git a/.config/shell/vendor/zsh/feature-syntax-highlighting b/.config/shell/vendor/zsh/feature-syntax-highlighting
new file mode 160000
+Subproject e0165eaa730dd0fa321a6a6de74f092fe87630b
diff --git a/.config/swayidle/config b/.config/swayidle/config
new file mode 100644
index 0000000..faee35b
--- /dev/null
+++ b/.config/swayidle/config
@@ -0,0 +1,6 @@
+timeout 30 'hyprctl dispatch dpms off' resume 'hyprctl dispatch dpms on'
+timeout 45 'loginctl lock-session'
+
+before-sleep 'loginctl lock-session'
+
+lock 'swaylock -f -c 000000'
diff --git a/.config/swaylock/config b/.config/swaylock/config
new file mode 100644
index 0000000..6004b6e
--- /dev/null
+++ b/.config/swaylock/config
@@ -0,0 +1,2 @@
+daemonize
+show-failed-attempts
diff --git a/.config/sxhkd/sxhkdrc b/.config/sxhkd/sxhkdrc
index 811895b..d00104b 100644
--- a/.config/sxhkd/sxhkdrc
+++ b/.config/sxhkd/sxhkdrc
@@ -1,6 +1,6 @@
# spawn applications
super + Return
- term
+ ~/.local/bin/term
super + alt + Return
term -c
@@ -32,7 +32,7 @@ XF86MonBrightness{Up,Down}
# lock screen
super + shift + s
- physlock
+ i3lock -c '#000000'
shift + Pause
xblank-toggle
diff --git a/.config/systemd/user/dapp-.service.d/00-ExecSearchPath.conf b/.config/systemd/user/dapp-.service.d/00-ExecSearchPath.conf
new file mode 100644
index 0000000..3fdf3af
--- /dev/null
+++ b/.config/systemd/user/dapp-.service.d/00-ExecSearchPath.conf
@@ -0,0 +1,4 @@
+[Service]
+ExecSearchPath=%h/.local/bin
+ExecSearchPath=/usr/local/bin
+ExecSearchPath=/usr/bin
diff --git a/.config/systemd/user/dapp-.service.d/00-WorkingDirectory.conf b/.config/systemd/user/dapp-.service.d/00-WorkingDirectory.conf
new file mode 100644
index 0000000..d99a24c
--- /dev/null
+++ b/.config/systemd/user/dapp-.service.d/00-WorkingDirectory.conf
@@ -0,0 +1,2 @@
+[Service]
+WorkingDirectory=%h
diff --git a/.config/systemd/user/dapp-bg-.service.d/00-Restart.conf b/.config/systemd/user/dapp-bg-.service.d/00-Restart.conf
new file mode 100644
index 0000000..8a764e3
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-.service.d/00-Restart.conf
@@ -0,0 +1,2 @@
+[Service]
+Restart=always
diff --git a/.config/systemd/user/dapp-bg-chwp.service b/.config/systemd/user/dapp-bg-chwp.service
new file mode 100644
index 0000000..0aed15a
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-chwp.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Change the wallpaper used in Xorg
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=oneshot
+ExecStart=chwp
+Restart=on-failure
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-bg-chwp.service.d/00-Restart.conf b/.config/systemd/user/dapp-bg-chwp.service.d/00-Restart.conf
new file mode 100644
index 0000000..eedb19c
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-chwp.service.d/00-Restart.conf
@@ -0,0 +1,2 @@
+[Service]
+Restart=on-failure
diff --git a/.config/systemd/user/dapp-bg-chwp.timer b/.config/systemd/user/dapp-bg-chwp.timer
new file mode 100644
index 0000000..947b3e0
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-chwp.timer
@@ -0,0 +1,10 @@
+[Unit]
+Description=Change the wallpaper on a regular schedule
+Requires=dapp-bg-chwp.service
+
+[Timer]
+Unit=dapp-bg-chwp.service
+OnUnitInactiveSec=1h
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-bg-dunst.service b/.config/systemd/user/dapp-bg-dunst.service
new file mode 100644
index 0000000..d74d5b2
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-dunst.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Run dunst
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=/usr/bin/dunst
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-bg-ntfy@.service b/.config/systemd/user/dapp-bg-ntfy@.service
new file mode 100644
index 0000000..bbf914c
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-ntfy@.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Run ntfy
+After=network.target
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=ntfy subscribe "%i" 'notify-send "$t" "$m"'
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-bg-redshift.service b/.config/systemd/user/dapp-bg-redshift.service
new file mode 100644
index 0000000..fe996e9
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-redshift.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Run redshift
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=redshift -l 51.50:4.59 -t 6500:3500
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-bg-sxhkd.service b/.config/systemd/user/dapp-bg-sxhkd.service
new file mode 100644
index 0000000..f345544
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-sxhkd.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Run the Simple X HotKey Daemon
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=/usr/bin/sxhkd
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-bg-xcompmgr.service b/.config/systemd/user/dapp-bg-xcompmgr.service
new file mode 100644
index 0000000..153ca81
--- /dev/null
+++ b/.config/systemd/user/dapp-bg-xcompmgr.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Run the X Compositing Manager
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=xcompmgr
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-fg-.service.d/00-Restart.conf b/.config/systemd/user/dapp-fg-.service.d/00-Restart.conf
new file mode 100644
index 0000000..eedb19c
--- /dev/null
+++ b/.config/systemd/user/dapp-fg-.service.d/00-Restart.conf
@@ -0,0 +1,2 @@
+[Service]
+Restart=on-failure
diff --git a/.config/systemd/user/dapp-fg-chromium.service b/.config/systemd/user/dapp-fg-chromium.service
new file mode 100644
index 0000000..48a8283
--- /dev/null
+++ b/.config/systemd/user/dapp-fg-chromium.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Manage an x11 application
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=chromium
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-fg-firefox.service b/.config/systemd/user/dapp-fg-firefox.service
new file mode 100644
index 0000000..0d434cc
--- /dev/null
+++ b/.config/systemd/user/dapp-fg-firefox.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Manage an x11 application
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=firefox
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-fg-gajim.service b/.config/systemd/user/dapp-fg-gajim.service
new file mode 100644
index 0000000..0ed24f0
--- /dev/null
+++ b/.config/systemd/user/dapp-fg-gajim.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Manage an x11 application
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=gajim
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-fg-irc.service b/.config/systemd/user/dapp-fg-irc.service
new file mode 100644
index 0000000..91cba0e
--- /dev/null
+++ b/.config/systemd/user/dapp-fg-irc.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Manage an x11 application
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=irc
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-fg-keepassxc.service b/.config/systemd/user/dapp-fg-keepassxc.service
new file mode 100644
index 0000000..f93cfac
--- /dev/null
+++ b/.config/systemd/user/dapp-fg-keepassxc.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Manage an x11 application
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=keepassxc
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-fg-networkmanager.service b/.config/systemd/user/dapp-fg-networkmanager.service
new file mode 100644
index 0000000..63b74b0
--- /dev/null
+++ b/.config/systemd/user/dapp-fg-networkmanager.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Manage an x11 application
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=nm-applet
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/dapp-fg-nextcloud.service b/.config/systemd/user/dapp-fg-nextcloud.service
new file mode 100644
index 0000000..7f175fd
--- /dev/null
+++ b/.config/systemd/user/dapp-fg-nextcloud.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Run the Nextcloud desktop application
+After=graphical-session.target
+Requires=graphical-session.target
+
+[Service]
+Type=exec
+ExecStart=nextcloud
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/.config/systemd/user/wsys-.service.d/00-Target.conf b/.config/systemd/user/wsys-.service.d/00-Target.conf
new file mode 100644
index 0000000..d80f469
--- /dev/null
+++ b/.config/systemd/user/wsys-.service.d/00-Target.conf
@@ -0,0 +1,6 @@
+[Service]
+ExecStartPost=systemctl --user --no-block start wsys-xorg.target
+ExecStartPost=systemctl --user --no-block start wsys.target
+
+ExecStopPost=systemctl --user stop wsys.target
+ExecStopPost=systemctl --user stop wsys-xorg.target
diff --git a/.config/systemd/user/wsys-xorg.service b/.config/systemd/user/wsys-xorg.service
new file mode 100644
index 0000000..8c7a43a
--- /dev/null
+++ b/.config/systemd/user/wsys-xorg.service
@@ -0,0 +1,20 @@
+# Ensure you are allowed to run X.org as a regular non-console user!
+
+# /etc/X11/Xwrapper.conf
+# allowed_users=anybody
+# needs_root_rights=yes
+
+[Unit]
+Description=Xorg server at display :0
+
+Requires=wsys-xorg.socket
+After=wsys-xorg.socket
+Before=wsys.target
+
+[Service]
+Type=simple
+SuccessExitStatus=0 1
+
+ExecStart=/usr/bin/Xorg :0 -nolisten tcp -noreset -verbose 2 "vt1"
+ExecStartPost=systemctl --user import-environment DISPLAY
+ExecStartPost=systemctl --user import-environment XAUTHORITY
diff --git a/.config/systemd/user/wsys-xorg.socket b/.config/systemd/user/wsys-xorg.socket
new file mode 100644
index 0000000..f471dab
--- /dev/null
+++ b/.config/systemd/user/wsys-xorg.socket
@@ -0,0 +1,5 @@
+[Unit]
+Description=Socket for xorg at display :1
+
+[Socket]
+ListenStream=/tmp/.X11-unix/X1
diff --git a/.config/systemd/user/wsys-xorg.target b/.config/systemd/user/wsys-xorg.target
new file mode 100644
index 0000000..248722e
--- /dev/null
+++ b/.config/systemd/user/wsys-xorg.target
@@ -0,0 +1,3 @@
+[Unit]
+Before = wsys.target
+Description = Window System managed by X.org
diff --git a/.config/systemd/user/wsys.target b/.config/systemd/user/wsys.target
new file mode 100644
index 0000000..60a9c9f
--- /dev/null
+++ b/.config/systemd/user/wsys.target
@@ -0,0 +1,2 @@
+[Unit]
+Description = Window System
diff --git a/.config/systemd/user/xss-lock.service b/.config/systemd/user/xss-lock.service
new file mode 100644
index 0000000..7b059d6
--- /dev/null
+++ b/.config/systemd/user/xss-lock.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Start xss-lock
+PartOf=graphical-session.target
+
+[Service]
+Type=exec
+Environment=DISPLAY=:0
+ExecStart=/usr/bin/xss-lock -- i3lock -c '#000000' -n
+Restart=on-failure
+
+[Install]
+WantedBy=desktop-x11.target
diff --git a/.config/waybar/config-hyprland.json b/.config/waybar/config-hyprland.json
new file mode 100644
index 0000000..a82cd37
--- /dev/null
+++ b/.config/waybar/config-hyprland.json
@@ -0,0 +1,24 @@
+{
+ "name": "waybar-bot",
+ "layer": "top",
+ "position": "bottom",
+ "height": 20,
+ "spacing": 4,
+ "modules-left": [
+ "hyprland/workspaces"
+ ],
+ "modules-center": [
+ ],
+ "modules-right": [
+ ],
+ "hyprland/workspaces": {
+ "persistent-workspaces": {
+ "*": 9
+ },
+ "sort": "number"
+ },
+ "hyprland/window": {
+ "max-length": 200,
+ "seperate-outputs": true
+ }
+}
diff --git a/.config/waybar/config.json b/.config/waybar/config.json
new file mode 100644
index 0000000..880565f
--- /dev/null
+++ b/.config/waybar/config.json
@@ -0,0 +1,94 @@
+{
+ "name": "waybar-top",
+ "layer": "top",
+ "position": "top",
+ "height": 20,
+ "spacing": 2,
+ "modules-left": [
+ "cpu",
+ "memory",
+ "network"
+ ],
+ "modules-center": [
+ "clock"
+ ],
+ "modules-right": [
+ "pulseaudio",
+ "temperature",
+ "backlight",
+ "battery",
+ "idle_inhibitor",
+ "tray"
+ ],
+ "idle_inhibitor": {
+ "format": "{icon}",
+ "format-icons": {
+ "activated": "๏ฎ",
+ "deactivated": "๏ฐ"
+ }
+ },
+ "tray": {
+ "spacing": 4
+ },
+ "clock": {
+ "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
+ "format-alt": "{:%Y-%m-%d}"
+ },
+ "cpu": {
+ "format": "CPU: {load}",
+ "tooltip": false
+ },
+ "memory": {
+ "format": "RAM: {used}Gi/{total}Gi"
+ },
+ "temperature": {
+ "critical-threshold": 80,
+ "format": "{temperatureC}ยฐC ๐Ÿซ "
+ },
+ "backlight": {
+ "format": "{percent}% ๐Ÿ’ก",
+ "on-scroll-up": "bl inc 1",
+ "on-scroll-down": "bl dec 1"
+ },
+ "battery": {
+ "states": {
+ "warning": 30,
+ "critical": 15
+ },
+ "format": "{capacity}% ๐Ÿ”‹",
+ "format-charging": "{capacity}% โšก",
+ "format-plugged": "{capacity}% ๐Ÿ”Œ",
+ "format-alt": "{time} {icon}"
+ },
+ "network": {
+ "interface": "wlp*",
+ "interval": 1,
+ "format": "RX: {bandwidthDownBits} / TX: {bandwidthUpBits}",
+ "format-wifi": "RX: {bandwidthDownBits} / TX: {bandwidthUpBits}"
+ },
+ "pulseaudio": {
+ "format": "{volume}% {icon} {format_source}",
+ "format-bluetooth": "{volume}% {icon}๏Š” {format_source}",
+ "format-bluetooth-muted": "๐Ÿ”‡ {icon}๏Š” {format_source}",
+ "format-muted": "๐Ÿ”‡ {format_source}",
+ "format-source": "{volume}% ๏„ฐ",
+ "format-source-muted": "๏„ฑ",
+ "format-icons": {
+ "headphone": "๏€ฅ",
+ "hands-free": "๏–",
+ "headset": "๏–",
+ "phone": "๏‚•",
+ "portable": "๏‚•",
+ "car": "๏†น",
+ "default": [
+ "๏€ฆ",
+ "๏€ง",
+ "๏€จ"
+ ]
+ },
+ "on-click": "pavucontrol",
+ "on-scroll-up": "vol inc 1",
+ "on-scroll-down": "vol dec 1",
+ "on-click-right": "vol toggle"
+ }
+}
diff --git a/.config/waybar/style.css b/.config/waybar/style.css
new file mode 100644
index 0000000..e322c10
--- /dev/null
+++ b/.config/waybar/style.css
@@ -0,0 +1,140 @@
+* {
+ font-family: Monospace;
+ font-size: 12px;
+}
+
+window#waybar {
+ background-color: rgba(43, 48, 59, 0.5);
+ color: #ffffff;
+ transition-property: background-color;
+ transition-duration: .5s;
+}
+
+window.waybar-top {
+ border-bottom: 1px solid rgba(100, 114, 125, 0.5);
+}
+
+window.waybar-bot {
+ border-top: 1px solid rgba(100, 114, 125, 0.5);
+}
+
+window#waybar.hidden {
+ opacity: 0.2;
+}
+
+button {
+ /* Use box-shadow instead of border so the text isn't offset */
+ box-shadow: inset 0 -3px transparent;
+ /* Avoid rounded borders under each button name */
+ border: none;
+ border-radius: 0;
+}
+
+/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
+button:hover {
+ background: inherit;
+ box-shadow: inset 0 -3px #ffffff;
+}
+
+#workspaces button {
+ padding: 0 5px;
+ background-color: transparent;
+ color: #ffffff;
+}
+
+#workspaces button:hover {
+ background: rgba(0, 0, 0, 0.2);
+}
+
+#workspaces button.focused {
+ background-color: #64727D;
+ box-shadow: inset 0 -3px #ffffff;
+}
+
+#workspaces button.urgent {
+ background-color: #eb4d4b;
+}
+
+#mode {
+ background-color: #64727D;
+ border-bottom: 3px solid #ffffff;
+}
+
+#clock,
+#battery,
+#cpu,
+#memory,
+#disk,
+#temperature,
+#backlight,
+#network,
+#pulseaudio,
+#wireplumber,
+#custom-media,
+#tray,
+#mode,
+#idle_inhibitor,
+#scratchpad,
+#mpd {
+ padding: 0 4px;
+ color: #ffffff;
+ background-color: rgba(53, 58, 69, 0.5);
+}
+
+#clock {
+ background-color: inherit;
+}
+
+#window,
+#workspaces {
+ margin: 0 4px;
+}
+
+/* If workspaces is the leftmost module, omit left margin */
+.modules-left > widget:first-child > #workspaces {
+ margin-left: 0;
+}
+
+/* If workspaces is the rightmost module, omit right margin */
+.modules-right > widget:last-child > #workspaces {
+ margin-right: 0;
+}
+
+@keyframes blink {
+ to {
+ background-color: #ffffff;
+ color: #000000;
+ }
+}
+
+#battery.critical:not(.charging) {
+ background-color: #f53c3c;
+ color: #ffffff;
+ animation-name: blink;
+ animation-duration: 0.5s;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ animation-direction: alternate;
+}
+
+label:focus {
+ background-color: #000000;
+}
+
+#wireplumber {
+ color: #000000;
+}
+
+#tray > .passive {
+ -gtk-icon-effect: dim;
+}
+
+#tray > .needs-attention {
+ -gtk-icon-effect: highlight;
+ background-color: #eb4d4b;
+}
+
+#idle_inhibitor.activated {
+ background-color: #ecf0f1;
+ color: #2d3436;
+}
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9e22cac
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,63 @@
+[submodule ".config/shell/vendor/bash/feature-auto-completion"]
+ path = .config/shell/vendor/bash/feature-auto-completion
+ url = https://github.com/scop/bash-completion.git
+[submodule ".vim/bundle/feature-camelcasemotion"]
+ path = .vim/bundle/feature-camelcasemotion
+ url = https://github.com/tyil/camelcasemotion.git
+[submodule ".vim/bundle/feature-rainbow-parenthesis"]
+ path = .vim/bundle/feature-rainbow-parenthesis
+ url = https://github.com/luochen1990/rainbow
+[submodule ".vim/bundle/syntax-go"]
+ path = .vim/bundle/syntax-go
+ url = https://github.com/fatih/vim-go
+[submodule ".vim/bundle/syntax-hcl"]
+ path = .vim/bundle/syntax-hcl
+ url = https://github.com/jvirtanen/vim-hcl.git
+[submodule ".vim/bundle/syntax-helm"]
+ path = .vim/bundle/syntax-helm
+ url = https://github.com/towolf/vim-helm
+[submodule ".vim/bundle/syntax-ledger"]
+ path = .vim/bundle/syntax-ledger
+ url = https://github.com/ledger/vim-ledger
+[submodule ".vim/bundle/syntax-markdown"]
+ path = .vim/bundle/syntax-markdown
+ url = https://github.com/plasticboy/vim-markdown
+[submodule ".vim/bundle/syntax-mustache"]
+ path = .vim/bundle/syntax-mustache
+ url = https://github.com/mustache/vim-mustache-handlebars
+[submodule ".vim/bundle/syntax-raku"]
+ path = .vim/bundle/syntax-raku
+ url = https://github.com/raku/vim-raku.git
+[submodule ".vim/bundle/syntax-terraform"]
+ path = .vim/bundle/syntax-terraform
+ url = https://github.com/hashivim/vim-terraform
+[submodule ".vim/bundle/syntax-todo"]
+ path = .vim/bundle/syntax-todo
+ url = https://github.com/freitass/todo.txt-vim.git
+[submodule ".vim/bundle/syntax-toml"]
+ path = .vim/bundle/syntax-toml
+ url = https://github.com/cespare/vim-toml.git
+[submodule ".vim/bundle/theme-codedark"]
+ path = .vim/bundle/theme-codedark
+ url = https://github.com/tomasiser/vim-code-dark
+[submodule ".vim/bundle/theme-colorsbox"]
+ path = .vim/bundle/theme-colorsbox
+ url = https://github.com/mkarmona/colorsbox.git
+[submodule ".vim/bundle/theme-jellybeans"]
+ path = .vim/bundle/theme-jellybeans
+ url = https://github.com/nanotech/jellybeans.vim.git
+[submodule ".vim/bundle/theme-molokai"]
+ path = .vim/bundle/theme-molokai
+ url = https://github.com/tomasr/molokai.git
+[submodule ".weechat/vendor/protocol-slack"]
+ path = .weechat/vendor/protocol-slack
+ url = https://github.com/wee-slack/wee-slack
+[submodule ".config/shell/vendor/zsh/feature-syntax-highlighting"]
+ path = .config/shell/vendor/zsh/feature-syntax-highlighting
+ url = https://github.com/zsh-users/zsh-syntax-highlighting.git
+[submodule ".vim/bundle/syntax-blade"]
+ path = .vim/bundle/syntax-blade
+ url = https://github.com/jwalton512/vim-blade
+[submodule ".config/awesome/awesome-wm-widgets"]
+ path = .config/awesome/awesome-wm-widgets
+ url = https://github.com/streetturtle/awesome-wm-widgets.git
diff --git a/.local/bin/awesome b/.local/bin/awesome
new file mode 120000
index 0000000..3da827c
--- /dev/null
+++ b/.local/bin/awesome
@@ -0,0 +1 @@
+../share/wrapper.sh \ No newline at end of file
diff --git a/.local/bin/bl b/.local/bin/bl
index da1980e..ee71504 100755
--- a/.local/bin/bl
+++ b/.local/bin/bl
@@ -29,13 +29,25 @@ main()
case "$1" in
inc)
- xbacklight -inc ${2:-5}
+ brightnessctl set +${2:-5}%
notify="Brightness increased to $(brightness_level)%"
;;
dec)
- xbacklight -dec ${2:-5}
+ if [ $(brightness_level) -lt $(( ${2:-5} + 1 )) ]
+ then
+ brightnessctl set 1
+ else
+ brightnessctl set ${2:-5}%-
+ fi
+
notify="Brightness lowered to $(brightness_level)%"
;;
+ set)
+ [ -z "$2" ] && usage && exit 1
+
+ brightnessctl set $2%
+ notify="Brightness set to $(brightness_level)%"
+ ;;
esac
notify-send -n 57492 -s -t 2 -i "$(brightness_icon)" "Backlight" "$notify"
@@ -43,7 +55,7 @@ main()
brightness_level()
{
- xbacklight -get
+ awk "BEGIN { printf(\"%d\", ($(brightnessctl get) / $(brightnessctl max)) * 100) }"
}
brightness_icon()
@@ -61,8 +73,16 @@ usage()
cat <<EOF
Usage:
${0##*/} -h
+ ${0##*/} inc [level]
+ ${0##*/} dec [level]
+ ${0##*/} set <level>
+
+Alter the backlight intensity.
-Nondescript
+Subcommands:
+ inc Increase the volume by level, defaults to 5.
+ dec Decrease the volume by level, defaults to 5.
+ set Set the volume to level.
Options:
-h Show this help text and exit.
diff --git a/.local/bin/notify-send b/.local/bin/count
index 8dc67a0..cc02a71 100755
--- a/.local/bin/notify-send
+++ b/.local/bin/count
@@ -13,15 +13,10 @@
main()
{
# Handle opts
- while getopts ":a:hi:n:st:" opt
+ while getopts ":h" opt
do
case "$opt" in
- a) app=$OPTARG ;;
h) usage && exit 0 ;;
- i) icon=$OPTARG ;;
- n) id=$OPTARG ;;
- s) silent=1 ;;
- t) timeout=$OPTARG ;;
*)
printf "Invalid option passed: %s\n" "$OPTARG" >&2
;;
@@ -30,34 +25,7 @@ main()
shift $(( OPTIND - 1 ))
- [ $# -lt 1 ] && usage && exit 1
-
- if [ -z "$id" ]
- then
- id=$(awk -v min=10 -v max=10000 'BEGIN { srand(); print int(min+rand() * (max-min+1)) }')
- fi
-
- gdbus call \
- --session \
- --dest org.freedesktop.Notifications \
- --object-path /org/freedesktop/Notifications \
- --method org.freedesktop.Notifications.Notify \
- "$app" \
- "$id" \
- "$icon" \
- "$1" \
- "$2" \
- "[]" \
- "{}" \
- "$((${timeout:-10} * 1000))" \
- > /dev/null
-
- if [[ -z "$silent" ]]
- then
- printf "%d\n" "$id"
- fi
-
- exit 0
+ cat - | sort | uniq -c | sort -h
}
usage()
@@ -66,7 +34,7 @@ usage()
Usage:
${0##*/} -h
-Nondescript
+Count occurrences of unique lines.
Options:
-h Show this help text and exit.
diff --git a/.local/bin/dmenu b/.local/bin/dmenu
index 78cdf52..6c1494b 100755
--- a/.local/bin/dmenu
+++ b/.local/bin/dmenu
@@ -1,8 +1,20 @@
#!/usr/bin/env sh
-if ! command -v rofi > /dev/null
-then
- exec /usr/bin/dmenu "$@"
-fi
+main() {
+ if [ -n "$WAYLAND_DISPLAY" ]
+ then
+ exec fuzzel --dmenu "$@"
+ fi
-exec rofi -dmenu "$@"
+ if [ -n "$DISPLAY" ]
+ then
+ if command -v rofi > /dev/null
+ then
+ exec rofi -dmenu "$@"
+ fi
+
+ exec /usr/bin/dmenu "$@"
+ fi
+}
+
+main "$@"
diff --git a/.local/bin/downloadgemist b/.local/bin/downloadgemist
new file mode 100755
index 0000000..d690ba8
--- /dev/null
+++ b/.local/bin/downloadgemist
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+import argparse
+import requests
+import sys
+
+# Handle arguments
+argparser = argparse.ArgumentParser(
+ description = "Command line utility to interact with downloadgemist.nl",
+)
+argparser.add_argument("url")
+
+args = argparser.parse_args()
+
+# Make the actual HTTP request
+form = requests.post(
+ "https://www.downloadgemist.nl/core/hyperbridge.php",
+ data={
+ "mode": "initiate",
+ "link": args.url,
+ "options": '{"size":"large","download_tt888":true}',
+ },
+)
+
+# Make sure the response is json
+if form.headers.get("Content-Type").lower() != "application/json":
+ print("oh no")
+ sys.exit(1)
+
+json = form.json()
+
+print(form.json())
+
+# Handle any errors
+if json["status"] == 1:
+ print(form.json()["error"])
+ sys.exit(form.json()["status"])
+
+# Write the downloaded file
+video = requests.get(json["file"])
+
+open(json["filename"], "wb").write(video.content)
diff --git a/.local/bin/get-focussed-monitor b/.local/bin/get-focussed-monitor
deleted file mode 100755
index 4d7bbd4..0000000
--- a/.local/bin/get-focussed-monitor
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env raku
-
-use v6.d;
-
-sub MAIN (
- #Bool:D :$shell = False,
- Bool:D :$coords = False,
-) {
- my @xrandr = shell('xrandr | grep -w connected', :out).out.slurp.lines;
- my %monitors;
-
- my $mouse = shell('xdotool getmouselocation', :out).out.slurp.trim ~~ m/
- 'x:' $<x> = [ \d+ ]
- \s*
- 'y:' $<y> = [ \d+ ]
- /;
-
- for @xrandr -> $monitor {
- my $match = $monitor ~~ m/
- $<width> = [ \d+ ]
- 'x'
- $<height> = [ \d+ ]
- '+'
- $<x> = [ \d+ ]
- '+'
- $<y> = [ \d+ ]
- /;
-
- next unless $match<x> โ‰ค $mouse<x> โ‰ค ($match<x> + $match<width>);
- next unless $match<y> โ‰ค $mouse<y> โ‰ค ($match<y> + $match<height>);
-
- say qq[MONITOR="{$monitor.words.first}"];
-
- if ($coords) {
- say qq[W="{$match<width>}"];
- say qq[H="{$match<height>}"];
- say qq[X="{$match<x>}"];
- say qq[Y="{$match<y>}"];
- }
-
- last;
- }
-}
diff --git a/.local/bin/herbstluftwm b/.local/bin/herbstluftwm
new file mode 120000
index 0000000..3da827c
--- /dev/null
+++ b/.local/bin/herbstluftwm
@@ -0,0 +1 @@
+../share/wrapper.sh \ No newline at end of file
diff --git a/.local/bin/hyprland-switch-tag b/.local/bin/hyprland-switch-tag
new file mode 100755
index 0000000..06840c9
--- /dev/null
+++ b/.local/bin/hyprland-switch-tag
@@ -0,0 +1,161 @@
+#!/usr/bin/env bash
+
+# Pascal Jaeger <pascal.jaeger@leimstift.de>
+
+# utils
+green="\033[0;32m"
+red="\033[0;31m"
+blue="\033[0;34m"
+nocolor="\033[0m"
+
+#util functions
+check() {
+ command -v "$1" 1>/dev/null
+}
+
+ok() {
+ echo -e "[$green ๎ฎฑ $nocolor] $*"
+}
+
+err() {
+ echo -e "[$red ๏— $nocolor] $*"
+}
+optional() {
+ echo -e "[$blue ๏— $nocolor] $*"
+}
+
+notify() {
+ # shellcheck disable=SC2015
+ check notify-send && {
+ notify-send "$@"
+ } || {
+ echo "$@"
+ }
+}
+
+checkUtils() {
+ # shellcheck disable=SC2015
+ check grep && ok "grep" || err "grep"
+ # shellcheck disable=SC2015
+ check grep && ok "cut" || err "cut"
+ # shellcheck disable=SC2015
+ check notify-send && ok "notify-send (Optional)" || optional "notify-send (Optional)"
+ exit
+}
+
+basicChecks() {
+ check hyprctl || {
+ notify "Seriously mate!!" "Start Hyprland before this script"
+ exit 1
+ }
+ pgrep -x Hyprland &>/dev/null || {
+ notify "Make Sure Hyprland Session is running."
+ exit 1
+ }
+}
+
+help() {
+ cat <<EOF
+ This is a bash script to move arbitrary workspace to arbritrary monitor and to swap workspaces between
+ monitors if the desired workspace is already active on a monitor for Hyprland using hyprctl.
+
+ flags:
+ -h: Displays This help menu
+ -c: Checks for all dependencies
+
+ Usage: try_swap_workspace [WORKSPACE]
+ bind = ALT,1,exec, /path/to/try_swap_workspace/binary 1
+ (where the last 1 is the workspace that should be shown on the currently active monitor)
+
+EOF
+}
+
+getArgs() {
+ while [ "$#" -gt 0 ]; do
+ case "$1" in
+ -h | --help)
+ help
+ exit 0
+ ;;
+ -c)
+ checkUtils
+ ;;
+ (*[!0-9]*)
+ # contains non-numbers
+ help
+ echo ""
+ echo "Wrong argument given"
+ exit 1
+ ;;
+ *)
+ # only nubers left, so good
+ switch_or_swap "$1"
+ ;;
+ esac
+ shift
+ done
+}
+
+
+#variables
+mon_wrkspcs=()
+
+get_active_mon() {
+ echo $(($(hyprctl monitors | grep 'focused' | grep -n 'yes' | cut -c1)-1))
+}
+
+get_workspaces_array() {
+ local workspaces
+ workspaces=$(hyprctl monitors | grep 'active workspace' | cut -f3 -d' ')
+ SAVEIFS=$IFS
+ IFS=$'\n'
+ mon_wrkspcs=($workspaces)
+ IFS=$SAVEIFS
+}
+
+# first argument: workspace to switch to
+# second argument: monitor to switch workspace on
+switch_workspace() {
+ local target_wrkspc=$1
+ local target_mon=$2
+ hyprctl dispatch moveworkspacetomonitor "$target_wrkspc" "$target_mon"
+ hyprctl dispatch workspace "$target_wrkspc"
+}
+# first argument: monitor the workspace should go to
+# second argument: monitor the workspace is currently displayed on
+swap_workspace() {
+ local target_mon=$1
+ local source_mon=$2
+ hyprctl dispatch swapactiveworkspaces "$target_mon" "$source_mon"
+}
+
+# first argument: workspace to switch to active monitor
+switch_or_swap() {
+ target_mon=$(get_active_mon)
+ target_wrkspc=$1
+ get_workspaces_array
+ # check if the workspace is currently displayed on another monitor
+ local currently_active_on_mon=-1
+ for (( i=0; i<${#mon_wrkspcs[@]}; i++ ))
+ do
+ if [[ "$target_wrkspc" == "${mon_wrkspcs[$i]}" ]]; then
+ currently_active_on_mon=$i
+ fi
+ done
+ if [[ $currently_active_on_mon -lt 0 ]]; then
+ # workspace is not active on any monitor, do a normal switch
+ ok "switching workspace $target_wrkspc to monitor $target_mon"
+ switch_workspace "$target_wrkspc" "$target_mon"
+ else
+ # workspace is already active on other monitor, swap workspaces between monitor
+ ok "swapping workspaces between $target_mon to monitor $currently_active_on_mon"
+ swap_workspace "$target_mon" "$currently_active_on_mon"
+ fi
+}
+
+main() {
+ basicChecks
+ getArgs "$@"
+}
+
+main "$@"
diff --git a/.local/bin/import-pape b/.local/bin/import-pape
new file mode 100755
index 0000000..b72c169
--- /dev/null
+++ b/.local/bin/import-pape
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+readonly PAPEDIR="$HOME/pictures/wallpapers"
+
+main() {
+ for s in "$@"
+ do
+ local format="$(identify -format "%m" "$s" | awk '{ print(tolower($0)) }')"
+ local resolution="$(identify -format "%wx%h" "$s")"
+ local hash="$(sha1sum "$s" | awk '{ print $1 }')"
+ local destination="$PAPEDIR/$resolution/$hash.$format"
+
+ mkdir -pv -- "$PAPEDIR/$resolution"
+ printf "Importing %s to %s\n" "$s" "$destination"
+ mv -- "$s" "$destination"
+ done
+}
+
+main "$@"
diff --git a/.local/bin/ipass b/.local/bin/ipass
index b19137a..4f555e4 100755
--- a/.local/bin/ipass
+++ b/.local/bin/ipass
@@ -1,19 +1,51 @@
#! /usr/bin/env sh
-main()
-{
+main() {
cd -- "$HOME/.password-store" || exit 1
- file="$(find ./* -type f | sed 's-^\./--' | sed 's-\.gpg$--' | dmenu)"
- dmenu_exit=$?
+ if [ -n "$WAYLAND_DISPLAY" ]
+ then
+ file="$(pass_list | fuzzel -d)"
+ pass_get "$file" | wl-copy
+
+ notify "Copied $file to clipboard!"
+ exit 0
+ fi
- if [ "$dmenu_exit" -ne 0 ]
+ if [ -n "$DISPLAY" ]
then
- exit 2
+ file="$(pass_list | dmenu)"
+ pass_get "$file" | xdotool type --file -
+ dmenu_exit=$?
+
+ if [ "$dmenu_exit" -ne 0 ]
+ then
+ notify "Error typing $file"
+ exit 2
+ fi
+
+ notify "Typing $file"
+
+ exit 0
fi
- pass show "$file" | head -n 1 | perl -pe 'chomp' | xdotool type --file -
- notify-send -i "dialog-password" "ipass" "$file"
+ notify "No graphical environment detected"
+}
+
+pass_list() {
+ find ./* -type f \
+ | sed 's-^\./--' \
+ | sed 's-\.gpg$--'
+}
+
+pass_get() {
+ pass show "$1" \
+ | head -n 1 \
+ | perl -pe 'chomp'
+}
+
+notify() {
+ notify-send -i "dialog-password" "ipass" "$*"
}
main "$@"
diff --git a/.local/bin/irc b/.local/bin/irc
new file mode 120000
index 0000000..3da827c
--- /dev/null
+++ b/.local/bin/irc
@@ -0,0 +1 @@
+../share/wrapper.sh \ No newline at end of file
diff --git a/.local/bin/krokify b/.local/bin/krokify
new file mode 100755
index 0000000..a01c1f6
--- /dev/null
+++ b/.local/bin/krokify
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+
+import argparse
+import base64
+import sys
+import zlib
+
+def main():
+ arg_parser = argparse.ArgumentParser(
+ prog='krokify',
+ description='Turn a given file into a kroki link',
+ )
+ arg_parser.add_argument("file", default="-", help="Path to the file to krokify, or - to read from stdin")
+ arg_parser.add_argument("-f", "--format", default="svg", help="Which output format to use")
+ arg_parser.add_argument("-s", "--service", required=True, help="Which back-end service to use")
+ arg_parser.add_argument("-u", "--base-url", default="https://kroki.tyil.nl", help="The base URL of the Kroki server")
+
+ args = arg_parser.parse_args()
+
+ if args.file == "-":
+ stream = sys.stdin
+ else:
+ stream = open(args.file, "r")
+
+ url = base64.urlsafe_b64encode(zlib.compress(stream.read().encode('utf-8'), 9)).decode('ascii')
+
+ print(f"{args.base_url}/{args.service}/{args.format}/{url}")
+
+if __name__ == "__main__":
+ main()
diff --git a/.local/bin/kubectl-hosts b/.local/bin/kubectl-hosts
deleted file mode 100755
index c347256..0000000
--- a/.local/bin/kubectl-hosts
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-kubectl get ing -o custom-columns=:.spec.rules[].host \
- | grep . \
- | sort \
- | uniq
diff --git a/.local/bin/kubectl-rageclean-pod b/.local/bin/kubectl-rageclean-pod
new file mode 100755
index 0000000..bbd3ba7
--- /dev/null
+++ b/.local/bin/kubectl-rageclean-pod
@@ -0,0 +1,17 @@
+#!/usr/bin/env sh
+
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+
+main() {
+ kubectl delete pod --force --grace-period=0 "$@"
+}
+
+main "$@"
diff --git a/.local/bin/kubectl-secret b/.local/bin/kubectl-secret
new file mode 100755
index 0000000..ab2e924
--- /dev/null
+++ b/.local/bin/kubectl-secret
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+
+import argparse
+import base64
+import sys
+
+import ruamel.yaml
+import ruamel.yaml.scalarstring
+
+def main():
+ argparser = argparse.ArgumentParser(description="Encode or decode Kubernetes Secrets.")
+ argparser.add_argument("mode", help="Mode of operation, either encode or decode.", choices=["encode", "decode"])
+ argparser.add_argument("path", help="Path to the file to operate on. If set to -, STDIN will be used instead.", default="-", nargs="?")
+
+ args = argparser.parse_args()
+ yaml = ruamel.yaml.YAML()
+
+ # Deduce whether to read from STDIN or open a file handle to a given path
+ if args.path == "-":
+ buffer = sys.stdin
+ else:
+ buffer = open(args.path)
+
+ # Load the manifest
+ manifest = yaml.load(buffer.read())
+
+ # Handle any known potential issues
+ if not "kind" in manifest:
+ print("No kind in manifest", file=sys.stderr)
+ return 3
+
+ if manifest["kind"] != "Secret":
+ print("Not a secret", file=sys.stderr)
+ return 4
+
+ if not "data" in manifest:
+ manifest["data"] = {}
+
+ # Call appropriate function with the manifest
+ manifest = globals()["secret_" + args.mode](manifest)
+
+ # Write the processed manifest back as yaml
+ yaml.dump(manifest, sys.stdout)
+
+ return 0
+
+def secret_decode(manifest):
+ for key in manifest["data"].keys():
+ # Decode the data
+ manifest["data"][key] = base64.b64decode(manifest["data"][key]).decode("utf-8")
+
+ # Turn this element into a block quoted string if there are newlines
+ if "\n" in manifest["data"][key]:
+ manifest["data"][key] = ruamel.yaml.scalarstring.LiteralScalarString(manifest["data"][key])
+
+ return manifest
+
+def secret_encode(manifest):
+ for key in manifest["data"].keys():
+ # Encode the data
+ manifest["data"][key] = base64.b64encode(str(manifest["data"][key]).encode("utf-8")).decode("utf-8")
+
+ return manifest
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/.local/bin/media-import b/.local/bin/media-import
new file mode 100755
index 0000000..a1a99b2
--- /dev/null
+++ b/.local/bin/media-import
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+main() {
+ # Show help
+ if (( $# < 2 ))
+ then
+ echo "You need help"
+ return 2
+ fi
+
+ # Set the paths
+ local basedir="${MEDIA_BASEDIR:-/var/media}"
+ local mediadir="$basedir/$1" ; shift
+
+ # Ask for confirmation through a keypress
+ printf "Going to import %d URIs to %s...\n" "$#" "$mediadir"
+ read -s -n1
+
+ # Loop over all given args
+ for uri in "$@"
+ do
+ case "$uri" in
+ http://*) import_http "$mediadir" "$uri" ;;
+ https://*) import_http "$mediadir" "$uri" ;;
+ *) import_local "$mediadir" "$uri" ;;
+ esac
+ done
+}
+
+import_http() {
+ local dest="$1" ; shift
+ local uri="$1" ; shift
+ local temp="$(mktemp)"
+
+ wget -c -O "$temp" "$uri"
+
+ local output="$(import_local "$dest" "$temp" | awk '{ print $NF }')"
+
+ printf "%s -> %s\n" "$uri" "$output"
+}
+
+import_local() {
+ local dest="$1" ; shift
+ local uri="$1" ; shift
+ local hash="$(sha1sum "$uri" | awk '{ print $1 }')"
+ local ext="$(identify "$uri" | awk '{ print tolower($2) }')"
+ local out="$dest/$hash.$ext"
+
+ mv -- "$uri" "$out"
+ printf "%s -> %s\n" "$uri" "$out"
+}
+
+main "$@"
diff --git a/.local/bin/open b/.local/bin/open
index b71e6f7..9877708 100755
--- a/.local/bin/open
+++ b/.local/bin/open
@@ -61,6 +61,7 @@ multi sub cmd (HttpUrl $t where { $_.hostname.ends-with('gitlab.com') && $_.path
multi sub cmd (HttpUrl $t where { $_.hostname.ends-with('gitlab.com') && $_.path[0].?fc eq 'xxllnc' }) { "chromium '$t'" }
multi sub cmd (HttpUrl $t where *.hostname.ends-with('atlassian.net')) { "chromium '$t'" }
multi sub cmd (HttpUrl $t where *.hostname.ends-with('aws.amazon.com')) { "chromium '$t'" }
+multi sub cmd (HttpUrl $t where *.hostname.ends-with('awsapps.com')) { "chromium '$t'" }
multi sub cmd (HttpUrl $t where *.hostname.ends-with('google.com')) { "chromium '$t'" }
multi sub cmd (HttpUrl $t where *.hostname.ends-with('slack.com')) { "chromium '$t'" }
multi sub cmd (HttpUrl $t where *.hostname.ends-with('zaaksysteem.net')) { "chromium '$t'" }
@@ -79,7 +80,7 @@ my @not-reddit = <
>;
my @not-youtube = <
- yewtu.be
+ youtube.alt.tyil.nl
>;
multi sub cmd (HttpUrl $t is copy where *.hostname eq "twitter.com") { cmd(URL.new(|$t.Hash, hostname => @not-twitter.pick())) }
diff --git a/.local/bin/pkgsrc b/.local/bin/pkgsrc
deleted file mode 100755
index e6b1a7e..0000000
--- a/.local/bin/pkgsrc
+++ /dev/null
@@ -1,221 +0,0 @@
-#!/bin/sh
-
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU Affero General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option) any
-# later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-# details.
-
-readonly PKGSRC_ROOT="$HOME/.local/src/pkgsrc" # TODO: Make configurable
-readonly PKGSRC_DURATIONS="$XDG_DATA_HOME/pkgsrc/durations"
-
-main()
-{
- PKGSRC_INIT="$(time_unix)"
-
- # Handle opts
- while getopts ":h" opt
- do
- case "$opt" in
- h) usage && exit 0 ;;
- *)
- printf "Invalid option passed: %s\n" "$OPTARG" >&2
- ;;
- esac
- done
-
- shift $(( OPTIND - 1 ))
-
- if [ $# -lt 1 ]
- then
- usage
- exit 1
- fi
-
- PKGSRC_ACTION="action_$1" ; shift
-
- if ! command -V "$PKGSRC_ACTION" 2>/dev/null | grep -qwi function
- then
- usage
- exit 1
- fi
-
- $PKGSRC_ACTION "$@"
-}
-
-action_add()
-{
- mkdir -p -- "$PKGSRC_DURATIONS"
-
- for package in "$@"
- do
- cd -- "$PKGSRC_ROOT"
-
- # cd wont fail here if pkg_resolve had no output
- if ! cd -- "$(pkg_resolve "$package")"
- then
- printf "No package found for %s\n" "$package"
- continue
- fi
-
- bmake install \
- && bmake clean-depends \
- && bmake clean \
- || continue
-
- printf "%d\n" "$(( $(time_unix) - PKGSRC_INIT ))" \
- >> "$PKGSRC_DURATIONS/$package"
- done
-
- printf "Installation took %s\n" "$(time_duration "$PKGSRC_INIT" "$(time_unix)")"
-}
-
-action_del()
-{
- for package in "$@"
- do
- cd -- "$PKGSRC_ROOT"
-
- if ! cd -- "$(pkg_resolve "$package")"
- then
- printf "No package found for %s\n" "$package"
- continue
- fi
-
- bmake deinstall
- done
-}
-
-action_grep()
-{
- cd -- "$PKGSRC_ROOT"
-
- find . -maxdepth 2 -type d \
- | cut -c 3- \
- | grep "$@"
-}
-
-action_init()
-{
- export CVS_RSH="ssh"
-
- if [ ! -d "$PKGSRC_ROOT" ]
- then
- mkdir -p -- "${PKGSRC_ROOT%/*}"
- cd -- "${PKGSRC_ROOT%/*}"
- cvs -d anoncvs@anoncvs.NetBSD.org:/cvsroot checkout -P pkgsrc || die 1
- fi
-
- if [ -d "$HOME/.pkgsrc" ]
- then
- printf "pkgsrc seems to have been initialized already at $HOME/.pkgsrc\n"
- exit 1
- fi
-
- cd -- "$PKGSRC_ROOT/bootstrap"
- rm -fr -- work
- ./bootstrap --unprivileged --prefix="$HOME/.pkgsrc" # TODO: Make configurable
-
- printf "Bootstrapped in %s\n" "$(time_duration "$PKGSRC_INIT" "$(time_unix)")"
-}
-
-action_opts()
-{
- cd -- "$(pkg_resolve "$1")" || die "No package found for $1"
-
- bmake show-options
-}
-
-action_time()
-{
- for package in "$@"
- do
- if [ ! -f "$PKGSRC_DURATIONS/$package" ]
- then
- printf "%s: No timings\n" "$package"
- continue
- fi
-
- package_min="$(sort "$PKGSRC_DURATIONS/$package" | head -n 1)"
- package_max="$(sort "$PKGSRC_DURATIONS/$package" | tail -n 1)"
- package_avg="$(awk '{ sum += $1 } END { printf("%.0f", (sum / NR)) }' "$PKGSRC_DURATIONS/$package")"
-
- printf "%s: %s / %s / %s \n" \
- "$package" \
- "$(time_duration "$package_min")" \
- "$(time_duration "$package_avg")" \
- "$(time_duration "$package_max")"
- done
-}
-
-action_update()
-{
- cd -- "$PKGSRC_ROOT"
-
- cvs up
-
- printf "Updated in %s\n" "$(time_duration "$PKGSRC_INIT" "$(time_unix)")"
-}
-
-usage()
-{
- cat <<EOF
-Usage:
- ${0##*/} -h <action> [target]
-
-Interact with pkgsrc.
-
-Options:
- -h Show this help text and exit.
-
-Actions:
- add Install one or more TARGETs.
- del Uninstall one or more TARGETs.
- grep Search for TARGET.
- init Initialize the pkgsrc repository, and run the bootstrap script.
- opts Show the options of TARGET.
- time Show min, avg, max installation times for TARGETs.
- update Update the pkgsrc repository.
-EOF
-}
-
-#
-# Helper utilities
-#
-
-die()
-{
- printf "%s\n" "$*" >&2
- exit 1
-}
-
-pkg_resolve()
-{
- readlink -f "$PKGSRC_ROOT"/*/"$1" || readlink -f "$PKGSRC_ROOT/$1" # What if both fail?
-}
-
-time_unix()
-{
- awk 'BEGIN{srand();print srand()}'
-}
-
-time_duration()
-{
- if [ ! -z "$2" ]
- then
- elapsed="$(( $2 - $1 ))"
- else
- elapsed="$1"
- fi
-
- printf "%dh %02dm %02ds" \
- "$(( elapsed / 60 / 60 ))" \
- "$(( elapsed / 60 % 60))" \
- "$(( elapsed % 60 ))"
-}
-
-main "$@"
diff --git a/.local/bin/updot b/.local/bin/updot
new file mode 100755
index 0000000..f0179ad
--- /dev/null
+++ b/.local/bin/updot
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+
+main()
+{
+ DOTFILES_REMOTE="origin"
+ DOTFILES_BRANCH="master"
+
+ # Handle opts
+ while getopts ":h" opt
+ do
+ case "$opt" in
+ b) DOTFILES_BRANCH="$OPTARG" ;;
+ h) usage && exit 0 ;;
+ u) DOTFILES_REMOTE="$OPTARG" ;;
+ *)
+ printf "Invalid option passed: %s\n" "$OPTARG" >&2
+ ;;
+ esac
+ done
+
+ shift $(( OPTIND - 1 ))
+
+ # Stash any pending changes
+ git -C "$HOME" stash
+
+ # Update the main repository
+ git -C "$HOME" fetch "$DOTFILES_REMOTE" "$DOTFILES_BRANCH"
+ git -C "$HOME" reset --hard "$DOTFILES_REMOTE/$DOTFILES_BRANCH"
+
+ # Pull updates for all submodules
+ git -C "$HOME" submodule update --init --recursive --remote
+
+ # Apply the stash
+ git -C "$HOME" stash apply
+}
+
+usage()
+{
+ cat <<EOF
+Usage:
+ ${0##*/} -h
+ ${0##*/} [-b <branch>] [-u <remote>]
+
+Utility application for dotfile maintenance.
+
+Options:
+ -b The name of the branch to use, defaults to "master"
+ -h Show this help text and exit.
+ -u The name of the remote to use, defaults to "origin"
+EOF
+}
+
+main "$@"
diff --git a/.local/bin/vol b/.local/bin/vol
index 8993689..84c913e 100755
--- a/.local/bin/vol
+++ b/.local/bin/vol
@@ -40,6 +40,8 @@ main()
notify="Decreased '$DEFAULT_SINK_NAME' ($DEFAULT_SINK_INDEX) to $(volume_level)%"
;;
set)
+ [ -z "$2" ] && usage && exit 1
+
pactl set-sink-volume @DEFAULT_SINK@ $2% > /dev/null
notify="Set '$DEFAULT_SINK_NAME' ($DEFAULT_SINK_INDEX) to $(volume_level)%"
;;
diff --git a/.local/bin/x b/.local/bin/x
index ed5e26f..a47b1bc 100755
--- a/.local/bin/x
+++ b/.local/bin/x
@@ -12,12 +12,10 @@ then
exit 1
fi
- # Remove the old config
- rm -f -- "$HOME/.xinitrc"
-
- # Add the base configuration
- cat -- "$HOME/.local/etc/x/xinitrc" "$HOME/.local/etc/x/xinit.d/$1.rc" \
- >> "$HOME/.xinitrc"
+ cat \
+ "$HOME/.local/etc/x/xinitrc" \
+ "$HOME/.local/etc/x/xinit.d/$1.rc" \
+ > "$HOME/.xinitrc"
fi
# Make sure the xinitrc exists, just in case
@@ -28,4 +26,6 @@ then
fi
# start the X session
-exec startx
+startx
+systemctl --user stop desktop-x11.target
+systemctl --user stop desktop.target
diff --git a/.local/bin/xdg-open b/.local/bin/xdg-open
deleted file mode 120000
index ce4a72b..0000000
--- a/.local/bin/xdg-open
+++ /dev/null
@@ -1 +0,0 @@
-open \ No newline at end of file
diff --git a/.local/etc/wrapper.d/awesome.sh b/.local/etc/wrapper.d/awesome.sh
new file mode 100644
index 0000000..5d72452
--- /dev/null
+++ b/.local/etc/wrapper.d/awesome.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+WRAPPER_BINS="
+ /usr/bin/awesome
+ /run/current-system/sw/bin/awesome
+"
diff --git a/.local/etc/wrapper.d/firefox.rc b/.local/etc/wrapper.d/firefox.rc
deleted file mode 100644
index 43e745c..0000000
--- a/.local/etc/wrapper.d/firefox.rc
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-WRAPPER_BINS="
- /usr/bin/librewolf
- /usr/bin/librewolf-bin
-"
-
-WRAPPER_OPTS="--profile \"$HOME/.config/firefox\""
-
-FIREJAIL_PROFILE="$HOME/.local/etc/firejail/firefox.profile"
diff --git a/.local/etc/wrapper.d/firefox.sh b/.local/etc/wrapper.d/firefox.sh
new file mode 100644
index 0000000..af0905d
--- /dev/null
+++ b/.local/etc/wrapper.d/firefox.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+WRAPPER_BINS="
+ /usr/bin/librewolf
+ /usr/bin/librewolf-bin
+ /usr/bin/firefox
+ /usr/bin/firefox-esr
+ /usr/bin/firefox-bin
+ /run/current-system/sw/bin/firefox
+"
+
+WRAPPER_OPTS="
+ --profile $HOME/.config/firefox
+"
diff --git a/.local/etc/wrapper.d/herbstluftwm.sh b/.local/etc/wrapper.d/herbstluftwm.sh
new file mode 100644
index 0000000..d213bc7
--- /dev/null
+++ b/.local/etc/wrapper.d/herbstluftwm.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+WRAPPER_BINS="
+ /run/current-system/sw/bin/herbstluftwm
+ /usr/bin/herbstluftwm
+"
diff --git a/.local/etc/wrapper.d/irc.sh b/.local/etc/wrapper.d/irc.sh
new file mode 100644
index 0000000..965abfa
--- /dev/null
+++ b/.local/etc/wrapper.d/irc.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+WRAPPER_BINS="
+ /usr/bin/alacritty
+"
+
+WRAPPER_OPTS="
+ --class irc
+ --command mosh oolah .local/bin/ta irc
+"
diff --git a/.local/etc/wrapper.d/jellyfin.rc b/.local/etc/wrapper.d/jellyfin.sh
index 08105db..08105db 100644
--- a/.local/etc/wrapper.d/jellyfin.rc
+++ b/.local/etc/wrapper.d/jellyfin.sh
diff --git a/.local/etc/wrapper.d/mpv.rc b/.local/etc/wrapper.d/mpv.rc
deleted file mode 100644
index de66126..0000000
--- a/.local/etc/wrapper.d/mpv.rc
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-
-wrap_before() {
- if command -v mpc > /dev/null && mpc status | awk 'NR==2 { print }' | grep -iq playing
- then
- wrap_run mpc pause > /dev/null
- WRAPPER_X_MPD_PAUSED="yes"
- fi
-}
-
-wrap_after() {
- if [ "$WRAPPER_X_MPD_PAUSED" ]
- then
- wrap_run mpc play > /dev/null
- fi
-}
diff --git a/.local/etc/wrapper.d/mpv.sh b/.local/etc/wrapper.d/mpv.sh
new file mode 100644
index 0000000..3ed6eb4
--- /dev/null
+++ b/.local/etc/wrapper.d/mpv.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+wrap_before() {
+ if command -v mpc > /dev/null && mpc status | awk 'NR==2 { print }' | grep -iq playing
+ then
+ wrap_log "> mpc pause"
+ mpc pause > /dev/null
+ WRAPPER_X_MPD_PAUSED="yes"
+ fi
+
+ if [ "$(cat "$XDG_RUNTIME_DIR/xblank/paused")" = "0" ]
+ then
+ wrap_log "> xblank-toggle"
+ xblank-toggle
+ WRAPPER_X_XBLANK_PAUSED="yes"
+ fi
+}
+
+wrap_after() {
+ if [ "$WRAPPER_X_MPD_PAUSED" ]
+ then
+ wrap_log "> mpc play"
+ mpc play > /dev/null
+ fi
+
+ if [ "$WRAPPER_X_XBLANK_PAUSED" ]
+ then
+ wrap_log "> xblank-toggle"
+ xblank-toggle
+ fi
+}
diff --git a/.local/etc/wrapper.d/signal.rc b/.local/etc/wrapper.d/signal.sh
index 19f7352..19f7352 100644
--- a/.local/etc/wrapper.d/signal.rc
+++ b/.local/etc/wrapper.d/signal.sh
diff --git a/.local/etc/x/autostart b/.local/etc/x/autostart
new file mode 100644
index 0000000..f2f32ba
--- /dev/null
+++ b/.local/etc/x/autostart
@@ -0,0 +1,9 @@
+redshift -l 51.50:4.59 -t 6500:3500
+dunst
+sxhkd
+chwp
+xcompmgr
+xss-lock -- i3lock -c '#000000' -n
+ntfy subscribe --from-config
+nm-applet
+albert
diff --git a/.local/etc/x/xinit.d/awesome.rc b/.local/etc/x/xinit.d/awesome.rc
index bd004e8..a5f5e37 100644
--- a/.local/etc/x/xinit.d/awesome.rc
+++ b/.local/etc/x/xinit.d/awesome.rc
@@ -1,7 +1 @@
-sxhkd &
-chwp &
-xcompmgr &
-
-nm-applet &
-
exec dbus-launch --exit-with-session awesome
diff --git a/.local/etc/x/xinit.d/kde.rc b/.local/etc/x/xinit.d/kde.rc
new file mode 100644
index 0000000..71e6deb
--- /dev/null
+++ b/.local/etc/x/xinit.d/kde.rc
@@ -0,0 +1,13 @@
+# start dbus if necessary
+[ -f "/etc/X11/xinit/xinitrc.d/80-dbus" ] && source /etc/X11/xinit/xinitrc.d/80-dbus
+
+# Background services
+xcompmgr &
+redshift -l 51.50:4.59 &
+
+# Export additional environment vars
+export KDEWM=awesome
+export WINIT_X11_SCALE_FACTOR=1
+
+# Start KDE
+exec dbus-launch --exit-with-session startplasma-x11
diff --git a/.local/etc/x/xinitrc b/.local/etc/x/xinitrc
index df3590f..3d0f0d9 100644
--- a/.local/etc/x/xinitrc
+++ b/.local/etc/x/xinitrc
@@ -1,24 +1,13 @@
-# start dbus if necessary
-[ -f "/etc/X11/xinit/xinitrc.d/80-dbus" ] && source /etc/X11/xinit/xinitrc.d/80-dbus
-
-# start polkit auth agent
-eval $(/usr/bin/gnome-keyring-daemon --start --components=pkcs11,secrets,ssh,gpg)
-export GNOME_KEYRING_PID
-export GNOME_KEYRING_SOCKET
-export SSH_AUTH_SOCK
-
-# Export additional environment vars
-export WINIT_X11_SCALE_FACTOR=1
-
-# extend the fontpath
-xset +fp ~/.fonts/Terminus/BDF
-#xset +fp /usr/share/fonts/local
+#!/bin/sh
# set the default mousepointer
xsetroot -cursor_name left_ptr
# load the .Xresources
-xrdb -merge ~/.Xresources
+if command -v xrdb > /dev/null
+then
+ xrdb -merge ~/.Xresources
+fi
# Set a custom layout with xrandr
if [ -f "$HOME/.local/etc/x/layout.sh" ]
@@ -29,30 +18,11 @@ fi
# xkbmap options
setxkbmap \
-layout us \
- \
-option compose:menu \
-option caps:escape
# add altgr
xmodmap -e "keycode 108 = ISO_Level3_Shift"
-# Enable sound through PulseAudio
-pulseaudio --start
-
-# set a wallpaper
-chwp &
-
-# Turn off any screen blanking features
-xset s off
-xset -dpms
-
-# start background processes
-redshift -l 51.50:4.59 &
-dunst &
-sxhkd &
-
-# Set up a screenlock
-#xss-lock -- physlock -d &
-
-# Start x11vnc
-x11vnc -display "$DISPLAY" &
+# Export variables for a better X11 experience
+export WINIT_X11_SCALE_FACTOR=1
diff --git a/.local/share/applications/firefox.desktop b/.local/share/applications/firefox.desktop
new file mode 100644
index 0000000..0285394
--- /dev/null
+++ b/.local/share/applications/firefox.desktop
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Version=1.0
+Type=Application
+Exec=$HOME/.local/bin/firefox
+Name=Firefox
diff --git a/.local/share/wrapper.sh b/.local/share/wrapper.sh
index 336d6e7..9335039 100755
--- a/.local/share/wrapper.sh
+++ b/.local/share/wrapper.sh
@@ -13,7 +13,7 @@
main()
{
WRAPPER_BINS_NORMALIZED="$(mktemp)"
- WRAPPER_ETCFILE="$HOME/.local/etc/wrapper.d/${0##*/}.rc"
+ WRAPPER_ETCFILE="$HOME/.local/etc/wrapper.d/${0##*/}.sh"
if [ ! -f "$WRAPPER_ETCFILE" ]
then
@@ -68,7 +68,7 @@ main()
# Add a firejail wrapper if desired
if [ -n "$FIREJAIL_PROFILE" ]
then
- WRAPPER_CMD="firejail --profile=\"$FIREJAIL_PROFILE\" -- $WRAPPER_CMD"
+ WRAPPER_CMD="firejail --profile=""$FIREJAIL_PROFILE"" -- $WRAPPER_CMD"
fi
# Optionally add WRAPPER_OPTS, so theres no double space in
@@ -80,7 +80,8 @@ main()
# Run all the things we want to run
wrap_before
- wrap_run "$WRAPPER_CMD" "$@"
+ wrap_log "> $WRAPPER_CMD $*"
+ $WRAPPER_CMD "$@"
WRAPPER_EXITCODE=$?
wrap_after
@@ -98,11 +99,6 @@ wrap_log() {
printf "[%s] %s\n" "$(date -u +%FT%TZ)" "$*" >&2
}
-wrap_run() {
- wrap_log "> $*"
- eval "$@"
-}
-
wrap_before() { :; }
wrap_after() { :; }
diff --git a/.ssh/authorized_keys b/.ssh/authorized_keys
index 3a990b9..9198118 100644
--- a/.ssh/authorized_keys
+++ b/.ssh/authorized_keys
@@ -3,16 +3,27 @@ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEeckhvozWNKJssM+PY44fBXkzVeoqg9H8NHreWLUvGY
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID7lMmT3CJEPk1BnpW0UHie6g0LUJ/i45Sp5l4DGocwg root@denahnu.tyil.net
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID2tje8i3DtCmLrc+qJ7d02qQQ0lO7wtwWXO4XthWGra root@edephas.tyil.net
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICcFE9oQT3s+vLsv4WNKTn3K0ZhntStXdCHfdBPj/62V root@faiwoo.tyil.net
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICj0hW49y+AGuMN2D672I5K6ZVLPVZLCsd+2MIat54nP root@gaeru.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILmqoy/OXsmmNpxEN/xISbHwDFt2u8f3HmGIvS2CASHm root@gaeru.tyil.net
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOdRI1JSQnDYqoCkCrNz/T9mLSCNb5vRSnNjb/3CGtDK root@hurzak.tyil.net
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC2UxgdieMtF0a9QXFRb5QVYOwxG9oN1Q85///Y6VCZj root@tyil.email
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOLpn3Tny1LSWaLeIDmdAkZZoAajSJN9CQvfFdgLFfsK tyil@anoia.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC331lDhnHuQl4vkTUU5riqJ72ShdZN6zWdt1E3UJ/CJ root@krohxe.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJLcXzcOPEYQWEARFgPpZCq2NZhTBWTsIezd4Mrkt0PY root@mieshu.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIh9xYBxb5n2N20Dj03lsij32UkPJ27EMQ/6VdKhjWVJ root@nouki.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6oh68n5HXeK45YaNnQC0mHufB/bUgsEyE500OW40B1 root@oolah.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBnTRndwR171+hR+3yMpvmbRZoM8QFUE2zdOQZaqd24E root@plarabe.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE7T/5E/oA5ZO1VcIfaQGTvHcKn1dCh5APRLZH4CTNXf root@qohrei.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMtUkeSiwk+1UnMfy8Z53cQkKTlBBFZXUuDiXfPcalHj tyil@anoia.tyil.net
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILQp0puZEEADKgUF1XYtLPzcYLTGNdDj0WCTf37adaZ2 tyil@caeghi.tyil.net
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE4sustL8Mw5lQpyuUnmhJgeypvlHy6UFaEKmFCGcltL tyil@denahnu.tyil.net
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHiNIpe2QCOk22YYU/mY7QA2rq0E07wetzj5R1wtWxeC tyil@edephas.tyil.net
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFCiQJhuTvL0vZoOAu1L0sU81VV2qH1l4U73bL9RYfrW tyil@faiwoo.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ94ffGPvEb/Hi2B2XSaYjKpMiV93fzGLe0QUlXRJb1L tyil@gaeru.tyil.net
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINRprF4gE0pGiMNSxR8Z0fFsBikoifsm7HpdbHkBsmDg tyil@hurzak.tyil.net
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE+Ki28DBM3A8QUpxbAlZx2x111+rhn8JPcec67y9xi/ tyil@ivdea.tyil.net
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDQDyWbo8Ma60t5wwKK2bdxo03gj2GxxCyn2/V2nNr5Z tyil@tyil.email
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIVPGs2LkDvdkMzwR1Crk8OblMQD2snClUuIcYgUYcu4 tyil@ludifah.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFqLhjoIYRZmkD9sv1l1c03x6EpkadjfrGJ+4gqgkmp5 tyil@mieshu.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILNztf75LVF+UvoIDyduHfynZupdC+9g7RaIs6cGgmCa tyil@nouki.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJkjrJ6agLK5Bdg2Y5B+88XDbP5UsQyvdUbd3LrOVmjI tyil@oolah.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7XKD3KxXTe5GlM4w4xdap3VAPvYCi4EowD88ymInFR tyil@plarabe.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPyOy5m6FnzyUBHplh8Eukz6rqvMW/mUh1cqUQrO3L/R tyil@qohrei.tyil.net
-AAAAB3NzaC1yc2EAAAADAQABAAABAQDLRsHpUMeMrXr1s++ZJ7AQ5bLzBAHOQXkTd34Bin72aRD/iLVrEnxrE7Bk5UzWhvcedHHltpc8sdy0m8UFozprFeogD+1EpfdelycmfEBgzJE+p0+W/qsSMGy9AMgar7cNo8G4/eJr63XjyAdXG7eztmoz5Whgv0qlh2lW2dJ2orrlPF0bUA8tAx0ZAtJqvVEJZlXIlYqHCbYLHrif7w52UWV7UlkOosCl4FDsTO7BGkpRj6A/tGhu7gtTX1P9NQav2KFPvFko+S6jlEwCE1StwbiRop90grdv6OZ5UJe6KE2MEntwoW01y7BnB+lvgMTQyqKLKS1ffEbXIQPhbjBL yubikey (7 835 697)
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC2UxgdieMtF0a9QXFRb5QVYOwxG9oN1Q85///Y6VCZj root@tyil.email
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDQDyWbo8Ma60t5wwKK2bdxo03gj2GxxCyn2/V2nNr5Z tyil@tyil.email
diff --git a/.ssh/config b/.ssh/config
index aa9f734..c0a670d 100644
--- a/.ssh/config
+++ b/.ssh/config
@@ -1,46 +1,22 @@
-IdentityFile ~/.ssh/id.d/%u@%L-ed25519
UserKnownHostsFile ~/.ssh/known_hosts
Include config.d/*
-Host home.tyil.nl
- Hostname 10.57.100.7
- ForwardAgent yes
-
-Host anoia
- Hostname 10.57.100.3
+Host *
+ IdentityFile ~/.ssh/id.d/%u@%L-ed25519
-Host bast
- Hostname 10.57.100.4
-
-Host caeghi
- Hostname 10.57.20.2
+Host home.tyil.nl
+ Hostname 10.57.0.1
ForwardAgent yes
-Host denahnu
- Hostname 10.57.20.4
-
-Host edephas
- Hostname 10.57.100.7
-
-Host email
- Hostname 10.57.20.3
-
-Host faiwoo
- Hostname 10.57.20.5
-
-Host gaeru
- Hostname 10.57.20.6
-
-Host hurzak
- Hostname 10.57.20.7
-
-Host ivdea
- Hostname 10.57.100.8
+Host git.tyil.nl
+ Hostname 10.57.3.1
+ User git
Host rsync.net
User zh1193
Hostname zh1193.rsync.net
+# Test cluster
# Libvirt VMs
Host 192.168.122.*
User root
diff --git a/.ssh/config.d/mintlab-lab b/.ssh/config.d/mintlab-lab
deleted file mode 100644
index 9cca614..0000000
--- a/.ssh/config.d/mintlab-lab
+++ /dev/null
@@ -1,19 +0,0 @@
-Host *.lab.zsys.nl
- IdentityFile ~/.ssh/id.d/%u@%L-ed25519.pub
- IdentitiesOnly yes
- ProxyCommand zs-ec2-ssh -P default -k "$HOME/.ssh/id.d/$USER@$(hostname -s)-ed25519.pub" -p "%p" -u "%r" -- "$(zs-ec2-id -P default %h | head -n 1)"
- StrictHostKeyChecking no
- User mintlab
- UserKnownHostsFile /dev/null
-
-Host *.lab.ec2
- IdentityFile ~/.ssh/id.d/%u@%L-ed25519.pub
- ProxyCommand zs-ec2-ssh -P default -k "$HOME/.ssh/id.d/$USER@$(hostname -s)-ed25519.pub" -p "%p" -u "%r" -- "$(echo %h | awk -F. '{print $1}')"
- StrictHostKeyChecking no
- User mintlab
- UserKnownHostsFile /dev/null
-
-Host bastion.lab.zsys.nl
- Hostname bastion-host-zaaksysteem-lab
-
-# vim: set ft=sshconfig :
diff --git a/.ssh/config.d/tyilnet b/.ssh/config.d/tyilnet
new file mode 100644
index 0000000..4bad39d
--- /dev/null
+++ b/.ssh/config.d/tyilnet
@@ -0,0 +1,39 @@
+Host anoia
+ Hostname fd68:1057:1992:3381::0:3317:4
+
+Host caeghi
+ Hostname fd68:1057:1992:3381::1:1:1
+ ForwardAgent yes
+
+Host edephas
+ Hostname fd68:1057:1992:3381::0:3317:1
+
+Host faiwoo
+ Hostname fd68:1057:1992:3381::1:1:2
+
+Host gaeru
+ Hostname fd68:1057:1992:3381::1:2:1
+
+Host jaomox
+ Hostname fd68:1057:1992:3381::3:3:1
+
+Host ludifah
+ Hostname fd68:1057:1992:3381::0:3317:2
+
+Host mieshu
+ Hostname fd68:1057:1992:3381::3:3317:1
+
+Host nouki
+ Hostname fd68:1057:1992:3381::2:3317:1
+
+Host oolah
+ Hostname fd68:1057:1992:3381::1:3317:1
+
+Host plarabe
+ Hostname fd68:1057:1992:3381::0:3317:3
+
+Host qohrei
+ Hostname fd68:1057:1992:3381::1:1:3
+
+Host ricui
+ Hostname fd68:1057:1992:3381::1:1:4
diff --git a/.ssh/config.d/mintlab b/.ssh/config.d/xxllnc
index c90ce27..47643e6 100644
--- a/.ssh/config.d/mintlab
+++ b/.ssh/config.d/xxllnc
@@ -3,4 +3,8 @@ Host *.zsys.nl
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
+Host git.xxllnc.nl gitlab.com
+ Hostname gitlab.com
+ User git
+
# vim: set ft=sshconfig :
diff --git a/.ssh/config.d/mintlab-prd b/.ssh/config.d/xxllnc-zgw
index 25159aa..25159aa 100644
--- a/.ssh/config.d/mintlab-prd
+++ b/.ssh/config.d/xxllnc-zgw
diff --git a/.ssh/id.d/root@bast-ed25519.pub b/.ssh/id.d/root@bast-ed25519.pub
deleted file mode 100644
index e1d7ab3..0000000
--- a/.ssh/id.d/root@bast-ed25519.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICk/6jLojpp5Jaum8C1trxqtZuLd/GJH8sh0SB/Z/y9J root@bast
diff --git a/.ssh/id.d/root@gaeru-ed25519.pub b/.ssh/id.d/root@gaeru-ed25519.pub
index 3056a3d..fe3c6a7 100644
--- a/.ssh/id.d/root@gaeru-ed25519.pub
+++ b/.ssh/id.d/root@gaeru-ed25519.pub
@@ -1 +1 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICj0hW49y+AGuMN2D672I5K6ZVLPVZLCsd+2MIat54nP root@gaeru.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILmqoy/OXsmmNpxEN/xISbHwDFt2u8f3HmGIvS2CASHm root@gaeru.tyil.net
diff --git a/.ssh/id.d/root@krohxe-ed25519.pub b/.ssh/id.d/root@krohxe-ed25519.pub
new file mode 100644
index 0000000..ed9e5ff
--- /dev/null
+++ b/.ssh/id.d/root@krohxe-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC331lDhnHuQl4vkTUU5riqJ72ShdZN6zWdt1E3UJ/CJ root@krohxe.tyil.net
diff --git a/.ssh/id.d/root@mieshu-ed25519.pub b/.ssh/id.d/root@mieshu-ed25519.pub
new file mode 100644
index 0000000..0faf439
--- /dev/null
+++ b/.ssh/id.d/root@mieshu-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJLcXzcOPEYQWEARFgPpZCq2NZhTBWTsIezd4Mrkt0PY root@mieshu.tyil.net
diff --git a/.ssh/id.d/root@nouki-ed25519.pub b/.ssh/id.d/root@nouki-ed25519.pub
new file mode 100644
index 0000000..a19b34e
--- /dev/null
+++ b/.ssh/id.d/root@nouki-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIh9xYBxb5n2N20Dj03lsij32UkPJ27EMQ/6VdKhjWVJ root@nouki.tyil.net
diff --git a/.ssh/id.d/root@oolah-ed25519.pub b/.ssh/id.d/root@oolah-ed25519.pub
new file mode 100644
index 0000000..d4c3c0d
--- /dev/null
+++ b/.ssh/id.d/root@oolah-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6oh68n5HXeK45YaNnQC0mHufB/bUgsEyE500OW40B1 root@oolah.tyil.net
diff --git a/.ssh/id.d/root@plarabe-ed25519.pub b/.ssh/id.d/root@plarabe-ed25519.pub
new file mode 100644
index 0000000..01cdb82
--- /dev/null
+++ b/.ssh/id.d/root@plarabe-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMC9ggRv1prBjbIeqh+Q2VgX6tcxk/4UiQh4sZWJT9YI root@plarabe
diff --git a/.ssh/id.d/tyil@anoia-ed25519.pub b/.ssh/id.d/tyil@anoia-ed25519.pub
index aea0daa..f1b7158 100644
--- a/.ssh/id.d/tyil@anoia-ed25519.pub
+++ b/.ssh/id.d/tyil@anoia-ed25519.pub
@@ -1 +1 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOLpn3Tny1LSWaLeIDmdAkZZoAajSJN9CQvfFdgLFfsK tyil@anoia.tyil.net
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMtUkeSiwk+1UnMfy8Z53cQkKTlBBFZXUuDiXfPcalHj tyil@anoia
diff --git a/.ssh/id.d/tyil@bast-ed25519.pub b/.ssh/id.d/tyil@bast-ed25519.pub
deleted file mode 100644
index 00e492d..0000000
--- a/.ssh/id.d/tyil@bast-ed25519.pub
+++ /dev/null
@@ -1,5 +0,0 @@
-<<<<<<< HEAD
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXhPa+EGS4NySl0YqG38xGEab6uqdimseqq4tlLWyV4 tyil@bast.tyil.net
-=======
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILE1+6HjG3XvLQDHLwnFzq78SEsPTNa8Wu6+inmTMqu7 tyil@bast
->>>>>>> d8b0063 (Update pubkey for tyil@bast)
diff --git a/.ssh/id.d/tyil@gaeru-ed25519.pub b/.ssh/id.d/tyil@gaeru-ed25519.pub
new file mode 100644
index 0000000..d5632d9
--- /dev/null
+++ b/.ssh/id.d/tyil@gaeru-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ94ffGPvEb/Hi2B2XSaYjKpMiV93fzGLe0QUlXRJb1L tyil@gaeru.tyil.net
diff --git a/.ssh/id.d/tyil@ludifah-ed25519.pub b/.ssh/id.d/tyil@ludifah-ed25519.pub
new file mode 100644
index 0000000..e3503e7
--- /dev/null
+++ b/.ssh/id.d/tyil@ludifah-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIVPGs2LkDvdkMzwR1Crk8OblMQD2snClUuIcYgUYcu4 tyil@ludifah.tyil.net
diff --git a/.ssh/id.d/tyil@mieshu-ed25519.pub b/.ssh/id.d/tyil@mieshu-ed25519.pub
new file mode 100644
index 0000000..a70b37c
--- /dev/null
+++ b/.ssh/id.d/tyil@mieshu-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFqLhjoIYRZmkD9sv1l1c03x6EpkadjfrGJ+4gqgkmp5 tyil@mieshu.tyil.net
diff --git a/.ssh/id.d/tyil@nouki-ed25519.pub b/.ssh/id.d/tyil@nouki-ed25519.pub
new file mode 100644
index 0000000..52f292a
--- /dev/null
+++ b/.ssh/id.d/tyil@nouki-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILNztf75LVF+UvoIDyduHfynZupdC+9g7RaIs6cGgmCa tyil@nouki.tyil.net
diff --git a/.ssh/id.d/tyil@oolah-ed25519.pub b/.ssh/id.d/tyil@oolah-ed25519.pub
new file mode 100644
index 0000000..dabadac
--- /dev/null
+++ b/.ssh/id.d/tyil@oolah-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJkjrJ6agLK5Bdg2Y5B+88XDbP5UsQyvdUbd3LrOVmjI tyil@oolah.tyil.net
diff --git a/.ssh/id.d/tyil@plarabe-ed25519.pub b/.ssh/id.d/tyil@plarabe-ed25519.pub
new file mode 100644
index 0000000..25a5449
--- /dev/null
+++ b/.ssh/id.d/tyil@plarabe-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7XKD3KxXTe5GlM4w4xdap3VAPvYCi4EowD88ymInFR tyil@plarabe
diff --git a/.ssh/id.d/xxllnc@edephas-ed25519.pub b/.ssh/id.d/xxllnc@edephas-ed25519.pub
new file mode 100644
index 0000000..b2d20a8
--- /dev/null
+++ b/.ssh/id.d/xxllnc@edephas-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILvrXVgXTxw/Y684WFfrpIveRoTmdCNZ7Da3g4ZNhwjT tyil@edephas
diff --git a/.ssh/id.d/xxllnc@ivdea-ed25519.pub b/.ssh/id.d/xxllnc@ivdea-ed25519.pub
new file mode 100644
index 0000000..fa7994d
--- /dev/null
+++ b/.ssh/id.d/xxllnc@ivdea-ed25519.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHANHdBJh/dMbYhKCbHMtYo9F+OwyL7BaGb6JHDNYn9V tyil@ivdea
diff --git a/.vim/bundle/feature-camelcasemotion b/.vim/bundle/feature-camelcasemotion
new file mode 160000
+Subproject dbf66cf6d66f6716b72c6f683624ad694c0b8d4
diff --git a/.vim/bundle/feature-rainbow-parenthesis b/.vim/bundle/feature-rainbow-parenthesis
new file mode 160000
+Subproject 61f719aebe0dc5c3048330c50db72cfee1afdd3
diff --git a/.vim/bundle/syntax-blade b/.vim/bundle/syntax-blade
new file mode 160000
+Subproject 9534101808cc320eef003129a40cab04b026a20
diff --git a/.vim/bundle/syntax-go b/.vim/bundle/syntax-go
new file mode 160000
+Subproject feef9b31507f8e942bcd21f9e1f22d587c83c72
diff --git a/.vim/bundle/syntax-hcl b/.vim/bundle/syntax-hcl
new file mode 160000
+Subproject 1e1116c17a5774851360ea8077f349e36fc733c
diff --git a/.vim/bundle/syntax-helm b/.vim/bundle/syntax-helm
new file mode 160000
+Subproject 86cc6766db60b8cc5731068d0c2111dee1f2b32
diff --git a/.vim/bundle/syntax-ledger b/.vim/bundle/syntax-ledger
new file mode 160000
+Subproject 281346a221434574dd7f8767a352b2bf0b218b7
diff --git a/.vim/bundle/syntax-markdown b/.vim/bundle/syntax-markdown
new file mode 160000
+Subproject a657e697376909c41475a686eeef7fc7a4972d9
diff --git a/.vim/bundle/syntax-mustache b/.vim/bundle/syntax-mustache
new file mode 160000
+Subproject 0153fe03a919add2d6cf2d41b2d5b6e1188bc0e
diff --git a/.vim/bundle/syntax-raku b/.vim/bundle/syntax-raku
new file mode 160000
+Subproject 84a65c415f966c3cd5536fdbc6d70ee8f0d7350
diff --git a/.vim/bundle/syntax-terraform b/.vim/bundle/syntax-terraform
new file mode 160000
+Subproject 21f756b933cd11ac5990a6046fdc7c4e2a6c0ae
diff --git a/.vim/bundle/syntax-todo b/.vim/bundle/syntax-todo
new file mode 160000
+Subproject 3bb5f9cf0d6c7ee91b476a97054c336104d2b6f
diff --git a/.vim/bundle/syntax-toml b/.vim/bundle/syntax-toml
new file mode 160000
+Subproject d36caa6b1cf508a4df1c691f915572fc0214325
diff --git a/.vim/bundle/theme-codedark b/.vim/bundle/theme-codedark
new file mode 160000
+Subproject 05d7843412c4fb7d1bdafefd04462ac4db841f0
diff --git a/.vim/bundle/theme-colorsbox b/.vim/bundle/theme-colorsbox
new file mode 160000
+Subproject 77de081eab27872f94c4eab120e23e4ca5481b7
diff --git a/.vim/bundle/theme-jellybeans b/.vim/bundle/theme-jellybeans
new file mode 160000
+Subproject ef83bf4dc8b3eacffc97bf5c96ab2581b415c9f
diff --git a/.vim/bundle/theme-molokai b/.vim/bundle/theme-molokai
new file mode 160000
+Subproject c67bdfcdb31415aa0ade7f8c003261700a88547
diff --git a/.vim/ftplugin/php.vim b/.vim/ftplugin/php.vim
new file mode 100644
index 0000000..b8df966
--- /dev/null
+++ b/.vim/ftplugin/php.vim
@@ -0,0 +1,3 @@
+set shiftwidth=4
+set tabstop=4
+set expandtab
diff --git a/.vim/ftplugin/yaml.vim b/.vim/ftplugin/yaml.vim
index 0c0c07f..ae4ce66 100644
--- a/.vim/ftplugin/yaml.vim
+++ b/.vim/ftplugin/yaml.vim
@@ -1,3 +1,52 @@
set shiftwidth=2
set tabstop=2
set expandtab
+
+function! GetAncestors(line)
+ if(indent(a:line) == 0 && a:line == 1)
+ return ''
+ endif
+
+ if(indent(a:line) == 0 && a:line > 1)
+ if(getline(a:line) !~ '^\s*$') " not an empty line
+ return ''
+ else
+ " sometimes there are newlines within a multiline key
+ return GetAncestors(a:line-1) " return ancestors of previous line
+ endif
+ endif
+
+ let lowerIndent = indent(a:line)-1
+ " check if line is part of a list
+ let isList = ''
+ if(getline(a:line) =~# '^\s*-')
+ let isList = '[]'
+ " find the first key above this in the file that has is not a list member
+ let lastKeyLine = search('^\s\{0,'.indent(a:line).'}[^-]\S\+:', 'bnW')
+ else
+ " find the first key above this in the file that has a lower indent or a
+ " containing list member
+ let lastKeyLine = search('^\s\{0,'.lowerIndent.'}\(\S\+\|-\s\S\+\):', 'bnW')
+ let lastKeyLineContent = getline(lastKeyLine)
+ " check if the containing key is not a member of the same object
+ if(lastKeyLineContent =~# ':\s.\+$' && lastKeyLineContent =~# '^\s*-')
+ let lastKeyLine = search('^\s\{0,'.lowerIndent.'}\(\S\+\|-\s\S\+\):\s*$', 'zbnW')
+ let isList = '[]'
+ endif
+ endif
+
+ let key = matchstr(getline(lastKeyLine), '\s*[\-]\?\s*\zs.\+\ze:').isList
+
+ if(indent(lastKeyLine) > 0)
+ return GetAncestors(lastKeyLine).' . '.key
+ endif
+
+ return key
+endfunction
+
+augroup YamlRevealer
+ au!
+ if(&filetype =~# 'yaml')
+ autocmd CursorMoved <buffer> redraw | echo GetAncestors(line('.'))
+ endif
+aug END
diff --git a/.vim/plugin/k8s-edit-secret.vim b/.vim/plugin/k8s-edit-secret.vim
new file mode 100644
index 0000000..1a3e063
--- /dev/null
+++ b/.vim/plugin/k8s-edit-secret.vim
@@ -0,0 +1,60 @@
+" This program is free software: you can redistribute it and/or modify it under
+" the terms of the GNU Affero General Public License as published by the Free
+" Software Foundation, either version 3 of the License, or (at your option) any
+" later version.
+"
+" This program is distributed in the hope that it will be useful, but WITHOUT
+" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+" FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+" details.
+
+" Ensure this plugin is only loaded once
+if (exists("g:k8s_edit_secret") || &cp || exists("#K8sEditSecret"))
+ finish
+endif
+
+let g:k8s_edit_secret = "1"
+
+" Define the file pattern
+let g:k8s_edit_secret_file_pattern = get(g:, "k8s_edit_secret_file_pattern", "/tmp/kubectl-edit-*.yaml")
+
+" Define the decode function
+function K8sEditSecretDecode (...)
+ " Check if the file is really a secret
+ silent exe "!grep -q \"^kind: Secret$\" \"%:p\""
+
+ " Not a secret, just read the file as usual
+ if (v:shell_error)
+ return
+ endif
+
+ " Pipe buffer through kubectl secret decode
+ let view = winsaveview()
+ silent exe "%!kubectl secret decode"
+ call winrestview(view)
+endfunction
+
+" Define the encode function
+function K8sEditSecretEncode (...)
+ " Check if the file is really a secret
+ silent exe "!grep -q \"^kind: Secret$\" \"%:p\""
+
+ " Not a secret, just read the file as usual
+ if (v:shell_error)
+ return
+ endif
+
+ " Pipe buffer through kubectl secret encode
+ let view = winsaveview()
+ silent exe "%!kubectl secret encode"
+ call winrestview(view)
+endfunction
+
+" Define the autocmd
+augroup K8sEditSecret
+ autocmd!
+
+ " Register autocmd on read/write
+ exe "autocmd BufWritePre " . g:k8s_edit_secret_file_pattern . " call K8sEditSecretEncode()"
+ exe "autocmd BufReadPost,BufWritePost " . g:k8s_edit_secret_file_pattern . " call K8sEditSecretDecode()"
+augroup END
diff --git a/.vim/vimrc b/.vim/vimrc
index 2ff5b86..b6172be 100644
--- a/.vim/vimrc
+++ b/.vim/vimrc
@@ -26,6 +26,9 @@ set ut=500
set wig=*.swp,*.bak,*.pyc,*.class,*.jar,*.gif,*.png,*.jpg,.precomp
set wmnu
+filetype plugin on
+syntax on
+
call PickTheme('codedark')
call SetIndent(8)
diff --git a/.weechat/trigger.conf b/.weechat/trigger.conf
new file mode 100644
index 0000000..b7daf6b
--- /dev/null
+++ b/.weechat/trigger.conf
@@ -0,0 +1,162 @@
+#
+# weechat -- trigger.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/weechat/quickstart/
+#
+
+[look]
+enabled = on
+monitor_strip_colors = off
+
+[color]
+flag_command = lightgreen
+flag_conditions = yellow
+flag_post_action = lightblue
+flag_regex = lightcyan
+flag_return_code = lightmagenta
+identifier = cyan
+regex = white
+replace = cyan
+
+[trigger]
+beep.arguments = ""
+beep.command = "/print -beep"
+beep.conditions = "${tg_displayed} && (${tg_highlight} || ${tg_msg_pv})"
+beep.enabled = on
+beep.hook = print
+beep.post_action = none
+beep.regex = ""
+beep.return_code = ok
+botcolor_libera_skbot.arguments = "weechat_print"
+botcolor_libera_skbot.command = ""
+botcolor_libera_skbot.conditions = "${tg_tags} =~ ,nick_skbot,"
+botcolor_libera_skbot.enabled = on
+botcolor_libera_skbot.hook = modifier
+botcolor_libera_skbot.post_action = none
+botcolor_libera_skbot.regex = "/.*/${tg_prefix}\t${color:3}${tg_message_nocolor}"
+botcolor_libera_skbot.return_code = ok
+botcolor_rizon_internets.arguments = "weechat_print"
+botcolor_rizon_internets.command = ""
+botcolor_rizon_internets.conditions = "${tg_tags} =~ ,nick_internets,"
+botcolor_rizon_internets.enabled = on
+botcolor_rizon_internets.hook = modifier
+botcolor_rizon_internets.post_action = none
+botcolor_rizon_internets.regex = "/.*/${tg_prefix}\t${color:3}${tg_message_nocolor}"
+botcolor_rizon_internets.return_code = ok
+botcolor_rizon_quotes.arguments = "weechat_print"
+botcolor_rizon_quotes.command = ""
+botcolor_rizon_quotes.conditions = "${tg_tags} =~ ,nick_quotes,"
+botcolor_rizon_quotes.enabled = on
+botcolor_rizon_quotes.hook = modifier
+botcolor_rizon_quotes.post_action = none
+botcolor_rizon_quotes.regex = "/.*/${tg_prefix}\t${color:3}${tg_message_nocolor}"
+botcolor_rizon_quotes.return_code = ok
+botcolor_rizon_yt_info.arguments = "weechat_print"
+botcolor_rizon_yt_info.command = ""
+botcolor_rizon_yt_info.conditions = "${tg_tags} =~ ,nick_yt-info,"
+botcolor_rizon_yt_info.enabled = on
+botcolor_rizon_yt_info.hook = modifier
+botcolor_rizon_yt_info.post_action = none
+botcolor_rizon_yt_info.regex = "/.*/${tg_prefix}\t${color:3}${tg_message_nocolor}"
+botcolor_rizon_yt_info.return_code = ok
+cmd_pass.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth"
+cmd_pass.command = ""
+cmd_pass.conditions = ""
+cmd_pass.enabled = on
+cmd_pass.hook = modifier
+cmd_pass.post_action = none
+cmd_pass.regex = "==^((/(msg|m|quote) +(-server +[^ ]+ +)?nickserv +(id|identify|set +password|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+|recover +[^ ]+) +)|/oper +[^ ]+ +|/quote +pass +|/set +[^ ]*password[^ ]* +|/secure +(passphrase|decrypt|set +[^ ]+) +)(.*)==${re:1}${hide:*,${re:+}}"
+cmd_pass.return_code = ok
+cmd_pass_register.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth"
+cmd_pass_register.command = ""
+cmd_pass_register.conditions = ""
+cmd_pass_register.enabled = on
+cmd_pass_register.hook = modifier
+cmd_pass_register.post_action = none
+cmd_pass_register.regex = "==^(/(msg|m|quote) +nickserv +register +)([^ ]+)(.*)==${re:1}${hide:*,${re:3}}${re:4}"
+cmd_pass_register.return_code = ok
+msg_auth.arguments = "5000|irc_message_auth"
+msg_auth.command = ""
+msg_auth.conditions = ""
+msg_auth.enabled = on
+msg_auth.hook = modifier
+msg_auth.post_action = none
+msg_auth.regex = "==^(.*(id|identify|set +password|register|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+|recover +[^ ]+) +)(.*)==${re:1}${hide:*,${re:+}}"
+msg_auth.return_code = ok
+notify_highlight.arguments = ""
+notify_highlight.command = "/exec -norc -nosw -bg ntfy pub -p 3 -T "speech_balloon" -t "${buffer.full_name}" -m "${tg_message}" chat"
+notify_highlight.conditions = "${tg_highlight} && ${buffer.notify} > 0 && ${tg_prefix_nocolor} !~ ^[[:space:]]+$"
+notify_highlight.enabled = on
+notify_highlight.hook = print
+notify_highlight.post_action = none
+notify_highlight.regex = "/.*/${weechat.look.nick_prefix}${tg_prefix_nocolor}${weechat.look.nick_suffix} ${tg_message_nocolor}/"
+notify_highlight.return_code = ok
+notify_privmsg.arguments = ""
+notify_privmsg.command = "/exec -norc -nosw -bg ntfy pub -p 3 -T "speech_balloon" -t "${buffer.full_name}" -m "${tg_message}" chat"
+notify_privmsg.conditions = "${tg_tag_notify} == private && ${buffer.notify} > 0"
+notify_privmsg.enabled = on
+notify_privmsg.hook = print
+notify_privmsg.post_action = none
+notify_privmsg.regex = "/.*/${tg_message_nocolor}/"
+notify_privmsg.return_code = ok
+notify_xxllnc_help-platform-xcp.arguments = ""
+notify_xxllnc_help-platform-xcp.command = "/exec -norc -nosw -bg ntfy pub -p 5 -T "right_anger_bubble" -t "${buffer.full_name}" -m "${tg_message}" chat"
+notify_xxllnc_help-platform-xcp.conditions = "${buffer.full_name} == python.slack.exxellencehq.#help-platform-xcp"
+notify_xxllnc_help-platform-xcp.enabled = on
+notify_xxllnc_help-platform-xcp.hook = print
+notify_xxllnc_help-platform-xcp.post_action = none
+notify_xxllnc_help-platform-xcp.regex = "/.*/${tg_prefix_nocolor}: ${tg_message_nocolor}/"
+notify_xxllnc_help-platform-xcp.return_code = ok
+notify_xxllnc_team-xcp.arguments = ""
+notify_xxllnc_team-xcp.command = "/exec -norc -nosw -bg ntfy pub -p 5 -T "right_anger_bubble" -t "${buffer.full_name}" -m "${tg_message}" chat"
+notify_xxllnc_team-xcp.conditions = "${buffer.full_name} == python.slack.exxellencehq.&team-xcp && ${tg_prefix_nocolor} != patrick.spek"
+notify_xxllnc_team-xcp.enabled = on
+notify_xxllnc_team-xcp.hook = print
+notify_xxllnc_team-xcp.post_action = none
+notify_xxllnc_team-xcp.regex = "/.*/${tg_prefix_nocolor}: ${tg_message_nocolor}/"
+notify_xxllnc_team-xcp.return_code = ok
+relaybot_libera_scriptkitties_gizmo.arguments = "weechat_print"
+relaybot_libera_scriptkitties_gizmo.command = ""
+relaybot_libera_scriptkitties_gizmo.conditions = "${tg_tag_host} == bot@darkorb.robotham.dev"
+relaybot_libera_scriptkitties_gizmo.enabled = on
+relaybot_libera_scriptkitties_gizmo.hook = modifier
+relaybot_libera_scriptkitties_gizmo.post_action = none
+relaybot_libera_scriptkitties_gizmo.regex = "/.*<([^>]+)> (.*)/${re:1}\t${re:2}/"
+relaybot_libera_scriptkitties_gizmo.return_code = ok
+relaybot_oftc_scriptkitties_skbot.arguments = "weechat_print"
+relaybot_oftc_scriptkitties_skbot.command = ""
+relaybot_oftc_scriptkitties_skbot.conditions = "${tg_tag_host} == ~skbot@2a01:4f9:c010:e20c::1"
+relaybot_oftc_scriptkitties_skbot.enabled = on
+relaybot_oftc_scriptkitties_skbot.hook = modifier
+relaybot_oftc_scriptkitties_skbot.post_action = none
+relaybot_oftc_scriptkitties_skbot.regex = "/[^\[]*\[(\w+)\] <(\W?)([^>]+)> (.*)/${color:darkgray}${re:2}${color:brown}${re:3}${color:darkgray}@${color:brown}${re:1}${color:reset}\t${re:4}/"
+relaybot_oftc_scriptkitties_skbot.return_code = ok
+relaybot_oftc_scriptkitties_skbot_action.arguments = "weechat_print"
+relaybot_oftc_scriptkitties_skbot_action.command = ""
+relaybot_oftc_scriptkitties_skbot_action.conditions = "${tg_tag_host} == ~skbot@2a01:4f9:c010:e20c::1"
+relaybot_oftc_scriptkitties_skbot_action.enabled = on
+relaybot_oftc_scriptkitties_skbot_action.hook = modifier
+relaybot_oftc_scriptkitties_skbot_action.post_action = none
+relaybot_oftc_scriptkitties_skbot_action.regex = "/[^\[]*\[(\w+)\] \* (\W?)(\S+) (.*)/*\t${color:brown}${re:3}${color:darkgray}@${color:brown}${re:1}${color:reset} ${re:4}/"
+relaybot_oftc_scriptkitties_skbot_action.return_code = ok
+selfcolor_msg_tyil.arguments = "weechat_print"
+selfcolor_msg_tyil.command = ""
+selfcolor_msg_tyil.conditions = "${tg_tags} =~ ,nick_tyil,"
+selfcolor_msg_tyil.enabled = on
+selfcolor_msg_tyil.hook = modifier
+selfcolor_msg_tyil.post_action = none
+selfcolor_msg_tyil.regex = "/.*/${tg_prefix}\t${color:7}${tg_message_nocolor}"
+selfcolor_msg_tyil.return_code = ok
+server_pass.arguments = "5000|input_text_display;5000|history_add"
+server_pass.command = ""
+server_pass.conditions = ""
+server_pass.enabled = on
+server_pass.hook = modifier
+server_pass.post_action = none
+server_pass.regex = "==^(/(server|connect) .*-(sasl_)?password=)([^ ]+)(.*)==${re:1}${hide:*,${re:4}}${re:5}"
+server_pass.return_code = ok
diff --git a/.weechat/vendor/protocol-slack b/.weechat/vendor/protocol-slack
new file mode 160000
+Subproject 0b5069e600b6fbe72affed97f0b36ef923ba407
diff --git a/.weechat/weechat.conf b/.weechat/weechat.conf
index 439288d..ae4e869 100644
--- a/.weechat/weechat.conf
+++ b/.weechat/weechat.conf
@@ -6,9 +6,11 @@
#
# Use commands like /set or /fset to change settings in WeeChat.
#
-# For more info, see: https://weechat.org/doc/quickstart
+# For more info, see: https://weechat.org/doc/weechat/quickstart/
#
+config_version = 2
+
[debug]
[startup]
@@ -36,6 +38,7 @@ buffer_search_regex = off
buffer_search_where = message
buffer_time_format = "${color:8}%H%M"
buffer_time_same = ""
+chat_space_right = off
color_basic_force_bold = off
color_inactive_buffer = off
color_inactive_message = on
@@ -56,8 +59,9 @@ day_change_message_2dates = "-- %%a, %%d %%b %%Y (%a, %d %b %Y) --"
eat_newline_glitch = off
emphasized_attributes = ""
highlight = ""
+highlight_disable_regex = ""
highlight_regex = ""
-highlight_tags = "slack_channel+nick_pingdom,slack_channel+nick_platform,slack_channel+nick_grafana"
+highlight_tags = ""
hotlist_add_conditions = "${away} || ${buffer.num_displayed} == 0"
hotlist_buffer_separator = " "
hotlist_count_max = 1
@@ -74,6 +78,7 @@ hotlist_suffix = ""
hotlist_unique_numbers = on
hotlist_update_on_buffer_switch = on
input_cursor_scroll = 20
+input_multiline_lead_linebreak = on
input_share = none
input_share_overwrite = off
input_undo_max = 32
@@ -95,7 +100,6 @@ nick_color_hash_salt = ""
nick_color_stop_chars = "_|["
nick_prefix = ""
nick_suffix = ":"
-paste_auto_add_newline = on
paste_bracketed = off
paste_bracketed_timer_delay = 10
paste_max_lines = 1
@@ -177,6 +181,8 @@ chat_prefix_suffix = 8
chat_read_marker = 9
chat_read_marker_bg = default
chat_server = 3
+chat_status_disabled = red
+chat_status_enabled = green
chat_tags = 9
chat_text_found = 11
chat_text_found_bg = 13
@@ -204,7 +210,8 @@ status_filter = 2
status_more = 11
status_mouse = 2
status_name = white
-status_name_ssl = 2
+status_name_insecure = lightmagenta
+status_name_tls = white
status_nicklist_count = default
status_number = default
status_time = default
@@ -249,7 +256,6 @@ proxy_curl = ""
[plugin]
autoload = "*"
-debug = off
extension = ".so,.dll"
path = "%h/plugins"
save_config_on_unload = on
@@ -413,6 +419,8 @@ vi_line_numbers.size = 0
vi_line_numbers.size_max = 0
vi_line_numbers.type = window
+[custom_bar_item]
+
[layout]
[notify]
@@ -436,8 +444,8 @@ irc.bitlbee.#secretaries = message
irc.bitlbee.#supportcenter = message
irc.bitlbee.#theladies = message
irc.bitlbee.patrick = message
-irc.darenet.#computertech = highlight
irc.darenet.#DareNET = highlight
+irc.darenet.#computertech = highlight
irc.darenet.#dnet.lua = highlight
irc.darenet.#dronebl = highlight
irc.darenet.#help = message
@@ -469,12 +477,17 @@ irc.libera.#libera = highlight
irc.libera.#libera-communities = highlight
irc.libera.#lobsters = highlight
irc.libera.#mageia = highlight
+irc.libera.#pkgsrc = highlight
irc.libera.#raku = highlight
irc.libera.#raku-dev = highlight
+irc.libera.#scriptkitties = none
+irc.libera.#sr.ht = highlight
irc.oftc.#awesome = highlight
+irc.oftc.#debian-mentors = highlight
irc.rizon.#/g/technology = highlight
irc.rizon.#computertech = highlight
irc.rizon.#nerds = highlight
+irc.rizon.#scriptkitties = none
irc.rizon.#xmas = highlight
irc.technet.#computertech = highlight
irc.technet.#nitemare = highlight
@@ -485,106 +498,71 @@ irc.twitch.#guardsmanbob = highlight
irc.twitch.#mrllamasc = highlight
irc.twitch.#sovietwomble = highlight
irc.twitch.#wintergaming = highlight
-python.matrix.matrix_org.!ddzRCzutkWCgzgivqI:matrix.org = none
python.matrix.matrix_org.!NaLbfexyNIBQDbzGwB:bonifacelabs.ca = highlight
python.matrix.matrix_org.!YfFXwehASBiYzWXVUj:matrix.org = none
-python.matrix.matrix_org.!yIUYRaVMuLMdcfxSBy:libera.chat = highlight
python.matrix.matrix_org.!ZNbQEkfWfdwLeUksKM:matrix.org = message
-python.mintlab.slack.com.#bier = all
-python.mintlab.slack.com.#development = all
-python.mintlab.slack.com.#devops = all
-python.mintlab.slack.com.#devops-alerts = all
-python.mintlab.slack.com.#devops-ci = all
-python.mintlab.slack.com.#devops-info = all
-python.mintlab.slack.com.#general = all
-python.mintlab.slack.com.#growth = all
-python.mintlab.slack.com.#gtd-ninjas = all
-python.mintlab.slack.com.#holacracy = all
-python.mintlab.slack.com.#javascript = all
-python.mintlab.slack.com.#livingroom = all
-python.mintlab.slack.com.#praatpythonmetme = all
-python.mintlab.slack.com.#python = all
-python.mintlab.slack.com.#quality_assurance = all
-python.mintlab.slack.com.#release = all
-python.mintlab.slack.com.#secretaries = all
-python.mintlab.slack.com.#supportcenter = message
-python.mintlab.slack.com.&devops-team = all
-python.mintlab.slack.com.&devopsbff = all
-python.mintlab.slack.com.&meems = all
-python.mintlab.slack.com.&patricia = all
-python.mintlab.slack.com.&platform = all
-python.mintlab.slack.com.&storytellers = all
-python.mintlab.slack.com.&theladies = all
-python.mintlab.slack.com.Anouar = all
-python.mintlab.slack.com.Anouk = all
-python.mintlab.slack.com.arne = all
-python.mintlab.slack.com.Bas = all
-python.mintlab.slack.com.blessy = all
-python.mintlab.slack.com.Crein = all
-python.mintlab.slack.com.Flippy = all
-python.mintlab.slack.com.Hazal = all
-python.mintlab.slack.com.jascha = all
-python.mintlab.slack.com.jos = all
-python.mintlab.slack.com.Karly = all
-python.mintlab.slack.com.Kayleigh = all
-python.mintlab.slack.com.Laurens = all
-python.mintlab.slack.com.marcel = all
-python.mintlab.slack.com.Martijn = all
-python.mintlab.slack.com.MichelleVeltman = all
-python.mintlab.slack.com.michiel = all
-python.mintlab.slack.com.petermoen = all
-python.mintlab.slack.com.Remco = all
-python.mintlab.slack.com.Robin = all
-python.mintlab.slack.com.rudolf = all
-python.mintlab.slack.com.Slackbot = all
-python.mintlab.slack.com.wesleys = all
+python.matrix.matrix_org.!ddzRCzutkWCgzgivqI:matrix.org = none
+python.matrix.matrix_org.!yIUYRaVMuLMdcfxSBy:libera.chat = highlight
python.slack-debug = none
python.slack.exxellencehq.#devops-ci = highlight
python.slack.exxellencehq.#devops-info = highlight
python.slack.exxellencehq.#zaaksysteem-commits = highlight
python.slack.exxellencehq.#zs-devops-alerts-lab = highlight
python.slack.exxellencehq.#zs-devops-ci-lab = highlight
-python.slack.mintlab.#zaaksysteem-commits = highlight
-python.slack.mintlab.Karly = all
[filter]
-irc_smart = on;*;irc_smart_filter;*
animebytes.OlolEeLFai = on;irc.animebytes.*;nick_OlolEeLFai;*
animebytes.riria = on;irc.animebytes.*;nick_riria;*
animebytes.riria_ = on;irc.animebytes.*;nick_riria_;*
animebytes.vice = on;irc.animebytes.*;nick_vice;*
animebytes.vonf = on;irc.animebytes.*;nick_vonf;*
+bitlbee.disroot.marmulak = on;irc.bitlbee.#disroot;nick_marmulak;*
+darenet.brandon = on;irc.darenet.*;nick_brandon;*
+darenet.drnrelay.status = on;irc.darenet.#dronebl;nick_drnrelay;^\*\*\*
darenet.puddin = on;irc.darenet.*;nick_puddin;*
-freenode.blaise = on;irc.freenode.*;nick_blaise;*
-freenode.captain_beezay = on;irc.freenode.*;nick_captain_beezay;*
-freenode.dhscholb = on;irc.freenode.*;nick_dhscholb;*
-freenode.flunk = on;irc.freenode.*;nick_flunk;*
-freenode.flunk[m] = on;irc.freenode.*;nick_flunk[m];*
-freenode.frobnicator = on;irc.freenode.*;nick_frobnicator;*
-freenode.havis = on;irc.freenode.*;nick_havis;*
-freenode.zanariyo = on;irc.freenode.*;nick_zanariyo;*
-freenode_cancer_live = on;irc.server.freenode;*;\[Global Notice\].*freenode.*live
+irc.irc-nerds = on;irc.irc-nerds.*;nick_mouses;*
+irc_smart = on;*;irc_smart_filter;*
+libera.ampersam = on;irc.libera.*;nick_ampersam;*
+libera.curvechacha25519 = on;irc.libera.*;nick_curvechacha25519;*
+libera.diffiehellman = on;irc.libera.*;nick_diffiehellman;*
+libera.mnrmnaugh = on;irc.libera.*;nick_mnrmnaugh;*
+libera.monopoly = on;irc.libera.*;nick_monopoly;*
+libera.pinecone = on;irc.libera.*;nick_pinecone;*
+oftc.harmen = on;irc.oftc.*;nick_harmen;*
+perl.mst = on;irc.perl.*;nick_mst;*
rizon.99dog = on;irc.rizon.*;nick_`99dog;*
rizon.RestedAlteran = on;irc.rizon.*;nick_restedalteran;*
+rizon.ahsoka = on;irc.rizon.*;nick_ahsoka;*
rizon.anshin = on;irc.rizon.*;nick_anshin;*
rizon.autisomosis = on;irc.rizon.*;nick_autismosis;*
+rizon.brandon = on;irc.rizon.*;nick_brandon;*
rizon.candygumdrop = on;irc.rizon.*;nick_candygumdrop;*
+rizon.crow = on;irc.rizon.*;nick_crow;*
rizon.densaakalte = on;irc.rizon.*;nick_densaakalte;*
rizon.eleeko = on;irc.rizon.*;nick_eleeko;*
rizon.ellenor = on;irc.rizon.*;nick_ellenor;*
rizon.flap.discord = on;irc.rizon.*;nick_discord-relay;^<flap>
+rizon.foxy = on;irc.rizon.*;nick_foxy;*
rizon.fs = on;irc.rizon.*;nick_fs;*
+rizon.gb.welcome = on;irc.rizon.#ComputerTech;*;Welcome to #computertech
rizon.happybox = on;irc.rizon.*;nick_happybox;*
rizon.jubajuba = on;irc.rizon.*;nick_jubajuba;*
+rizon.karma = on;irc.rizon.*;nick_karma;*
+rizon.karma.trigger = on;irc.rizon.#ComputerTech;*;\!jizz
rizon.kenster = on;irc.rizon.*;nick_kenster;*
rizon.lighto = on;irc.rizon.*;nick_lighto;*
rizon.mabynogy = on;irc.rizon.*;nick_mabynogy;*
rizon.maznaz = on;irc.rizon.*;nick_maznaz;*
rizon.meekcell = on;irc.rizon.*;nick_meekcell;*
rizon.mikoolo = on;irc.rizon.*;nick_mikoolo;*
+rizon.mr_cat = on;irc.rizon.*;nick_mr_cat;*
rizon.mrfifths = on;irc.rizon.*;nick_mrfifths;*
+rizon.naemuti = on;irc.rizon.*;nick_naemuti;*
+rizon.naemutti = on;irc.rizon.*;nick_naemutti;*
+rizon.phonekait = on;irc.rizon.*;nick_phonekait;*
rizon.s7sost = on;irc.rizon.*;nick_s7sost;*
rizon.short_circuit = on;irc.rizon.*;nick_short_circuit;*
+rizon.sorcerer = on;irc.rizon.*;nick_sorcerer;*
rizon.stoned_immaculate = on;irc.rizon.*;nick_stoned_immaculate;*
rizon.trentski = on;irc.rizon.*;nick_trentski;*
rizon.trivia = on;irc.rizon.*;nick_trivia;*
@@ -597,81 +575,57 @@ twitch.guardsmanbob.!# = on;irc.twitch.#guardsmanbob;*;^\![0-3]
twitch.guardsmanbob.!pick = on;irc.twitch.#guardsmanbob;*;\!pick.*
twitch.guardsmanbob.!rate = on;irc.twitch.#guardsmanbob;*;\!rate.*
twitch.guardsmanbob.!wins = on;irc.twitch.#guardsmanbob;*;\!wins.*
-darenet.brandon = on;irc.darenet.*;nick_brandon;*
-perl.mst = on;irc.perl.*;nick_mst;*
-freenode.mst = on;irc.freenode.*;nick_mst;*
-bitlbee.disroot.marmulak = on;irc.bitlbee.#disroot;nick_marmulak;*
-rizon.brandon = on;irc.rizon.*;nick_brandon;*
-rizon.karma = on;irc.rizon.*;nick_karma;*
-rizon.gb.welcome = on;irc.rizon.#ComputerTech;*;Welcome to #computertech
-rizon.karma.trigger = on;irc.rizon.#ComputerTech;*;\!jizz
-rizon.sorcerer = on;irc.rizon.*;nick_sorcerer;*
-rizon.crow = on;irc.rizon.*;nick_crow;*
-rizon.naemutti = on;irc.rizon.*;nick_naemutti;*
-freenode.karty = on;irc.freenode.*;nick_karthy;*
-rizon.foxy = on;irc.rizon.*;nick_foxy;*
-rizon.naemuti = on;irc.rizon.*;nick_naemuti;*
-freenode.kill-animals = on;irc.freenode.*;host_uid481358@gateway/web/irccloud.com/x-ctajnhqqypxnmmxf;*
-freenode.kill-9-animals = on;irc.freenode.*;host_uid481358@gateway/web/irccloud.com/x-sqvzvbzqnylvwake;*
-darenet.drnrelay.status = on;irc.darenet.#dronebl;nick_drnrelay;^\*\*\*
-rizon.phonekait = on;irc.rizon.*;nick_phonekait;*
-libera.monopoly = on;irc.libera.*;nick_monopoly;*
-libera.diffiehellman = on;irc.libera.*;nick_diffiehellman;*
-libera.pinecone = on;irc.libera.*;nick_pinecone;*
-libera.ampersam = on;irc.libera.*;nick_ampersam;*
-oftc.harmen = on;irc.oftc.*;nick_harmen;*
-libera.mnrmnaugh = on;irc.libera.*;nick_mnrmnaugh;*
-libera.curvechacha25519 = on;irc.libera.*;nick_curvechacha25519;*
-rizon.mr_cat = on;irc.rizon.*;nick_mr_cat;*
[key]
-ctrl-? = "/input delete_previous_char"
-ctrl-A = "/input move_beginning_of_line"
-ctrl-B = "/input move_previous_char"
-ctrl-C_ = "/input insert \x1F"
-ctrl-Cb = "/input insert \x02"
-ctrl-Cc = "/input insert \x03"
-ctrl-Ci = "/input insert \x1D"
-ctrl-Co = "/input insert \x0F"
-ctrl-Cr = "/input insert \x12"
-ctrl-Cu = "/input insert \x15"
-ctrl-Cv = "/input insert \x16"
-ctrl-D = "/input delete_next_char"
-ctrl-E = "/input move_end_of_line"
-ctrl-F = "/input move_next_char"
-ctrl-H = "/input delete_previous_char"
-ctrl-I = "/input complete_next"
-ctrl-J = "/buffer +1"
-ctrl-K = "/buffer -1"
-ctrl-L = "/window refresh"
-ctrl-M = "/input return"
-ctrl-N = "/buffer +1"
-ctrl-P = "/buffer -1"
-ctrl-R = "/input search_text"
-ctrl-Sctrl-U = "/input set_unread"
-ctrl-T = "/input transpose_chars"
-ctrl-U = "/input delete_beginning_of_line"
-ctrl-W = "/input delete_previous_word"
-ctrl-X = "/input switch_active_buffer"
-ctrl-Y = "/input clipboard_paste"
-meta-meta-OP = "/bar scroll buflist * b"
-meta-meta-OQ = "/bar scroll buflist * e"
-meta-meta2-11~ = "/bar scroll buflist * b"
-meta-meta2-12~ = "/bar scroll buflist * e"
-meta-meta2-1~ = "/window scroll_top"
-meta-meta2-23~ = "/bar scroll nicklist * b"
-meta-meta2-24~ = "/bar scroll nicklist * e"
-meta-meta2-4~ = "/window scroll_bottom"
-meta-meta2-5~ = "/window scroll_up"
-meta-meta2-6~ = "/window scroll_down"
-meta-meta2-7~ = "/window scroll_top"
-meta-meta2-8~ = "/window scroll_bottom"
-meta-meta2-A = "/buffer -1"
-meta-meta2-B = "/buffer +1"
-meta-meta2-C = "/buffer +1"
-meta-meta2-D = "/buffer -1"
+backspace = "/input delete_previous_char"
+ctrl-_ = "/input undo"
+ctrl-a = "/input move_beginning_of_line"
+ctrl-b = "/input move_previous_char"
+ctrl-c,_ = "/input insert \x1F"
+ctrl-c,b = "/input insert \x02"
+ctrl-c,c = "/input insert \x03"
+ctrl-c,i = "/input insert \x1D"
+ctrl-c,o = "/input insert \x0F"
+ctrl-c,r = "/input insert \x12"
+ctrl-c,u = "/input insert \x15"
+ctrl-c,v = "/input insert \x16"
+ctrl-d = "/input delete_next_char"
+ctrl-down = "/input history_global_next"
+ctrl-e = "/input move_end_of_line"
+ctrl-f = "/input move_next_char"
+ctrl-f1 = "/bar scroll buflist * -100%"
+ctrl-f2 = "/bar scroll buflist * +100%"
+ctrl-k = "/buffer -1"
+ctrl-l = "/window refresh"
+ctrl-left = "/input move_previous_word"
+ctrl-n = "/buffer +1"
+ctrl-p = "/buffer -1"
+ctrl-r = "/input search_text"
+ctrl-right = "/input move_next_word"
+ctrl-s,ctrl-u = "/allbuf /buffer set unread"
+ctrl-t = "/input transpose_chars"
+ctrl-u = "/input delete_beginning_of_line"
+ctrl-up = "/input history_global_previous"
+ctrl-w = "/input delete_previous_word"
+ctrl-x = "/buffer switch"
+ctrl-y = "/input clipboard_paste"
+delete = "/input delete_next_char"
+down = "/input history_next"
+end = "/input move_end_of_line"
+f1 = "/bar scroll buflist * -100%"
+f10 = "/bar scroll title * +30%"
+f11 = "/bar scroll nicklist * -100%"
+f12 = "/bar scroll nicklist * +100%"
+f2 = "/bar scroll buflist * +100%"
+f5 = "/buffer -1"
+f6 = "/buffer +1"
+f7 = "/window -1"
+f8 = "/window +1"
+f9 = "/bar scroll title * -30%"
+home = "/input move_beginning_of_line"
+left = "/input move_previous_char"
meta-- = "/filter toggle @"
-meta-/ = "/input jump_last_buffer_displayed"
+meta-/ = "/buffer jump last_displayed"
meta-0 = "/buffer *10"
meta-1 = "/buffer *1"
meta-2 = "/buffer *2"
@@ -682,250 +636,197 @@ meta-6 = "/buffer *6"
meta-7 = "/buffer *7"
meta-8 = "/buffer *8"
meta-9 = "/buffer *9"
-meta-< = "/input jump_previously_visited_buffer"
+meta-< = "/buffer jump prev_visited"
meta-= = "/filter toggle"
-meta-> = "/input jump_next_visited_buffer"
+meta-> = "/buffer jump next_visited"
meta-B = "/buflist toggle"
-meta-OA = "/input history_global_previous"
-meta-OB = "/input history_global_next"
-meta-OC = "/input move_next_word"
-meta-OD = "/input move_previous_word"
-meta-OF = "/input move_end_of_line"
-meta-OH = "/input move_beginning_of_line"
-meta-OP = "/bar scroll buflist * -100%"
-meta-OQ = "/bar scroll buflist * +100%"
-meta-Oa = "/input history_global_previous"
-meta-Ob = "/input history_global_next"
-meta-Oc = "/input move_next_word"
-meta-Od = "/input move_previous_word"
-meta2-11^ = "/bar scroll buflist * -100%"
-meta2-11~ = "/bar scroll buflist * -100%"
-meta2-12^ = "/bar scroll buflist * +100%"
-meta2-12~ = "/bar scroll buflist * +100%"
-meta2-15~ = "/buffer -1"
-meta2-17~ = "/buffer +1"
-meta2-18~ = "/window -1"
-meta2-19~ = "/window +1"
-meta2-1;3A = "/buffer -1"
-meta2-1;3B = "/buffer +1"
-meta2-1;3C = "/buffer +1"
-meta2-1;3D = "/buffer -1"
-meta2-1;3F = "/window scroll_bottom"
-meta2-1;3H = "/window scroll_top"
-meta2-1;3P = "/bar scroll buflist * b"
-meta2-1;3Q = "/bar scroll buflist * e"
-meta2-1;5A = "/input history_global_previous"
-meta2-1;5B = "/input history_global_next"
-meta2-1;5C = "/input move_next_word"
-meta2-1;5D = "/input move_previous_word"
-meta2-1;5P = "/bar scroll buflist * -100%"
-meta2-1;5Q = "/bar scroll buflist * +100%"
-meta2-1~ = "/input move_beginning_of_line"
-meta2-200~ = "/input paste_start"
-meta2-201~ = "/input paste_stop"
-meta2-20~ = "/bar scroll title * -30%"
-meta2-21~ = "/bar scroll title * +30%"
-meta2-23;3~ = "/bar scroll nicklist * b"
-meta2-23~ = "/bar scroll nicklist * -100%"
-meta2-24;3~ = "/bar scroll nicklist * e"
-meta2-24~ = "/bar scroll nicklist * +100%"
-meta2-3~ = "/input delete_next_char"
-meta2-4~ = "/input move_end_of_line"
-meta2-5;3~ = "/window scroll_up"
-meta2-5~ = "/window page_up"
-meta2-6;3~ = "/window scroll_down"
-meta2-6~ = "/window page_down"
-meta2-7~ = "/input move_beginning_of_line"
-meta2-8~ = "/input move_end_of_line"
-meta2-A = "/input history_previous"
-meta2-B = "/input history_next"
-meta2-C = "/input move_next_char"
-meta2-D = "/input move_previous_char"
-meta2-F = "/input move_end_of_line"
-meta2-G = "/window page_down"
-meta2-H = "/input move_beginning_of_line"
-meta2-I = "/window page_up"
-meta2-Z = "/input complete_previous"
-meta2-[E = "/buffer -1"
meta-_ = "/input redo"
-meta-a = "/input jump_smart"
+meta-a = "/buffer jump smart"
meta-b = "/input move_previous_word"
meta-d = "/input delete_next_word"
+meta-down = "/buffer +1"
meta-e = "/buffer *13"
+meta-end = "/window scroll_bottom"
meta-f = "/input move_next_word"
-meta-h = "/input hotlist_clear"
+meta-f1 = "/bar scroll buflist * b"
+meta-f11 = "/bar scroll nicklist * b"
+meta-f12 = "/bar scroll nicklist * e"
+meta-f2 = "/bar scroll buflist * e"
+meta-h = "/hotlist clear"
+meta-home = "/window scroll_top"
meta-i = "/buffer *18"
-meta-jmeta-f = "/buffer -"
-meta-jmeta-l = "/input jump_last_buffer"
-meta-jmeta-r = "/server raw"
-meta-jmeta-s = "/server jump"
-meta-j01 = "/buffer 1"
-meta-j02 = "/buffer 2"
-meta-j03 = "/buffer 3"
-meta-j04 = "/buffer 4"
-meta-j05 = "/buffer 5"
-meta-j06 = "/buffer 6"
-meta-j07 = "/buffer 7"
-meta-j08 = "/buffer 8"
-meta-j09 = "/buffer 9"
-meta-j10 = "/buffer 10"
-meta-j11 = "/buffer 11"
-meta-j12 = "/buffer 12"
-meta-j13 = "/buffer 13"
-meta-j14 = "/buffer 14"
-meta-j15 = "/buffer 15"
-meta-j16 = "/buffer 16"
-meta-j17 = "/buffer 17"
-meta-j18 = "/buffer 18"
-meta-j19 = "/buffer 19"
-meta-j20 = "/buffer 20"
-meta-j21 = "/buffer 21"
-meta-j22 = "/buffer 22"
-meta-j23 = "/buffer 23"
-meta-j24 = "/buffer 24"
-meta-j25 = "/buffer 25"
-meta-j26 = "/buffer 26"
-meta-j27 = "/buffer 27"
-meta-j28 = "/buffer 28"
-meta-j29 = "/buffer 29"
-meta-j30 = "/buffer 30"
-meta-j31 = "/buffer 31"
-meta-j32 = "/buffer 32"
-meta-j33 = "/buffer 33"
-meta-j34 = "/buffer 34"
-meta-j35 = "/buffer 35"
-meta-j36 = "/buffer 36"
-meta-j37 = "/buffer 37"
-meta-j38 = "/buffer 38"
-meta-j39 = "/buffer 39"
-meta-j40 = "/buffer 40"
-meta-j41 = "/buffer 41"
-meta-j42 = "/buffer 42"
-meta-j43 = "/buffer 43"
-meta-j44 = "/buffer 44"
-meta-j45 = "/buffer 45"
-meta-j46 = "/buffer 46"
-meta-j47 = "/buffer 47"
-meta-j48 = "/buffer 48"
-meta-j49 = "/buffer 49"
-meta-j50 = "/buffer 50"
-meta-j51 = "/buffer 51"
-meta-j52 = "/buffer 52"
-meta-j53 = "/buffer 53"
-meta-j54 = "/buffer 54"
-meta-j55 = "/buffer 55"
-meta-j56 = "/buffer 56"
-meta-j57 = "/buffer 57"
-meta-j58 = "/buffer 58"
-meta-j59 = "/buffer 59"
-meta-j60 = "/buffer 60"
-meta-j61 = "/buffer 61"
-meta-j62 = "/buffer 62"
-meta-j63 = "/buffer 63"
-meta-j64 = "/buffer 64"
-meta-j65 = "/buffer 65"
-meta-j66 = "/buffer 66"
-meta-j67 = "/buffer 67"
-meta-j68 = "/buffer 68"
-meta-j69 = "/buffer 69"
-meta-j70 = "/buffer 70"
-meta-j71 = "/buffer 71"
-meta-j72 = "/buffer 72"
-meta-j73 = "/buffer 73"
-meta-j74 = "/buffer 74"
-meta-j75 = "/buffer 75"
-meta-j76 = "/buffer 76"
-meta-j77 = "/buffer 77"
-meta-j78 = "/buffer 78"
-meta-j79 = "/buffer 79"
-meta-j80 = "/buffer 80"
-meta-j81 = "/buffer 81"
-meta-j82 = "/buffer 82"
-meta-j83 = "/buffer 83"
-meta-j84 = "/buffer 84"
-meta-j85 = "/buffer 85"
-meta-j86 = "/buffer 86"
-meta-j87 = "/buffer 87"
-meta-j88 = "/buffer 88"
-meta-j89 = "/buffer 89"
-meta-j90 = "/buffer 90"
-meta-j91 = "/buffer 91"
-meta-j92 = "/buffer 92"
-meta-j93 = "/buffer 93"
-meta-j94 = "/buffer 94"
-meta-j95 = "/buffer 95"
-meta-j96 = "/buffer 96"
-meta-j97 = "/buffer 97"
-meta-j98 = "/buffer 98"
-meta-j99 = "/buffer 99"
+meta-j,0,1 = "/buffer 1"
+meta-j,0,2 = "/buffer 2"
+meta-j,0,3 = "/buffer 3"
+meta-j,0,4 = "/buffer 4"
+meta-j,0,5 = "/buffer 5"
+meta-j,0,6 = "/buffer 6"
+meta-j,0,7 = "/buffer 7"
+meta-j,0,8 = "/buffer 8"
+meta-j,0,9 = "/buffer 9"
+meta-j,1,0 = "/buffer 10"
+meta-j,1,1 = "/buffer 11"
+meta-j,1,2 = "/buffer 12"
+meta-j,1,3 = "/buffer 13"
+meta-j,1,4 = "/buffer 14"
+meta-j,1,5 = "/buffer 15"
+meta-j,1,6 = "/buffer 16"
+meta-j,1,7 = "/buffer 17"
+meta-j,1,8 = "/buffer 18"
+meta-j,1,9 = "/buffer 19"
+meta-j,2,0 = "/buffer 20"
+meta-j,2,1 = "/buffer 21"
+meta-j,2,2 = "/buffer 22"
+meta-j,2,3 = "/buffer 23"
+meta-j,2,4 = "/buffer 24"
+meta-j,2,5 = "/buffer 25"
+meta-j,2,6 = "/buffer 26"
+meta-j,2,7 = "/buffer 27"
+meta-j,2,8 = "/buffer 28"
+meta-j,2,9 = "/buffer 29"
+meta-j,3,0 = "/buffer 30"
+meta-j,3,1 = "/buffer 31"
+meta-j,3,2 = "/buffer 32"
+meta-j,3,3 = "/buffer 33"
+meta-j,3,4 = "/buffer 34"
+meta-j,3,5 = "/buffer 35"
+meta-j,3,6 = "/buffer 36"
+meta-j,3,7 = "/buffer 37"
+meta-j,3,8 = "/buffer 38"
+meta-j,3,9 = "/buffer 39"
+meta-j,4,0 = "/buffer 40"
+meta-j,4,1 = "/buffer 41"
+meta-j,4,2 = "/buffer 42"
+meta-j,4,3 = "/buffer 43"
+meta-j,4,4 = "/buffer 44"
+meta-j,4,5 = "/buffer 45"
+meta-j,4,6 = "/buffer 46"
+meta-j,4,7 = "/buffer 47"
+meta-j,4,8 = "/buffer 48"
+meta-j,4,9 = "/buffer 49"
+meta-j,5,0 = "/buffer 50"
+meta-j,5,1 = "/buffer 51"
+meta-j,5,2 = "/buffer 52"
+meta-j,5,3 = "/buffer 53"
+meta-j,5,4 = "/buffer 54"
+meta-j,5,5 = "/buffer 55"
+meta-j,5,6 = "/buffer 56"
+meta-j,5,7 = "/buffer 57"
+meta-j,5,8 = "/buffer 58"
+meta-j,5,9 = "/buffer 59"
+meta-j,6,0 = "/buffer 60"
+meta-j,6,1 = "/buffer 61"
+meta-j,6,2 = "/buffer 62"
+meta-j,6,3 = "/buffer 63"
+meta-j,6,4 = "/buffer 64"
+meta-j,6,5 = "/buffer 65"
+meta-j,6,6 = "/buffer 66"
+meta-j,6,7 = "/buffer 67"
+meta-j,6,8 = "/buffer 68"
+meta-j,6,9 = "/buffer 69"
+meta-j,7,0 = "/buffer 70"
+meta-j,7,1 = "/buffer 71"
+meta-j,7,2 = "/buffer 72"
+meta-j,7,3 = "/buffer 73"
+meta-j,7,4 = "/buffer 74"
+meta-j,7,5 = "/buffer 75"
+meta-j,7,6 = "/buffer 76"
+meta-j,7,7 = "/buffer 77"
+meta-j,7,8 = "/buffer 78"
+meta-j,7,9 = "/buffer 79"
+meta-j,8,0 = "/buffer 80"
+meta-j,8,1 = "/buffer 81"
+meta-j,8,2 = "/buffer 82"
+meta-j,8,3 = "/buffer 83"
+meta-j,8,4 = "/buffer 84"
+meta-j,8,5 = "/buffer 85"
+meta-j,8,6 = "/buffer 86"
+meta-j,8,7 = "/buffer 87"
+meta-j,8,8 = "/buffer 88"
+meta-j,8,9 = "/buffer 89"
+meta-j,9,0 = "/buffer 90"
+meta-j,9,1 = "/buffer 91"
+meta-j,9,2 = "/buffer 92"
+meta-j,9,3 = "/buffer 93"
+meta-j,9,4 = "/buffer 94"
+meta-j,9,5 = "/buffer 95"
+meta-j,9,6 = "/buffer 96"
+meta-j,9,7 = "/buffer 97"
+meta-j,9,8 = "/buffer 98"
+meta-j,9,9 = "/buffer 99"
+meta-j,meta-f = "/buffer -"
+meta-j,meta-l = "/buffer +"
+meta-j,meta-r = "/server raw"
+meta-j,meta-s = "/server jump"
meta-k = "/input grab_key_command"
meta-l = "/window bare"
-meta-m = "/mute mouse toggle"
+meta-left = "/buffer -1"
meta-n = "/window scroll_next_highlight"
meta-o = "/buffer *19"
meta-p = "/window scroll_previous_highlight"
+meta-pgdn = "/window scroll_down"
+meta-pgup = "/window scroll_up"
meta-q = "/buffer *11"
meta-r = "/input delete_line"
+meta-right = "/buffer +1"
meta-s = "/mute aspell toggle"
meta-t = "/buffer *15"
meta-u = "/window scroll_unread"
+meta-up = "/buffer -1"
meta-w = "/buffer *12"
-meta-wmeta-meta2-A = "/window up"
-meta-wmeta-meta2-B = "/window down"
-meta-wmeta-meta2-C = "/window right"
-meta-wmeta-meta2-D = "/window left"
-meta-wmeta2-1;3A = "/window up"
-meta-wmeta2-1;3B = "/window down"
-meta-wmeta2-1;3C = "/window right"
-meta-wmeta2-1;3D = "/window left"
-meta-wmeta-b = "/window balance"
-meta-wmeta-s = "/window swap"
-meta-x = "/input zoom_merged_buffer"
+meta-w,meta-b = "/window balance"
+meta-w,meta-down = "/window down"
+meta-w,meta-left = "/window left"
+meta-w,meta-right = "/window right"
+meta-w,meta-s = "/window swap"
+meta-w,meta-up = "/window up"
+meta-x = "/buffer zoom"
meta-y = "/buffer *16"
meta-z = "/window zoom"
-ctrl-_ = "/input undo"
+pgdn = "/window page_down"
+pgup = "/window page_up"
+return = "/input return"
+right = "/input move_next_char"
+shift-tab = "/input complete_previous"
+tab = "/input complete_next"
+up = "/input history_previous"
[key_search]
-ctrl-I = "/input search_switch_where"
-ctrl-J = "/input search_stop"
-ctrl-M = "/input search_stop"
-ctrl-Q = "/input search_stop"
-ctrl-R = "/input search_switch_case"
-meta2-A = "/input search_previous"
-meta2-B = "/input search_next"
+ctrl-q = "/input search_stop"
+ctrl-r = "/input search_switch_case"
+down = "/input search_next"
meta-c = "/input search_switch_case"
+return = "/input search_stop"
+tab = "/input search_switch_where"
+up = "/input search_previous"
[key_cursor]
-ctrl-J = "/cursor stop"
-ctrl-M = "/cursor stop"
-meta-meta2-A = "/cursor move area_up"
-meta-meta2-B = "/cursor move area_down"
-meta-meta2-C = "/cursor move area_right"
-meta-meta2-D = "/cursor move area_left"
-meta2-1;3A = "/cursor move area_up"
-meta2-1;3B = "/cursor move area_down"
-meta2-1;3C = "/cursor move area_right"
-meta2-1;3D = "/cursor move area_left"
-meta2-A = "/cursor move up"
-meta2-B = "/cursor move down"
-meta2-C = "/cursor move right"
-meta2-D = "/cursor move left"
@chat(python.*):D = "hsignal:slack_cursor_delete"
@chat(python.*):L = "hsignal:slack_cursor_linkarchive"
@chat(python.*):M = "hsignal:slack_cursor_message"
@chat(python.*):R = "hsignal:slack_cursor_reply"
@chat(python.*):T = "hsignal:slack_cursor_thread"
@chat(python.matrix.*):r = "hsignal:matrix_cursor_reply"
+@chat:Q = "hsignal:chat_quote_time_prefix_message;/cursor stop"
+@chat:m = "hsignal:chat_quote_message;/cursor stop"
+@chat:q = "hsignal:chat_quote_prefix_message;/cursor stop"
@item(buffer_nicklist):K = "/window ${_window_number};/kickban ${nick}"
@item(buffer_nicklist):b = "/window ${_window_number};/ban ${nick}"
@item(buffer_nicklist):k = "/window ${_window_number};/kick ${nick}"
@item(buffer_nicklist):q = "/window ${_window_number};/query ${nick};/cursor stop"
@item(buffer_nicklist):w = "/window ${_window_number};/whois ${nick}"
-@chat:Q = "hsignal:chat_quote_time_prefix_message;/cursor stop"
-@chat:m = "hsignal:chat_quote_message;/cursor stop"
-@chat:q = "hsignal:chat_quote_prefix_message;/cursor stop"
+down = "/cursor move down"
+left = "/cursor move left"
+meta-down = "/cursor move area_down"
+meta-left = "/cursor move area_left"
+meta-right = "/cursor move area_right"
+meta-up = "/cursor move area_up"
+return = "/cursor stop"
+right = "/cursor move right"
+up = "/cursor move up"
[key_mouse]
+@*:button3 = "/cursor go ${_x},${_y}"
@bar(buffers):ctrl-wheeldown = "hsignal:buffers_mouse"
@bar(buffers):ctrl-wheelup = "hsignal:buffers_mouse"
@bar(buflist):ctrl-wheeldown = "hsignal:buflist_mouse"
@@ -935,6 +836,8 @@ meta2-D = "/cursor move left"
@bar(nicklist):button1-gesture-down-long = "/bar scroll nicklist ${_window_number} e"
@bar(nicklist):button1-gesture-up = "/bar scroll nicklist ${_window_number} -100%"
@bar(nicklist):button1-gesture-up-long = "/bar scroll nicklist ${_window_number} b"
+@bar:wheeldown = "/bar scroll ${_bar_name} ${_window_number} +20%"
+@bar:wheelup = "/bar scroll ${_bar_name} ${_window_number} -20%"
@chat(fset.fset):button1 = "/window ${_window_number};/fset -go ${_chat_line_y}"
@chat(fset.fset):button2* = "hsignal:fset_mouse"
@chat(fset.fset):wheeldown = "/fset -down 5"
@@ -944,6 +847,15 @@ meta2-D = "/cursor move left"
@chat(script.scripts):button2 = "/window ${_window_number};/script go ${_chat_line_y};/script installremove -q ${script_name_with_extension}"
@chat(script.scripts):wheeldown = "/script down 5"
@chat(script.scripts):wheelup = "/script up 5"
+@chat:button1 = "/window ${_window_number}"
+@chat:button1-gesture-left = "/window ${_window_number};/buffer -1"
+@chat:button1-gesture-left-long = "/window ${_window_number};/buffer 1"
+@chat:button1-gesture-right = "/window ${_window_number};/buffer +1"
+@chat:button1-gesture-right-long = "/window ${_window_number};/buffer +"
+@chat:ctrl-wheeldown = "/window scroll_horiz -window ${_window_number} +10%"
+@chat:ctrl-wheelup = "/window scroll_horiz -window ${_window_number} -10%"
+@chat:wheeldown = "/window scroll_down -window ${_window_number}"
+@chat:wheelup = "/window scroll_up -window ${_window_number}"
@item(buffer_nicklist):button1 = "/window ${_window_number};/query ${nick}"
@item(buffer_nicklist):button1-gesture-left = "/window ${_window_number};/kick ${nick}"
@item(buffer_nicklist):button1-gesture-left-long = "/window ${_window_number};/kickban ${nick}"
@@ -957,15 +869,3 @@ meta2-D = "/cursor move left"
@item(buflist2):button2* = "hsignal:buflist_mouse"
@item(buflist3):button1* = "hsignal:buflist_mouse"
@item(buflist3):button2* = "hsignal:buflist_mouse"
-@bar:wheeldown = "/bar scroll ${_bar_name} ${_window_number} +20%"
-@bar:wheelup = "/bar scroll ${_bar_name} ${_window_number} -20%"
-@chat:button1 = "/window ${_window_number}"
-@chat:button1-gesture-left = "/window ${_window_number};/buffer -1"
-@chat:button1-gesture-left-long = "/window ${_window_number};/buffer 1"
-@chat:button1-gesture-right = "/window ${_window_number};/buffer +1"
-@chat:button1-gesture-right-long = "/window ${_window_number};/input jump_last_buffer"
-@chat:ctrl-wheeldown = "/window scroll_horiz -window ${_window_number} +10%"
-@chat:ctrl-wheelup = "/window scroll_horiz -window ${_window_number} -10%"
-@chat:wheeldown = "/window scroll_down -window ${_window_number}"
-@chat:wheelup = "/window scroll_up -window ${_window_number}"
-@*:button3 = "/cursor go ${_x},${_y}"
diff --git a/.zshrc b/.zshrc
index 63a89a1..347f3c0 100644
--- a/.zshrc
+++ b/.zshrc
@@ -126,6 +126,14 @@ function precmd() # {{{
sshd|*/sshd|mosh*) HOSTNAME_COLOR="%F{6}" ;;
esac
# }}}
+
+ # Color last directory name in case of python venv
+ PROMPT_PATH='%F{4}%~%k'
+
+ if [[ -n "$PYTHON_VENV_DIR" ]]
+ then
+ PROMPT_PATH+='%F{6}#'"$(awk -F/ '{ print $(NF-1) }' <<< "$PYTHON_VENV_DIR")"
+ fi
}
# }}}
@@ -162,7 +170,8 @@ PROMPT='%(!.%F{1}.%F{2})%n%F{8}'
PROMPT=$PROMPT'@'
PROMPT=$PROMPT'${HOSTNAME_COLOR}%M%F{8}'
PROMPT=$PROMPT':'
-PROMPT=$PROMPT'%F{4}%~%k${PGIT}'
+PROMPT=$PROMPT'${PROMPT_PATH}'
+PROMPT=$PROMPT'%k${PGIT}'
PROMPT=$PROMPT' '
PROMPT=$PROMPT'%(?.%F{7}.%F{1})${EXITCODE}%k'
PROMPT=$PROMPT' ${vi_mode}ยป%b %f${CURSOR_STYLE}'
diff --git a/README.md b/README.md
index 7fd8781..17c8f43 100644
--- a/README.md
+++ b/README.md
@@ -38,8 +38,15 @@ Other dependencies may exist.
## Theming
- GTK theme: [arc-dark](https://github.com/horst3180/Arc-theme)
+- KDE theme: [arc-dark](https://github.com/PapirusDevelopmentTeam/arc-kde)
- Icons: [ACYLS](https://github.com/worron/ACYLS)
+- Colors:
+ - Text: `#eeeeee`
+ - Dark: `#252a35`
+ - Light: `#4b5165`
+ - Focus: `#5294e2`
+
## Browsers
There are no good browsers. Firefox may be a cancer upon its userbase, but the