4chan-x/src/appchan.coffee
Zixaphir cdd9929034 Make things more-or-less work.
Doesn't mean it's done right, yet.
2013-03-19 21:50:47 -07:00

1762 lines
88 KiB
CoffeeScript

Style =
init: ->
@agent = {
'gecko': '-moz-'
'webkit': '-webkit-'
'presto': '-o-'
}[$.engine]
@sizing = "#{if $.engine is 'gecko' then @agent else ''}box-sizing"
$.ready ->
return unless $.id 'navtopright'
Style.padding.nav = $ "#boardNavDesktop", d.body
Style.padding.pages = $(".pagelist", d.body)
Style.padding()
$.on window, "resize", Style.padding
# Give ExLinks and 4sight a little time to append their dialog links
setTimeout (->
Style.iconPositions()
if exLink = $ "#navtopright .exlinksOptionsLink", d.body
$.on exLink, "click", ->
setTimeout Style.rice, 100
), 500
@setup()
setup: ->
@addStyleReady()
if d.head
@remStyle()
unless Style.headCount
return @cleanup()
@observe()
observe: ->
if MutationObserver
Style.observer = new MutationObserver onMutationObserver = @wrapper
Style.observer.observe d,
childList: true
subtree: true
else
$.on d, 'DOMNodeInserted', @wrapper
wrapper: ->
if d.head
Style.remStyle()
if not Style.headCount or d.readyState is 'complete'
if Style.observer
Style.observer.disconnect()
else
$.off d, 'DOMNodeInserted', Style.wrapper
Style.cleanup()
cleanup: ->
delete Style.observe
delete Style.wrapper
delete Style.remStyle
delete Style.headCount
delete Style.cleanup
addStyle: (theme) ->
_conf = Conf
unless theme
theme = Themes[_conf['theme']]
MascotTools.init _conf["mascot"]
Style.layoutCSS.textContent = Style.layout()
Style.themeCSS.textContent = Style.theme(theme)
Style.iconPositions()
headCount: 12
addStyleReady: ->
theme = Themes[Conf['theme']]
$.extend Style,
layoutCSS: $.addStyle Style.layout(), 'layout'
themeCSS: $.addStyle Style.theme(theme), 'theme'
icons: $.addStyle "", 'icons'
paddingSheet: $.addStyle "", 'padding'
mascot: $.addStyle "", 'mascotSheet'
# Non-customizable
$.addStyle JSColor.css(), 'jsColor'
delete Style.addStyleReady
remStyle: ->
nodes = d.head.children
i = nodes.length
while i--
break unless Style.headCount
node = nodes[i]
if (node.nodeName is 'STYLE' and !node.id) or ("#{node.rel}".contains('stylesheet') and node.href[..3] isnt 'data')
Style.headCount--
$.rm node
continue
return
filter: (text, background) ->
matrix = (fg, bg) -> "
#{bg.r} #{-fg.r} 0 0 #{fg.r}
#{bg.g} #{-fg.g} 0 0 #{fg.g}
#{bg.b} #{-fg.b} 0 0 #{fg.b}
"
fgHex = Style.colorToHex text
bgHex = Style.colorToHex background
string = matrix {
r: parseInt(fgHex.substr(0, 2), 16) / 255
g: parseInt(fgHex.substr(2, 2), 16) / 255
b: parseInt(fgHex.substr(4, 2), 16) / 255
}, {
r: parseInt(bgHex.substr(0, 2), 16) / 255
g: parseInt(bgHex.substr(2, 2), 16) / 255
b: parseInt(bgHex.substr(4, 2), 16) / 255
}
return "filter: url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='filters' color-interpolation-filters='sRGB'><feColorMatrix values='#{string} 0 0 0 1 0' /></filter></svg>#filters\");"
layout: ->
_conf = Conf
agent = Style.agent
xOffset = if _conf["Sidebar Location"] is "left" then '' else '-'
# Position of submenus in relation to the post menu.
position = {
right:
{
hide:
if parseInt(_conf['Right Thread Padding'], 10) < 100
"right"
else
"left"
minimal: "right"
}[_conf["Sidebar"]] or "left"
left:
if parseInt(_conf['Right Thread Padding'], 10) < 100
"right"
else
"left"
}[_conf["Sidebar Location"]]
# Offsets various UI of the sidebar depending on the sidebar's width.
# Only really used for the board banner or right sidebar.
Style['sidebarOffset'] = if _conf["Sidebar"] is "large"
{
W: 51
H: 17
}
else
{
W: 0
H: 0
}
Style.logoOffset =
if _conf["4chan Banner"] is "at sidebar top"
83 + Style.sidebarOffset.H
else
0
width = 248 + Style.sidebarOffset.W
Style.sidebarLocation = if _conf["Sidebar Location"] is "left"
["left", "right"]
else
["right", "left" ]
if _conf['editMode'] is "theme"
editSpace = {}
editSpace[Style.sidebarLocation[1]] = 300
editSpace[Style.sidebarLocation[0]] = 0
else
editSpace =
left: 0
right: 0
Style.sidebar = {
minimal: 20
hide: 2
}[_conf.Sidebar] or (252 + Style.sidebarOffset.W)
Style.replyMargin = _conf["Post Spacing"]
css = """<%= grunt.file.read('css/layout.css') %>"""
theme: (theme) ->
_conf = Conf
agent = Style.agent
bgColor = new Style.color Style.colorToHex backgroundC = theme["Background Color"]
Style.lightTheme = bgColor.isLight()
icons = Icons.header.png + Icons.themes[_conf["Icons"]]
css = """<%= grunt.file.read('css/theme.css') %>"""
iconPositions: ->
css = """<%= grunt.file.read('css/icons.base.css') %>"""
i = 0
align = Style.sidebarLocation[0]
_conf = Conf
notCatalog = !g.CATALOG
notEither = notCatalog and g.BOARD isnt 'f'
aligner = (first, checks) ->
# Create a position to hold values
position = [first]
# Check which elements we actually have. Some are easy, because the script creates them so we'd know they're here
# Some are hard, like 4sight, which we have no way of knowing if available without looking for it.
for enabled in checks
position[position.length] =
if enabled
first += 19
else
first
position
if _conf["Icon Orientation"] is "horizontal"
position = aligner(
2
[
notCatalog
_conf['Slideout Navigation'] isnt 'hide'
_conf['Announcements'] is 'slideout' and $ '#globalMessage', d.body
notCatalog and _conf['Slideout Watcher'] and _conf['Thread Watcher']
$ '#navtopright .exlinksOptionsLink', d.body
notCatalog and $ 'body > a[style="cursor: pointer; float: right;"]', d.body
notEither and _conf['Image Expansion']
notEither
g.REPLY
notEither and _conf['Fappe Tyme']
navlinks = ((!g.REPLY and _conf['Index Navigation']) or (g.REPLY and _conf['Reply Navigation'])) and notCatalog
navlinks
]
)
iconOffset =
position[position.length - 1] - (if _conf['4chan SS Navigation']
0
else
Style.sidebar + parseInt(_conf["Right Thread Padding"], 10))
if iconOffset < 0 then iconOffset = 0
css += """<%= grunt.file.read('css/icons.horz.css') %>"""
if _conf["Updater Position"] isnt 'moveable'
css += """<%= grunt.file.read('css/icons.horz.tu.css') %>"""
else
position = aligner(
2 + (if _conf["4chan Banner"] is "at sidebar top" then (Style.logoOffset + 19) else 0)
[
notEither and _conf['Image Expansion']
notCatalog
_conf['Slideout Navigation'] isnt 'hide'
_conf['Announcements'] is 'slideout' and $ '#globalMessage', d.body
notCatalog and _conf['Slideout Watcher'] and _conf['Thread Watcher']
notCatalog and $ 'body > a[style="cursor: pointer; float: right;"]', d.body
$ '#navtopright .exlinksOptionsLink', d.body
notEither
g.REPLY
notEither and _conf['Fappe Tyme']
navlinks = ((!g.REPLY and _conf['Index Navigation']) or (g.REPLY and _conf['Reply Navigation'])) and notCatalog
navlinks
]
)
iconOffset = (
if g.REPLY and _conf['Prefetch']
250 + Style.sidebarOffset.W
else
20 + (
if g.REPLY and _conf['Updater Position'] is 'top'
100
else
0
)
) - (
if _conf['4chan SS Navigation']
0
else
Style.sidebar + parseInt _conf[align.capitalize() + " Thread Padding"], 10
)
css += """<%= grunt.file.read('css/icons.vert.css') %>"""
if _conf["Updater Position"] isnt 'moveable'
css += """<%= grunt.file.read('css/icons.vert.tu.css') %>"""
Style.icons.textContent = css
padding: ->
return unless sheet = Style.paddingSheet
_conf = Conf
Style.padding.nav.property = _conf["Boards Navigation"].split(" ")
Style.padding.nav.property = Style.padding.nav.property[Style.padding.nav.property.length - 1]
if Style.padding.pages
Style.padding.pages.property = _conf["Pagination"].split(" ")
Style.padding.pages.property = Style.padding.pages.property[Style.padding.pages.property.length - 1]
css = "body::before {\n"
if _conf["4chan SS Emulation"]
if Style.padding.pages and (_conf["Pagination"] is "sticky top" or _conf["Pagination"] is "sticky bottom")
css += " #{Style.padding.pages.property}: #{Style.padding.pages.offsetHeight}px !important;\n"
if _conf["Boards Navigation"] is "sticky top" or _conf["Boards Navigation"] is "sticky bottom"
css += " #{Style.padding.nav.property}: #{Style.padding.nav.offsetHeight}px !important;\n"
css += """
}
body {
padding-bottom: 0;\n
"""
if Style.padding.pages? and (_conf["Pagination"] is "sticky top" or _conf["Pagination"] is "sticky bottom" or _conf["Pagination"] is "top")
css += " padding-#{Style.padding.pages.property}: #{Style.padding.pages.offsetHeight}px;\n"
unless _conf["Boards Navigation"] is "hide"
css += " padding-#{Style.padding.nav.property}: #{Style.padding.nav.offsetHeight}px;\n"
css += "}"
sheet.textContent = css
color: (hex) ->
@hex = "#" + hex
@calc_rgb = (hex) ->
hex = parseInt hex, 16
[ # 0xRRGGBB to [R, G, B]
(hex >> 16) & 0xFF
(hex >> 8) & 0xFF
hex & 0xFF
]
@private_rgb = @calc_rgb(hex)
@rgb = @private_rgb.join ","
@isLight = ->
rgb = @private_rgb
return (rgb[0] + rgb[1] + rgb[2]) >= 400
@shiftRGB = (shift, smart) ->
minmax = (base) ->
Math.min Math.max(base, 0), 255
rgb = @private_rgb.slice 0
shift = if smart
(
if @isLight rgb
-1
else
1
) * Math.abs shift
else
shift
return [
minmax rgb[0] + shift
minmax rgb[1] + shift
minmax rgb[2] + shift
].join ","
@hover = @shiftRGB 16, true
colorToHex: (color) ->
if color.substr(0, 1) is '#'
return color.slice 1, color.length
if digits = color.match /(.*?)rgba?\((\d+), ?(\d+), ?(\d+)(.*?)\)/
# [R, G, B] to 0xRRGGBB
hex = (
(parseInt(digits[2], 10) << 16) |
(parseInt(digits[3], 10) << 8) |
(parseInt(digits[4], 10))
).toString 16
while hex.length < 6
hex = "0#{hex}"
hex
else
false
Emoji =
init: ->
Emoji.icons.not.push ['PlanNine', Emoji.icons.not[0][1]]
css: (position) ->
_conf = Conf
css = []
margin = "margin-#{if position is "before" then "right" else "left"}: #{parseInt _conf['Emoji Spacing']}px;"
for key, category of Emoji.icons
if (_conf['Emoji'] isnt "disable ponies" and key is "pony") or (_conf['Emoji'] isnt "only ponies" and key is "not")
for icon in category
name = icon[0]
css[css.length] = """
a.useremail[href*='#{name}']:last-of-type::#{position},
a.useremail[href*='#{name.toLowerCase()}']:last-of-type::#{position},
a.useremail[href*='#{name.toUpperCase()}']:last-of-type::#{position} {
content: url('#{Icons.header.png + icon[1]}');
vertical-align: top;
#{margin}
}\n
"""
css.join ""
icons:
pony: [
['Pinkie', 'BAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA3dJREFUGBlNwUtoXFUcB+Df/zzuY553pp2MmUwSk5TGpnamiokWRdNCSkCrUChKCnVZQUEUdy5sQZC6cyd2VWgoutCFXWTjIyp1UdqmEDBRsSZNmkmaZF6Zx32ccyzowu8j/M883pH5A9kBYfNkFOpu0OiulyqXmnhkDmdYHYJexzX1Ef51EQDhP9fxpjU0PDCd7IldYIxGVag3/KZ/ZX1p8/P0k/0U47qs291M2NS3f6ncuLeFeQ3A8KuYoNPoY/3e2Ej6scSnqUJ8gksmhC2y3OJHpSUHU0/3HU+WCuddyV6VSpVyYv/aUuPefWAP4iDG8AhJWyYYo972tg8DQ1wyWHGZSfcmZmQ+YeKTw1bQ70H8uJw3xtDp6NzG15VLf/DLWMBZHGPkwuWGyq7njLoZyzAiCtqRIddioifBxYBHIpeE0oaw0yoG7WA755dvi8Xih66BOSZj4rwds45bSQkuOeOCQYWG2PjjcEq94JwjQgQ+kCW+tBl3H7Ym4jnbE/nDmamwqz9mnEaYoBgiZaJIGW5zEIHEPheykMD2w12ztPIXCrZHec+GdOVAUI8ygjvifeHQESiNoKtMlIoRxSV0owMjAeY5+P3BKrbTDq3n02B/7yDTDkBANSXiewKgjFbahEwQe34IiVIfRNqCv1qDanQR9Di4+tU16N409o2WMXnyJeNWb9PO4s6WroZawOiSiozCoR7lPFUQezICCzXF+pPGYRna6/rotNqY/eJLUzh4mM5dP4Va0YXV45x0O9F9FhkN5auq4eznaq3WmP1pDkuibW5uraNaqyNh23ihPA6v7wAVS+PwXAGkbYiUnU3kYm8JzvgGpJGdG6vzm15+ce6H79/9bnnBhCxG702dwnTaw4nyM/jsiTHsHx+DEyjKWnGEUpBOyjTTgbpsNHyLojPe7PK3qci58NvNu0Gl0YA8NIxWp4MkdzCdK2Ci6iNYXIV6UEfUDBC2Q/A3WqVbUUfVucWftYhP9fLiFf7yRPGVmZmhE88dJVmpGRMqRH4E3emSbnQR3lkzaqNB3br/J39tb1ibJglGfJDZbMReb37Td/bFhcnB/iNppXNUbZEKFGBJ6FBT+9cVo5c3yd/trDV3OxdFDDHFOV8IffVJtNNOC+J3xtYqATWw0Mm6RIJ9YAy9rdtt07q1ZtjdVXCYFRBG4Bv8A+lliGhzN164AAAAAElFTkSuQmCC']
['Applejack', 'A4AAAAQCAYAAAAmlE46AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAv9JREFUOE9dkmtIU2EYx88Roi9FfahkfQmS6kNGEBRlZWSY5tylTKepqR8MHYGl2R1POm2u5bCVlzbog2Ze591pzZPZxUskDZLMMLSWzcIca3MXt7N/55woqhf+PC8Pz+99n+fPQxAEQf6vhINb1nG5/ISdobWXo+9eSd4tyM7OJimKImmaJhsaGjjmX/DGqfDQmkvRg1x+9YrlZPd18fdupXiu6mxkOAcqlUqyuLiYB/+cayfD1rKFH0w3pYEHV4/omhTCyieVcYEB7TEYSyX21Mita/6u/91qUBMV00JrjmKwMg4zI2fgnlfD90PLx+nhMyinIrb91SFBFqaHBevPHb7G/fS06jhs0wXwO8rBOLXws2Kct/k4//HKRE+jZD0Pl2buD2FnmOlVSUFrpJg15/JFgcWKP0Bg8Q6fs1sVs+11wmAebKaEuiG1CC81Yozci+cL4KoC3JUIuCp4+R23+Ee4Dr5bisZmJi7fJzpLRJZPOin8vSlwdSXDO54Hz+vT8LzLh3uuCIuzBfDa1DzMPcrJMVfkIHpVEu94uYgH/aaTvOxdJzDZkI76smhY2mVwDmfg8zM5RukcvH8pbx96mLiPMBTG0nSpGK7mePg6k+DsSUZbSQwem02oba3DRsFKzNQfx9sHSdi1dzve5Ow4xM+ozorY1K2U2MY0IrhbEuB7lIqB6gxY7B9R3XoHAoEAivN74O5LAaXNwvNLe9PlcjlJACANRaIRztFh1iRvfRyYx5kIOCwY+GCE9GIUOjrzwZjS4H16FV80UT1WqzWIWFhYIBsLhDf7y46Ck1UvATNKgXlxHgHbJDyub2DGVPC2s+bVyGDTx74ym80kwe2fKvNASN8NySK3NeayWNagNPj7WaP62Uhn8HdPkwyWW3IoEjdv0Ol0JGE0GvmV0+dFpj9SS5kOKuahr01Wwbb2lXV6aakjkfF1p8DXlwHnaB5yTm1bbzAYfs34e/+0pyNic+N2ruIWmQWXcdE1dUEGd9UYq6kle1mXqVW6imWIn290AGVZutJTAAAAAElFTkSuQmCC']
['Fluttershy', 'BAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA2xJREFUOE9dU91PWmcYP2uybDfrxdIlu9vN/oglverWNN3Fmu1iN7vY2q4utnE2Nu26ukyrUUGwExGpn3xY+TyACjrskFrcEYoWnCAM4YAgcJjROkFA1q789nJczNaTPHnfk+f5/d7n4/dQ1Cvf3Ut3Xp//Qnze36gYCt56kIgJpyqRFvrvcIvxMNxhSa9eV993XJK/+yqO/zdf7j7tbRz1RdstLzOKReRoLxJSOzb7HyKtdCEumgErmEbwO03U2aR8738kzq8ln8e6bXlWYMWmZA6Z8SUk5U5ytyPeY0Oy1w5O50FO+wQ5jbtG4lK19L5BGehzb9sE19+JtFt2c8ZlJPvmwAqtSA06EWs3g+2aQnacwdbwAmLknuiZxaZ4FiTD6tLFvi+pBeenb/3mvvo4Yu3D5v1ZsP1axHpUiAo0iPyg41/dGiNgiQI5PXmdXkai92dkVItYbZ6YpVZWLrrKFSOynBip9W6U/7LwViqZ8SykRWpcR8BqJNlmJCZp1LLMkIxSAw6s39WHqUCo/mDnWTdKhwRUMaNMzvLh5NFZsaBIbD+rJ34jgsxtcLQH3IQbKakDoVZDmnpk+irA/fEjCkXlv+AawX+MEJQJcaFEY8bWAJdMgYxyESn5PILNumUqJNVVA4EG7OXlx8Bf3T2QyRuh0X2P5ad9pCQTcjtqDI3UwTMuReIeaaKagb9u6B6VVi9Wg1YRUhkhH1g6NKFf3gD/2gAYz08YVd5AdltDfDS2d2QIrH6DcNcwUjLHc+aC8AMqLrW/4EwesBoligUTCgc05h52IH9gwu6+ERwBb+9pkc0IwLJNWHPXIyrUIdysW2POd52gopIZjtOSpgzOI2NToVAmwD0D9osmvvZSxcCXtr5wA08627Ah0yHZ74D3ysBNXokR8XQ8q2SQM3gQbZtAPm1AiZRyNIUawZGFl5qIRqbBdk4Sndjy1iviIymzIquXldirWRXDzzdOZr63q8J66OqOf+2yL8be+nMr3fry91A9NlRjvKT9tx88Pt6Djdaps0RZxQRZmCzpbHrMBV9b5/YM/dn7tSCT/cNTvpauFdasR5xkkCaS9n07Kj0mIKm+GbujP5OQ/vI8Ofyomhx0sOmxhU9W6wYp5uOO12qB3guik2TuI2QPXmwpXLGnjSMf3RRdO1Hz/QNneMt7Iqmg5QAAAABJRU5ErkJggg==']
['Twilight', 'BIAAAAQCAYAAAAbBi9cAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA6lJREFUOE+VlF9Mk1cYxj+Kc3+yXWimFxuk2zTIn4bQFkppF0hDNmBpBtgKixhhQZAhbSkFBp1uZWg3ZLRMxFKE0qKtrMy2E2ztn2+1tLQgbuJiorvQ7c5pgplZNjfmePZ9nwtZMm52kufqvOeX5zznfQ9B/M9l/8CXPP2R/6ajy+u0amZeoI8D2PpfTLqMlZQpT9vE2fPOc9l73302q7rs6Sz5K6zM3ZuJzD2EVf1VytejC4hNXoWj2/vlF71+FgVKIsZVHrbnEzLoPkYOqqtPNm7j1l1J4R9Y4wgVkOR3Qcvrg+uNXmTnt9zfmdcUFRd1XqQhC+eWMXP8MiwKdyUDOqMLEG49qYtYlhA+vQi7zocGmQHFYi2UnM9wq/RzNEsOQyDWMBIWtjNurjivw2ucg+toyM+A6LWZU72vvsqwFjwVZwrmrEvoq7DBLDDiltQAobidgeRRUipMTA0t32AU3hNzD7zGSANBZMi2UFe5nyZohrREB9dxEnMTS+jgnUBYMghv2afrbhhHb3aAnFxkQMHhOALDid8p0EHiKU6VklvQil0UiJakqBsf77dCmTmASPEAhoqPIEN4CGmCJvAkauzKfw/5pRr4J+JUTtfo693zGSM7iBdzan10sE9gh5AragNXoEKtvB+93ZMY0TthGraB92oJVlYewDTgQJ96DKTtiStXb8jvNoafIV7i19+lndC2X+bXPyqXffj4kmV+PYexY1aQMwnkv1YGWUUljryvQ0/dqfV9+Vs9zVTYLILKZ5UGsXMbb2/llJaWCN8OnzNMrxda9JNYjt+ENL0RrQol0nekQVtlRHA8gsWpZQhEmrviws5yIpXfcG87t+52UpY8NZXN3lIjPRiOReZxfugCA7s4EsCN727ArHChQiKDYGchRrumELbFEbQmkFvQ+ofg9TYX8Xx2zfnkLDmHbgM2m00M1tortQf06FC2Y2HqGgMjvSR+WfkVplYPzCoX3EOziDmuwjMSRk6BajVP1PYT/fzb/j0nZ7tmN+n3mUlpUTmCo1EGFHJE8NvDR/g+egd0fj5LDN6xKHo6bOAL1D/niTTRDUd2rMW13VBj/zFu/5YZBaYBp69j0blMPfs8zhj9KCjspPNZ+6fjd28IGld4MgIn5x/HJr9ByJRYDz5oS2B6KIT9Nf3IEaj+pCBrXFELOTERZm0Ichy+lHy2czZlpv+y80JfmILFVwPDsTvmo26SJ1I9zBU1/UVBfqAk35ujpb+RpL8BJjxIUjyXvSgAAAAASUVORK5CYII=']
['Rainbow', 'A8AAAAQCAYAAADJViUEAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA3tJREFUGBk9wV9MG3UAB/Dv7+531971aKGMlr+OwjoGBUZwRDrRBJwj0bHEmeiS6Xwyxn8x8UVNzHAPPvliFhMzsy0m8uDD5h/QZWoUNxYMBoZbZCBjZTBoKRwtLde7cv9+bg/6+ZDnzk6C44lw6f6whdOnETpzla+0803RMD3ZGSH95V62lzGQtMH9M7MhfpPUyIX5HE1uvNXDaCQgtykB70cR/4unrn3aqzYkZt7v18ZfezyTkfy0HlJ7FMWKEBJFpYMSVq7bngMlGvvc/OTiLzRYLp8K1waObaS16MDIRfupG9c6SuwCsSt2kJ+/B+3HMdC6MBofa0N1a2sVJTWj02mh4BFCCpV84jN4oHyX3KYEJAi2BWYR2CkPmMlBiOgwE0mYiymo1Qu0Mx4/8VLVnrtnF4VxfuCN9z5mDBA9FJt7mzDe3oXkjou69CqoxkA4gC9xQAggankMa7uTm3m32SLKD+Sz6XXGGCDJAv6j7di4MzqBo199Adk2EIqkQGQHDy3Ij2Q+bHr9g3UxyFHLdFyvJHAg+J/ipYgdjuMyzwELCfRsTWG/NQEwhqCVC0YLy/qKGJzmD77w9pHSoFyjbWWxtjAH5jIIHi8EKkCpq8JteCD2H0F2u4BwZhE+x8BEWbt6i6df8kr/s0+H/HKMc1yo02MYaG9APjGLxJ+T2NxYRV7fxu66GqjwYyrn2AG7YFGw4FygeYiXjva/KoipxoaKGPY1N+PJfRHEauvQaIj47vsLSN67i87ew6hOLGFeTS38FO45XhR8lQlffS0tmGViwbmCdKEb3tJSGLYLieMwMfQr1tZSqOzqheCVkDWIk7i/vvJ7WdVVxd96XWBU4kzb55qOiZvqJazmCxhLGzBFiqbnuzD71xyij8bxEN/XzXccf7PyxJ6+lkxuwknnftP4vorBd9O1mXBAnsbfaQW6VQadcWC7gmiIH0JlrBWuw+DYgFyiSGqu+O2NjZllPMBJRUevuH4Ipu1DyOefrS6RzmQN211iFGUtzSAcD8dh2Ll8cyStai8vra/8MQhgEADvjx/bX78c6rgT1ddl722/btSelEz69eaWoZqms1kwrGVt27xV1I1zgdWfRw6Ww8lmswQAo6QR2dnM6JC6HT3PEfvctjSsnx+3J1uob6qt6gAtSgEu4BbdV2KO80T3O0QQBFiWRQRBwL/txI3OlzkSKwAAAABJRU5ErkJggg==']
['Rarity', 'BMAAAAQCAYAAAD0xERiAAAEEElEQVR4Xm2SW2xURRyHfzPnsmcvlO4ulN1uF2sLrIpdJNS0KUZFUq1t0AiKkpASbyQSjRKENEGrPuCTiUoTjSENKAnFYKokbZOmIBaoTRXB1AjbWmrabmVpt3SvZ899PFnTxAe+ZF7+D998mf/gbmwt30131B58YM+WTw7vbTnW/+oTHZda6490723uPP1KY0fna40dh/Y0fFz/4pq3XRFEsATB/2i71EauvDcplHN173p8of2gnI8KPHLxm/AEqwgIARUEeywyS1dVPZ+9kJ6OHdB/uzF2BmcYXRIdHxkhO/0vR/e9+c4p7+pIO+92+wlHaGE+QV1lYWpLCe90kdKVTvJo80rqDTic4nJfk7c62kM3rltfgQpSLGOM4ZfR0apQIPQTpSR04uhVqhUYSkoItLyMVFaEIjNENpTg8ZbVyGYK6PpyHIYGBhCmLiYHZ2NDzxZlpwYHaX3V2mMet3sPpZSbjc/B5y+Fw8GDgWEukcbURBLR2jB0TcPpz4cwO5aBBQJuWSnsbC09eeN50tnZSYy0s6p5V+MwIVghSQ4iFwqQHBIIIcVjGEaxXtd1XO2P4dr3N6EqCvJyFoqmgvqDlqZqp+jxD4/z8etKGxjxm6ZJxmIxnB8YwNDQEGITE5iemQHHcRAEATYIVPvB8ZQRQu05D45QGPNx2PYNNFxWV21y/h0AiCiKkGUZcwsZnDjTg7cOtuOr098hYxLYQJIklK8ps5hoaAyM2ZeAFwRQEJi5FEclT/BpxZBKFhdkQimFx+NBTbQG+1pfQFZ34tZtFd29PTAtC+N2dU9vH/t18sKCwPP4r46DQ3QySzcGKBGERzRFpYl4CkubPdd3Fj1nu5GduAxvdQNIPgNV1zBw/hy6+y+D510xUZQYzwlM5CXT5iID+5RailLNDINN/ZUCoQTLlnkQj8dx8uRJW2DA7V2F6H0RGJoGt8vFgqF7c2vD0T4wMANgd0yjP2Mqb+Ty2RkqMrhhmbh+JYnk7TSWl/pwuP0DrIvWoX73EWx/LIIV3lKIgoitT21Dy7aWPzU125/JpbOLukrA8U1ly8uGwxWVz1CXwOvE0qHIGq4NJ4qPHApVoKurC4defw6bKigCwfLiRkMBPzavL39w5/tPChk5vV+ZvzVHUknm4DhB13RKeZ5LlthlzDAQG00jkykU/5VTYKgJiTANE6LkhKIqTNW0nKqpvYauj89PzX5jcqxG0/WmeGK6bj6V+IHPy7nfV/hWbS5kM0gnC5iMLWBjXfhnAA0FRQGz0XVtzmJsZEHOH52a+uPirubtOmw2BfYmg9cSP2YsJ7uIbxlpfaitdk3l/Q/rlv7FnVzucmXdPS+1HtjyD8dzWCIvy76/Z6bY5MTs4tfjn7HBjwZxN/4Fq6rr1ZuF0oUAAAAASUVORK5CYII=']
['Spike', 'A4AAAAQCAYAAAAmlE46AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAsFJREFUOE+Fk1tM0nEUx/9QPtCD7z30nE9sbbVeXJJR6j8DkVsIhg6HTqSXnBHSMMEbEy+AgPwVQpcgNy+kKLc/lCgF09Wquaab67kHX1pulif+mHRdne3sd3Z2Pt/fOee3H4J8N/ow2lrj4H64OljRfEXBIZ/k/3lWquXIrQl2ROAVA98jOro2XKUtvV9Dpj/iFV/ppwvLVfzThEBZGRWh0S4hmFx+rId2ysmMSU6WAAUeMfDcdYe0gUrGdUOl7rZXBDRdRQtRp1PeIRlVctIzk+lHR6itJnwC1nkbgOXgZlhO3h6RY9rZKYT7W9NUKpUklUqRKjPDQADEjYTz3SLgzQjzMWua/5E5xLpQrqOX/jEzamTc4LqEX/KQRwRMBwfEDgnUOyXAdgk+1zr5e0w7J/vA15OfN28PW5SnZlRuVT3WeMia5oHW1AthawSS40mIjcWhW98HfF89Ifa6qb+hqAA6FA5xzIp/dVncYDc/hkQOiI/jBcctCegwdRJgsERWcszpZTrKU/3S7s+Ff4vn9UG4aWbGyofoaB60d05dDJuiR/8DcXMCpLY24GPsrlRWcxZxKmaqF0aCsDy8ArgtAVFL/Jc2C4LWBEwFNLCUbt9PZrpEiEk2VjbmMYIdm4TQ6Cq4RmYB02CwZAlB2ByBkHEVYhYcEmEreNZl4F+/C8F0+0vE2x1IL3qDsDgZhKg5Bt7ULAgHa+HVzlt4v7MHMQyHpM8LrlQzuNdaIfJCub+R0Z5DfNrAxsJAEHJbhXhue5nQJmS3t2D73S6suVK5XBKiYQMs4B3xSEbZ83xTc3ljq5eMmNts5/3d82/8jicQDc0Cbo8BjiVyQsez4rYkeNRzfqfadUYgEJBRFCVRKBQS0tTUSM7BxaauUelyenwunnZ+SnhXDkKG0EGgb+5g4p5dpa5TFEkk1bmfQSu8/TfTXs+Z8UbptgAAAABJRU5ErkJggg==']
]
not: [
['Plan9', 'AwAAAAPCAYAAAGn5h7fAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAzE15J1s7QAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAACAElEQVQoz3WSz4sSARTHvzMjygpqYg3+KIhkE83DKtKlf0C9SrTRuZNEx0VowU6CuSeJNlwwpEO2kJ6SQBiIUAzFjRDF4wrjKosnGx3HmdehFDfpe/2+z/s++D5gU7IsEwRByICIiAEAIiIAYAFAXsjYVr/fLxMRNVvN+prJ5/OA3+/XERFNf02JyeVyDx0OxyvLNQsnimLKfcf9KRQKXQAAnE6nlf5qMpnQycnbP/kAoKoqsSwLAJhOp+AAwOv1otvtpqxWq73dbt/r9XqvEQ6HUalUEvF4XLd5IpvNZqlerzd5nlf6/f6tTCZjBACk0+nb+XxeW4UrikLJZPImAGA0Gq0NIqJyuSyyANDr9Q5Wu1utFvR6/SULAI1G4+vK8Pv90DTtGwsAJpPpaGUYDAZ0Op3PHAAEg8H3tVqtbrtu21sqyxuRSOQJk0ql9IvF4r7b7f7pcrlejkaj57IsH58Pzp8dvjhc/lsBk0gkbLFYrFqtVvd27+4qOk733ePxPDCbzVBVFfP5fCiK4rvhxfDN/qP9wSasGwwGMv1HiqJQsVg8ZlfTHMepkiR1t05gGJBGmM/nMBqNj9nN9kql0lNN064ARISzH2cQBAGz2ewLu2na7XYLwzBbvxYIBBCNRrFj3BmsAZ/PZ+J5/kOhUIAkSVeA8XiMZqt5efrx9OA3GfcgvyVno9cAAAAASUVORK5CYII=']
['Neko', 'BMAAAARCAMAAAAIRmf1AAACoFBMVEUAAABnUFZoUVddU1T6+PvFwLzn4eFXVlT/+vZpZGCgm5dKU1Cfnpz//flbWljr5uLp5OCalpNZWFb//f3r6+n28ff9+PRaVVH59Pr//vr38vj57/Dp7eyjn5zq8O5aVVJbYV9nVFhjUFRiWFlZVlFgZGOboJzm5uZhamfz9/bt8fDw6+drb26bl5j/8/lkX1z06uldWFS5r61UT0tfWlbDwr3Ew76moqNRTU7Mx8P75OpeY19pWl1XW1qzr6x5eHaLiojv7+1UT0xIU0uzqadVS0nV0MxkZGT5+PPk497///ra29Xq5eFtY2H28e2hnJignJlUUE1dXV2vrqxkY2FkYF/m3d5vZmfDuruhl5aZlJHx8O75+PZWVVP29vT/9fTj3trv6ubh5eRdXFqTkpBOTUtqZmX88/RMQ0T78vPEvr7HwcHDwsDq6ef///3Gx8H++fXEv7tZWVedmZZXXVudnJp0c3FZU1f79fnb1dlXUVVjXWFrZmy8t7359/qLj455e3q4s69vamZjX1zy4+avpaReWFz/+f1NR0vu6Ozp4+f48/lnYmi8ur3Iw7/69fHz7+xbV1SZmJZVUk1ZV1zq5ez++f/c196uqbDn4uj9+P7z7vRVVVXt6ORiXl/OycXHw8CPi4ihoJ5aWF3/+v/k3+axrLOsp67LzMZYU1m2sq9dWF5WUU1WUk/Au7eYlJGqpqObmphYVV749f7p5Or38fPu6OpiXFz38fH79vLz7urv6+hhYF5cWWKal6D//f/Z09Xg29exraqbl5RqaW6kpKTq5uPv7Of/+PDj29D//vP18Ozs5+OloJymoZ1ZVVJZWVlkYF2hnpmblIyspJmVjYKQi4enop5STUlRTUpcWUhqY1BgWT9ZUjhcV1NiXVkkhke3AAAABHRSTlMA5vjapJ+a9wAAAP9JREFUGBk9wA1EAwEAhuHv3dTQAkLiUlJFJWF0QDLFYDRXIMkomBgxNIYxhOk4wwCqQhQjxgxSGIsALFA5BiYbMZHajz1oJlx51sBJpf6Gd3zONcrqm/r1W8ByK0r+XV1LXyOLLnjW6hMGpu0u1IzPSdO17DgrGC6AadrVodGcDQYbhguP6wAvAaC0BRZQalkUQ8UQDz5tAof0XbejOFcV5xiUoCfjj3O/nf0ZbqAMPYmzU18KSDaRQ08qnfw+B2JNdAEQt2O5vctUGjhoIBU4ygPsj2Vh5zYopDK73hsirdkPTwGCbSHpiYFwYVVC/17pCFSBeUmoqwYQuZtWxx+BVEz0LeVKIQAAAABJRU5ErkJggg==']
['Madotsuki', 'BQAAAAPCAMAAADTRh9nAAAALVBMVEUAAAC3iopWLTtWPkHnvqUcBxx5GCZyAAARERGbdXJrRUyGRUyYbY23coZFGDRFGEYfAAAAAXRSTlMAQObYZgAAAGhJREFUeF5Vy1kOQyEMQ1Fshzd12P9y61AixLX4yJFo1cvVUfT23GaflF0HPLln6bhnZVKCcrIWGqpCUcKYSP3JSIRySKTtULPNwMaD8/NC8tsyqsd1hR+6qeqIDHc3LD0B3KdtV1f2A+LJBBIHSgcEAAAAAElFTkSuQmCC']
['Sega', 'CwAAAALBAMAAAD2A3K8AAAAMFBMVEUAAACMjpOChImytLmdnqMrKzDIyM55dnkODQ94foQ7PkXm5Olsb3VUUVVhZmw8Sl6klHLxAAAAAXRSTlMAQObYZgAAANFJREFUGJVjYIACRiUlJUUGDHBk4syTkxQwhO3/rQ/4ZYsuymi3YEFUqAhC4LCJZJGIi1uimKKjk3KysbOxsaMnAwNLyqoopaXhttf2it1anrJqke1pr1DlBAZhicLnM5YXZ4RWlIYoezx0zrjYqG6czCDsYRzxIko6Q/qFaKy0690Ij0MxN8K2MIhJXF+hsfxJxuwdpYGVaUU3Mm5bqgKFOZOFit3Vp23J3pgsqLxFUXpLtlD5bgcGBs45794dn6mkOVFQUOjNmXPPz8ysOcAAANw6SHLtrqolAAAAAElFTkSuQmCC']
['Sakamoto', 'BEAAAAQCAYAAADwMZRfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAxVJREFUOE+Nk19IU1EYwK+GQQTVQ39egh6ibKlzw91z7rn3bvfOmddNszl1bjKXc5rJJGmBUr7Yg9qTD0IalFgRBEYg6EDQQB+GovQyQgiaUZsoLcgHMcr069w7MgcGXfi453zn+37fv3MYZt/n99e76tzVj4JN/hP79fvXnV3hnNabwUBjoOHcgTYOu/JQspgTzsqKgn9BfD4vkWTzur287PqLVy+zM+yePB7KsRXLywTjnSpnZctBkPCdW8ccDuU55vBO8RXbkC/oP5ph19V5+7LIky0OY1BKbZEbLcFSt7u6pN7jLmltCVrr3DV5jY3+KovFEsccB1KJNVpefe10BqS2tqqO4/AuphBB4L/LkrRqNgtJs1lMypLls1kU38mytMLz/E8VIlutqVqX6/weZG52OttRXjbE0cP/FYLRlpVjDXuQ/r77x2XZPKkCHA4HBAIBkCQpAygIAvh8Pu2MZgO0Lz+QSa/sQfwN9RfpVN66XC6Ynp6GhYUFGBwczAC1t7fD0tISxONx6O7upgHILmsqvLcHodOggfiV/v5+SCaT4HQ6IRaLgdfr1bIRRREmJyfBZrNBNBqF+fl5sNsdgE2GiAbp6bmbdbXC7qWQbxMTE7C2tgY6nQ5SqRSEw2ENopaoZpCXlwdTU1NaoECgCbgiU6y8QH+ECYWaTymK7TWdys7MzIwGaWtrg42NDejo6AB1WjU1NZo+FArB2NgYrK6uQrAlCASxn2z6wkuMp87VIAhkE2MEAwMDkEgkYHx8HBYXF0HtkQpRy1BLiEQisLy8rPVNKSsFjEzrXH4+z1hlS4xDhKadNu7t7YPR0VHweDzAEVWfHru6HxkZgeHhYVAURYNjkylVWKArZjjMzqmdVi+QCsLUkQiEjvDvncEkvU7/qQ0Vgukeo48Go87IiCJnZNmipxiz7wXEbVDnbUxQOgM12h9n6qTq6NvapRdtkwaP0XK8RmPuYSbxYfaQ/sJJhjfknuFRURUi7AMOozcCwl94hLZp5F+EioDQVwqYI6jomZU1NFtM+rOSxZjVazcyvwHr/p/Kws1jegAAAABJRU5ErkJggg==']
['Baka', 'BAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA0pJREFUOE91k3tI01EUx39JOpA0H4jNx0pbD3XTalISWf8YFlEgldqDsBLLyqjEKBCiLLWiggh6/KEV1WZ7OaelLZvDdDafNW1JFraWe/32+01FrUZ9uy4ylLpw4Z5z7/nc77n3HIqaMRIjZJyEcNX+uFCFeGmI/GZciEIsCFJUTvoAzDz+1y7K76MSwhX5hXl6z+WSbrzU2KB8YEGDwgrTaxZ3b7xHcaHhR3xw7Z5/UviB1ReP5XSg3+TAqYJOxMzWISFIC0GQDomhTVA9skCnsaAwp/vnMq66dBokNuBR9uFd7T9Z1zCunjci0qcRJUVdoJ3DYOhRnC/qBZ+jQbfeCc+37yjY2UEg0iwvJE0k9l8Z+8xqHmTgot0QLdQgTaQFQ2AsOzlHvOu1S5pwOLsHHo8HjHMCq2MazNvTlByKHyrJLDvdR25jMWRxYx5HjeMH2r1BDOOeguRua4OI14jx8a8YH5tA+al3EHKlW6mYOapb2oZBOOwMbEMseAE12L+jjUh3w+VipyAZ65oxn1NP/GMYGR6Ftn4Qsf7qa9S82Y/l/X122G0uL2TbxmZEz1WhXW8mUol8moXu+SCi/OoQ6VsDh3UUwyQ1k9GOaI5MTkX4yWTGHutvgI1F28sviAlRgxeoRm62HvsyW8En9pZ1TYgi6TntoyQtFm86rVgUoJZRvDnKMmXVAGxWmkAYOBwudBqGcHCvHulrGpGT2Uy+z4yT+QYsCXtCUpp8GxbKhx8gDK0ro+KjJGvzdjfDZnN6VdisLD5/JjArQ2zW66PJOj2lEZtStaBphkwah7K6kMJ/GEulp1bMWhAmMbTozOQRaWRtfoZVgjo4iRra4SYgGi26TwjxVeDKhR7Y7U606ixICq9tr7hd7+OthRWL7yUnJ1WPmXotqLhpRICPHCePtuFV6xdUPTAhcWEtRHEqfHpPyto4hPXLXnzflSEJnFaN3OCKDcsFsrEntR9RUmxARLAUgT5iBPuJsXWDBj0dZjRU9yNV+PTbpjTp9OA/pOSk24nRkXf1J462oPxcJ65f6ULlHSMulepRerYDgvj7A0cKpNz/tyTZqbzXO4t0ZZGQJ34RH11lFHIlA8LIqreCCMUZRY3cd2bwL/5/RmjNSXqtAAAAAElFTkSuQmCC']
['Ponyo', 'BAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAuNJREFUOE+Nk3tI01EUx39BTytConQTt1am07m5abi5KT5S8z2dj1yOEMUC7aUgIoimlmUEWX9kBZGWaamEmE6U1BI1XNPNGTrnHs33IwuSXrL4NgcJ0mNdOHDh3PPhnPP9XoKwcroJYvMQiRSicHCQKCgUyZC9/T5rNet5KUFs0zCZbZMsFmZ9fTEjEEBDp4/KSSSb/4JoGIyWaTYbiykpWEhOxhSHAzWD0aqkUGhWAcVkW58xlvuPhfh4zItEmOHxYDR3MhcdDaNAsKJydAz5IySKRNjEUmy88vjOVaU8F0iPCqCNjEBHkC/UYaGYFwqxmJoKLYOhkxPElg0QsbNtTlmox9yjRD9UCbnoOR+J/lwRWtOCcdXfDc2BPpg0d7CQlIQZPh9KKlVkAQjJ2x2zmOSsQu7hpzUJfBhLjsNQmADjxcT10Bcl4rE4EHc5LjBEhEPn7f1WTqXSLQB/s1Tp7vslsoIkyPPiMJAbi86McBguiaHKjoEqR4jJy2K0nAxApzMN5iUGrclrKVaz2fUvuF4tRbxDKA90w5VjTFyLZKHpTBSq4/1QnxGB2qxoVIZx0JopRCPHFSNOThfWZzfrXDcZEowH4iA05ATg68hDtBaL0HAuCm3lJ9Bfcx2fFNUoi/DCjRgfNHHd1wCZA2TyXjNkE6F0cBDpPFiojeNi8EkJdFoN3vXch0nbBJOhDd907dANv8JITxNqziag3ZsJbUDAwLin50Q9QWwl1qSYoNOVvUcOoqOqAAa9Fu9H2/F9+B5WZLcwOyxFX18flLI+VASyMGVeoJHD+Tzq5BS1PoaKRrNT8127P74swsq4FCa9FKvqBqwaOiz3hdEuLKueYSyECT2LNW0eIfo3E/WmEbvnG1MUJnWdpWhDGDvxQXZHo+RR0uW2tnv+auPX+TvtJm7zKpaen/4y2yjBUlcxlvtvmvT16ZWDpQeoVv3/60F/NrHjTf4ugazIXtJ8ivjnz/sJ+yGQRjcqUdIAAAAASUVORK5CYII=']
['Rabite', 'BIAAAAQCAYAAAAbBi9cAAAD/0lEQVR4Xl2MXUxbdQDFz/9+9Lb3tkBLCxTKhzgoOOZAsokbJmZxDFHnd+LL4hKVzBgfNCY++ODbjDEaZowvErOM6HRu6hKZY2rIAOkCY4OSDTpFaAsrlJa2t5+39+NvjT7tnJzknIfzI98Nf/C6TuXdguWBd1q9rcb8/CwsZiu2Ywm4nDVo3VWLZCKDaDwJq9mCg31PgjAMKKUwmcyYvTbek9iJRDm6M/XswEDjwNz6plWW6wdZhjUAintFCEEhn0N04zYskljaDLaj8ar49oUrsYR6mrFJNj322w46H8y+mitM/ZJKZmyE4XAvjJSsazpyuSzslVZIkgWKOvvRgQ6Xrdlhqmds7o7bFZoLkctreKxf7GtuCE7IyUQjBQcQ8j/lvxCGQJZz0IoCVpamTtzfIh9nwiaIrCQyjNg8mq11oDLUhNXRJfT1Ozr3tS/PqpnQ80qRgjAmKIqBfK4ItbSLKoOZqR/6neLkENlSUAIhlktvEf+sD2rkm8nWTHtvZCGMVON1ePuaoBER31/MXGly1wSqq9Uug6FluYyWXJiPqFXmjd4Dh9oF9ZKKimYXRtYCx8lmMIDIxlIPGz591av0mtanF7FcCEN6iMXeox2wOJ0QJAmUAoRQaIqCnWAQY1/ewKNGNeQuYXkm0d2NC2e+wvmRr/Hx+6+8PHayrbDyyQBNDb9As3PHKDWG6MTM23RoeJAWsqeoWvyUUv0UHf7pBB0fe4OeeXe3/vmHbx3+8dwIGJ4IsFpMMFe0fbtAn+nwZePr1u4MBK8XIALG/Rt479wYrs2vgeNNAMNgMbiNzybuoKVvn+Gs9kbr6qpBfJfGYHFIkJUCoGwfqcoMX/b27EGhwgOjoCADDlP+CA51ugFFRzoB8FYNaQ1oqKD44+eNL+wNj7zJGQSIhe8+jgQ9thk+27v/KRY6L4FSCkVOwtlQj6P73Qgt/o1ERoKt4iUkE7+jrZMHyzIoK9cOBFfT4LbWAk+0a7ZLnvqHcTNdACgFScfAcjxEdy00VQclHGo7dqGeYxHbvIo6hwhSghCehb3G5p6eW7VxXC5/xGWToMgrKKoaCnIalI9CIARasQAqloMI/x4BWrLLYwE1AEPTwCGHaGjz7pw/leZUNV8wNm9BLy6CxsvxZ1kMbaY4TKIIXlNBsynoVjvAC4CuAoYOVi+CMfLYCUfg95tPHuzZB0YtKzsb58RMucWE/fZmhCbdOP9rNnLnxko6GVoB8lFwyVVw8b/AyeulHoJyN4Rb19dTFyeqBlu6njvfsWcvOJvLs7DMmw/7bvpeE4pU2OIcgcqmp4fGAgt2Txwvqr7lTp5V7LquZxXC6+BqEvGcY5pyjaM1tffJbk89NE3FP5VQ6y7a+paZAAAAAElFTkSuQmCC']
['Arch', 'BAAAAAQCAMAAAAoLQ9TAAABCFBMVEUAAAAA//8rqtVAqtUQj88tpdIYks46otwVldUbktEaldMjldM2qNcXk9IWktQZkdIYlc8mnNUXlNEZktEZlNIYktIWlNMXktE7o9klmdMXktFHqdkXk9EWk9EYk9IlmtQXlNEXktAWk9AWlNEYlNFDptkZldMYk9E4otg/p9kXktEXk9AXlNA4otclmdQXk9IYktEXlNEwn9YXk9IXk9FFp9o3otgXk9FPrdwXk9E2otdCptkXk9E/ptkcldIXk9Edl9IXk9EjmdUXk9EXk9EXk9EbldIcldIjmdMmmtQsndUvntYyn9YyoNYzoNc0odc1odc2odc6pNg7pNg9pdlDp9pJqttOrdzlYlFbAAAARXRSTlMAAQYMEBEVFhgcHR0mLS8zNTY3PT4/RU1kdXp6e3+Cg4WIiYqMjZGXl5mbnqSnrbS3zMzV3OPk7Ozv8fT29vf4+fz8/f7SyXIjAAAAmUlEQVR4XlXI1WLCUBQF0YM3SHB3a1B3l7Bx1///E6ANkDtva0jKbCW2XIH1z2hiZEZ4uUgxo7JedTQye/KN/Sb5tbJ+7V9OXd1n+O+38257TL+tah3mADAwSMM7wzQWF4Hff6ubQIZIAIb6vxEF4CZyATXhZa4HwEnEA+2QgoiyQDnIEWkjVSBBZBqXbCRlKYo8+Rwkyx54AOYfFe7HhFa7AAAAAElFTkSuQmCC']
['CentOS', 'BAAAAAQCAMAAAAoLQ9TAAAB5lBMVEUAAADy8tng4Ovs9tnk5O3c7bX44LLduNO1tdDh7r/eutj43q2kocX23az07N+qqsvUqcmXl7331ZXJj7r40o/Pn8T42qP63KjNw9n21p3Y387Ml7732JzR55z05MSxtMLGn8TC4Hx8eqt8e62Af6/B4HnG4oPC4HzH44fBf7LCgbOkoMTcsrmtn8PWqcfFtKrj4Jvs2ZOz2FnMqLXT3KfY5p60Z6NUU5XRuqHzwWSywqDn3JaiiLWahrWhkry5zJjRmqm1Z6P1wmb1y319fK632mK5cKi5nH+73Gu73Gy73W283W+9eK17e6y1yZS3aqRZWJdcW5ldXJplXZppaKBwb6VwcKV5eKswL306OYNPTpGkfK+m0kGpUJWq1EnEqIuXK3+Xh7ahP4qhkryMfK6BgK+CdpGMaKKMa6O9ea2+eq6+oYW/eq+NbqWVlL2Wlr7AjanA4HnA4HrBkqbBlafB33rCgbLCmKjCxIzC1mSs1UytV5mtxIWt1lCuz2evWpuvXJywxYzHjrvH4oXIjrrN2HXO5pTO5pXUlYnUlYvVl5Hb0G7e0XTg03rhr5fpzHPpzXTp0Hvtz3/wrDHytknyt0zyuE3yuVHzvVr0wGP1x3T1yHf1yXe0ZaL2zYP30o730pD31ZeRIcF5AAAAQ3RSTlMAFBkbHEhJS0xMTk5UWWBsd4SEiIiPkJCVlZaam6CjpK29wMPDxMTFxcnK193e3+Dg4uTn5+fo6e/v8/P4+fn7/P7+J4XBAAAAAOBJREFUeF5Vj1OvAwEYBb/yGlu717atLW0b17Zt2/6nze42TTpvMw8nOZCAmwUpiIY6c5IiLi9tPX64GairqszHQ4X2VB64v1Cs6PxMPJSdHM777s6/jyaMRGiRLyyrb88OpjZ3CzAXrm1sqzSNNeN7kVBPNgB7cG51abE5l9cXDces7emQ1uadHhutFUg6gpPKkSIqQGavwz7r7O/+/3t/rSdjI9XDM3qz4fr3B/3iA0aJTG9x71+9oR/PLDwUe2wm19bly+fTIxHyEETatbPewGEw6Mk/tKZCEqSQQUlIHB/QNBEjjVN1AAAAAElFTkSuQmCC']
['Debian', 'A0AAAAQCAYAAADNo/U5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAZ5JREFUOE+Nkk0oBHEYxv8fu5GQj3JwcaDkIAc5IpR87M7MKnIVJVKclaIQ5Sy5OLkgR7n5OigcSNpmd2c2Vyfl4KT8/muWiVU79TTv+7zv837NCBF6PG1X+NpZyEYSD9mIc+tHnBPe23B9xKrCuTmbQA/JKfABrhBswa1hH4A38IwfOxPdX1qcjiCQxO5NyrjKV70TnSbeRPwJvGN3i4yyqnEucPY8ZZX9GSEgGK+RvFfyjk2VKZxzBNG8wJWWgh/xtDOeUXZ7Slr6TrSLYL9N4SMgYTTcwdc2ArvJcElhSVcM6mCNSV8n9hA59yTU5UWMG6HIbLhIWlglgWiC2L4Z79qTdo40D6ISuOWwKCWHyk9Fv8ldpUHOuGTuynwSBUynddPdlbEosVpP9Eu4FnOsRzUYNTsdmZN/d5LDiqM0w+2CMdAFFsFGWgfXxZnheqe/z+0puwEM0HHYV3Z9Sgz8TEz7GkQvpuJ/36ggj2AaHLrSlkULWV5x+h2E8xkZL16YVjGNaAUscfZ/f6c/k9ywLKI2MMcRWl0RLy007idmRbQJ7RIfDAAAAABJRU5ErkJggg==']
['Fedora', 'BAAAAAQCAMAAAAoLQ9TAAABPlBMVEUAAAApQXIpQXIpQXIqQ3UpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIqQ3QpQXIpQXIqRHYpQXIpQXIqQ3QqRHYpQXI8brT///8uTYMpQnM5Zqg5ZqnS1+I4ZaY4ZactSn8uRnYrQ3MrRXgsRHUsR3s8bbM8brMtSX4wUosxVI01XZw2X50vUIguToQvR3c6X5o6aKs6aq08Un8qQnM9VIFDWINJXohKcKlXapEqQ3UvUIc2X55bhcBdcJVgcpdhfapmd5tuk8dxgqJ1hKR5jbB6iah/m8Shudq3v9C4wNG/x9bFy9nFzNnFzNrIz9zK0NzK0t/O2+3P1eA2YaDU2eTb3+jb4Oje4urj6fHm6e/s7/Tz9fj3+fz7/P38/f3+/v83YaEa/NNxAAAAHnRSTlMABAoVGyY1SVlpeIuQsLfDzdHW4+3y8/b39/n6+vr4+ns8AAAAyklEQVR4XiWN5XrDMAxF75KOknYdZJS0klNmHjMzMzO9/wvMcH7I37mSJShsJ+5NjMT6umDoHyXDcI/2qJadh++P3cle1de+9yPe3/bTY92wzfzr7wGtP3JrAI72BZGVtcAdQlwHy+JS1pDbBE9qamZF3BYrjQxPEXwKc6dC8bXFm0QIpmt8kn0Rn093q82UCtK8oXZckwFJzuulV8bHkajPyXdbnJnARfDHs0trz+JQ+5AFvzp/L0+cL2qPAINUPrq5OC6p/64F/AMnrST+Dq/r7QAAAABJRU5ErkJggg==']
['FreeBSD', 'BAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAABIAAAASABGyWs+AAAABmJLR0QA/wD/AP+gvaeTAAADXklEQVQYGQXBS2wUZQDA8f83j33M9rF9d7u4loaWklaDpkSo9KDGaIKUaGxshD2YSPRiuDVeTDyhBxosJCoa40ktpAkPDcUqAYVIpUSUPrAulEdD2bbb7e7ObGcfM/P5+4kwKDvq6yJ1FYYcvb+YAkqAHo/HQ7FYrFIoCiurq9ZXJ06YSOkA+kBzfX06bys3zHxS9EL0tXDVyZfefacqV+X/ZSJx5+qLbx98LhaL9RiGEZWlEsWC/Thd9q6Pf3vs2u6Orc83rFsvTwwfLf5obgywT1Vjh2Hh+rbNsnTssJdNLedK5aIrpSuldKVXKsnH4+Pyn6FDXn5tMef9O+3NvdkvP1V4+EYw2AoQ+KSx8dRYS6NXXnwovaItXduSrrkinWxGOmZWJi9OyOK9m1LmsjIz9IH8QUMOd3WfAQwNKCy2tJwbHB5+XasPaxIHmc4g7WWEZ1MquBiRFlJTf1E7+Tl/H/8asavPzTY1nWd2ZkMDRPeBeHPz5ojwsilEQCBvTSKunCF3M8FSNkBGVTHDYYrLj8jVNhDZ2SMa2zo3MTamaIC/u6Ojr3DtrOrvP0BpdATnyBeIhTxpR5ABUlKSUlXS1dWstbVxdz6hPL0l1quGqkLaKwNvVcjEXNRd/4mit4Z19DjefBEPyCKxgQJQcF28dBrHNDGTSZSezsjeff0hraa2Vs2vrvit81O4vj9xLJcC4ADrQA7YAGqBGsAql/EtLdFQE/L7dF1XZmdnSrbPMJfXoLDmolQK8gJyQBowgQhQDRQBD+hsraVhd4e5MH+/oExfvWLJ9q3/3S7qMpNH2hsS40kFS4EUUAMA2IANRIBXv4uzuO67c2PykqkA5YmZ6bN18YPi0Yoknxc4AsJPCMLVAk2BLKDosCWqs/PZaulkuxk9fekcUBAAQGDks5FT0W++3NuYuC0DVUL4DIEdlIQDAj0IRkigaMjArkFx0tf523sffrQHyKsAgHPhwoXLL+yP9/kePNhk5ExUTyKFkJVAUAiCFZrQup4Rv9ftuLV/6ONBYBVABQAArMvJ5MXW7duD6P62sD8UrPAFRU1TpeCpCnGvPZr7WW///v0jpw+VC9ZdAAABAAAAAMLo7drWrmQyPWG/r8tnaGIjaM05ujr16x/ZBFh5AACA/wGZnIuw4Z4A3AAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMi0wNy0wNFQxMDowOTo0OS0wNDowMOPVpFwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTItMDctMDRUMTA6MDk6NDktMDQ6MDCSiBzgAAAAAElFTkSuQmCC']
['Gentoo', 'BAAAAAQCAMAAAAoLQ9TAAAB9VBMVEUAAAD///+AgICqqv+AgIC/v9+Ojqqii9GAgKptYZKQkOmPj/ddUYBgW4eVjeCTgfiWjO5wbJaZkvPBvepkXomYkNldV4Bzbpl6dJ+Uj7ynoO6Vi+1qZI63se2mnudjXYjOy+GCfaqZjvWlm/Pc2e+Oh7NeWIOWjfeXjeW1sd+gl+diXIfp5/KHgKnn5/F2cZx6c6ZgWoXc2e6dltrAvNu0scrX1eTOyujCvup4c5qpovVpY43///+6uPPJyPXq6fvm5vrz8/z8/P7+/v/d3PixqvmxrPSyrfe0sPO0sfS3tMve2/3r6vy6ufPz8/3d3fi3tM63tPO4tsu5tsu5tvO6tfe6t/Vva5KRjKy7tvW7t/W9vPO/vM+/vvPCwfPEw/TFwvTFxOfGxfTGxvTHxvTIx/TJx/aTiOrNzPXNzfXQzfnRzuHS0fbS0vbT0uHU0e/U0uTU0/bW0+zW1ffX1vfY1/jZ2Pjb2/jc2uSTiemVkLSlnvbe3PTe3vng3fzg3f3g4Pnh4Pnh4fri4enj4/nk5Prl5Prm4/ymn/bn5vro5/rp6O/p6funoPWsqs3t7Pvt7fXv7vzv7v3w7/nx7/3y8f3y8v3z8vytqPWuqPX09P319P319P719f339v739/34+P35+f37+/+uqev9/f6vqvSwrPQAR0dcAAAAPHRSTlMAAQIDBAgJCwwVFyAsNUFHSVBneH+Bh4mVmZmanKCxsrK2tr3ExtDW19rb4ODl5u3t7u/w8/T6+/z9/f4MkNJ1AAAA8ElEQVR4XjXNw5aDURSE0YrRtm3b54+dtm3btm3bz9k3Wek9+2pSYFwT8ibzE93hwAtdJqK3nZo4J9hFXbP+vFHOthV6gnGzstZq94wdCs4UCCDymQ2v7X0LdYoSQ0MIENRYzJbRlPTTHu73ZNAL8vivmVui98PpzuqffX0mIPHJGtOQenukteJ+aS3b9htNpDnT9TeZH1bHAwBRMhGpd6e6uNrLoRgxBKmsX47nBlp678ojpEA40fejcmW4e/No0V8IIPfj6eKgbEJ3ZUnzgE1OqWp9Q3VeWRAsg51f1dZ8c31RmAsc+N5JGbG+zvj3BzDCPrzMDC9SAAAAAElFTkSuQmCC']
['Mint', 'BAAAAAQCAMAAAAoLQ9TAAACVVBMVEUAAADh4eEAAAAAAAAAAAAAAAAAAAAsLCyXl5dgYGCnp6eTk5N3d3fBwcGqqqq8vLzNzc3Ozs7Ozs7Pz8/Pz9DQ0NHR0dLS0tLS0tPT09Pf3t/Pz8/i4eLb29vZ2drZ2tna2dra2trf3t/u7O/u7e/u7O/r6+vt7O/w7/Lw8PDy8fTz8fXz8fbx8fHz8/P19fb49/j49/n6+vuPxlmWyGOx437h9NDr9eD6/fj////+/v75/vTA5Jv6/fb7/fnL5bDL5q+AxjeDxUCEzTyGxUaGzjyHxkiHzz6J0D+Kxk6K0kCLyE2M00WNy06P00mSz1OUyF+W2FGX1FiY0F6Z02CZ21ac0Wiez2yfz2+f2mOh4GCi4GOi4WKi4mOk12+k3Wul32um1Hin0nun4G6n5Gin5Wmo23Op2Huq1n+q43Cr526s4Hit23+v6XSw34Cw34Gw6nWx4IKy4IOy44Cy63ez146z34az4IWz4YW03Y217nu38H2625e645G74pK83pu98Iq984W+4ZjA4px0tzDA5ZrB8ZDC5p7D55/E947F6KHF+JHH4qvH6qTI46/K5LLL5LN1tzLL5bN1uTDL57DM5bPM6qzM66/N5rTP6LbP6bTR6rfS573T67vT7LrV7r3X68XX7MHX773Y77/Y9rvZ8cHa7cjd88bi88/j8tTk8djk9tHm8trn89vo89zo9N3p9N3p9d7p9tvq9d/s+93s/dzy+erz+O73+vT4/PX5/fT5/fX5/vN1uzB3vTD6/ff6/fh5uTj8/fv9/vr9/vx8wjV/xDmrMRH0AAAAOXRSTlMAAAECAwQJDzk/RUlNU3F0kpSVlpeYmpucnaKjpKWqqqqtu8LExMTEzdTU1NXY4evy8vP+/v7+/v6LaR1mAAABD0lEQVR4XiXI03bEABAA0KltW9kaW3eSZW3btm3btm3b/q4mp/fxgqKOtpamhrqaqoqykrQYABh+PVMU9fjE5Xp8o54kgPHN0EBHU2N5YXZykiua0HHd2759VF2Sk5IYE5GGsmCEWLV1kVWwt5O+3x/qpgsy8k4ja+cJl2/v5C22tlgCAHtw9TQSa4s+AzfPSm0BRNl9SydhWJzLC567KrNhgrNwHIJ5qTz/2f9w7Jw/DNqIjVr04exW0AEOXcN3Ab7enr9eDW2VTJgehONyc2Z8XP5YdD0Tcuhcc4/r45OjGX51TEjYPbh8THRPvbz+CHusgSZlT7rP8PkCwfQKaQUi9Igr6JsRBMFiWZgb/AHKElRzKopZJQAAAABJRU5ErkJggg==']
['Osx', 'BAAAAAQCAMAAAAoLQ9TAAABrVBMVEUAAAD///////+qqqr///+ZmZn///+qqqqAgID///////+tra339/eAgICoqKjx8fGMjIzm5ubh4eGPj4/g4ODIyMiAgICSkpKLi4vS1tbPz8+Xl5eMjIypqanIyMjW1tZ2dnbR0dGamprFxcV3d3d+fn60tbV3d3dcXFx3d3epqal7fHxxcXF+foCnp6hYWFhyc3Ojo6SMjI5fX196enp+fn6Li4xERERqamqgoKFpaWmFhoeen6A/Pz9QUFCWlpeSk5SUlZWUlZaOjo+Tk5RHR0cuLi5YWFgwMDAeHh40NDQ3Nzc6OjpcXF1rbG0XFxdSU1NVVVVXV1dZWVlbW1tnZ2lwcHABAQEEBAQXFxchISI+P0BISUpaW1xHR0kNDg4qKyszNDU1NTY9Pj8NDQ1cXF4XFxhSU1QSEhIDAwMrKywtLS4uLi4wMDFHSElISEggISE0NDVJSktNTU1FRUVWVlhGRkYEBAVBQUE0NTZQUVJQUVMFBQUqKitWV1lXV1daWlpaWlw+Pj8bGxtcXV9dXV1fX19fYGFgYGBkZGRlZmhpaWlsbGxwcHB2dna844Y9AAAAV3RSTlMAAQIDAwUFBggMDhkeICMkKCgqMDIzPj9ERFBib4CCg4iMjZCcnp+jqamrw83W1tvb3ePl6Ojp6+vs7u7v8PHy9PT09PT3+vr7/f39/f39/v7+/v7+/v50ou7NAAAA30lEQVR4XkXIY3vDYABG4SepMdq2bRSz/capzdm2fvOuDO397Rw0Ly4tz2QAQPbcxuZ2E/STJwfxPhWgG355fRrVAIVb1zeP9UDLfiSwkAcADe8fn7tFxWuEXFRDoer/OgoMTRBCumj8yJwPBo8Zhpk14U856/HI8n0ZUtpZ1udrSzfVneA4roNKjdrwpcMRilb8d8G60+lKnrpWcn9bO+B23w2O8Tzfq4aiNSZJqzn5O4Kw16h06fPZ+VUlUHfo97+VAEb7rSh2UgDd4/U+TBlQY7FMj5gBIGvcarVVfQPVPTG94D0j9QAAAABJRU5ErkJggg==']
['Rhel', 'BAAAAAQCAMAAAAoLQ9TAAABj1BMVEUAAAD///////8AAAD///////8AAAD///8AAAD///////8AAAD///8AAAD+/v4AAAAAAAAAAAArKysAAAD///////8AAAAAAAAAAAAAAAD///8AAAAAAAAAAAD///8AAAD///8AAAAAAAAAAAAAAAB5eXn+/v5JSUnKysrS0tJ5eXmqqqqxsrL+/v4ZCgknJyeHh4eIiIjo6OgZCAdOTk7t7e3///8GCwwPAAArKyv19fX29vb9/f0EAAD////+/v4AAAAGBgYHAAAJAAAMAAANAQAPAQAVAQFyCQV9fX2pIRzmEQjn5+cBAAAFAAAAAADnEQjvEgn////uEQjyEgnsEQjzEgnxEgljBwPaEAj9EwnwEglHBQJHBQNNBQIBAAB3CQR5CQSHCgWLCgWRCgWTCwadDAWmDAapDAa/DgfKDwjWEAgGAADh4eHiEQjmEQjmEQkKAADoEQgLAQDtEQgMAQDuEQnvEQjvEQkPAQAfAgEuAwEvAwE8BAL1Egn3Egn4Egn6Egk+BAL+/v5CBQJrB0muAAAAT3RSTlMAAAMEBAkYGhsbMTRLUmpvcHeIjLe6vcHCxM3P0NbW3Ojp6u/w9ff5+fn6+vr6+/v7+/v8/Pz9/f39/f39/f7+/v7+/v7+/v7+/v7+/v7+Q8UoNAAAAO5JREFUeF4tiwVPA0EYRL9SXIsWl+LuxfcOd2Z3764quLu788NZNrxkksmbDP2R7vH6GioLs+iffEzNXd4+TqPErUUpVqMOvwgdzMPn1rv5vPsVeufBTaBK/bH2FPvkEUuIG5jIIc+sHYn/HJ3dC/Hxuo4y8s44dzwBbFkisHN8bVIdXs6jb+H97aCwbHEIqgcml64CD7YllNkAVQC940MLYe5YzvIeQAXNrd19Roc5MdzfdQLUUKaUYyuG9I8y1g4gj6hIak4X5cBIT2MquZJrJdOqpY11ZpAiqVwbY/C7KY1cRCrZxX4pWXVuiuq/hs49kg4OyP4AAAAASUVORK5CYII=']
['Sabayon', 'BAAAAAQCAMAAAAoLQ9TAAABvFBMVEUAAAAcUaYdVKwAAAAAAAUABAwWRY4YSZYhZtIhaNYHDx0KCgoFDBcKCgoRMmYSNm0fXL0fXb8AAAAYS5gaTp8fXLwgXsEGBgYFBQUZSpgZTZ4JFSgODg4IEiIOJkwOKVIkW7EnXbQLGzUTExMKGC8LHjwMIkITExMiIiIPEBEPJ00QEhMXOXAaPncOJEgoXbApXbEcHBwwMDAEAgAfHRgQDgo3NC8AAAAHBwcKCgoLCwsJCQkaGhofHx8lJSUwMDA0NDQ4ODiRkZEICQocHBweHh4GBgYHCg8mJiYnJycpKSkrKystLS0uLi4ICAgODg43NzcRERF1dXUUFBSjo6O1tbUbGxsEBAMLGS8MDA0iIiIjIyMkJCQNDQ0NHTYKCQkoKCgPDw8QEBArMDkKCgkRERIREhMxMTEyMjISIz00Njk1NTU2NjYCAgIVFRU5OTo5P0c8PD0+Pj4/QURAQEBHR0dKSkpMTExSUlJiYmJlZWVnZ2cWFhZ2dnZ4eHh8fHx9fX2FhYUXFxeVlZWXl5eYmJiZmZmcnJwZGRmlpaWrq6usrKyvr68KFiq/v7/FxcXY2Nji4uLn5+ft7e0yif9uAAAAN3RSTlMAAAApKSkqKioqg4OEhISEhoa1tra3t7y9vr7S09PT09TU+Pj5+fn5+/v7+/v7+/v7/v7+/v7+70RY/wAAAPpJREFUeF4dyWNjw2AUBeC7dfYyorM6rx1exKltzLZt2/rDa/J8OgBVVlFDX39jcTZoUqCse251a2dvu6ccUtWlanLQ4Vpel+ThlWq1l3wEz58tx4dOt1dMlAJk9A5gMjG75LHwo46hzkwosGOMbejumoRvubC9EOrMviT0E0Us9fvN9dA6zxJCNv6+ECGsb6oNWsgmpZT9/UTUZo3Em6AW34guTL4jiAudiCM1kLcw8/SmHERfT1/eueBiDqR1GK1n9w+K8nglxYxd6QAML4ztXoQuj8YFgWcgqdJp8qzty26vaboCNIxBCshyQDKov0aXr29v1ufq1PwPx5Q7bCoh6eoAAAAASUVORK5CYII=']
['Slackware', 'BAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AcEDi0qZWWDgAAAAx1JREFUOMt9kktoXHUchb/ffc1M7rySSdJMOknFPMRitLgoNKKI8ZHGKkgrjU8SitidimSh2UkXoQmoO1dGQSxJjdvOtqSaqlR0USEGSjVJGxuSmWR6M3fu4/93YX0g4rc9HA6cc4Q7DI+fpzz7PA8++2mxvZAeBZ4xhHtFcJRmXWsWvb36/OLcyxf5B/KHeYHy7DmGx1+YSDjmWTdlobTGMAStQGkNoLXS4tXDq7u7tUcWz49tA8jR8QUuzB5n5NTCV13F9JEo1JJwTLKuzU61QiOMcd0UDb+BncwQK3Rl15eNja3ui/Njq8aF2eMcO/XlBz0H8oO2ZUkum6A13WB99TtyzXlaCi24SaFa+ZFCzsG2DNnfkdbFjsI1APPhk+d6ujqznycdCxFozadYWvyMpx47wa+bPkGksKwUNnsk3TaCGASRXDZh5LpHXPPg4Rcni+3uYBxrtBbQghlscOVKmYHeEm0ZIZ9xyLffw41ND6VAa43SmjiMByzHYtjzwr9arfshxf5jOKlvKZfn8es77N2uks24PPfSFD/9Uvt7AtPKWmEU9d645eHYJo5tcKi/FX/zG+zmQxQH+rANk862DOW5N/hhaY64cJSa5xNFCgDDILZACMKYWAmh73HmzFsMlBQJ06LeiMinE1S3KzRCm5rXIIoUIoKIYCVM36urZFbEoiBLNMIhAE6/NsSB7h6SKZdL8xsUOnpx9j1KbTdARACIowArYe1ergfNT2i0mIbJys0GI6PT3N1/hJvrPxOFdRJNBQIy/FapI4Bpgohgcjuw+jq8jy8tV55MNBWI4ohS802CpizKv8q+FgALZAfYgSyAZtNro1oLaU1VvxCA029Oraxs7u/tKnXiNjn8HyKwur6lI++6vPK4V7IA7u+1Dyu1tr183ddNbkHuXP8/zEIYeFqiLRl6YO/p0bHJdflT/PD9qZa1W+ry99fcvlAlcZwUpuUAglIRYVgnDEIOlna4q0M/NPnuO1/PzMwg/045O/XeibUt5/Xangx6viSVFpK2jtMpvdyWCz+5ryf10clX3/amp6eZmJjgd441URWWJY8BAAAAAElFTkSuQmCC']
['Trisquel', 'BAAAAAQCAMAAAAoLQ9TAAABjFBMVEX///8AAAAAAAAAAAAAADMAAGYAAAAAHFUAGWYAF10AImYAIGAAHloAHGMAKGsAGmYAJmYAJGEAKnUAJ1gAMXYAJnEAJGQAI2EAK28AK3cAGTEAMHgALXEALXgALG0AFUAAI2oAK3EAMngANoYALXMANIAAM4IANIIAL3gANIcANokANoQANYQAOY0ANIYANooAN4kAN40AOY0APZMANIUAOY0AO5AAPZUAPJAAP5MAPpQAQJUAOYsAPpYANoUAPpoAPpUAM4AAQJkAPZIAPJEAQpgAN4cAPpQAPZUAPJEAO4oAOosAOo8AQJoAOYsAO44AQpsAO48AQp0AP5UAQpoARJwAQ58ARaAAQZgAQ54AQ50AQpgARaIARqMARaMARaIAR6QARaIARaEASakARKEAR6MASqsARKEASKcAR6MARqYAR6UATbEATa8ARqUARKAAR6oARqMASKgATK8AR6QATbIATbAASq0AR6cASKgASqwAR6UASKcATa8ASqoASqwAS6wASKoAS60ATbHn4CTpAAAAhHRSTlMAAQIFBQUGCQoLDxAREhMUFBUYGhobHB0eHh8gIiIjJCQkJCYoLC0xMTE0NDo6Oz1BQUNHSUxOVFVVVldaWl5iY2RkZWZoamtsb3FycnR1ent9f4KDhIiJioyNkJGYm5+foqOkpqamqKmqrKytsLKzs7e4uLy8v8TFxcXGx8rO0NXY2eZc4XYcAAAA00lEQVR4XkWN1VoCUQAG/3NWtwh7CTsQJOyk7BaDxuxA6bbrxf32gt25m7kZqDRYxziooDV7+1AalMUavQh2AsEZoWvzigLun+T17/c8QiJZ7qu2QKiNmyZthdcR1/as353jIeU1GxMHo5XHdqPFeX8IaDMdHPYN6dRN7LR4qQewdTa35HWkyh+fbxERAMjwlAWJv3CPSKDQ+H7XvHdkV4Pua3Gtm4sPKIF/WV8dop4VKBw/NU33B3x1JbTt+XwhkJQoqRfWvHOy28uqH8JIdomR/R+s9yR3Cso77AAAAABJRU5ErkJggg==']
['Ubuntu', 'BAAAAAQCAMAAAAoLQ9TAAABKVBMVEX////ojFzplGf1zbnqnHLvs5P10b3yuZv1xKrytZXvtJXys5LysI32waT0n3HxiVHwg0jxhk31kFn0h0zxf0P0hUrveTv2iU3yfkD1hEfyejv5eDLybSX0aR7zZxvyayH6ZxnxZBj4YhH7XAb5WALlUQLeTwHgUAHeTgHfTwD65NzdTQDdTQHdTgD31MfcTgLcTADcTQD////xt5/31Mf54dfmfE/dUAbeVQ/jcUDcTgHeWBnnflHohFvpjGbqkGztnX342Mz53dLgXiP65d399PHdUgrtoYLyu6Xzvaf76eLfXB/rkm/fWhvupojwrpTeVhTgYSfgYynzwa30xbL1ybnngFT31snngljhZS3539XhZzDiajbibDn77OX88Ovrl3X99vTjbz1fisGCAAAAMHRSTlMABgYGBwcHJiorMDA1NXGHjY2Nl5mZmZyfn6O5u8XHzc3X193j9fj4+vr6/f39/f08OUojAAAAx0lEQVR4Xi3HZVbDYBhGwQctWqzFPXiQ+36pu+LubvtfBKcN82/UEhld2vWXxyL6F92gbTPabse8hU/uHMx1SZoyyJWPTwq1Rs7GpYE9+Cg+OJcs1MHvU9y4fnrN31yUm18vMCIPjtw3QMndw4rs8ieVzAAcBlewpe1KM3uaBuD3Dda1BhWXAsi6AFY1a2SqifxZ+rnxWYcJDRkUS3fO1R5vwe+XZgw4D4L3RAJiknoXCVX3WeiUpJ5pIxTvVmg45pl5k4Ot/AGV2iqZBWgJJAAAAABJRU5ErkJggg==']
['Windows', 'BIAAAAQCAYAAAAbBi9cAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA+pJREFUOE+F0n84FHYcB3CWSsL9ojo/6ik64c6PnTjmSS0limmrpBm2G002y++xzXRz6zE0R4nbw+RnTj/WD4sbanLkkAe55ccYlyNme4SrO9u9d13PI3/saZ+/vs/3831ez+f9eb5aWsuqy2mjRYeNUa7YmtjfTico7jNJ8z0eG24NB9vvnDrvufzpq89Npnr8VjMddNmuRh9rDfp36mFg91oM7qPIc5JdbDJq3An/JfCu7Hl53W2lpS220pP2OuniN299jAYbYizSENIoAgbCTdrTKtxOJVdvGo8psUwKy7Vxe4ez1YEVudGP8YEZzyveInFJ6mZRHHqYazDspw/pJwTIuERM5JIwmUdGdyo9K7/BszGzzg6fXzZHGJ8KvzQqXKOpoIeZLjofWR++BPWyCEnPY4xFGEKWQcLjMjKmr1MwfcMYwmz/Y4KOgNki0V5k1dkjUWCK93Kp2PMFFawos8cm1gZ2GqjLXktL4mbQPHLQ4B9ZDFE5+S356fQlyuJMqzH++HnTo6ui2OO1ko9Ul+4fxfd3d4F7k4YTReqpuFS88bGZUE2QNNDobuIq8Q5CduHb7lFJaTnvnym9ergjMWD/FG8zf+aKS3G9JO5C01Asah6wUXrvALKEDoitMMHhDKrKJdg8RU2s0EB2EWWur8dd7PDPFv6dUC0Gv3kAN36VPRGP/5k5NS6lljWxG0TDiSr1VKhoPwhevRMSqkwRxDObc/DavGtpP6zoi8XOyZfhnyNEvKANBU0P8VPfI/wyNCGXSn7wlEmyA9KrgmOKGth3eDVvPfyywq2dnUEv2R9qG2rLsH7xJXziKnWcI8tlTvEC7Mu8hROlImTU9aKqcwQ1vWOihWFu+sJknmph5CvxQh87c7bNh/NXo03hrMCosyvLmMNgMF7TQL6J1dsZIUVwjKqEO+cajp5vxPN439U/gKBt8PTcYHzL/BgHCyOf4unAISj6mFC2bYC82kB5Ls460NHRUVsDeYSXpGw7UgC7sAtwShDgzdM38W7BbURXtqpqhfmB8sEQuXwoCM/6faGQuGCxyxyKWhIm+PrSD495WL3cT0hhi8Whc3NbAs9KaOyCTvrJ8qkdX19XBeTUDU00+55USFzVU2yHstcaix0mUAjJkJeuRU868Ucmk0lcguiBnMAVxjbbdHV1yeq8+u4Hgo22huSG+iQXp83ftaxW3lsPZcs6KG5T8OwaAfJiPcxlrVRVRhvF02i0F/t5VbHZ7JWDfErKTLnhE3mFPuRFepg/uxqz6TqLv6euGj3ut87t/4ylvre3t3ZehOWWO1zjSFEqMVP4GfGb/DBykJcjmaZOoLsc+hcVY/LaAgcTQAAAAABJRU5ErkJggg==']
['OpenBSD', 'BAAAAAQCAYAAAFo9M/3AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAykIPu64pQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAADTklEQVQ4y32RXUxTdxjGn3N6eujoKT3SUkuk3VoBGfVjgFXAsZ7WkipyYXQbuu3CzUXZmGRbssnFEseFkWzgcGzGXky9MWL8TsC4IeFgtK4oAqOnG5vMVl1pCMVWQD7b/y5M6jLdflfvxfPked/nBQA0NDSChqnGVrLuGkES742NhJAdhAKAuk9yyUs5Gry7RQMZAARCWgivpQiPe71P5DUfH0xaqTL7m/iiLkJmphawa+e4SM2PvUyC4yUIBu8CnAQKAK53rCA5OUtQtStVpJ4Gw/FOBddZVKhCfq4MP4n6+at+DUsJm/e0G9JZzYEvI2tHwlEYjDxomkZ+3nG8WroRtHihZVOhVlorDQzh0okhcByDP4ZGcf+X9XAsvY5/RsBa7Kq5H/CqLctKyl/g08S2i6fq8W/MS3P34T9wNDVYSeDX1eTD9xhiLXbtB/Akwmmv6Kr+ICFkLpGhtNSM3qsSstS3oX8lSsmsxS6ZVn3j6PvVVqhUcvC8AtPxVPxwygVKvngN89WOjgVprggGA4eenjB4nsXsTASpC63I0wVTZYPR11FoKRB8Ax54PCFk6BhMTk5CPR3GSbHouGzknr/bYFq9EAvfc9Tu1sLjHcXNKxLuTOTgzOlOe7IHBc/beAXWpWmXlz8a84nhcLQ+ecVzsAEQrMWuMX+f9HZF2YPZ28FVSNfoPWqOzMUmqYMAJm7+/OOzXQFwHGpyEV+vi+yvtxBC9pDmpgJC4tvI3mo9GTitIxvW24nT7ug67HY/3eDs2bbyrVsrY2day70rV6kRfDAHk5lDLJqAmmeRiD9GJDKHvwb74R8G0mkTPjrQTTG122xkTTbwaV2b1H4u16JQKXGr7yG2b8/H1MQ09IsTSEmRwzf4CCwzD+dmE1re8CI7wwi5XNlFf9vaTXX4dWJg4LLl7h05fpNGwNAMWpp9CIVYNO/tRCzGwpDFQaVMQTS2CKY0BWr3GVGWNSXKACDDaA4Mh976pq9f5Sy09GgKlmeAMIBKzUKpU+BFoxJecRhUfAbMxDi4eADfHVmE79v7q575gvvYeVvjZ58LD5mwsKUyX0hnf0feslnQCWD4zxnc6reKisxsfH2oscqcmTmK/+Ow252cna7K52r+Bky6PqmoT5HBAAAAAElFTkSuQmCC']
['Gnu', 'BAAAAAQCAYAAAFo9M/3AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAywUV5gQrwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAADcElEQVQ4y43Tb0jjBRzH8c9v+7nNMebcUW21Cc78g/wcuhByIScoMRwoTBmFlZCmIJ14axqkgoYIkXIqKIVBEuJNUBEUPRlpqDC3Q2Ex0nTezun2YOaPLXNIv7Vvj7zgiOj1+PPk/eADjuNEuHN6ekqMw+H4IzMz8xChUCjV1NT0JbO7uxtfXFy8NZvNr21tbd0AAEQikY6I0m1tbQbx2NjYZiqV+vn29jY+PDw8xhYWFj45PDzcb25uhlQqfSTief6X0dFRpqKigvF4PPPipaWlY7lcXhCLxXJnZmY+ZTY2NnzX19ePGxsbHw0MDLivrq5mc3Jy2pPJZLVWq/2cdbvdDSzLholoNJ1OMy6Xq0Ymk5HNZktOTU29qMgA8HYqlaKDgwNKp9M0PT09BgAM/iGuqqoimUx2yPP8U5/P9wEAMB0dHRUKheJHiUTyeGhoqAUAnE7nR0qlsjcQCLwjlsvlz+bm5mQWi0VSWlr6bXV1tU6hUMj6+/vfN5lMN0xxcfG1zWZ7SETTSqWSGhoamPHxcajV6s+8Xu9Xou7u7t9VKtW00+mkSCTC6PV6aDQa8Dw/Wl9fP8UAQCgUosvLSyovL2eWl5dRUFBw7Ha7v9vc3By5K3g1EAg8FQSBiIguLi4IgBwA2LtEjuPuJxKJ62AwKFpdXf0eQBIvYVmW/cLlchEAWK1WAADT09NzX6PR/OTz+eKVlZUzKpVqTyqVvsnzfLCkpGSrtrb2t97eXnFeXl5ZKpWyZ2RkPPP7/UUnJyefGI3GU+zt7aU4jotOTk7mAUBfX1+b1Wq9kcvlBIAcDgctLCyQxWKhoqIi6uzs/BoAVlZW3qqpqbllZmdnf1hfX//Q4/HEzWbzX+3t7fcMBgMFg0EYjUYmEolAEAREo1Hk5+fT+fk5Mzg4GD86OpJ0dXXJGQBoaWl5Ra/XP6yrq3tQVlam2N7ehslkAsuySCaTUKvVSCQS2NnZSXAcJxYEQTEyMvKeIAhLDADY7fZ7BoPhm6ysLFpbWzuan5//WKvVvsHzPEWjUSYSiSA3N5d0Oh0TjUaf+/1+S2Nj46/4FwYAr7e2tnbF4/E/iYjC4TCFw+F0LBaj/f19mpiYeID/IAagAyABYLXb7cLZ2Rml02nyer3POY6rwv8hEr34u0IkEk1mZ2cTgGMA7768/RtL5JKsGzrLIgAAAABJRU5ErkJggg==']
['CrunchBang', 'BYAAAAQCAQAAAC45EetAAAA8ElEQVR4XnWOsUpCYQBGz1TIHYu2Qix6g0DEtSeQu/UIISJtUS8gJq61F1wcdMohcBDxKUR8hsz1xA/y44/cs3znbB+RJ0Skl3pSkeFQbUs79VAPzrwPFRmN1Ja0Ug/16I93+1oi4lKte+zMXv32WuoAm43lXMrqzbFncgWw21lORf4+/PREKpAhYqZuPXZ+T/3yXbZEajV1JavUQ104sRcq0myqc5mnHurWqc/7yhExVwuPncl+C4Bu13L60ueAwcByOtLhgAIRCzU38fRGTmSxUBvSSD3Ui1NvQkXWa7Uq1dRD9R17HiqyRUSy1NP6B7e1Yu2GtlUKAAAAAElFTkSuQmCC']
['Yuno', 'BgAAAAPCAYAAAD+pA/bAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAABDtJREFUOE+FlHtMm1UYxrtsi8aEgCb+oTFmZur+WNS5RaPERU10C2qGaBgb6hgwLwMmHTIKlIKlQIHSQrmU24BSSmnpBVooUmihtEC5yKWDjVu5uOkcEca4lG5E93j6EQmELX7Jky/fOed9fu973vMdGu0xT3Cgz57yXMZLDdXcy821PFWLKmuA6HqLMqtLX5POl4iYb2ukWW8IOOFe/qfe3/M4n0eOjwyZD8//bldODOk37N1yDJgl+LVdjEGLFKO9KkzZm8hbje7mIrTXZ7sMtTydrJh15H8hHW11XvN/jGS7VudcD5w34ZZzeQYb67fwYO03LN4exo1+LWzNxbA05O5QuzbHqRYn+++CHDx4YK9WLfaedfQzV5em54g5Zbi8OIml+VFMDLWQ7GXoaSmFWZsDZVGCO2u0EbkhHTrhFqi9PmelSsQ8tAtSVch60dpUeGe4kxgZxegzVkBzlQ2NKBG2+iJIMqMok9r8OLRIMqApToSqmAWTmk9B2+o2YW79oshU7ABcuvAFrVGWXkVKpBYoSaBSxIS2mINpiwbjZiUMZRloVfJQyaXDKObBpimBScpHFe8KmmXpaKhK3arGrBVuVBclHN2CiPNin1OVs1tVJYlQlyZBxA6DviQVo6ZaOKd7sTplw53BVugruBBzfsRslw7rZPxaczWutSpQV/gzJPxo1JexyfaxKBBpuiEx+tw+CpKdEvGWTprGlhcwqbIzL5/DYKMYndpK3L1hxf3ZfkrzwybUZjPhnOqmvlcmutFF1jis9QSShOrcWNSXJ1MA0ou/NZWc8Ddfe4VGO3bk0JON1dyMMlK+gmxNrZCFhZF2Kng7YNO0awt4b7wLNp2EqtAsF6ImP56SG0B6siovTYpIjg15gapCVhAfJRUyIBFEo6k8AyuTtkcC/qvG/XbDexulWJvqgYH0o0nKhVHFJ40XwFQnWM5OCX+XMg86c3KvVMSMapCmPpSTIygTxGKZZOcOXhrr3Mp4uzkFuG6B3ajE3TELDDU8qEmsmvRATxquKkxAnSTFjwKEfv3JU9JC5unG6rQ1bTkbQ4Yq/DVgxOqwBWt2K9Yne3ZCZvrgHO2k5paHzOhSiVCZSkdNTgzy40JRlPgDhDHBCxUZdCs91G8fLeK87zOl6XSOICZYXMGNhDqX9fDP/mbK2DXVi/szm03eLpejl5pzOfqwOt4JBT8OeYwQt/4R/BR0OzXiLCM5LOCji/4nXt46rpywgG+zor5RxgSdupBzJdglSY+5ZZbl3XNY6mbn7W0Lcx06zBg1WBjtcC6OmG+OmRTrFrnIUZESZeVeCpwh8TpiPsQ47/tloM97T+/6m8mg55mT3tStyL54mhlwwtszNvjzD8/6HH8i7PvvPPRioZdRWuDBZUR6pEWG7I8P9Xs1Jsj36MfvvO5J/+rTw58dP7afJPfBgeef3XGz/gskFVpJc4HwGwAAAABJRU5ErkJggg==']
]
Banner =
init: ->
$.asap (-> doc), ->
$.asap (-> $ '.abovePostForm'), Banner.ready
ready: ->
banner = $ ".boardBanner"
title = $.el "div",
id: "boardTitle"
children = banner.children
i = children.length
nodes = []
while i--
child = children[i]
if child.tagName.toLowerCase() is "img"
child.id = "Banner"
continue
if Conf['Custom Board Titles']
cachedTest = child.innerHTML
if not Conf['Persistent Custom Board Titles'] or cachedTest is $.get "#{g.BOARD}.#{child.className}.orig", cachedTest
child.innerHTML = $.get "#{g.BOARD}.#{child.className}", cachedTest
else
$.set "#{g.BOARD}.#{child.className}.orig", cachedTest
$.set "#{g.BOARD}.#{child.className}", cachedTest
$.on child, 'click', (e) ->
if e.shiftKey
@contentEditable = true
$.on child, 'keydown', (e) ->
e.stopPropagation()
$.on child, 'focus', ->
@textContent = @innerHTML
$.on child, 'blur', ->
$.set "#{g.BOARD}.#{@className}", @textContent
@innerHTML = @textContent
@contentEditable = false
nodes.push child
$.add title, nodes.reverse()
$.after banner, title
return
GlobalMessage =
init: ->
$.asap (-> doc), ->
$.asap (-> $.id 'delform'), GlobalMessage.ready
ready: ->
if el = $ "#globalMessage", d.body
for child in el.children
child.cssText = ""
return
Rice =
init: ->
$.ready ->
Rice.nodes d.body
Post::callbacks.push
name: 'Rice Checkboxes'
cb: @node
checkclick: ->
@check.click()
selectclick: ->
e.stopPropagation()
if Rice.ul
return Rice.remSelect()
rect = @getBoundingClientRect()
{clientHeight} = d.documentElement
ul = Rice.ul = $.el 'ul',
id: "selectrice"
{style} = ul
style.width = "#{rect.width}px"
if clientHeight - rect.bottom < 200
style.bottom = "#{clientHeight - rect.top}px"
else
style.top = "#{rect.bottom}px"
style.left = "#{rect.left}px"
input = @previousSibling
for option in input.options
li = $.el 'li',
textContent: option.textContent
li.setAttribute 'data-value', option.value
$.on li, 'click', (e) ->
e.stopPropagation()
container = @parentElement.parentElement
input = container.previousSibling
container.firstChild.textContent = @textContent
input.value = @getAttribute 'data-value'
ev = document.createEvent 'HTMLEvents'
ev.initEvent "change", true, true
$.event input, ev
Rice.remSelect()
$.add ul, li
$.on ul, 'click scroll blur', (e) ->
e.stopPropagation()
$.on d, 'click scroll blur resize', Rice.remSelect
$.add @, ul
remSelect: ->
$.off d, 'click scroll blur resize', Rice.remSelect
$.rm Rice.ul
delete Rice.ul
nodes: (source) ->
checkboxes = $$('[type=checkbox]:not(.riced)', source)
checkrice = Rice.checkbox
for input in checkboxes
checkrice input
selects = $$('select:not(.riced)', source)
selectrice = Rice.select
for input in selects
selectrice input
return
node: ->
Rice.checkbox $ '.postInfo input', @nodes.post
checkbox: (input) ->
return if $.hasClass input, 'riced'
$.addClass input, 'riced'
div = $.el 'div',
className: 'rice'
div.check = input
$.after input, div
if div.parentElement.tagName.toLowerCase() != 'label'
$.on div, 'click', Rice.checkclick
select: (input) ->
$.addClass input, 'riced'
div = $.el 'div',
className: 'selectrice'
innerHTML: "<div>#{input.options[input.selectedIndex].textContent or null}</div>"
$.on div, "click", (e) ->
Rice.selectclick
$.after input, div
###
JSColor
http://github.com/hotchpotch/jscolor/tree/master
JSColor is color library for JavaScript.
JSColor code is porting from AS3 Color library ColorSB < http://sketchbook.libspark.org/trac/wiki/ColorSB >.
###
JSColor =
css: ->
agent = Style.agent
"""<%= grunt.file.read('css/jscolor.css') %>"""
bind: (el) ->
el.color = new JSColor.color(el) if not el.color
fetchElement: (mixed) ->
if typeof mixed is "string" then $.id mixed else mixed
fireEvent: (el, evnt) ->
return unless el
ev = document.createEvent 'HTMLEvents'
ev.initEvent evnt, true, true
el.dispatchEvent ev
getRelMousePos: (e = window.event) ->
x = 0
y = 0
if typeof e.offsetX is 'number'
x = e.offsetX
y = e.offsetY
else if typeof e.layerX is 'number'
x = e.layerX
y = e.layerY
x: x
y: y
color: (target) ->
# Read Only
@hsv = [0, 0, 1] # 0-6, 0-1, 0-1
@rgb = [1, 1, 1] # 0-1, 0-1, 0-1
# Writable.
# Value holder / Where to reflect current color
@valueElement = @styleElement = target
# Blur / Drag trackers
abortBlur = holdPad = holdSld = false
@hidePicker = ->
if isPickerOwner() then removePicker()
@showPicker = ->
unless isPickerOwner() then drawPicker()
@importColor = ->
unless valueElement
@exportColor()
else
unless @fromString valueElement.value, leaveValue
styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor
@exportColor leaveValue | leaveStyle
@exportColor = (flags) ->
if !(flags & leaveValue) and valueElement
value = '#' + @toString()
valueElement.value = value
valueElement.previousSibling.value = value
editTheme[valueElement.previousSibling.name] = value
setTimeout -> Style.themeCSS.textContent = Style.theme editTheme
if not (flags & leaveStyle) and styleElement
styleElement.style.backgroundColor = '#' + @toString()
if not (flags & leavePad) and isPickerOwner()
redrawPad()
if not (flags & leaveSld) and isPickerOwner()
redrawSld()
@fromHSV = (h, s, v, flags) -> # null = don't change
@hsv = [
h =
if h
$.minmax h, 0.0, 6.0
else
@hsv[0]
s =
if s
$.minmax s, 0.0, 1.0
else
@hsv[1]
v =
if v
$.minmax v, 0.0, 1.0
else
@hsv[2]
]
@rgb = HSV_RGB(h, s, v)
@exportColor flags
@fromRGB = (r, g, b, flags) -> # null = don't change
r =
if r?
$.minmax r, 0.0, 1.0
else
@rgb[0]
g =
if g?
$.minmax g, 0.0, 1.0
else
@rgb[1]
b =
if b?
$.minmax b, 0.0, 1.0
else
@rgb[2]
hsv = RGB_HSV(r, g, b)
if hsv[0]?
@hsv[0] = $.minmax hsv[0], 0.0, 6.0
if hsv[2] isnt 0
@hsv[1] =
unless hsv[1]?
null
else
$.minmax hsv[1], 0.0, 1.0
@hsv[2] =
unless hsv[2]?
null
else
$.minmax hsv[2], 0.0, 1.0
# update RGB according to final HSV, as some values might be trimmed
@rgb = HSV_RGB @hsv[0], @hsv[1], @hsv[2]
@exportColor flags
@fromString = (number, flags) ->
m = number.match /^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i
unless m
return false
else
if m[1].length is 6 # 6-char notation
@fromRGB(
parseInt(m[1].substr(0, 2), 16) / 255
parseInt(m[1].substr(2, 2), 16) / 255
parseInt(m[1].substr(4, 2), 16) / 255
flags
)
else # 3-char notation
@fromRGB(
# Double-up each character to fake 6-char notation.
parseInt((val = m[1].charAt 0) + val, 16) / 255
parseInt((val = m[1].charAt 1) + val, 16) / 255
parseInt((val = m[1].charAt 2) + val, 16) / 255
flags
)
true
@toString = ->
(0x100 | Math.round(255 * @rgb[0])).toString(16).substr(1) +
(0x100 | Math.round(255 * @rgb[1])).toString(16).substr(1) +
(0x100 | Math.round(255 * @rgb[2])).toString(16).substr(1)
RGB_HSV = (r, g, b) ->
n = if (n = if r < g then r else g) < b then n else b
v = if (v = if r > g then r else g) > b then v else b
m = v - n
return [ null, 0, v ] if m is 0
h =
if r is n
3 + (b - g) / m
else
if g is n
5 + (r - b) / m
else
1 + (g - r) / m
[
if h is 6 then 0 else h
m / v
v
]
HSV_RGB = (h, s, v) ->
return [ v, v, v ] unless h?
i = Math.floor(h)
f =
if i % 2
h - i
else
1 - (h - i)
m = v * (1 - s)
n = v * (1 - s * f)
switch i
when 6, 0
[v,n,m]
when 1
[n,v,m]
when 2
[m,v,n]
when 3
[m,n,v]
when 4
[n,m,v]
when 5
[v,m,n]
removePicker = ->
delete JSColor.picker.owner
$.rm JSColor.picker.boxB
drawPicker = (x, y) ->
unless p = JSColor.picker
elements = ['box', 'boxB', 'pad', 'padB', 'padM', 'sld', 'sldB', 'sldM', 'btn']
p = {}
for item in elements
p[item] = $.el 'div', {className: "jsc#{item.capitalize()}"}
p.btnS = $.el 'span', {className: 'jscBtnS'}
p.btnT = $.tn 'Close'
JSColor.picker = p
$.add p.box, [p.sldB, p.sldM, p.padB, p.padM, p.btn]
$.add p.sldB, p.sld
$.add p.padB, p.pad
$.add p.btnS, p.btnT
$.add p.btn, p.btnS
$.add p.boxB, p.box
# controls interaction
{box, boxB, btn, btnS, pad, padB, padM, sld, sldB, sldM} = p
box.onmouseup =
box.onmouseout = -> target.focus()
box.onmousedown = -> abortBlur=true
box.onmousemove = (e) ->
if holdPad or holdSld
holdPad and setPad e
holdSld and setSld e
if d.selection
d.selection.empty()
else if window.getSelection
window.getSelection().removeAllRanges()
padM.onmouseup =
padM.onmouseout = -> if holdPad
holdPad = false
JSColor.fireEvent valueElement, 'change'
padM.onmousedown = (e) ->
# If the slider is at the bottom, move it up
if THIS.hsv[2] is 0
THIS.fromHSV null, null, 1.0
holdPad = true
setPad e
sldM.onmouseup =
sldM.onmouseout = -> if holdSld
holdSld = false
JSColor.fireEvent valueElement, 'change'
sldM.onmousedown = (e) ->
holdSld = true
setSld e
btn.onmousedown = ->
THIS.hidePicker()
# place pointers
redrawPad()
redrawSld()
JSColor.picker.owner = THIS
$.add ThemeTools.dialog, p.boxB
# redraw the pad pointer
redrawPad = ->
# The X and Y positions of the picker crosshair, based on the hsv Hue and Saturation values as percentages and the picker's dimensions.
JSColor.picker.padM.style.backgroundPosition =
"#{4 + Math.round (THIS.hsv[0] / 6) * 180}px #{4 + Math.round (1 - THIS.hsv[1]) * 100}px"
rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1)
JSColor.picker.sld.style.backgroundColor = "rgb(#{rgb[0] * 100}%, #{rgb[1] * 100}%, #{rgb[2] * 100}%)"
return
redrawSld = ->
# redraw the slider pointer. X will always be 0, Y will always be a percentage of the HSV 'Value' value.
JSColor.picker.sldM.style.backgroundPosition =
"0 #{6 + Math.round (1 - THIS.hsv[2]) * 100}px"
isPickerOwner = ->
return JSColor.picker and JSColor.picker.owner is THIS
blurTarget = ->
if valueElement is target
THIS.importColor()
blurValue = ->
if valueElement isnt target
THIS.importColor()
setPad = (e) ->
mpos = JSColor.getRelMousePos e
x = mpos.x - 11
y = mpos.y - 11
THIS.fromHSV(
x * (1 / 30)
1 - y / 100
null
leaveSld
)
setSld = (e) ->
mpos = JSColor.getRelMousePos e
y = mpos.y - 9
THIS.fromHSV(
null
null
1 - y / 100
leavePad
)
THIS = @
valueElement = JSColor.fetchElement @valueElement
styleElement = JSColor.fetchElement @styleElement
leaveValue = 1 << 0
leaveStyle = 1 << 1
leavePad = 1 << 2
leaveSld = 1 << 3
# target
$.on target, 'focus', ->
THIS.showPicker()
$.on target, 'blur', ->
unless abortBlur
window.setTimeout(->
abortBlur or blurTarget()
abortBlur = false
)
else
abortBlur = false
# valueElement
if valueElement
$.on valueElement, 'keyup input', ->
THIS.fromString valueElement.value, leaveValue
$.on valueElement, 'blur', blurValue
valueElement.setAttribute 'autocomplete', 'off'
# styleElement
if styleElement
styleElement.jscStyle =
backgroundColor: styleElement.style.backgroundColor
@importColor()
MascotTools =
init: (mascot = Conf[g.MASCOTSTRING][Math.floor(Math.random() * Conf[g.MASCOTSTRING].length)]) ->
Conf['mascot'] = mascot
@el = el = $('#mascot img', d.body)
if !Conf['Mascots'] or (g.CATALOG and Conf['Hide Mascots on Catalog'])
return if el then el.src = "" else null
position = "#{if Conf['Mascot Position'] is 'bottom' or !(Conf['Mascot Position'] is "default" and Conf['Post Form Style'] is "fixed")
0 + (if (!g.REPLY or Conf['Boards Navigation'] is 'sticky bottom') and Conf['4chan SS Navigation'] then 1.6 else 0)
else
20.3 + (if !g.REPLY or !!$ '#postForm input[name=spoiler]' then 1.4 else 0) + (if Conf['Show Post Form Header'] then 1.5 else 0) + (if Conf['Post Form Decorations'] then 0.2 else 0)
}em"
# If we're editting anything, let's not change mascots any time we change a value.
if Conf['editMode']
unless mascot = editMascot or mascot = Mascots[Conf["mascot"]]
return
else
unless Conf["mascot"]
return if el then el.src = "" else null
unless mascot = Mascots[Conf["mascot"]]
Conf[g.MASCOTSTRING].remove Conf["mascot"]
return @init()
@addMascot mascot
if Conf["Sidebar Location"] is 'left'
if Conf["Mascot Location"] is "sidebar"
location = 'left'
else
location = 'right'
else if Conf["Mascot Location"] is "sidebar"
location = 'right'
else
location = 'left'
filters = []
if Conf["Grayscale Mascots"]
filters.push '<feColorMatrix id="color" type="saturate" values="0" />'
Style.mascot.textContent = """
#mascot img {
position: fixed;
z-index: #{
if Conf['Mascots Overlap Posts']
'3'
else
'-1'
};
#{if Style.sidebarLocation[0] is "left" then "#{Style.agent}transform: scaleX(-1);" else ""}
bottom: #{
if mascot.position is 'top'
'auto'
else if (mascot.position is 'bottom' and Conf['Mascot Position'] is 'default') or !$.id 'postForm'
'0'
else
position
};
#{location}: #{
(mascot.hOffset or 0) + (
if Conf['Sidebar'] is 'large' and mascot.center
25
else
0
)
}px;
top: #{
if mascot.position is 'top'
'0'
else
'auto'
};
height: #{
if mascot.height and isNaN parseFloat mascot.height
mascot.height
else if mascot.height
parseInt(mascot.height, 10) + 'px'
else
'auto'
};
width: #{
if mascot.width and isNaN parseFloat mascot.width
mascot.width
else if mascot.width
parseInt(mascot.width, 10) + 'px'
else
'auto'
};
margin-#{location}: #{mascot.hOffset or 0}px;
margin-bottom: #{mascot.vOffset or 0}px;
opacity: #{Conf['Mascot Opacity']};
pointer-events: none;
#{if filters.length > 0 then "filter: url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\"><filter id=\"filters\">" + filters.join("") + "</filter></svg>#filters');" else ""}
}
"""
categories: [
'Anime'
'Ponies'
'Questionable'
'Silhouette'
'Western'
]
dialog: (key) ->
Conf['editMode'] = 'mascot'
if Mascots[key]
editMascot = JSON.parse(JSON.stringify(Mascots[key]))
else
editMascot = {}
editMascot.name = key or ''
MascotTools.addMascot editMascot
Style.addStyle()
layout =
name: [
"Mascot Name"
""
"text"
]
image: [
"Image"
""
"text"
]
category: [
"Category"
MascotTools.categories[0]
"select"
MascotTools.categories
]
position: [
"Position"
"default"
"select"
["default", "top", "bottom"]
]
height: [
"Height"
"auto"
"text"
]
width: [
"Width"
"auto"
"text"
]
vOffset: [
"Vertical Offset"
"0"
"number"
]
hOffset: [
"Horizontal Offset"
"0"
"number"
]
center: [
"Center Mascot"
false
"checkbox"
]
dialog = $.el "div",
id: "mascotConf"
className: "reply dialog"
innerHTML: "
<div id=mascotbar>
</div>
<hr>
<div id=mascotcontent>
</div>
<div id=save>
<a href='javascript:;'>Save Mascot</a>
</div>
<div id=close>
<a href='javascript:;'>Close</a>
</div>
"
for name, item of layout
switch item[2]
when "text"
div = @input item, name
input = $ 'input', div
if name is 'image'
$.on input, 'blur', ->
editMascot[@name] = @value
MascotTools.addMascot editMascot
Style.addStyle()
fileInput = $.el 'input',
type: "file"
accept: "image/*"
title: "imagefile"
hidden: "hidden"
$.on input, 'click', (evt) ->
if evt.shiftKey
@.nextSibling.click()
$.on fileInput, 'change', (evt) ->
MascotTools.uploadImage evt, @
$.after input, fileInput
if name is 'name'
$.on input, 'blur', ->
@value = @value.replace /[^a-z-_0-9]/ig, "_"
unless /^[a-z]/i.test @value
return alert "Mascot names must start with a letter."
editMascot[@name] = @value
MascotTools.addMascot editMascot
Style.addStyle()
else
$.on input, 'blur', ->
editMascot[@name] = @value
MascotTools.addMascot editMascot
Style.addStyle()
when "number"
div = @input item, name
$.on $('input', div), 'blur', ->
editMascot[@name] = parseInt @value
MascotTools.addMascot editMascot
Style.addStyle()
when "select"
value = editMascot[name] or item[1]
optionHTML = "<div class=optionlabel>#{item[0]}</div><div class=option><select name='#{name}' value='#{value}'><br>"
for option in item[3]
optionHTML = optionHTML + "<option value=\"#{option}\">#{option}</option>"
optionHTML = optionHTML + "</select>"
div = $.el 'div',
className: "mascotvar"
innerHTML: optionHTML
setting = $ "select", div
setting.value = value
$.on $('select', div), 'change', ->
editMascot[@name] = @value
MascotTools.addMascot editMascot
Style.addStyle()
when "checkbox"
value = editMascot[name] or item[1]
div = $.el "div",
className: "mascotvar"
innerHTML: "<label><input type=#{item[2]} class=field name='#{name}' #{if value then 'checked'}>#{item[0]}</label>"
$.on $('input', div), 'click', ->
editMascot[@name] = if @checked then true else false
MascotTools.addMascot editMascot
Style.addStyle()
$.add $("#mascotcontent", dialog), div
$.on $('#save > a', dialog), 'click', ->
MascotTools.save editMascot
$.on $('#close > a', dialog), 'click', MascotTools.close
Style.rice(dialog)
$.add d.body, dialog
input: (item, name) ->
if Array.isArray(editMascot[name])
if Style.lightTheme
value = editMascot[name][1]
else
value = editMascot[name][0]
else
value = editMascot[name] or item[1]
editMascot[name] = value
div = $.el "div",
className: "mascotvar"
innerHTML: "<div class=optionlabel>#{item[0]}</div><div class=option><input type=#{item[2]} class=field name='#{name}' placeholder='#{item[0]}' value='#{value}'></div>"
return div
uploadImage: (evt, el) ->
file = evt.target.files[0]
reader = new FileReader()
reader.onload = (evt) ->
val = evt.target.result
el.previousSibling.value = val
editMascot.image = val
Style.addStyle()
reader.readAsDataURL file
addMascot: (mascot) ->
if el = @el
el.src = if Array.isArray(mascot.image) then (if Style.lightTheme then mascot.image[1] else mascot.image[0]) else mascot.image
else
@el = el = $.el 'div',
id: "mascot"
innerHTML: "<img src='#{if Array.isArray(mascot.image) then (if Style.lightTheme then mascot.image[1] else mascot.image[0]) else mascot.image}'>"
$.add d.body, el
save: (mascot) ->
{name, image} = mascot
if !name? or name is ""
alert "Please name your mascot."
return
if !image? or image is ""
alert "Your mascot must contain an image."
return
unless mascot.category
mascot.category = MascotTools.categories[0]
if Mascots[name]
if Conf["Deleted Mascots"].contains name
Conf["Deleted Mascots"].remove name
$.set "Deleted Mascots", Conf["Deleted Mascots"]
else
if confirm "A mascot named \"#{name}\" already exists. Would you like to over-write?"
delete Mascots[name]
else
return alert "Creation of \"#{name}\" aborted."
for type in ["Enabled Mascots", "Enabled Mascots sfw", "Enabled Mascots nsfw"]
unless Conf[type].contains name
Conf[type].push name
$.set type, Conf[type]
Mascots[name] = JSON.parse(JSON.stringify(mascot))
Conf["mascot"] = name
delete Mascots[name].name
userMascots = $.get "userMascots", {}
userMascots[name] = Mascots[name]
$.set 'userMascots', userMascots
alert "Mascot \"#{name}\" saved."
close: ->
Conf['editMode'] = false
editMascot = {}
$.rm $("#mascotConf", d.body)
Style.addStyle()
Options.dialog("mascot")
importMascot: (evt) ->
file = evt.target.files[0]
reader = new FileReader()
reader.onload = (e) ->
try
imported = JSON.parse e.target.result
catch err
alert err
return
unless (imported["Mascot"])
alert "Mascot file is invalid."
name = imported["Mascot"]
delete imported["Mascot"]
if Mascots[name] and not Conf["Deleted Mascots"].remove name
unless confirm "A mascot with this name already exists. Would you like to over-write?"
return
Mascots[name] = imported
userMascots = $.get "userMascots", {}
userMascots[name] = Mascots[name]
$.set 'userMascots', userMascots
alert "Mascot \"#{name}\" imported!"
$.rm $("#mascotContainer", d.body)
Options.mascotTab.dialog()
reader.readAsText(file)
###
Style.color adapted from 4chan Style Script
###
ThemeTools =
init: (key) ->
Conf['editMode'] = "theme"
if Themes[key]
editTheme = JSON.parse(JSON.stringify(Themes[key]))
if ($.get "userThemes", {})[key]
editTheme["Theme"] = key
else
editTheme["Theme"] = key += " [custom]"
else
editTheme = JSON.parse(JSON.stringify(Themes['Yotsuba B']))
editTheme["Theme"] = "Untitled"
editTheme["Author"] = "Author"
editTheme["Author Tripcode"] = "Unknown"
# Objects are not guaranteed to have any type of arrangement, so we use a presorted
# array to generate the layout of of the theme editor.
# (Themes aren't even guaranteed to have any of these values, actually)
layout = [
"Background Image"
"Background Attachment"
"Background Position"
"Background Repeat"
"Background Color"
"Thread Wrapper Background"
"Thread Wrapper Border"
"Dialog Background"
"Dialog Border"
"Reply Background"
"Reply Border"
"Highlighted Reply Background"
"Highlighted Reply Border"
"Backlinked Reply Outline"
"Input Background"
"Input Border"
"Hovered Input Background"
"Hovered Input Border"
"Focused Input Background"
"Focused Input Border"
"Checkbox Background"
"Checkbox Border"
"Checkbox Checked Background"
"Buttons Background"
"Buttons Border"
"Navigation Background"
"Navigation Border"
"Links"
"Hovered Links"
"Quotelinks"
"Backlinks"
"Navigation Links"
"Hovered Navigation Links"
"Names"
"Tripcodes"
"Emails"
"Subjects"
"Text"
"Inputs"
"Post Numbers"
"Greentext"
"Sage"
"Board Title"
"Timestamps"
"Warnings"
"Shadow Color"
]
ThemeTools.dialog = $.el "div",
id: "themeConf"
className: "reply dialog"
innerHTML: "
<div id=themebar>
</div>
<hr>
<div id=themecontent>
</div>
<div id=save>
<a href='javascript:;'>Save</a>
</div>
<div id=upload>
<a href='javascript:;'>Select Image</a>
</div>
<div id=close>
<a href='javascript:;'>Close</a>
</div>
"
header = $.el "div",
innerHTML: "
<input class='field subject' name='Theme' placeholder='Theme' value='#{key}'> by
<input class='field name' name='Author' placeholder='Author' value='#{editTheme['Author']}'>
<input class='field postertrip' name='Author Tripcode' placeholder='Author Tripcode' value='#{editTheme['Author Tripcode']}'>"
#Setup inputs that are not generated from the layout variable.
for input in $$("input", header)
$.on input, 'blur', ->
editTheme[@name] = @value
$.add $("#themebar", ThemeTools.dialog), header
themecontent = $("#themecontent", ThemeTools.dialog)
for item in layout
unless editTheme[item]
editTheme[item] = ''
div = $.el "div",
className: "themevar"
innerHTML: "<div class=optionname><b>#{item}</b></div><div class=option><input name='#{item}' placeholder='#{if item == "Background Image" then "Shift+Click to upload image" else item}'>"
input = $('input', div)
input.value = editTheme[item]
switch item
when "Background Image"
input.className = 'field'
fileInput = $.el 'input',
type: 'file'
accept: "image/*"
title: "BG Image"
hidden: "hidden"
$.on input, 'click', (evt) ->
if evt.shiftKey
@nextSibling.click()
$.on fileInput, 'change', (evt) ->
ThemeTools.uploadImage evt, @
$.after input, fileInput
when "Background Attachment" ,"Background Position", "Background Repeat"
input.className = 'field'
else
input.className = "colorfield"
colorInput = $.el 'input',
className: 'color'
value: "##{Style.colorToHex input.value}"
JSColor.bind colorInput
$.after input, colorInput
$.on input, 'blur', ->
depth = 0
unless @value.length > 1000
for i in [0..@value.length - 1]
switch @value[i]
when '(' then depth++
when ')' then depth--
when '"' then toggle1 = not toggle1
when "'" then toggle2 = not toggle2
if depth != 0 or toggle1 or toggle2
return alert "Syntax error on #{@name}."
if @className == "colorfield"
@nextSibling.value = "##{Style.colorToHex @value}"
@nextSibling.color.importColor()
editTheme[@name] = @value
Style.addStyle(editTheme)
$.add themecontent, div
$.add themecontent, div
unless editTheme["Custom CSS"]
editTheme["Custom CSS"] = ""
div = $.el "div",
className: "themevar"
innerHTML: "<div class=optionname><b>Custom CSS</b></div><div class=option><textarea name='Custom CSS' placeholder='Custom CSS'>#{editTheme['Custom CSS']}</textarea>"
$.on $('textarea', div), 'blur', ->
editTheme["Custom CSS"] = @value
Style.themeCSS.textContent = Style.theme editTheme
$.add themecontent, div
$.on $('#save > a', ThemeTools.dialog), 'click', ->
ThemeTools.save editTheme
$.on $('#close > a', ThemeTools.dialog), 'click', ThemeTools.close
$.add d.body, ThemeTools.dialog
Style.themeCSS.textContent = Style.theme editTheme
uploadImage: (evt, el) ->
file = evt.target.files[0]
reader = new FileReader()
reader.onload = (evt) ->
val = 'url("' + evt.target.result + '")'
el.previousSibling.value = val
editTheme["Background Image"] = val
Style.themeCSS.textContent = Style.theme editTheme
reader.readAsDataURL file
importtheme: (origin, evt) ->
file = evt.target.files[0]
reader = new FileReader()
reader.onload = (e) ->
try
imported = JSON.parse e.target.result
catch err
alert err
return
unless (origin != 'appchan' and imported.mainColor) or (origin == 'appchan' and imported["Author Tripcode"])
alert "Theme file is invalid."
return
name = imported.name or imported["Theme"]
delete imported.name
if Themes[name] and not Themes[name]["Deleted"]
if confirm "A theme with this name already exists. Would you like to over-write?"
delete Themes[name]
else
return
if origin == "oneechan" or origin == "SS"
bgColor = new Style.color(imported.bgColor);
mainColor = new Style.color(imported.mainColor);
brderColor = new Style.color(imported.brderColor);
inputColor = new Style.color(imported.inputColor);
inputbColor = new Style.color(imported.inputbColor);
blinkColor = new Style.color(imported.blinkColor);
jlinkColor = new Style.color(imported.jlinkColor);
linkColor = new Style.color(imported.linkColor);
linkHColor = new Style.color(imported.linkHColor);
nameColor = new Style.color(imported.nameColor);
quoteColor = new Style.color(imported.quoteColor);
sageColor = new Style.color(imported.sageColor);
textColor = new Style.color(imported.textColor);
titleColor = new Style.color(imported.titleColor);
tripColor = new Style.color(imported.tripColor);
timeColor = new Style.color(imported.timeColor || imported.textColor);
if imported.bgRPA
bgRPA = imported.bgRPA.split(' ')
else
bgRPA = ['no-repeat', 'bottom', 'left', 'fixed']
if origin == "oneechan"
Themes[name] = {
'Author' : "Anonymous"
'Author Tripcode' : "!POMF.9waa"
'Background Image' : 'url("' + (imported.bgImg or '') + '")'
'Background Attachment' : bgRPA[3] or ''
'Background Position' : ((bgRPA[1] + " ") or '') + (bgRPA[2] or '')
'Background Repeat' : bgRPA[0] or ''
'Background Color' : 'rgb(' + bgColor.rgb + ')'
'Dialog Background' : 'rgba(' + mainColor.rgb + ',.98)'
'Dialog Border' : 'rgb(' + brderColor.rgb + ')'
'Thread Wrapper Background' : 'rgba(0,0,0,0)'
'Thread Wrapper Border' : 'rgba(0,0,0,0)'
'Reply Background' : 'rgba(' + mainColor.rgb + ',' + imported.replyOp + ')'
'Reply Border' : 'rgb(' + brderColor.rgb + ')'
'Highlighted Reply Background': 'rgba(' + mainColor.shiftRGB(4, true) + ',' + imported.replyOp + ')'
'Highlighted Reply Border' : 'rgb(' + linkColor.rgb + ')'
'Backlinked Reply Outline' : 'rgb(' + linkColor.rgb + ')'
'Checkbox Background' : 'rgba(' + inputColor.rgb + ',' + imported.replyOp + ')'
'Checkbox Border' : 'rgb(' + inputbColor.rgb + ')'
'Checkbox Checked Background' : 'rgb(' + inputColor.rgb + ')'
'Input Background' : 'rgba(' + inputColor.rgb + ',' + imported.replyOp + ')'
'Input Border' : 'rgb(' + inputbColor.rgb + ')'
'Hovered Input Background' : 'rgba(' + inputColor.hover + ',' + imported.replyOp + ')'
'Hovered Input Border' : 'rgb(' + inputbColor.rgb + ')'
'Focused Input Background' : 'rgba(' + inputColor.hover + ',' + imported.replyOp + ')'
'Focused Input Border' : 'rgb(' + inputbColor.rgb + ')'
'Buttons Background' : 'rgba(' + inputColor.rgb + ',' + imported.replyOp + ')'
'Buttons Border' : 'rgb(' + inputbColor.rgb + ')'
'Navigation Background' : 'rgba(' + bgColor.rgb + ',0.8)'
'Navigation Border' : 'rgb(' + mainColor.rgb + ')'
'Quotelinks' : 'rgb(' + linkColor.rgb + ')'
'Links' : 'rgb(' + linkColor.rgb + ')'
'Hovered Links' : 'rgb(' + linkHColor.rgb + ')'
'Navigation Links' : 'rgb(' + textColor.rgb + ')'
'Hovered Navigation Links' : 'rgb(' + linkHColor.rgb + ')'
'Subjects' : 'rgb(' + titleColor.rgb + ')'
'Names' : 'rgb(' + nameColor.rgb + ')'
'Sage' : 'rgb(' + sageColor.rgb + ')'
'Tripcodes' : 'rgb(' + tripColor.rgb + ')'
'Emails' : 'rgb(' + linkColor.rgb + ')'
'Post Numbers' : 'rgb(' + linkColor.rgb + ')'
'Text' : 'rgb(' + textColor.rgb + ')'
'Backlinks' : 'rgb(' + linkColor.rgb + ')'
'Greentext' : 'rgb(' + quoteColor.rgb + ')'
'Board Title' : 'rgb(' + textColor.rgb + ')'
'Timestamps' : 'rgb(' + timeColor.rgb + ')'
'Inputs' : 'rgb(' + textColor.rgb + ')'
'Warnings' : 'rgb(' + sageColor.rgb + ')'
'Shadow Color' : 'rgba(0,0,0,0.1)'
'Custom CSS' : """<%= grunt.file.read('css/theme.oneechan.css') %>""" + (imported.customCSS or '') }
else if origin == "SS"
Themes[name] = {
'Author' : "Anonymous"
'Author Tripcode' : "!.pC/AHOKAg"
'Background Image' : 'url("' + (imported.bgImg or '') + '")'
'Background Attachment' : bgRPA[3] or ''
'Background Position' : ((bgRPA[1] + " ") or '') + (bgRPA[2] or '')
'Background Repeat' : bgRPA[0] or ''
'Background Color' : 'rgb(' + bgColor.rgb + ')'
'Dialog Background' : 'rgba(' + mainColor.rgb + ',.98)'
'Dialog Border' : 'rgb(' + brderColor.rgb + ')'
'Thread Wrapper Background' : 'rgba(' + mainColor.rgb + ',.5)'
'Thread Wrapper Border' : 'rgba(' + brderColor.rgb + ',.9)'
'Reply Background' : 'rgba(' + mainColor.rgb + ',.9)'
'Reply Border' : 'rgb(' + brderColor.rgb + ')'
'Highlighted Reply Background': 'rgba(' + mainColor.shiftRGB(4, true) + ',.9)'
'Highlighted Reply Border' : 'rgb(' + linkColor.rgb + ')'
'Backlinked Reply Outline' : 'rgb(' + linkColor.rgb + ')'
'Checkbox Background' : 'rgba(' + inputColor.rgb + ',.9)'
'Checkbox Border' : 'rgb(' + inputbColor.rgb + ')'
'Checkbox Checked Background' : 'rgb(' + inputColor.rgb + ')'
'Input Background' : 'rgba(' + inputColor.rgb + ',.9)'
'Input Border' : 'rgb(' + inputbColor.rgb + ')'
'Hovered Input Background' : 'rgba(' + inputColor.hover + ',.9)'
'Hovered Input Border' : 'rgb(' + inputbColor.rgb + ')'
'Focused Input Background' : 'rgba(' + inputColor.hover + ',.9)'
'Focused Input Border' : 'rgb(' + inputbColor.rgb + ')'
'Buttons Background' : 'rgba(' + inputColor.rgb + ',.9)'
'Buttons Border' : 'rgb(' + inputbColor.rgb + ')'
'Navigation Background' : 'rgba(' + bgColor.rgb + ',0.8)'
'Navigation Border' : 'rgb(' + mainColor.rgb + ')'
'Quotelinks' : 'rgb(' + linkColor.rgb + ')'
'Links' : 'rgb(' + linkColor.rgb + ')'
'Hovered Links' : 'rgb(' + linkHColor.rgb + ')'
'Navigation Links' : 'rgb(' + textColor.rgb + ')'
'Hovered Navigation Links' : 'rgb(' + linkHColor.rgb + ')'
'Subjects' : 'rgb(' + titleColor.rgb + ')'
'Names' : 'rgb(' + nameColor.rgb + ')'
'Sage' : 'rgb(' + sageColor.rgb + ')'
'Tripcodes' : 'rgb(' + tripColor.rgb + ')'
'Emails' : 'rgb(' + linkColor.rgb + ')'
'Post Numbers' : 'rgb(' + linkColor.rgb + ')'
'Text' : 'rgb(' + textColor.rgb + ')'
'Backlinks' : 'rgb(' + linkColor.rgb + ')'
'Greentext' : 'rgb(' + quoteColor.rgb + ')'
'Board Title' : 'rgb(' + textColor.rgb + ')'
'Timestamps' : 'rgb(' + timeColor.rgb + ')'
'Inputs' : 'rgb(' + textColor.rgb + ')'
'Warnings' : 'rgb(' + sageColor.rgb + ')'
'Shadow Color' : 'rgba(0,0,0,0.1)'
'Custom CSS' : """<%= grunt.file.read('css/theme.4chanss.css') %>""" + (imported.customCSS or '') }
else if origin == 'appchan'
Themes[name] = imported
userThemes = $.get "userThemes", {}
userThemes[name] = Themes[name]
$.set 'userThemes', userThemes
alert "Theme \"#{name}\" imported!"
$.rm $("#themes", d.body)
Options.themeTab()
reader.readAsText(file)
save: (theme) ->
name = theme["Theme"]
if Themes[name] and not Themes[name]["Deleted"]
if confirm "A theme with this name already exists. Would you like to over-write?"
delete Themes[name]
else
return
Themes[name] = JSON.parse(JSON.stringify(theme))
delete Themes[name]["Theme"]
userThemes = $.get "userThemes", {}
userThemes[name] = Themes[name]
$.set 'userThemes', userThemes
$.set "theme", Conf['theme'] = name
alert "Theme \"#{name}\" saved."
close: ->
Conf['editMode'] = false
$.rm $("#themeConf", d.body)
Style.addStyle()
Options.dialog("theme")