diff --git a/LICENSE b/LICENSE index 90d0d3801..2920971b2 100755 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.27 - 2013-08-12 +* 4chan X - Version 1.2.27 - 2013-08-13 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 77d678b38..9173a2b0a 100755 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -19,7 +19,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== /* -* 4chan X - Version 1.2.27 - 2013-08-12 +* 4chan X - Version 1.2.27 - 2013-08-13 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -134,7 +134,6 @@ 'Index Navigation': [false, 'Add buttons to navigate between threads.'], 'Reply Navigation': [false, 'Add buttons to navigate to top / bottom of thread.'], 'Show Dice Roll': [true, 'Show dice that were entered into the email field.'], - 'Check for Updates': [true, 'Check for updated versions of 4chan X.'], 'Show Updated Notifications': [true, 'Show notifications when 4chan X is successfully updated.'], 'Emoji': [false, 'Adds icons next to names for different emails'], 'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'], @@ -188,9 +187,7 @@ 'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'], '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.'], - 'Toggleable Thread Watcher': [true, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'], - 'Auto Watch': [true, 'Automatically watch threads you start.'], - 'Auto Watch Reply': [false, 'Automatically watch threads you reply to.'] + 'Toggleable Thread Watcher': [true, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'] }, 'Posting': { 'Quick Reply': [true, 'All-in-one form to reply, create threads, automate dumping and more.'], @@ -231,6 +228,12 @@ 'Expand from here': [false, 'Expand all images only from current position to thread end.'], 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.'] }, + threadWatcher: { + 'Current Board': [false, 'Only show watched threads from the current board.'], + 'Auto Watch': [true, 'Automatically watch threads you start.'], + 'Auto Watch Reply': [false, 'Automatically watch threads you reply to.'], + 'Auto Prune': [false, 'Automatically prune 404\'d threads.'] + }, filter: { name: "# Filter any namefags:\n#/^(?!Anonymous$)/", uniqueID: "# Filter a specific ID:\n#/Txhvk1Tl/", @@ -418,25 +421,30 @@ } }; - $.ajax = function(url, options, extra) { - var form, headers, key, r, sync, type, upCallbacks, val; - if (extra == null) { - extra = {}; - } - type = extra.type, headers = extra.headers, upCallbacks = extra.upCallbacks, form = extra.form, sync = extra.sync; - r = new XMLHttpRequest(); - r.overrideMimeType('text/html'); - type || (type = form && 'post' || 'get'); - r.open(type, url, !sync); - for (key in headers) { - val = headers[key]; - r.setRequestHeader(key, val); - } - $.extend(r, options); - $.extend(r.upload, upCallbacks); - r.send(form); - return r; - }; + $.ajax = (function() { + var lastModified; + lastModified = {}; + return function(url, options, extra) { + var form, r, sync, type, upCallbacks, whenModified; + if (extra == null) { + extra = {}; + } + type = extra.type, whenModified = extra.whenModified, upCallbacks = extra.upCallbacks, form = extra.form, sync = extra.sync; + r = new XMLHttpRequest(); + type || (type = form && 'post' || 'get'); + r.open(type, url, !sync); + if (whenModified) { + r.setRequestHeader('If-Modified-Since', lastModified[url] || '0'); + $.on(r, 'load', function() { + return lastModified[url] = r.getResponseHeader('Last-Modified'); + }); + } + $.extend(r, options); + $.extend(r.upload, upCallbacks); + r.send(form); + return r; + }; + })(); $.cache = (function() { var reqs; @@ -1162,16 +1170,18 @@ })(Post); - DataBoards = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts']; + DataBoards = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']; DataBoard = (function() { - function DataBoard(key, sync) { + function DataBoard(key, sync, dontClean) { var init, _this = this; this.key = key; this.data = Conf[key]; $.sync(key, this.onSync.bind(this)); - this.clean(); + if (!dontClean) { + this.clean(); + } if (!sync) { return; } @@ -1182,6 +1192,10 @@ $.on(d, '4chanXInitFinished', init); } + DataBoard.prototype.save = function() { + return $.set(this.key, this.data); + }; + DataBoard.prototype["delete"] = function(_arg) { var boardID, postID, threadID; boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID; @@ -1199,7 +1213,7 @@ } else { delete this.data.boards[boardID]; } - return $.set(this.key, this.data); + return this.save(); }; DataBoard.prototype.deleteIfEmpty = function(_arg) { @@ -1227,7 +1241,7 @@ } else { this.data.boards[boardID] = val; } - return $.set(this.key, this.data); + return this.save(); }; DataBoard.prototype.get = function(_arg) { @@ -1269,7 +1283,7 @@ this.ajaxClean(boardID); } } - return $.set(this.key, this.data); + return this.save(); }; DataBoard.prototype.ajaxClean = function(boardID) { @@ -1297,7 +1311,7 @@ boardID: boardID }); } - return $.set(_this.key, _this.data); + return _this.save(); }); }; @@ -1358,8 +1372,24 @@ Polyfill = { init: function() { + Polyfill.toBlob(); return Polyfill.visibility(); }, + toBlob: function() { + var _base; + return (_base = HTMLCanvasElement.prototype).toBlob || (_base.toBlob = function(cb) { + var data, i, l, ui8a, _i; + data = atob(this.toDataURL().slice(22)); + l = data.length; + ui8a = new Uint8Array(l); + for (i = _i = 0; 0 <= l ? _i < l : _i > l; i = 0 <= l ? ++_i : --_i) { + ui8a[i] = data.charCodeAt(i); + } + return cb(new Blob([ui8a], { + type: 'image/png' + })); + }); + }, visibility: function() { if (!('webkitHidden' in document)) { return; @@ -1761,7 +1791,7 @@ date: data.now, dateUTC: data.time, comment: data.com, - capReps: data.capcode_replies, + capcodeReplies: data.capcode_replies, isSticky: !!data.sticky, isClosed: !!data.closed }; @@ -1789,8 +1819,8 @@ @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ - var a, array, boardID, capReps, capcode, capcodeClass, capcodeReplies, capcodeStart, capcodeType, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, generateCapcodeReplies, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; - postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, capReps = o.capReps, file = o.file; + var a, boardID, capcode, capcodeClass, capcodeReplies, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; + postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, capcodeReplies = o.capcodeReplies, file = o.file; isOP = postID === threadID; staticPath = '//static.4chan.org/image/'; if (email) { @@ -1864,32 +1894,10 @@ tripcode = tripcode ? " " + tripcode + "" : ''; sticky = isSticky ? " Sticky" : ''; closed = isClosed ? " Closed" : ''; - capcodeReplies = ''; - if (capReps) { - generateCapcodeReplies = function(capcodeType, array) { - return "" + ((function() { - switch (capcodeType) { - case 'admin': - return 'Administrator'; - case 'mod': - return 'Moderator'; - case 'developer': - return 'Developer'; - } - })()) + " Repl" + (array.length > 1 ? 'ies' : 'y') + ": " + (array.map(function(ID) { - return ">>" + ID + ""; - }).join(' ')) + "
"; - }; - for (capcodeType in capReps) { - array = capReps[capcodeType]; - capcodeReplies += generateCapcodeReplies(capcodeType, array); - } - capcodeReplies = "

" + capcodeReplies + ""; - } container = $.el('div', { id: "pc" + postID, className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", - innerHTML: "" + (isOP ? '' : "
>>
") + "
" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "
" + date + "No." + postID + "
" + (isOP ? fileHTML : '') + "
" + subject + "" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "" + date + "" + " " + "No." + postID + "
" + (isOP ? '' : fileHTML) + "
" + (comment || '') + capcodeReplies + "
" + " " + "
" + innerHTML: "" + (isOP ? '' : "
>>
") + "
" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "
" + date + "No." + postID + "
" + (isOP ? fileHTML : '') + "
" + subject + "" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "" + date + "" + " " + "No." + postID + "
" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + " " + "
" }); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -1900,7 +1908,46 @@ } quote.href = "/" + boardID + "/res/" + href; } + Build.capcodeReplies({ + boardID: boardID, + threadID: threadID, + root: container, + capcodeReplies: capcodeReplies + }); return container; + }, + capcodeReplies: function(_arg) { + var array, boardID, bq, capcodeReplies, capcodeType, generateCapcodeReplies, html, root, threadID; + boardID = _arg.boardID, threadID = _arg.threadID, bq = _arg.bq, root = _arg.root, capcodeReplies = _arg.capcodeReplies; + if (!capcodeReplies) { + return; + } + generateCapcodeReplies = function(capcodeType, array) { + return "" + ((function() { + switch (capcodeType) { + case 'admin': + return 'Administrator'; + case 'mod': + return 'Moderator'; + case 'developer': + return 'Developer'; + } + })()) + " Repl" + (array.length > 1 ? 'ies' : 'y') + ": " + (array.map(function(ID) { + return ">>" + ID + ""; + }).join(' ')) + "
"; + }; + html = []; + for (capcodeType in capcodeReplies) { + array = capcodeReplies[capcodeType]; + html.push(generateCapcodeReplies(capcodeType, array)); + } + bq || (bq = $('blockquote', root)); + return $.add(bq, [ + $.el('br'), $.el('br'), $.el('span', { + className: 'capcodeReplies', + innerHTML: html.join('') + }) + ]); } }; @@ -4221,7 +4268,7 @@ if (g.VIEW === 'catalog' || !Conf['Linkify']) { return; } - this.regString = Conf['Allow False Positives'] ? /(\b([-a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[\d]+\.[\d]+\.[\d]+\.[\d]+\/|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; + this.regString = Conf['Allow False Positives'] ? /([-a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[\d]+\.[\d]+\.[\d]+\.[\d]+\/|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])/i : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1})/i; if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } @@ -4234,7 +4281,7 @@ }); }, node: function() { - var data, el, i, items, links, node, range, snapshot, _i, _len, _ref; + var data, el, end, endNode, i, index, items, lIndex, length, link, links, node, range, result, saved, snapshot, space, test, text, _i, _len, _ref; if (this.isClone) { if (Conf['Embedding']) { i = 0; @@ -4248,16 +4295,49 @@ } return; } + test = /[^\s'"]+/g; + space = /[\s'"]/; snapshot = $.X('.//br|.//text()', this.nodes.comment); i = 0; while (node = snapshot.snapshotItem(i++)) { - if (node.parentElement.nodeName === "A") { + links = []; + data = node.data; + if (node.parentElement.nodeName === "A" || !data) { continue; } - links = []; - if (Linkify.regString.test(node.data)) { - Linkify.regString.lastIndex = 0; - Linkify.gatherLinks(snapshot, this, node, links, i); + while (result = test.exec(data)) { + index = result.index; + endNode = node; + if ((length = index + result[0].length) === data.length) { + while ((saved = snapshot.snapshotItem(i++))) { + if (saved.nodeName === 'BR') { + break; + } + endNode = saved; + length = saved.data.length; + if (end = space.exec(saved.data)) { + length = end.index; + i--; + break; + } + } + if (length === endNode.data.length) { + test.lastIndex = 0; + } + range = Linkify.makeRange(node, endNode, index, length); + if (link = Linkify.regString.exec(text = range.toString())) { + if (lIndex = link.index) { + range.setStart(node, lIndex + index); + } + links.push([range, text]); + } + break; + } else { + if (link = Linkify.regString.exec(result[0])) { + range = Linkify.makeRange(node, node, link.index, link.length); + links.push([range, link]); + } + } } _ref = links.reverse(); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -4281,57 +4361,23 @@ } } }, - gatherLinks: function(snapshot, post, node, links, i) { - var data, index, len, len2, link, match, range; - data = node.data; - len = data.length; - while ((match = Linkify.regString.exec(data))) { - index = match.index; - link = match[0]; - len2 = index + link.length; - if (len === len2) { - break; - } - range = document.createRange(); - range.setStart(node, index); - range.setEnd(node, len2); - links.push(range); - } - Linkify.regString.lastIndex = 0; - if (match) { - links.push(Linkify.seek(snapshot, post, node, links, match, i)); - } - }, - seek: function(snapshot, post, node, links, match, i) { - var data, index, link, next, range, result; - link = match[0]; + makeRange: function(startNode, endNode, startOffset, endOffset) { + var range; range = document.createRange(); - range.setStart(node, match.index); - while ((next = snapshot.snapshotItem(i++)) && next.nodeName !== 'BR') { - node = next; - data = node.data; - if (result = /[\s'"]/.exec(data)) { - index = result.index; - range.setEnd(node, index); - Linkify.regString.lastIndex = index; - Linkify.gatherLinks(snapshot, post, node, links, i); - return range; - } - } - if (range.collapsed) { - range.setEndAfter(node); - } + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); return range; }, - makeLink: function(range) { - var a, link; - link = range.toString(); - link = link.contains(':') ? link : (link.contains('@') ? 'mailto:' : 'http://') + link; + makeLink: function(_arg) { + var a, range, text; + range = _arg[0], text = _arg[1]; + text; + text = text.contains(':') ? text : (text.contains('@') ? 'mailto:' : 'http://') + text; a = $.el('a', { className: 'linkify', rel: 'nofollow noreferrer', target: '_blank', - href: link + href: text }); $.add(a, range.extractContents()); range.insertNode(a); @@ -5407,7 +5453,7 @@ _this = this; img = $.el('img'); img.onload = function() { - var applyBlob, cv, data, height, i, l, s, ui8a, width, _i; + var cv, height, s, width; s = 90 * 2; if (_this.file.type === 'image/gif') { s *= 3; @@ -5430,23 +5476,10 @@ cv.width = img.width = width; cv.getContext('2d').drawImage(img, 0, 0, width, height); URL.revokeObjectURL(fileURL); - applyBlob = function(blob) { + return cv.toBlob(function(blob) { _this.URL = URL.createObjectURL(blob); return _this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")"; - }; - if (cv.toBlob) { - cv.toBlob(applyBlob); - return; - } - data = atob(cv.toDataURL().split(',')[1]); - l = data.length; - ui8a = new Uint8Array(l); - for (i = _i = 0; 0 <= l ? _i < l : _i > l; i = 0 <= l ? ++_i : --_i) { - ui8a[i] = data.charCodeAt(i); - } - return applyBlob(new Blob([ui8a], { - type: 'image/png' - })); + }); }; fileURL = URL.createObjectURL(this.file); return img.src = fileURL; @@ -6276,7 +6309,7 @@ }, menu: { init: function() { - var conf, createSubEntry, el, key, subEntries, _ref; + var conf, createSubEntry, el, name, subEntries, _ref; if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { return; } @@ -6287,9 +6320,9 @@ createSubEntry = ImageExpand.menu.createSubEntry; subEntries = []; _ref = Config.imageExpansion; - for (key in _ref) { - conf = _ref[key]; - subEntries.push(createSubEntry(key, conf)); + for (name in _ref) { + conf = _ref[name]; + subEntries.push(createSubEntry(name, conf[1])); } return $.event('AddMenuEntry', { type: 'header', @@ -6298,21 +6331,19 @@ subEntries: subEntries }); }, - createSubEntry: function(type, config) { + createSubEntry: function(name, desc) { var input, label; label = $.el('label', { - innerHTML: " " + type + innerHTML: " " + name, + title: desc }); input = label.firstElementChild; - if (type === 'Fit width' || type === 'Fit height') { + if (name === 'Fit width' || name === 'Fit height') { $.on(input, 'change', ImageExpand.cb.setFitness); } - if (config) { - label.title = config[1]; - input.checked = Conf[type]; - $.event('change', null, input); - $.on(input, 'change', $.cb.checked); - } + input.checked = Conf[name]; + $.event('change', null, input); + $.on(input, 'change', $.cb.checked); return { el: label }; @@ -6958,7 +6989,6 @@ this.postCountEl = $('#post-count', sc); this.fileCountEl = $('#file-count', sc); this.pageCountEl = $('#page-count', sc); - this.lastModified = '0'; return Thread.prototype.callbacks.push({ name: 'Thread Stats', cb: this.node @@ -7010,18 +7040,12 @@ return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", { onload: ThreadStats.onThreadsLoad }, { - headers: { - 'If-Modified-Since': ThreadStats.lastModified - } + whenModified: true }); }, onThreadsLoad: function() { var page, pages, thread, _i, _j, _len, _len1, _ref; - if (!Conf["Page Count in Stats"]) { - return; - } - ThreadStats.lastModified = this.getResponseHeader('Last-Modified'); - if (this.status !== 200) { + if (!(Conf["Page Count in Stats"] && this.status === 200)) { return; } pages = JSON.parse(this.response); @@ -7115,7 +7139,6 @@ ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; ThreadUpdater.outdateCount = 0; - ThreadUpdater.lastModified = '0'; ThreadUpdater.cb.interval.call($.el('input', { value: Conf['Interval'] })); @@ -7208,10 +7231,7 @@ case 200: g.DEAD = false; ThreadUpdater.parse(JSON.parse(req.response).posts); - ThreadUpdater.lastModified = req.getResponseHeader('Last-Modified'); - if (Conf['Auto Update']) { - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); - } + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); break; case 404: g.DEAD = true; @@ -7225,16 +7245,8 @@ }); break; default: - if (Conf['Auto Update']) { - ThreadUpdater.outdateCount++; - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); - } - /* - Status Code 304: Not modified - By sending the `If-Modified-Since` header we get a proper status code, and no response. - This saves bandwidth for both the user and the servers and avoid unnecessary computation. - */ - + ThreadUpdater.outdateCount++; + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); _ref = req.status === 304 ? [null, null] : ["" + req.statusText + " (" + req.status + ")", 'warning'], text = _ref[0], klass = _ref[1]; ThreadUpdater.set('status', text, klass); } @@ -7302,9 +7314,7 @@ return ThreadUpdater.req = $.ajax(url, { onloadend: ThreadUpdater.cb.load }, { - headers: { - 'If-Modified-Since': ThreadUpdater.lastModified - } + whenModified: true }); }, updateThreadStatus: function(title, OP) { @@ -7439,7 +7449,7 @@ ThreadWatcher = { init: function() { - var sc; + var now, sc; if (!Conf['Thread Watcher']) { return; } @@ -7449,21 +7459,46 @@ href: 'javascript:;', className: 'disabled' }); - this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '
Thread Watcher×
'); + this.db = new DataBoard('watchedThreads', this.refresh, true); + this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', "
Thread Watcher ×
"); + this.status = $('#watcher-status', this.dialog); + this.list = this.dialog.lastElementChild; $.on(d, 'QRPostSuccessful', this.cb.post); - $.sync('WatchedThreads', this.refresh); + if (g.VIEW === 'thread') { + $.on(d, 'ThreadUpdate', this.cb.threadUpdate); + } $.on(sc, 'click', this.toggleWatcher); $.on($('.move>.close', ThreadWatcher.dialog), 'click', this.toggleWatcher); + $.on(d, '4chanXInitFinished', this.ready); if (Conf['Toggleable Thread Watcher']) { Header.addShortcut(sc); $.addClass(doc, 'fixed-watcher'); } - $.ready(function() { - ThreadWatcher.refresh(); - $.add(d.body, ThreadWatcher.dialog); - if (Conf['Toggleable Thread Watcher']) { - return ThreadWatcher.dialog.hidden = true; + now = Date.now(); + if ((this.db.data.lastChecked || 0) < now - 2 * $.HOUR) { + this.db.data.lastChecked = now; + ThreadWatcher.fetchAllStatus(); + this.db.save(); + } + $.get('WatchedThreads', null, function(_arg) { + var WatchedThreads, boardID, data, threadID, threads, _ref; + WatchedThreads = _arg.WatchedThreads; + if (!WatchedThreads) { + return; } + _ref = ThreadWatcher.convert(WatchedThreads); + for (boardID in _ref) { + threads = _ref[boardID]; + for (threadID in threads) { + data = threads[threadID]; + ThreadWatcher.db.set({ + boardID: boardID, + threadID: threadID, + val: data + }); + } + } + return $["delete"]('WatchedThreads'); }); return Thread.prototype.callbacks.push({ name: 'Thread Watcher', @@ -7471,77 +7506,86 @@ }); }, node: function() { - var favicon, - _this = this; - favicon = $.el('a', { - className: 'watch-thread-link', - href: 'javascript:;' + var toggler; + toggler = $.el('img', { + className: 'watcher-toggler' }); - $.on(favicon, 'click', ThreadWatcher.cb.toggle); - $.before($('input', this.OP.nodes.post), favicon); - if (g.VIEW !== 'thread') { + $.on(toggler, 'click', ThreadWatcher.cb.toggle); + return $.before($('input', this.OP.nodes.post), toggler); + }, + ready: function() { + $.off(d, '4chanXInitFinished', ThreadWatcher.ready); + if (!Main.isThisPageLegit()) { return; } - return $.get('AutoWatch', 0, function(item) { - if (item['AutoWatch'] !== _this.ID) { + ThreadWatcher.refresh(); + $.add(d.body, ThreadWatcher.dialog); + if (Conf['Toggleable Thread Watcher']) { + ThreadWatcher.dialog.hidden = true; + } + if (!Conf['Auto Watch']) { + return; + } + return $.get('AutoWatch', 0, function(_arg) { + var AutoWatch, thread; + AutoWatch = _arg.AutoWatch; + if (!(thread = g.BOARD.threads[AutoWatch])) { return; } - ThreadWatcher.watch(_this); + ThreadWatcher.add(thread); return $["delete"]('AutoWatch'); }); }, - refresh: function(watched) { - var ID, board, div, favicon, id, link, nodes, props, thread, x, _ref, _ref1; - if (!watched) { - $.get('WatchedThreads', {}, function(item) { - return ThreadWatcher.refresh(item['WatchedThreads']); - }); - return; - } - nodes = [$('.move', ThreadWatcher.dialog)]; - for (board in watched) { - _ref = watched[board]; - for (id in _ref) { - props = _ref[id]; - x = $.el('a', { - textContent: '×', - className: 'close', - href: 'javascript:;' - }); - $.on(x, 'click', ThreadWatcher.cb.x); - link = $.el('a', props); - link.title = link.textContent; - div = $.el('div'); - $.add(div, [x, $.tn(' '), link]); - nodes.push(div); - } - } - $.rmAll(ThreadWatcher.dialog); - $.add(ThreadWatcher.dialog, nodes); - watched = watched[g.BOARD] || {}; - _ref1 = g.BOARD.threads; - for (ID in _ref1) { - thread = _ref1[ID]; - favicon = $('.watch-thread-link', thread.OP.nodes.post); - if (ID in watched) { - $.addClass(favicon, 'watched'); - } else { - $.rmClass(favicon, 'watched'); - } - } - }, toggleWatcher: function() { $.toggleClass(ThreadWatcher.shortcut, 'disabled'); return ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden; }, cb: { + openAll: function() { + var a, _i, _len, _ref; + if ($.hasClass(this, 'disabled')) { + return; + } + _ref = $$('a[title]', ThreadWatcher.list); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + a = _ref[_i]; + $.open(a.href); + } + return $.event('CloseMenu'); + }, + checkThreads: function() { + if ($.hasClass(this, 'disabled')) { + return; + } + return ThreadWatcher.fetchAllStatus(); + }, + pruneDeads: function() { + var boardID, data, threadID, _i, _len, _ref, _ref1; + if ($.hasClass(this, 'disabled')) { + return; + } + _ref = ThreadWatcher.getAll(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + _ref1 = _ref[_i], boardID = _ref1.boardID, threadID = _ref1.threadID, data = _ref1.data; + if (!data.isDead) { + continue; + } + delete ThreadWatcher.db.data.boards[boardID][threadID]; + ThreadWatcher.db.deleteIfEmpty({ + boardID: boardID + }); + } + ThreadWatcher.db.save(); + ThreadWatcher.refresh(); + return $.event('CloseMenu'); + }, toggle: function() { return ThreadWatcher.toggle(Get.postFromNode(this).thread); }, - x: function() { - var thread; - thread = this.nextElementSibling.pathname.split('/'); - return ThreadWatcher.unwatch(thread[1], thread[3]); + rm: function() { + var boardID, threadID, _ref; + _ref = this.parentNode.dataset.fullID.split('.'), boardID = _ref[0], threadID = _ref[1]; + return ThreadWatcher.rm(boardID, +threadID); }, post: function(e) { var board, postID, threadID, _ref; @@ -7551,41 +7595,332 @@ return $.set('AutoWatch', threadID); } } else if (Conf['Auto Watch Reply']) { - return ThreadWatcher.watch(board.threads[threadID]); + return ThreadWatcher.add(board.threads[threadID]); } + }, + threadUpdate: function(e) { + var thread; + thread = e.detail.thread; + if (!(e.detail[404] && ThreadWatcher.db.get({ + boardID: thread.board.ID, + threadID: thread.ID + }))) { + return; + } + return ThreadWatcher.add(thread); + } + }, + fetchCount: { + fetched: 0, + fetching: 0 + }, + fetchAllStatus: function() { + var thread, _i, _len, _ref; + ThreadWatcher.status.textContent = '...'; + _ref = ThreadWatcher.getAll(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + thread = _ref[_i]; + ThreadWatcher.fetchStatus(thread); + } + }, + fetchStatus: function(_arg) { + var boardID, data, fetchCount, threadID; + boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data; + if (data.isDead) { + return; + } + fetchCount = ThreadWatcher.fetchCount; + fetchCount.fetching++; + return $.ajax("//api.4chan.org/" + boardID + "/res/" + threadID + ".json", { + onloadend: function() { + var status; + fetchCount.fetched++; + if (fetchCount.fetched === fetchCount.fetching) { + fetchCount.fetched = 0; + fetchCount.fetching = 0; + status = ''; + } else { + status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%"; + } + ThreadWatcher.status.textContent = status; + if (this.status !== 404) { + return; + } + if (Conf['Auto Prune']) { + ThreadWatcher.rm(boardID, threadID); + } else { + data.isDead = true; + ThreadWatcher.db.set({ + boardID: boardID, + threadID: threadID, + val: data + }); + } + return ThreadWatcher.refresh(); + } + }, { + type: 'head' + }); + }, + getAll: function() { + var all, boardID, data, threadID, threads, _ref; + all = []; + _ref = ThreadWatcher.db.data.boards; + for (boardID in _ref) { + threads = _ref[boardID]; + if (Conf['Current Board'] && boardID !== g.BOARD.ID) { + continue; + } + for (threadID in threads) { + data = threads[threadID]; + all.push({ + boardID: boardID, + threadID: threadID, + data: data + }); + } + } + return all; + }, + makeLine: function(boardID, threadID, data) { + var div, fullID, href, link, x; + x = $.el('a', { + textContent: '×', + href: 'javascript:;' + }); + $.on(x, 'click', ThreadWatcher.cb.rm); + if (data.isDead) { + href = Redirect.to('thread', { + boardID: boardID, + threadID: threadID + }); + } + link = $.el('a', { + href: href || ("/" + boardID + "/res/" + threadID), + textContent: data.excerpt, + title: data.excerpt + }); + div = $.el('div'); + fullID = "" + boardID + "." + threadID; + div.dataset.fullID = fullID; + if (g.VIEW === 'thread' && fullID === ("" + g.BOARD + "." + g.THREADID)) { + $.addClass(div, 'current'); + } + if (data.isDead) { + $.addClass(div, 'dead-thread'); + } + $.add(div, [x, $.tn(' '), link]); + return div; + }, + refresh: function() { + var boardID, data, list, nodes, refresher, thread, threadID, toggler, watched, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3; + nodes = []; + _ref = ThreadWatcher.getAll(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + _ref1 = _ref[_i], boardID = _ref1.boardID, threadID = _ref1.threadID, data = _ref1.data; + nodes.push(ThreadWatcher.makeLine(boardID, threadID, data)); + } + list = ThreadWatcher.list; + $.rmAll(list); + $.add(list, nodes); + _ref2 = g.BOARD.threads; + for (threadID in _ref2) { + thread = _ref2[threadID]; + toggler = $('.watcher-toggler', thread.OP.nodes.post); + watched = ThreadWatcher.db.get({ + boardID: thread.board.ID, + threadID: threadID + }); + $[watched ? 'addClass' : 'rmClass'](toggler, 'watched'); + } + _ref3 = ThreadWatcher.menu.refreshers; + for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { + refresher = _ref3[_j]; + refresher(); } }, toggle: function(thread) { - if (!$.hasClass($('.watch-thread-link', thread.OP.nodes.post), 'watched')) { - return ThreadWatcher.watch(thread); + var boardID, threadID; + boardID = thread.board.ID; + threadID = thread.ID; + if (ThreadWatcher.db.get({ + boardID: boardID, + threadID: threadID + })) { + return ThreadWatcher.rm(boardID, threadID); } else { - return ThreadWatcher.unwatch(thread.board, thread.ID); + return ThreadWatcher.add(thread); } }, - unwatch: function(board, threadID) { - return $.get('WatchedThreads', {}, function(item) { - var watched; - watched = item['WatchedThreads']; - delete watched[board][threadID]; - if (!Object.keys(watched[board]).length) { - delete watched[board]; + add: function(thread) { + var boardID, data, threadID; + data = {}; + boardID = thread.board.ID; + threadID = thread.ID; + if (thread.isDead) { + if (Conf['Auto Prune'] && ThreadWatcher.db.get({ + boardID: boardID, + threadID: threadID + })) { + ThreadWatcher.rm(boardID, threadID); + return; } - ThreadWatcher.refresh(watched); - return $.set('WatchedThreads', watched); + data.isDead = true; + } + data.excerpt = Get.threadExcerpt(thread); + ThreadWatcher.db.set({ + boardID: boardID, + threadID: threadID, + val: data }); + return ThreadWatcher.refresh(); }, - watch: function(thread) { - return $.get('WatchedThreads', {}, function(item) { - var watched, _name; - watched = item['WatchedThreads']; - watched[_name = thread.board] || (watched[_name] = {}); - watched[thread.board][thread] = { - href: "/" + thread.board + "/res/" + thread, - textContent: Get.threadExcerpt(thread) - }; - ThreadWatcher.refresh(watched); - return $.set('WatchedThreads', watched); + rm: function(boardID, threadID) { + ThreadWatcher.db["delete"]({ + boardID: boardID, + threadID: threadID }); + return ThreadWatcher.refresh(); + }, + convert: function(oldFormat) { + var boardID, data, newFormat, threadID, threads; + newFormat = {}; + for (boardID in oldFormat) { + threads = oldFormat[boardID]; + for (threadID in threads) { + data = threads[threadID]; + (newFormat[boardID] || (newFormat[boardID] = {}))[threadID] = { + excerpt: data.textContent + }; + } + } + return newFormat; + }, + menu: { + refreshers: [], + init: function() { + var menu; + if (!Conf['Thread Watcher']) { + return; + } + menu = new UI.Menu('thread watcher'); + $.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) { + return menu.toggle(e, this, ThreadWatcher); + }); + this.addHeaderMenuEntry(); + return this.addMenuEntries(); + }, + addHeaderMenuEntry: function() { + var entryEl; + if (g.VIEW !== 'thread') { + return; + } + entryEl = $.el('a', { + href: 'javascript:;' + }); + $.event('AddMenuEntry', { + type: 'header', + el: entryEl, + order: 60 + }); + $.on(entryEl, 'click', function() { + return ThreadWatcher.toggle(g.threads["" + g.BOARD + "." + g.THREADID]); + }); + return this.refreshers.push(function() { + var addClass, rmClass, text, _ref; + _ref = $('.current', ThreadWatcher.list) ? ['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); + return entryEl.textContent = text; + }); + }, + addMenuEntries: function() { + var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1, _results; + entries = []; + entries.push({ + cb: ThreadWatcher.cb.openAll, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Open all threads' + }) + }, + refresh: function() { + return (ThreadWatcher.list.firstElementChild ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + entries.push({ + cb: ThreadWatcher.cb.checkThreads, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Check 404\'d threads' + }) + }, + refresh: function() { + return ($('div:not(.dead-thread)', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + entries.push({ + cb: ThreadWatcher.cb.pruneDeads, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Prune 404\'d threads' + }) + }, + refresh: function() { + return ($('.dead-thread', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + subEntries = []; + _ref = Config.threadWatcher; + for (name in _ref) { + conf = _ref[name]; + subEntries.push(this.createSubEntry(name, conf[1])); + } + entries.push({ + entry: { + type: 'thread watcher', + el: $.el('span', { + textContent: 'Settings' + }), + subEntries: subEntries + } + }); + _results = []; + for (_i = 0, _len = entries.length; _i < _len; _i++) { + _ref1 = entries[_i], entry = _ref1.entry, cb = _ref1.cb, refresh = _ref1.refresh; + if (entry.el.nodeName === 'A') { + entry.el.href = 'javascript:;'; + } + if (cb) { + $.on(entry.el, 'click', cb); + } + if (refresh) { + this.refreshers.push(refresh.bind(entry)); + } + _results.push($.event('AddMenuEntry', entry)); + } + return _results; + }, + createSubEntry: function(name, desc) { + var entry, input; + entry = { + type: 'thread watcher', + el: $.el('label', { + innerHTML: " " + name, + title: desc + }) + }; + input = entry.el.firstElementChild; + input.checked = Conf[name]; + $.on(input, 'change', $.cb.checked); + if (name === 'Current Board') { + $.on(input, 'change', ThreadWatcher.refresh); + } + return entry; + } } }; @@ -7912,8 +8247,8 @@ http: true, https: true, software: 'foolfuuka', - boards: ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv'], - files: ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv'] + boards: ['adv', 'asp', 'cm', 'd', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y'], + files: ['cm', 'd', 'e', 'i', 'n', 'o', 'p', 's', 'trv', 'y'] }, 'Foolz Beta': { domain: 'beta.foolz.us', @@ -8319,10 +8654,8 @@ }, callbacks: [], cb: function(e) { - var post; e.preventDefault(); - post = Get.postFromNode(this); - return ExpandComment.expand(post); + return ExpandComment.expand(Get.postFromNode(this)); }, expand: function(post) { var a; @@ -8382,6 +8715,12 @@ } quote.href = "/" + post.board + "/res/" + href; } + Build.capcodeReplies({ + boardID: post.board.ID, + threadID: post.thread.ID, + bq: clone, + capcodeReplies: postObj.capcode_replies + }); post.nodes.shortComment = comment; $.replace(comment, clone); post.nodes.comment = post.nodes.longComment = clone; @@ -9392,7 +9731,8 @@ $.on(d, '4chanXInitFinished', Settings.open); } return $.set({ - lastchecked: Date.now(), + archives: Conf['archives'], + lastarchivecheck: now, previousversion: g.VERSION }); }); @@ -9578,7 +9918,6 @@ version: g.VERSION, date: now }; - Conf['WatchedThreads'] = {}; for (_i = 0, _len = DataBoards.length; _i < _len; _i++) { db = DataBoards[_i]; Conf[db] = { @@ -9710,14 +10049,13 @@ }); } } - data.Conf.WatchedThreads = data.WatchedThreads; - } else if (version[0] === '3') { - data = Settings.convertSettings(data, { - 'Reply Hiding': 'Reply Hiding Buttons', - 'Thread Hiding': 'Thread Hiding Buttons', - 'Bottom header': 'Bottom Header', - 'Unread Tab Icon': 'Unread Favicon' - }); + data.Conf['WatchedThreads'] = data.WatchedThreads; + } + if (data.Conf['WatchedThreads']) { + data.Conf['watchedThreads'] = { + boards: ThreadWatcher.convert(data.Conf['WatchedThreads']) + }; + delete data.Conf['WatchedThreads']; } return $.set(data.Conf); }, @@ -10144,6 +10482,7 @@ 'Thread Stats': ThreadStats, 'Thread Updater': ThreadUpdater, 'Thread Watcher': ThreadWatcher, + 'Thread Watcher (Menu)': ThreadWatcher.menu, 'Index Navigation': Nav, 'Keybinds': Keybinds, 'Show Dice Roll': Dice @@ -10238,8 +10577,7 @@ } Main.callbackNodes(Thread, threads); Main.callbackNodesDB(Post, posts, function() { - $.event('4chanXInitFinished'); - return Main.checkUpdate(); + return $.event('4chanXInitFinished'); }); if (styleSelector = $.id('styleSelector')) { passLink = $.el('a', { @@ -10259,8 +10597,7 @@ err = _error; new Notification('warning', 'Cookies need to be enabled on 4chan for 4chan X to properly function.', 30); } - $.event('4chanXInitFinished'); - return Main.checkUpdate(); + return $.event('4chanXInitFinished'); }, callbackNodes: function(klass, nodes) { var callback, err, errors, i, len, node, _i, _len, _ref; @@ -10362,37 +10699,6 @@ obj.callback.isAddon = true; return Klass.prototype.callbacks.push(obj.callback); }, - message: function(e) { - var el, version; - version = e.data.version; - if (version && version !== g.VERSION) { - el = $.el('span', { - innerHTML: "Update: 4chan X v" + version + " is out, get it here." - }); - return new Notification('info', el, 120); - } - }, - checkUpdate: function() { - var now; - if (!(Conf['Check for Updates'] && Main.isThisPageLegit())) { - return; - } - now = Date.now(); - return $.get('lastchecked', 0, function(_arg) { - var lastchecked; - lastchecked = _arg.lastchecked; - if (lastchecked > now - $.DAY) { - return; - } - return $.ready(function() { - $.on(window, 'message', Main.message); - $.set('lastchecked', now); - return $.add(d.head, $.el('script', { - src: 'https://github.com/seaweedchan/4chan-x/raw/master/latest.js' - })); - }); - }); - }, handleErrors: function(errors) { var div, error, logs, _i, _len; if (!(errors instanceof Array)) { @@ -10443,7 +10749,7 @@ } return Main.thisPageIsLegit; }, - css: "/* General */\n.dialog {\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder: 1px solid;\ndisplay: block;\npadding: 0;\n}\n.captcha-img,\n.field {\nbackground-color: #FFF;\nborder: 1px solid #CCC;\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\ncolor: #333;\nfont: 13px sans-serif;\noutline: none;\ntransition: color .25s, border-color .25s;\ntransition: color .25s, border-color .25s;\n}\n.field::-moz-placeholder,\n.field:hover::-moz-placeholder {\ncolor: #AAA !important;\nfont-size: 13px !important;\nopacity: 1.0 !important;\n}\n.captch-img:hover,\n.field:hover {\nborder-color: #999;\n}\n.field:hover, .field:focus {\ncolor: #000;\n}\n.field[disabled] {\nbackground-color: #F2F2F2;\ncolor: #888;\n}\n.move {\ncursor: move;\noverflow: hidden;\n}\nlabel, .favicon {\ncursor: pointer;\n}\na[href=\"javascript:;\"] {\ntext-decoration: none;\n}\n.warning {\ncolor: red;\n}\n#boardNavDesktop {\ndisplay: none !important;\n}\na {\noutline: none !important;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#navlinks, .fixed #header-bar,\n:root.float #updater,\n:root.float #thread-stats,\n#qr {\nposition: fixed;\n}\n#fourchanx-settings {\nz-index: 999;\n}\n#overlay {\nz-index: 900;\n}\n#notifications {\nz-index: 70;\n}\n#qp, #ihover {\nz-index: 60;\n}\n#menu {\nz-index: 50;\n}\n#navlinks, #updater, #thread-stats {\nz-index: 40;\n}\n.fixed #header-bar.autohide {\nz-index: 35;\n}\n#qr {\nz-index: 30;\n}\n#watcher {\nz-index: 8;\n}\n:root.fixed-watcher #watcher {\nz-index: 20;\n}\n.fixed #header-bar {\nz-index: 10;\n}\n/* Header */\n.fixed.top body {\npadding-top: 2em;\n}\n.fixed.bottom body {\npadding-bottom: 2em;\n}\n.fixed #header-bar {\nright: 0;\nleft: 0;\npadding: 3px 4px 4px;\n}\n.fixed.top #header-bar {\ntop: 0;\n}\n.fixed.bottom #header-bar {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\ntransition: all .1s .05s ease-in-out;\n}\n:root.centered-links #shortcuts {\nwidth: 300px;\ntext-align: right;\n}\n:root.centered-links #header-bar {\ntext-align: center;\n}\n:root.centered-links #custom-board-list {\nposition: relative;\nleft: 150px;\n}\n.fixed.top #header-bar {\nborder-bottom-width: 1px;\n}\n.fixed.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n.fixed.bottom #header-bar .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n.fixed #header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n.fixed.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#scroll-marker {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n:root:not(.autohide) #scroll-marker {\npointer-events: none;\n}\n#header-bar #scroll-marker {\ndisplay: none;\n}\n.fixed #header-bar #scroll-marker {\ndisplay: block;\n}\n.fixed.top #header-bar #scroll-marker {\ntop: 100%;\n}\n.fixed.bottom #header-bar #scroll-marker {\nbottom: 100%;\n}\n#header-bar a:not(.entry):not(.close) {\ntext-decoration: none;\npadding: 1px;\n}\n#header-bar input {\nmargin: 0;\nvertical-align: bottom;\n}\n#shortcuts:empty {\ndisplay: none;\n}\n.brackets-wrap::before {\ncontent: \"\\00a0[\";\n}\n.brackets-wrap::after {\ncontent: \"]\\00a0\";\n}\n.disabled,\n.expand-all-shortcut {\nopacity: .45;\n}\n#shortcuts {\nfloat: right;\n}\n.shortcut {\nmargin-left: 3px;\n}\n#navbotright,\n#navtopright {\ndisplay: none;\n}\n#toggleMsgBtn {\ndisplay: none !important;\n}\n.current {\nfont-weight: bold;\n}\n/* 4chan X link brackets */\n.fourchanx-link::after {\ncontent: \"]\";\n}\n.fourchanx-link::before {\ncontent: \"[\";\n}\n/* Notifications */\n#notifications {\nposition: fixed;\ntop: 0;\nheight: 0;\ntext-align: center;\nright: 0;\nleft: 0;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar #notifications {\nposition: absolute;\ntop: 100%;\n}\n.notification {\ncolor: #FFF;\nfont-weight: 700;\ntext-shadow: 0 1px 2px rgba(0, 0, 0, .5);\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder-radius: 2px;\nmargin: 1px auto;\nwidth: 500px;\nmax-width: 100%;\nposition: relative;\ntransition: all .25s ease-in-out;\n}\n.notification.error {\nbackground-color: hsla(0, 100%, 38%, .9);\n}\n.notification.warning {\nbackground-color: hsla(36, 100%, 38%, .9);\n}\n.notification.info {\nbackground-color: hsla(200, 100%, 38%, .9);\n}\n.notification.success {\nbackground-color: hsla(104, 100%, 38%, .9);\n}\n.notification a {\ncolor: white;\n}\n.notification > .close {\npadding: 6px;\ntop: 0;\nright: 5px;\nposition: absolute;\n}\n.message {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\npadding: 6px 20px;\nmax-height: 200px;\nwidth: 100%;\noverflow: auto;\n}\n\n/* Settings */\n:root.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#overlay {\nbackground-color: rgba(0, 0, 0, .5);\ntop: 0;\nleft: 0;\nheight: 100%;\nwidth: 100%;\n}\n#fourchanx-settings {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nbox-shadow: 0 0 15px rgba(0, 0, 0, .15);\nheight: 600px;\nmax-height: 100%;\nwidth: 900px;\nmax-width: 100%;\nmargin: auto;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\nheight: 15px;\n}\n#fourchanx-settings > nav a {\ntext-decoration: underline;\n}\n#fourchanx-settings > nav a.close {\ntext-decoration: none;\npadding: 2px;\n}\n.section-container {\noverflow: auto;\nposition: absolute;\ntop: 2.1em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\npadding-right: 5px;\n}\n.sections-list {\npadding: 0 3px;\nfloat: left;\n}\n.credits {\nfloat: right;\n}\n.tab-selected {\nfont-weight: 700;\n}\n.section-sauce ul,\n.section-advanced ul {\nlist-style: none;\nmargin: 0;\n}\n.section-sauce ul {\npadding: 8px;\n}\n.section-advanced ul {\npadding: 0px;\n}\n.section-sauce li,\n.section-advanced li {\npadding-left: 4px;\n}\n.section-main label {\ntext-decoration: underline;\n}\n.section-filter ul {\npadding: 0;\n}\n.section-filter li {\nmargin: 10px 40px;\n}\n.section-filter textarea {\nheight: 500px;\n}\n.section-sauce textarea {\nheight: 350px;\n}\n.section-advanced .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-advanced textarea {\nheight: 150px;\n}\n.section-advanced .archive-cell {\nmin-width: 160px;\ntext-align: center;\n}\n.section-advanced #archive-board-select {\nposition: absolute;\n}\n.section-advanced .note {\nfont-size: 0.8em;\nfont-style: italic;\nmargin-left: 10px;\n}\n.section-advanced .note code {\nfont-style: normal;\nfont-size: 11px;\n}\n.section-keybinds .field {\nfont-family: monospace;\n} \n#fourchanx-settings fieldset {\nborder: 1px solid;\nborder-radius: 3px;\n}\n#fourchanx-settings legend {\nfont-weight: 700;\n}\n#fourchanx-settings textarea {\nfont-family: monospace;\nmin-width: 100%;\nmax-width: 100%;\n}\n#fourchanx-settings code {\ncolor: #000;\nbackground-color: #FFF;\npadding: 0 2px;\n}\n.unscroll {\noverflow: hidden;\n}\n\n/* Announcement Hiding */\n:root.hide-announcement #globalMessage {\ndisplay: none;\n}\na.hide-announcement {\nfloat: left;\n}\n\n/* Unread */\n#unread-line {\nmargin: 0;\nborder-color: rgb(255,0,0);\n}\n\n/* Thread Updater */\n#updater {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 5px 3px 0px;\nmargin-bottom: -3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n:root.float #updater {\npadding: 0px 3px;\n}\n.new {\ncolor: limegreen;\n}\n#update-status.new {\nmargin-right: 5px;\n}\n#update-timer {\ncursor: pointer;\n}\n\n/* Thread Watcher */\n#watcher {\nposition: absolute;\n}\n#watcher {\npadding-bottom: 3px;\noverflow: hidden;\nwhite-space: nowrap;\nmin-width: 120px;\nmax-height: 92%;\noverflow-y: auto;\n}\n:root.fixed-watcher #watcher {\nposition: fixed;\n}\n:root:not(.fixed-watcher) #watcher:not(:hover) {\nmax-height: 210px;\noverflow-y: hidden;\n}\n#watcher > .move {\npadding-top: 3px;\n}\n#watcher > div {\nmax-width: 250px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#watcher a {\ntext-decoration: none;\n}\n#watcher .move>.close {\nposition: absolute;\nright: 0px;\ntop: 0px;\npadding: 0px 4px;\n}\n.watch-thread-link {\npadding-top: 18px;\nwidth: 18px;\nheight: 0px;\ndisplay: inline-block;\nbackground-repeat: no-repeat;\nopacity: 0.2;\nposition: relative;\ntop: 1px;\n}\n.watch-thread-link.watched {\nopacity: 1;\n}\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n:root.float #post-count, :root.float #file-count {\npointer-events: none;\n}\n:root.float #thread-stats {\npadding: 0px 3px;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink),\n.quotelink.deadlink:not(.forwardlink) {\ntext-decoration: underline !important;\n}\n.inlined {\nopacity: .5;\n}\n#qp input, .forwarded {\ndisplay: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\ntext-decoration: none;\nborder-bottom: 1px dashed;\n}\n.filtered {\ntext-decoration: underline line-through;\n}\n:root.hide-backlinks .backlink.filtered {\ndisplay: none;\n}\n.inline {\nborder: 1px solid;\ndisplay: table;\nmargin: 2px 0;\n}\n.inline .post {\nborder: 0 !important;\nbackground-color: transparent !important;\ndisplay: table !important;\nmargin: 0 !important;\npadding: 1px 2px !important;\n}\n#qp > .opContainer::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n#qp .post {\nborder: none;\nmargin: 0;\npadding: 2px 2px 5px;\n}\n#qp img {\nmax-height: 80vh;\nmax-width: 50vw;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\n}\n:root.highlight-own .yourPost > .reply,\n:root.highlight-you .quotesYou > .reply {\nborder-left: 2px solid rgba(221,0,0,.5);\n}\n/* Quote Threading */\n.threadContainer {\nmargin-left: 20px;\nborder-left: 1px solid rgba(128,128,128,.3);\n}\n.threadOP {\nclear: both;\n} \n\n/* File */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull,\n.expanded-image > .post > .file > .fileThumb > img[data-md5],\n:not(.expanded-image) > .post > .file > .fileThumb > .full-image {\ndisplay: none;\n}\n.expanding {\nopacity: .5;\n}\n:root.fit-height .full-image {\nmax-height: 100vh;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image {\nwidth: 100%;\n}\n#ihover {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nmax-height: 100%;\nmax-width: 75%;\npadding-bottom: 16px;\n}\n.fappeTyme .thread > .noFile,\n.fappeTyme .threadContainer > .noFile {\ndisplay: none;\n}\n\n/* Index/Reply Navigation */\n#navlinks {\nfont-size: 16px;\ntop: 25px;\nright: 10px;\n}\n\n/* Filter */\n.opContainer.filter-highlight {\nbox-shadow: inset 5px 0 rgba(255, 0, 0, .5);\n}\n.filter-highlight > .reply {\nbox-shadow: -5px 0 rgba(255, 0, 0, .5);\n}\n\n/* Spoiler text */\n:root.reveal-spoilers s {\ncolor: white !important;\n}\n\n/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ * {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n:root.hide-original-post-form #postForm,\n:root.hide-original-post-form .postingMode,\n:root.hide-original-post-form #togglePostForm,\n#qr.autohide:not(.has-focus):not(:hover) > form,\n.postingMode ~ #qr select,\n#file-n-submit:not(.has-file) #qr-filerm {\ndisplay: none;\n}\n#qr select,\n#dump-button,\n.remove,\n.captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 300px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nmargin-bottom: 1px;\n}\n#qr .close {\nfloat: right;\npadding: 0 3px;\n}\n#qr .warning {\nmin-height: 1.6em;\nvertical-align: middle;\npadding: 0 1px;\nborder-width: 1px;\nborder-style: solid;\n}\n.qr-link-container {\ntext-align: center;\n}\n.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nwidth: 10%;\nmargin: 0;\nmargin-right: 4px;\nfont: 13px sans-serif;\npadding: 1px 0px 2px;\nopacity: 0.6;\n}\n.persona .field:not(#dump) {\nwidth: 95px;\nmin-width: 33.3%;\nmax-width: 33.3%;\n}\n#qr textarea.field {\nheight: 14.8em;\nmin-height: 9em;\n}\n#qr.has-captcha textarea.field {\nheight: 9em;\n}\ninput.field.tripped:not(:hover):not(:focus) {\ncolor: transparent !important;\ntext-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 0px;\ntext-align: center;\nbackground-image: #fff;\nfont-size: 0px;\nmin-height: 59px;\nmin-width: 302px;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.captcha-input.error:focus {\nborder-color: rgb(255,0,0) !important;\n}\n.field {\n-moz-box-sizing: border-box;\nmargin: 0px;\npadding: 2px 4px 3px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\nvertical-align: top;\n}\n:root.webkit #qr [type='submit'] {\nheight: 24px;\n}\n/* Fake File Input */\n#qr-filename,\n.has-file #qr-no-file {\ndisplay: none;\n}\n#qr-no-file,\n.has-file #qr-filename {\ndisplay: inline-block;\npadding: 0px 4px;\nmargin-bottom: 2px;\noverflow: hidden;\ntext-overflow: ellipsis;\nmax-width: 88%;\n}\n#qr-no-file {\ncolor: #AAA;\n}\n#qr-filename-container {\n-moz-box-sizing: border-box;\ndisplay: inline-block;\nposition: relative;\nwidth: 100px;\nmin-width: 74.6%;\nmax-width: 74.6%;\nmargin-right: 0.4%;\nmargin-top: 1px;\noverflow: hidden;\npadding: 2px 1px 0;\nheight: 22px;\n}\n#qr-filename-container:hover {\ncursor: text;\n}\n#qr-extras-container {\nposition: absolute;\nright: 0px;\n}\n#qr-filerm {\nmargin-right: 2px;\nz-index: 2;\n}\n#file-n-submit {\nheight: 23px;\n}\n#qr input[type=file] {\nvisibility: hidden;\nposition: absolute;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\n}\n#qr.has-spoiler .has-file #qr-spoiler-label {\nwidth: 6.7%;\nmin-width: 6.7%;\nmax-width: 6.7%;\ndisplay: inline-block;\ntext-align: center;\nvertical-align: top;\n}\n#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {\ndisplay: none;\n}\n#qr.has-spoiler .has-file #qr-filename-container {\nmax-width: 67.9%;\nmin-width: 67.9%;\n}\n#qr-spoiler-label input {\nposition: relative;\ntop: 3px;\n}\n/* Dumping UI */\n.dump #dump-list-container {\ndisplay: block;\n}\n#dump-list-container {\ndisplay: none;\nposition: relative;\noverflow-y: hidden;\nmargin-top: 1px;\n}\n#dump-list {\noverflow-x: auto;\noverflow-y: hidden;\nwhite-space: nowrap;\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-list:hover {\noverflow-x: auto;\n}\n.qr-preview {\n-moz-box-sizing: border-box;\ncounter-increment: thumbnails;\ncursor: move;\ndisplay: inline-block;\nheight: 90px;\nwidth: 90px;\npadding: 2px;\nopacity: .5;\noverflow: hidden;\nposition: relative;\ntext-shadow: 0 1px 1px #000;\n-moz-transition: opacity .25s ease-in-out;\nvertical-align: top;\nbackground-size: cover;\n}\n.qr-preview:hover,\n.qr-preview:focus {\nopacity: .9;\n}\n.qr-preview::before {\ncontent: counter(thumbnails);\ncolor: #fff;\nposition: absolute;\ntop: 3px;\nright: 3px;\ntext-shadow: 0 0 3px #000, 0 0 8px #000;\n}\n.qr-preview#selected {\nopacity: 1;\n}\n.qr-preview.drag {\nbox-shadow: 0 0 10px rgba(0,0,0,.5);\n}\n.qr-preview.over {\nborder-color: #fff;\n}\n.qr-preview > span {\ncolor: #fff;\n}\n.remove {\nbackground: none;\ncolor: #e00;\nfont-weight: 700;\npadding: 3px;\n}\na:only-of-type > .remove {\ndisplay: none;\n}\n.remove:hover::after {\ncontent: \" Remove\";\n}\n.qr-preview > label {\nbackground: rgba(0,0,0,.5);\ncolor: #fff;\nright: 0;\nbottom: 0;\nleft: 0;\nposition: absolute;\ntext-align: center;\n}\n.qr-preview > label > input {\nmargin: 0;\n}\n#add-post {\ncursor: pointer;\nfont-size: 2em;\nposition: absolute;\ntop: 50%;\nright: 10px;\n-moz-transform: translateY(-50%);\n}\n.textarea {\nposition: relative;\n}\n:root.webkit .textarea {\nmargin-bottom: -2px;\n}\n#char-count {\ncolor: #000;\nbackground: hsla(0, 0%, 100%, .5);\nfont-size: 8pt;\nposition: absolute;\nbottom: 1px;\nright: 1px;\npointer-events: none;\n}\n\n/* Menu */\n.menu-button {\ndisplay: inline-block;\nposition: relative;\ncursor: pointer;\n}\n.menu-button i {\nborder-top: 6px solid;\nborder-right: 4px solid transparent;\nborder-left: 4px solid transparent;\ndisplay: inline-block;\nmargin: 2px;\nvertical-align: middle;\n}\n#menu {\nposition: fixed;\noutline: none;\n}\n.entry {\nborder-bottom: 1px solid rgba(0,0,0,.25);\ncursor: pointer;\ndisplay: block;\noutline: none;\npadding: 3px 7px;\nposition: relative;\ntext-decoration: none;\nwhite-space: nowrap;\n}\n.left>.entry.has-submenu {\npadding-right: 17px !important;\n}\n.entry:last-child {\nborder-bottom: 0;\n}\n.has-submenu::after {\ncontent: \"\";\nborder-left: .5em solid;\nborder-top: .3em solid transparent;\nborder-bottom: .3em solid transparent;\ndisplay: inline-block;\nmargin: .3em;\nposition: absolute;\nright: 3px;\n}\n.left .has-submenu::after {\nborder-left: 0;\nborder-right: .5em solid;\n}\n.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\n}\n.imp-exp-result {\nposition: absolute;\ntext-align: center;\nmargin: auto;\nright: 0px;\nleft: 0px;\nwidth: 200px;\n}\n.export, .import {\ncursor: pointer;\ntext-decoration: none !important;\n}\n/* Link Title Favicons */\n.linkify.YouTube {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vimeo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.SoundCloud {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.audio {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.LiveLeak {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vocaroo {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.pastebin {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.gist {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.image {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.InstallGentoo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n\n/* General */\n:root.yotsuba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.yotsuba #header-bar, :root.yotsuba #notifications {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar a, :root.yotsuba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.yotsuba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.yotsuba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.yotsuba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba #menu {\ncolor: #800000;\n}\n:root.yotsuba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 10pt;\n}\n:root.yotsuba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.yotsuba-b .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.yotsuba-b #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.yotsuba-b .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.yotsuba-b .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba-b #menu {\ncolor: #000;\n}\n:root.yotsuba-b .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 10pt;\n}\n:root.yotsuba-b .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba-b .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.futaba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.futaba #header-bar, :root.futaba #notifications {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar a, :root.futaba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.futaba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.futaba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.futaba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.futaba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.futaba #menu {\ncolor: #800000;\n}\n:root.futaba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 12pt;\n}\n:root.futaba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.futaba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.burichan .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.burichan #header-bar, :root.burichan #header-bar #notifications {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.burichan #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.burichan .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.burichan .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.burichan #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.burichan #menu {\ncolor: #000000;\n}\n:root.burichan .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 12pt;\n}\n:root.burichan .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.burichan .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n\n/* Header */\n:root.tomorrow #header-bar, :root.tomorrow #notifications {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\ncolor: #81A2BE;\n}\n\n/* Settings */\n:root.tomorrow #fourchanx-settings fieldset {\nborder-color: #111;\n}\n\n/* Quote */\n:root.tomorrow .backlink.deadlink {\ncolor: #81A2BE !important;\n}\n:root.tomorrow .inline {\nborder-color: #111;\nbackground-color: rgba(0, 0, 0, .14);\n}\n\n/* QR */\n.tomorrow #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\n}\n:root.tomorrow #qr .field {\nbackground-color: rgb(26, 27, 29);\ncolor: rgb(197,200,198);\nborder-color: rgb(40, 41, 42);\n}\n:root.tomorrow #qr .field:focus {\nborder-color: rgb(129, 162, 190) !important;\nbackground-color: rgb(30,32,36);\n}\n\n/* Menu */\n:root.tomorrow #menu {\ncolor: #C5C8C6;\n}\n:root.tomorrow .entry {\nborder-bottom: 1px solid #111;\nfont-size: 10pt;\n}\n:root.tomorrow .focused.entry {\nbackground: rgba(0, 0, 0, .33);\n}\n\n/* Watcher Favicon */\n:root.tomorrow .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.photon .dialog {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.photon #header-bar, :root.photon #notifications {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar a, :root.photon #notifications a {\ncolor: #FF6600;\n}\n\n/* Settings */\n:root.photon #fourchanx-settings fieldset {\nborder-color: #CCC;\n}\n\n/* Quote */\n:root.photon .backlink.deadlink {\ncolor: #F60 !important;\n}\n:root.photon .inline {\nborder-color: #CCC;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.photon #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.photon #menu {\ncolor: #333;\n}\n:root.photon .entry {\nborder-bottom: 1px solid #CCC;\nfont-size: 10pt;\n}\n:root.photon .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.photon .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n" + css: "/* General */\n.dialog {\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder: 1px solid;\ndisplay: block;\npadding: 0;\n}\n.captcha-img,\n.field {\nbackground-color: #FFF;\nborder: 1px solid #CCC;\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\ncolor: #333;\nfont: 13px sans-serif;\noutline: none;\ntransition: color .25s, border-color .25s;\ntransition: color .25s, border-color .25s;\n}\n.field::-moz-placeholder,\n.field:hover::-moz-placeholder {\ncolor: #AAA !important;\nfont-size: 13px !important;\nopacity: 1.0 !important;\n}\n.captch-img:hover,\n.field:hover {\nborder-color: #999;\n}\n.field:hover, .field:focus {\ncolor: #000;\n}\n.field[disabled] {\nbackground-color: #F2F2F2;\ncolor: #888;\n}\n.move {\ncursor: move;\noverflow: hidden;\n}\nlabel,\n.watcher-toggler {\ncursor: pointer;\n}\na[href=\"javascript:;\"] {\ntext-decoration: none;\n}\n.warning {\ncolor: red;\n}\n#boardNavDesktop {\ndisplay: none !important;\n}\na {\noutline: none !important;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#navlinks, .fixed #header-bar,\n:root.float #updater,\n:root.float #thread-stats,\n#qr {\nposition: fixed;\n}\n#fourchanx-settings {\nz-index: 999;\n}\n#overlay {\nz-index: 900;\n}\n#notifications {\nz-index: 70;\n}\n#qp, #ihover {\nz-index: 60;\n}\n#menu {\nz-index: 50;\n}\n#navlinks, #updater, #thread-stats {\nz-index: 40;\n}\n.fixed #header-bar.autohide {\nz-index: 35;\n}\n#qr {\nz-index: 30;\n}\n#thread-watcher {\nz-index: 8;\n}\n:root.fixed-watcher #thread-watcher {\nz-index: 20;\n}\n.fixed #header-bar {\nz-index: 10;\n}\n/* Header */\n.fixed.top body {\npadding-top: 2em;\n}\n.fixed.bottom body {\npadding-bottom: 2em;\n}\n.fixed #header-bar {\nright: 0;\nleft: 0;\npadding: 3px 4px 4px;\n}\n.fixed.top #header-bar {\ntop: 0;\n}\n.fixed.bottom #header-bar {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\ntransition: all .1s .05s ease-in-out;\n}\n:root.centered-links #shortcuts {\nwidth: 300px;\ntext-align: right;\n}\n:root.centered-links #header-bar {\ntext-align: center;\n}\n:root.centered-links #custom-board-list {\nposition: relative;\nleft: 150px;\n}\n.fixed.top #header-bar {\nborder-bottom-width: 1px;\n}\n.fixed.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n.fixed.bottom #header-bar .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n.fixed #header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n.fixed.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#scroll-marker {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n:root:not(.autohide) #scroll-marker {\npointer-events: none;\n}\n#header-bar #scroll-marker {\ndisplay: none;\n}\n.fixed #header-bar #scroll-marker {\ndisplay: block;\n}\n.fixed.top #header-bar #scroll-marker {\ntop: 100%;\n}\n.fixed.bottom #header-bar #scroll-marker {\nbottom: 100%;\n}\n#header-bar a:not(.entry):not(.close) {\ntext-decoration: none;\npadding: 1px;\n}\n#header-bar input {\nmargin: 0;\nvertical-align: bottom;\n}\n#shortcuts:empty {\ndisplay: none;\n}\n.brackets-wrap::before {\ncontent: \"\\00a0[\";\n}\n.brackets-wrap::after {\ncontent: \"]\\00a0\";\n}\n.dead-thread,\n.disabled,\n.expand-all-shortcut {\nopacity: .45;\n}\n#shortcuts {\nfloat: right;\n}\n.shortcut {\nmargin-left: 3px;\n}\n#navbotright,\n#navtopright {\ndisplay: none;\n}\n#toggleMsgBtn {\ndisplay: none !important;\n}\n.current {\nfont-weight: bold;\n}\n/* 4chan X link brackets */\n.fourchanx-link::after {\ncontent: \"]\";\n}\n.fourchanx-link::before {\ncontent: \"[\";\n}\n/* Notifications */\n#notifications {\nposition: fixed;\ntop: 0;\nheight: 0;\ntext-align: center;\nright: 0;\nleft: 0;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar #notifications {\nposition: absolute;\ntop: 100%;\n}\n.notification {\ncolor: #FFF;\nfont-weight: 700;\ntext-shadow: 0 1px 2px rgba(0, 0, 0, .5);\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder-radius: 2px;\nmargin: 1px auto;\nwidth: 500px;\nmax-width: 100%;\nposition: relative;\ntransition: all .25s ease-in-out;\n}\n.notification.error {\nbackground-color: hsla(0, 100%, 38%, .9);\n}\n.notification.warning {\nbackground-color: hsla(36, 100%, 38%, .9);\n}\n.notification.info {\nbackground-color: hsla(200, 100%, 38%, .9);\n}\n.notification.success {\nbackground-color: hsla(104, 100%, 38%, .9);\n}\n.notification a {\ncolor: white;\n}\n.notification > .close {\npadding: 6px;\ntop: 0;\nright: 5px;\nposition: absolute;\n}\n.message {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\npadding: 6px 20px;\nmax-height: 200px;\nwidth: 100%;\noverflow: auto;\n}\n\n/* Settings */\n:root.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#overlay {\nbackground-color: rgba(0, 0, 0, .5);\ntop: 0;\nleft: 0;\nheight: 100%;\nwidth: 100%;\n}\n#fourchanx-settings {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nbox-shadow: 0 0 15px rgba(0, 0, 0, .15);\nheight: 600px;\nmax-height: 100%;\nwidth: 900px;\nmax-width: 100%;\nmargin: auto;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\nheight: 15px;\n}\n#fourchanx-settings > nav a {\ntext-decoration: underline;\n}\n#fourchanx-settings > nav a.close {\ntext-decoration: none;\npadding: 2px;\n}\n.section-container {\noverflow: auto;\nposition: absolute;\ntop: 2.1em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\npadding-right: 5px;\n}\n.sections-list {\npadding: 0 3px;\nfloat: left;\n}\n.credits {\nfloat: right;\n}\n.tab-selected {\nfont-weight: 700;\n}\n.section-sauce ul,\n.section-advanced ul {\nlist-style: none;\nmargin: 0;\n}\n.section-sauce ul {\npadding: 8px;\n}\n.section-advanced ul {\npadding: 0px;\n}\n.section-sauce li,\n.section-advanced li {\npadding-left: 4px;\n}\n.section-main label {\ntext-decoration: underline;\n}\n.section-filter ul {\npadding: 0;\n}\n.section-filter li {\nmargin: 10px 40px;\n}\n.section-filter textarea {\nheight: 500px;\n}\n.section-sauce textarea {\nheight: 350px;\n}\n.section-advanced .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-advanced textarea {\nheight: 150px;\n}\n.section-advanced .archive-cell {\nmin-width: 160px;\ntext-align: center;\n}\n.section-advanced #archive-board-select {\nposition: absolute;\n}\n.section-advanced .note {\nfont-size: 0.8em;\nfont-style: italic;\nmargin-left: 10px;\n}\n.section-advanced .note code {\nfont-style: normal;\nfont-size: 11px;\n}\n.section-keybinds .field {\nfont-family: monospace;\n} \n#fourchanx-settings fieldset {\nborder: 1px solid;\nborder-radius: 3px;\n}\n#fourchanx-settings legend {\nfont-weight: 700;\n}\n#fourchanx-settings textarea {\nfont-family: monospace;\nmin-width: 100%;\nmax-width: 100%;\n}\n#fourchanx-settings code {\ncolor: #000;\nbackground-color: #FFF;\npadding: 0 2px;\n}\n.unscroll {\noverflow: hidden;\n}\n\n/* Announcement Hiding */\n:root.hide-announcement #globalMessage {\ndisplay: none;\n}\na.hide-announcement {\nfloat: left;\n}\n\n/* Unread */\n#unread-line {\nmargin: 0;\nborder-color: rgb(255,0,0);\n}\n\n/* Thread Updater */\n#updater {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 5px 3px 0px;\nmargin-bottom: -3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n:root.float #updater {\npadding: 0px 3px;\n}\n.new {\ncolor: limegreen;\n}\n#update-status.new {\nmargin-right: 5px;\n}\n#update-timer {\ncursor: pointer;\n}\n\n/* Thread Watcher */\n#thread-watcher {\nposition: absolute;\n}\n#thread-watcher {\npadding-bottom: 3px;\npadding-left: 3px;\noverflow: hidden;\nwhite-space: nowrap;\nmin-width: 136px;\nmax-height: 92%;\noverflow-y: auto;\n}\n#thread-watcher .menu-button {\nbottom: 1px;\n}\n:root.fixed-watcher #thread-watcher {\nposition: fixed;\n}\n:root:not(.fixed-watcher) #thread-watcher:not(:hover) {\nmax-height: 210px;\noverflow-y: hidden;\n}\n#thread-watcher > .move {\npadding-top: 3px;\n}\n#watched-threads > div {\nmax-width: 250px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#thread-watcher a {\ntext-decoration: none;\n}\n#thread-watcher .move>.close {\nposition: absolute;\nright: 0px;\ntop: 0px;\npadding: 0px 4px;\n}\n.watcher-toggler {\npadding-top: 18px;\nwidth: 18px;\nheight: 0px;\ndisplay: inline-block;\nbackground-repeat: no-repeat;\nopacity: 0.2;\nposition: relative;\ntop: 1px;\n}\n.watcher-toggler.watched {\nopacity: 1;\n}\n\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n:root.float #post-count, :root.float #file-count {\npointer-events: none;\n}\n:root.float #thread-stats {\npadding: 0px 3px;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink),\n.quotelink.deadlink:not(.forwardlink) {\ntext-decoration: underline !important;\n}\n.inlined {\nopacity: .5;\n}\n#qp input, .forwarded {\ndisplay: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\ntext-decoration: none;\nborder-bottom: 1px dashed;\n}\n.filtered {\ntext-decoration: underline line-through;\n}\n:root.hide-backlinks .backlink.filtered {\ndisplay: none;\n}\n.inline {\nborder: 1px solid;\ndisplay: table;\nmargin: 2px 0;\n}\n.inline .post {\nborder: 0 !important;\nbackground-color: transparent !important;\ndisplay: table !important;\nmargin: 0 !important;\npadding: 1px 2px !important;\n}\n#qp > .opContainer::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n#qp .post {\nborder: none;\nmargin: 0;\npadding: 2px 2px 5px;\n}\n#qp img {\nmax-height: 80vh;\nmax-width: 50vw;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\n}\n:root.highlight-own .yourPost > .reply,\n:root.highlight-you .quotesYou > .reply {\nborder-left: 2px solid rgba(221,0,0,.5);\n}\n/* Quote Threading */\n.threadContainer {\nmargin-left: 20px;\nborder-left: 1px solid rgba(128,128,128,.3);\n}\n.threadOP {\nclear: both;\n} \n\n/* File */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull,\n.expanded-image > .post > .file > .fileThumb > img[data-md5],\n:not(.expanded-image) > .post > .file > .fileThumb > .full-image {\ndisplay: none;\n}\n.expanding {\nopacity: .5;\n}\n:root.fit-height .full-image {\nmax-height: 100vh;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image {\nwidth: 100%;\n}\n#ihover {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nmax-height: 100%;\nmax-width: 75%;\npadding-bottom: 16px;\n}\n.fappeTyme .thread > .noFile,\n.fappeTyme .threadContainer > .noFile {\ndisplay: none;\n}\n\n/* Index/Reply Navigation */\n#navlinks {\nfont-size: 16px;\ntop: 25px;\nright: 10px;\n}\n\n/* Filter */\n.opContainer.filter-highlight {\nbox-shadow: inset 5px 0 rgba(255, 0, 0, .5);\n}\n.filter-highlight > .reply {\nbox-shadow: -5px 0 rgba(255, 0, 0, .5);\n}\n\n/* Spoiler text */\n:root.reveal-spoilers s {\ncolor: white !important;\n}\n\n/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ * {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n:root.hide-original-post-form #postForm,\n:root.hide-original-post-form .postingMode,\n:root.hide-original-post-form #togglePostForm,\n#qr.autohide:not(.has-focus):not(:hover) > form,\n.postingMode ~ #qr select,\n#file-n-submit:not(.has-file) #qr-filerm {\ndisplay: none;\n}\n#qr select,\n#dump-button,\n.remove,\n.captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 300px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nmargin-bottom: 1px;\n}\n#qr .close {\nfloat: right;\npadding: 0 3px;\n}\n#qr .warning {\nmin-height: 1.6em;\nvertical-align: middle;\npadding: 0 1px;\nborder-width: 1px;\nborder-style: solid;\n}\n.qr-link-container {\ntext-align: center;\n}\n.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nwidth: 10%;\nmargin: 0;\nmargin-right: 4px;\nfont: 13px sans-serif;\npadding: 1px 0px 2px;\nopacity: 0.6;\n}\n.persona .field:not(#dump) {\nwidth: 95px;\nmin-width: 33.3%;\nmax-width: 33.3%;\n}\n#qr textarea.field {\nheight: 14.8em;\nmin-height: 9em;\n}\n#qr.has-captcha textarea.field {\nheight: 9em;\n}\ninput.field.tripped:not(:hover):not(:focus) {\ncolor: transparent !important;\ntext-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 0px;\ntext-align: center;\nbackground-image: #fff;\nfont-size: 0px;\nmin-height: 59px;\nmin-width: 302px;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.captcha-input.error:focus {\nborder-color: rgb(255,0,0) !important;\n}\n.field {\n-moz-box-sizing: border-box;\nmargin: 0px;\npadding: 2px 4px 3px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\nvertical-align: top;\n}\n:root.webkit #qr [type='submit'] {\nheight: 24px;\n}\n/* Fake File Input */\n#qr-filename,\n.has-file #qr-no-file {\ndisplay: none;\n}\n#qr-no-file,\n.has-file #qr-filename {\ndisplay: inline-block;\npadding: 0px 4px;\nmargin-bottom: 2px;\noverflow: hidden;\ntext-overflow: ellipsis;\nmax-width: 88%;\n}\n#qr-no-file {\ncolor: #AAA;\n}\n#qr-filename-container {\n-moz-box-sizing: border-box;\ndisplay: inline-block;\nposition: relative;\nwidth: 100px;\nmin-width: 74.6%;\nmax-width: 74.6%;\nmargin-right: 0.4%;\nmargin-top: 1px;\noverflow: hidden;\npadding: 2px 1px 0;\nheight: 22px;\n}\n#qr-filename-container:hover {\ncursor: text;\n}\n#qr-extras-container {\nposition: absolute;\nright: 0px;\n}\n#qr-filerm {\nmargin-right: 2px;\nz-index: 2;\n}\n#file-n-submit {\nheight: 23px;\n}\n#qr input[type=file] {\nvisibility: hidden;\nposition: absolute;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\n}\n#qr.has-spoiler .has-file #qr-spoiler-label {\nwidth: 6.7%;\nmin-width: 6.7%;\nmax-width: 6.7%;\ndisplay: inline-block;\ntext-align: center;\nvertical-align: top;\n}\n#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {\ndisplay: none;\n}\n#qr.has-spoiler .has-file #qr-filename-container {\nmax-width: 67.9%;\nmin-width: 67.9%;\n}\n#qr-spoiler-label input {\nposition: relative;\ntop: 3px;\n}\n/* Dumping UI */\n.dump #dump-list-container {\ndisplay: block;\n}\n#dump-list-container {\ndisplay: none;\nposition: relative;\noverflow-y: hidden;\nmargin-top: 1px;\n}\n#dump-list {\noverflow-x: auto;\noverflow-y: hidden;\nwhite-space: nowrap;\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-list:hover {\noverflow-x: auto;\n}\n.qr-preview {\n-moz-box-sizing: border-box;\ncounter-increment: thumbnails;\ncursor: move;\ndisplay: inline-block;\nheight: 90px;\nwidth: 90px;\npadding: 2px;\nopacity: .5;\noverflow: hidden;\nposition: relative;\ntext-shadow: 0 1px 1px #000;\n-moz-transition: opacity .25s ease-in-out;\nvertical-align: top;\nbackground-size: cover;\n}\n.qr-preview:hover,\n.qr-preview:focus {\nopacity: .9;\n}\n.qr-preview::before {\ncontent: counter(thumbnails);\ncolor: #fff;\nposition: absolute;\ntop: 3px;\nright: 3px;\ntext-shadow: 0 0 3px #000, 0 0 8px #000;\n}\n.qr-preview#selected {\nopacity: 1;\n}\n.qr-preview.drag {\nbox-shadow: 0 0 10px rgba(0,0,0,.5);\n}\n.qr-preview.over {\nborder-color: #fff;\n}\n.qr-preview > span {\ncolor: #fff;\n}\n.remove {\nbackground: none;\ncolor: #e00;\nfont-weight: 700;\npadding: 3px;\n}\na:only-of-type > .remove {\ndisplay: none;\n}\n.remove:hover::after {\ncontent: \" Remove\";\n}\n.qr-preview > label {\nbackground: rgba(0,0,0,.5);\ncolor: #fff;\nright: 0;\nbottom: 0;\nleft: 0;\nposition: absolute;\ntext-align: center;\n}\n.qr-preview > label > input {\nmargin: 0;\n}\n#add-post {\ncursor: pointer;\nfont-size: 2em;\nposition: absolute;\ntop: 50%;\nright: 10px;\n-moz-transform: translateY(-50%);\n}\n.textarea {\nposition: relative;\n}\n:root.webkit .textarea {\nmargin-bottom: -2px;\n}\n#char-count {\ncolor: #000;\nbackground: hsla(0, 0%, 100%, .5);\nfont-size: 8pt;\nposition: absolute;\nbottom: 1px;\nright: 1px;\npointer-events: none;\n}\n\n/* Menu */\n.menu-button {\ndisplay: inline-block;\nposition: relative;\ncursor: pointer;\n}\n.menu-button i {\nborder-top: 6px solid;\nborder-right: 4px solid transparent;\nborder-left: 4px solid transparent;\ndisplay: inline-block;\nmargin: 2px;\nvertical-align: middle;\n}\n#menu {\nposition: fixed;\noutline: none;\n}\n.entry {\nborder-bottom: 1px solid rgba(0,0,0,.25);\ncursor: pointer;\ndisplay: block;\noutline: none;\npadding: 3px 7px;\nposition: relative;\ntext-decoration: none;\nwhite-space: nowrap;\n}\n.left>.entry.has-submenu {\npadding-right: 17px !important;\n}\n.entry:last-child {\nborder-bottom: 0;\n}\n.has-submenu::after {\ncontent: \"\";\nborder-left: .5em solid;\nborder-top: .3em solid transparent;\nborder-bottom: .3em solid transparent;\ndisplay: inline-block;\nmargin: .3em;\nposition: absolute;\nright: 3px;\n}\n.left .has-submenu::after {\nborder-left: 0;\nborder-right: .5em solid;\n}\n.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\n}\n.imp-exp-result {\nposition: absolute;\ntext-align: center;\nmargin: auto;\nright: 0px;\nleft: 0px;\nwidth: 200px;\n}\n.export, .import {\ncursor: pointer;\ntext-decoration: none !important;\n}\n/* Link Title Favicons */\n.linkify.YouTube {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vimeo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.SoundCloud {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.audio {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.LiveLeak {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vocaroo {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.pastebin {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.gist {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.image {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.InstallGentoo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n\n/* General */\n:root.yotsuba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.yotsuba #header-bar, :root.yotsuba #notifications {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar a, :root.yotsuba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.yotsuba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.yotsuba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.yotsuba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba #menu {\ncolor: #800000;\n}\n:root.yotsuba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 10pt;\n}\n:root.yotsuba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.yotsuba-b .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.yotsuba-b #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.yotsuba-b .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.yotsuba-b .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba-b #menu {\ncolor: #000;\n}\n:root.yotsuba-b .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 10pt;\n}\n:root.yotsuba-b .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba-b .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.futaba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.futaba #header-bar, :root.futaba #notifications {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar a, :root.futaba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.futaba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.futaba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.futaba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.futaba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.futaba #menu {\ncolor: #800000;\n}\n:root.futaba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 12pt;\n}\n:root.futaba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.futaba .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.burichan .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.burichan #header-bar, :root.burichan #header-bar #notifications {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.burichan #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.burichan .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.burichan .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.burichan #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.burichan #menu {\ncolor: #000000;\n}\n:root.burichan .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 12pt;\n}\n:root.burichan .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.burichan .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n\n/* Header */\n:root.tomorrow #header-bar, :root.tomorrow #notifications {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\ncolor: #81A2BE;\n}\n\n/* Settings */\n:root.tomorrow #fourchanx-settings fieldset {\nborder-color: #111;\n}\n\n/* Quote */\n:root.tomorrow .backlink.deadlink {\ncolor: #81A2BE !important;\n}\n:root.tomorrow .inline {\nborder-color: #111;\nbackground-color: rgba(0, 0, 0, .14);\n}\n\n/* QR */\n.tomorrow #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\n}\n:root.tomorrow #qr .field {\nbackground-color: rgb(26, 27, 29);\ncolor: rgb(197,200,198);\nborder-color: rgb(40, 41, 42);\n}\n:root.tomorrow #qr .field:focus {\nborder-color: rgb(129, 162, 190) !important;\nbackground-color: rgb(30,32,36);\n}\n\n/* Menu */\n:root.tomorrow #menu {\ncolor: #C5C8C6;\n}\n:root.tomorrow .entry {\nborder-bottom: 1px solid #111;\nfont-size: 10pt;\n}\n:root.tomorrow .focused.entry {\nbackground: rgba(0, 0, 0, .33);\n}\n\n/* Watcher Favicon */\n:root.tomorrow .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.photon .dialog {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.photon #header-bar, :root.photon #notifications {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar a, :root.photon #notifications a {\ncolor: #FF6600;\n}\n\n/* Settings */\n:root.photon #fourchanx-settings fieldset {\nborder-color: #CCC;\n}\n\n/* Quote */\n:root.photon .backlink.deadlink {\ncolor: #F60 !important;\n}\n:root.photon .inline {\nborder-color: #CCC;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.photon #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.photon #menu {\ncolor: #333;\n}\n:root.photon .entry {\nborder-bottom: 1px solid #CCC;\nfont-size: 10pt;\n}\n:root.photon .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.photon .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n" }; Main.init(); diff --git a/builds/crx/script.js b/builds/crx/script.js index 517371033..f046693a8 100755 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.27 - 2013-08-12 +* 4chan X - Version 1.2.27 - 2013-08-13 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -170,9 +170,7 @@ 'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'], '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.'], - 'Toggleable Thread Watcher': [true, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'], - 'Auto Watch': [true, 'Automatically watch threads you start.'], - 'Auto Watch Reply': [false, 'Automatically watch threads you reply to.'] + 'Toggleable Thread Watcher': [true, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'] }, 'Posting': { 'Quick Reply': [true, 'All-in-one form to reply, create threads, automate dumping and more.'], @@ -212,6 +210,12 @@ 'Expand from here': [false, 'Expand all images only from current position to thread end.'], 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.'] }, + threadWatcher: { + 'Current Board': [false, 'Only show watched threads from the current board.'], + 'Auto Watch': [true, 'Automatically watch threads you start.'], + 'Auto Watch Reply': [false, 'Automatically watch threads you reply to.'], + 'Auto Prune': [false, 'Automatically prune 404\'d threads.'] + }, filter: { name: "# Filter any namefags:\n#/^(?!Anonymous$)/", uniqueID: "# Filter a specific ID:\n#/Txhvk1Tl/", @@ -399,25 +403,30 @@ } }; - $.ajax = function(url, options, extra) { - var form, headers, key, r, sync, type, upCallbacks, val; - if (extra == null) { - extra = {}; - } - type = extra.type, headers = extra.headers, upCallbacks = extra.upCallbacks, form = extra.form, sync = extra.sync; - r = new XMLHttpRequest(); - r.overrideMimeType('text/html'); - type || (type = form && 'post' || 'get'); - r.open(type, url, !sync); - for (key in headers) { - val = headers[key]; - r.setRequestHeader(key, val); - } - $.extend(r, options); - $.extend(r.upload, upCallbacks); - r.send(form); - return r; - }; + $.ajax = (function() { + var lastModified; + lastModified = {}; + return function(url, options, extra) { + var form, r, sync, type, upCallbacks, whenModified; + if (extra == null) { + extra = {}; + } + type = extra.type, whenModified = extra.whenModified, upCallbacks = extra.upCallbacks, form = extra.form, sync = extra.sync; + r = new XMLHttpRequest(); + type || (type = form && 'post' || 'get'); + r.open(type, url, !sync); + if (whenModified) { + r.setRequestHeader('If-Modified-Since', lastModified[url] || '0'); + $.on(r, 'load', function() { + return lastModified[url] = r.getResponseHeader('Last-Modified'); + }); + } + $.extend(r, options); + $.extend(r.upload, upCallbacks); + r.send(form); + return r; + }; + })(); $.cache = (function() { var reqs; @@ -1174,16 +1183,18 @@ })(Post); - DataBoards = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts']; + DataBoards = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']; DataBoard = (function() { - function DataBoard(key, sync) { + function DataBoard(key, sync, dontClean) { var init, _this = this; this.key = key; this.data = Conf[key]; $.sync(key, this.onSync.bind(this)); - this.clean(); + if (!dontClean) { + this.clean(); + } if (!sync) { return; } @@ -1194,6 +1205,10 @@ $.on(d, '4chanXInitFinished', init); } + DataBoard.prototype.save = function() { + return $.set(this.key, this.data); + }; + DataBoard.prototype["delete"] = function(_arg) { var boardID, postID, threadID; boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID; @@ -1211,7 +1226,7 @@ } else { delete this.data.boards[boardID]; } - return $.set(this.key, this.data); + return this.save(); }; DataBoard.prototype.deleteIfEmpty = function(_arg) { @@ -1239,7 +1254,7 @@ } else { this.data.boards[boardID] = val; } - return $.set(this.key, this.data); + return this.save(); }; DataBoard.prototype.get = function(_arg) { @@ -1281,7 +1296,7 @@ this.ajaxClean(boardID); } } - return $.set(this.key, this.data); + return this.save(); }; DataBoard.prototype.ajaxClean = function(boardID) { @@ -1309,7 +1324,7 @@ boardID: boardID }); } - return $.set(_this.key, _this.data); + return _this.save(); }); }; @@ -1370,8 +1385,24 @@ Polyfill = { init: function() { + Polyfill.toBlob(); return Polyfill.visibility(); }, + toBlob: function() { + var _base; + return (_base = HTMLCanvasElement.prototype).toBlob || (_base.toBlob = function(cb) { + var data, i, l, ui8a, _i; + data = atob(this.toDataURL().slice(22)); + l = data.length; + ui8a = new Uint8Array(l); + for (i = _i = 0; 0 <= l ? _i < l : _i > l; i = 0 <= l ? ++_i : --_i) { + ui8a[i] = data.charCodeAt(i); + } + return cb(new Blob([ui8a], { + type: 'image/png' + })); + }); + }, visibility: function() { if (!('webkitHidden' in document)) { return; @@ -1773,7 +1804,7 @@ date: data.now, dateUTC: data.time, comment: data.com, - capReps: data.capcode_replies, + capcodeReplies: data.capcode_replies, isSticky: !!data.sticky, isClosed: !!data.closed }; @@ -1801,8 +1832,8 @@ @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ - var a, array, boardID, capReps, capcode, capcodeClass, capcodeReplies, capcodeStart, capcodeType, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, generateCapcodeReplies, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; - postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, capReps = o.capReps, file = o.file; + var a, boardID, capcode, capcodeClass, capcodeReplies, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; + postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, capcodeReplies = o.capcodeReplies, file = o.file; isOP = postID === threadID; staticPath = '//static.4chan.org/image/'; if (email) { @@ -1876,32 +1907,10 @@ tripcode = tripcode ? " " + tripcode + "" : ''; sticky = isSticky ? " Sticky" : ''; closed = isClosed ? " Closed" : ''; - capcodeReplies = ''; - if (capReps) { - generateCapcodeReplies = function(capcodeType, array) { - return "" + ((function() { - switch (capcodeType) { - case 'admin': - return 'Administrator'; - case 'mod': - return 'Moderator'; - case 'developer': - return 'Developer'; - } - })()) + " Repl" + (array.length > 1 ? 'ies' : 'y') + ": " + (array.map(function(ID) { - return ">>" + ID + ""; - }).join(' ')) + "
"; - }; - for (capcodeType in capReps) { - array = capReps[capcodeType]; - capcodeReplies += generateCapcodeReplies(capcodeType, array); - } - capcodeReplies = "

" + capcodeReplies + ""; - } container = $.el('div', { id: "pc" + postID, className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", - innerHTML: "" + (isOP ? '' : "
>>
") + "
" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "
" + date + "No." + postID + "
" + (isOP ? fileHTML : '') + "
" + subject + "" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "" + date + "" + " " + "No." + postID + "
" + (isOP ? '' : fileHTML) + "
" + (comment || '') + capcodeReplies + "
" + " " + "
" + innerHTML: "" + (isOP ? '' : "
>>
") + "
" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "
" + date + "No." + postID + "
" + (isOP ? fileHTML : '') + "
" + subject + "" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "" + date + "" + " " + "No." + postID + "
" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + " " + "
" }); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -1912,7 +1921,46 @@ } quote.href = "/" + boardID + "/res/" + href; } + Build.capcodeReplies({ + boardID: boardID, + threadID: threadID, + root: container, + capcodeReplies: capcodeReplies + }); return container; + }, + capcodeReplies: function(_arg) { + var array, boardID, bq, capcodeReplies, capcodeType, generateCapcodeReplies, html, root, threadID; + boardID = _arg.boardID, threadID = _arg.threadID, bq = _arg.bq, root = _arg.root, capcodeReplies = _arg.capcodeReplies; + if (!capcodeReplies) { + return; + } + generateCapcodeReplies = function(capcodeType, array) { + return "" + ((function() { + switch (capcodeType) { + case 'admin': + return 'Administrator'; + case 'mod': + return 'Moderator'; + case 'developer': + return 'Developer'; + } + })()) + " Repl" + (array.length > 1 ? 'ies' : 'y') + ": " + (array.map(function(ID) { + return ">>" + ID + ""; + }).join(' ')) + "
"; + }; + html = []; + for (capcodeType in capcodeReplies) { + array = capcodeReplies[capcodeType]; + html.push(generateCapcodeReplies(capcodeType, array)); + } + bq || (bq = $('blockquote', root)); + return $.add(bq, [ + $.el('br'), $.el('br'), $.el('span', { + className: 'capcodeReplies', + innerHTML: html.join('') + }) + ]); } }; @@ -4226,7 +4274,7 @@ if (g.VIEW === 'catalog' || !Conf['Linkify']) { return; } - this.regString = Conf['Allow False Positives'] ? /(\b([-a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[\d]+\.[\d]+\.[\d]+\.[\d]+\/|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])[^\s'"]+)/gi : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi; + this.regString = Conf['Allow False Positives'] ? /([-a-z]+:\/\/|[a-z]{3,}\.[-a-z0-9]+\.[a-z]|[-a-z0-9]+\.[a-z]|[\d]+\.[\d]+\.[\d]+\.[\d]+\/|[a-z]{3,}:[a-z0-9?]|[^\s@]+@[a-z0-9.-]+\.[a-z0-9])/i : /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1})/i; if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } @@ -4239,7 +4287,7 @@ }); }, node: function() { - var data, el, i, items, links, node, range, snapshot, _i, _len, _ref; + var data, el, end, endNode, i, index, items, lIndex, length, link, links, node, range, result, saved, snapshot, space, test, text, _i, _len, _ref; if (this.isClone) { if (Conf['Embedding']) { i = 0; @@ -4253,16 +4301,49 @@ } return; } + test = /[^\s'"]+/g; + space = /[\s'"]/; snapshot = $.X('.//br|.//text()', this.nodes.comment); i = 0; while (node = snapshot.snapshotItem(i++)) { - if (node.parentElement.nodeName === "A") { + links = []; + data = node.data; + if (node.parentElement.nodeName === "A" || !data) { continue; } - links = []; - if (Linkify.regString.test(node.data)) { - Linkify.regString.lastIndex = 0; - Linkify.gatherLinks(snapshot, this, node, links, i); + while (result = test.exec(data)) { + index = result.index; + endNode = node; + if ((length = index + result[0].length) === data.length) { + while ((saved = snapshot.snapshotItem(i++))) { + if (saved.nodeName === 'BR') { + break; + } + endNode = saved; + length = saved.data.length; + if (end = space.exec(saved.data)) { + length = end.index; + i--; + break; + } + } + if (length === endNode.data.length) { + test.lastIndex = 0; + } + range = Linkify.makeRange(node, endNode, index, length); + if (link = Linkify.regString.exec(text = range.toString())) { + if (lIndex = link.index) { + range.setStart(node, lIndex + index); + } + links.push([range, text]); + } + break; + } else { + if (link = Linkify.regString.exec(result[0])) { + range = Linkify.makeRange(node, node, link.index, link.length); + links.push([range, link]); + } + } } _ref = links.reverse(); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -4286,57 +4367,23 @@ } } }, - gatherLinks: function(snapshot, post, node, links, i) { - var data, index, len, len2, link, match, range; - data = node.data; - len = data.length; - while ((match = Linkify.regString.exec(data))) { - index = match.index; - link = match[0]; - len2 = index + link.length; - if (len === len2) { - break; - } - range = document.createRange(); - range.setStart(node, index); - range.setEnd(node, len2); - links.push(range); - } - Linkify.regString.lastIndex = 0; - if (match) { - links.push(Linkify.seek(snapshot, post, node, links, match, i)); - } - }, - seek: function(snapshot, post, node, links, match, i) { - var data, index, link, next, range, result; - link = match[0]; + makeRange: function(startNode, endNode, startOffset, endOffset) { + var range; range = document.createRange(); - range.setStart(node, match.index); - while ((next = snapshot.snapshotItem(i++)) && next.nodeName !== 'BR') { - node = next; - data = node.data; - if (result = /[\s'"]/.exec(data)) { - index = result.index; - range.setEnd(node, index); - Linkify.regString.lastIndex = index; - Linkify.gatherLinks(snapshot, post, node, links, i); - return range; - } - } - if (range.collapsed) { - range.setEndAfter(node); - } + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); return range; }, - makeLink: function(range) { - var a, link; - link = range.toString(); - link = link.contains(':') ? link : (link.contains('@') ? 'mailto:' : 'http://') + link; + makeLink: function(_arg) { + var a, range, text; + range = _arg[0], text = _arg[1]; + text; + text = text.contains(':') ? text : (text.contains('@') ? 'mailto:' : 'http://') + text; a = $.el('a', { className: 'linkify', rel: 'nofollow noreferrer', target: '_blank', - href: link + href: text }); $.add(a, range.extractContents()); range.insertNode(a); @@ -5407,7 +5454,7 @@ _this = this; img = $.el('img'); img.onload = function() { - var applyBlob, cv, data, height, i, l, s, ui8a, width, _i; + var cv, height, s, width; s = 90 * 2; if (_this.file.type === 'image/gif') { s *= 3; @@ -5430,23 +5477,10 @@ cv.width = img.width = width; cv.getContext('2d').drawImage(img, 0, 0, width, height); URL.revokeObjectURL(fileURL); - applyBlob = function(blob) { + return cv.toBlob(function(blob) { _this.URL = URL.createObjectURL(blob); return _this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")"; - }; - if (cv.toBlob) { - cv.toBlob(applyBlob); - return; - } - data = atob(cv.toDataURL().split(',')[1]); - l = data.length; - ui8a = new Uint8Array(l); - for (i = _i = 0; 0 <= l ? _i < l : _i > l; i = 0 <= l ? ++_i : --_i) { - ui8a[i] = data.charCodeAt(i); - } - return applyBlob(new Blob([ui8a], { - type: 'image/png' - })); + }); }; fileURL = URL.createObjectURL(this.file); return img.src = fileURL; @@ -6257,7 +6291,7 @@ }, menu: { init: function() { - var conf, createSubEntry, el, key, subEntries, _ref; + var conf, createSubEntry, el, name, subEntries, _ref; if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { return; } @@ -6268,9 +6302,9 @@ createSubEntry = ImageExpand.menu.createSubEntry; subEntries = []; _ref = Config.imageExpansion; - for (key in _ref) { - conf = _ref[key]; - subEntries.push(createSubEntry(key, conf)); + for (name in _ref) { + conf = _ref[name]; + subEntries.push(createSubEntry(name, conf[1])); } return $.event('AddMenuEntry', { type: 'header', @@ -6279,21 +6313,19 @@ subEntries: subEntries }); }, - createSubEntry: function(type, config) { + createSubEntry: function(name, desc) { var input, label; label = $.el('label', { - innerHTML: " " + type + innerHTML: " " + name, + title: desc }); input = label.firstElementChild; - if (type === 'Fit width' || type === 'Fit height') { + if (name === 'Fit width' || name === 'Fit height') { $.on(input, 'change', ImageExpand.cb.setFitness); } - if (config) { - label.title = config[1]; - input.checked = Conf[type]; - $.event('change', null, input); - $.on(input, 'change', $.cb.checked); - } + input.checked = Conf[name]; + $.event('change', null, input); + $.on(input, 'change', $.cb.checked); return { el: label }; @@ -6939,7 +6971,6 @@ this.postCountEl = $('#post-count', sc); this.fileCountEl = $('#file-count', sc); this.pageCountEl = $('#page-count', sc); - this.lastModified = '0'; return Thread.prototype.callbacks.push({ name: 'Thread Stats', cb: this.node @@ -6991,18 +7022,12 @@ return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", { onload: ThreadStats.onThreadsLoad }, { - headers: { - 'If-Modified-Since': ThreadStats.lastModified - } + whenModified: true }); }, onThreadsLoad: function() { var page, pages, thread, _i, _j, _len, _len1, _ref; - if (!Conf["Page Count in Stats"]) { - return; - } - ThreadStats.lastModified = this.getResponseHeader('Last-Modified'); - if (this.status !== 200) { + if (!(Conf["Page Count in Stats"] && this.status === 200)) { return; } pages = JSON.parse(this.response); @@ -7096,7 +7121,6 @@ ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; ThreadUpdater.outdateCount = 0; - ThreadUpdater.lastModified = '0'; ThreadUpdater.cb.interval.call($.el('input', { value: Conf['Interval'] })); @@ -7189,10 +7213,7 @@ case 200: g.DEAD = false; ThreadUpdater.parse(JSON.parse(req.response).posts); - ThreadUpdater.lastModified = req.getResponseHeader('Last-Modified'); - if (Conf['Auto Update']) { - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); - } + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); break; case 404: g.DEAD = true; @@ -7206,16 +7227,8 @@ }); break; default: - if (Conf['Auto Update']) { - ThreadUpdater.outdateCount++; - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); - } - /* - Status Code 304: Not modified - By sending the `If-Modified-Since` header we get a proper status code, and no response. - This saves bandwidth for both the user and the servers and avoid unnecessary computation. - */ - + ThreadUpdater.outdateCount++; + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); _ref = req.status === 304 ? [null, null] : ["" + req.statusText + " (" + req.status + ")", 'warning'], text = _ref[0], klass = _ref[1]; ThreadUpdater.set('status', text, klass); } @@ -7283,9 +7296,7 @@ return ThreadUpdater.req = $.ajax(url, { onloadend: ThreadUpdater.cb.load }, { - headers: { - 'If-Modified-Since': ThreadUpdater.lastModified - } + whenModified: true }); }, updateThreadStatus: function(title, OP) { @@ -7420,7 +7431,7 @@ ThreadWatcher = { init: function() { - var sc; + var now, sc; if (!Conf['Thread Watcher']) { return; } @@ -7430,21 +7441,46 @@ href: 'javascript:;', className: 'disabled' }); - this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '
Thread Watcher×
'); + this.db = new DataBoard('watchedThreads', this.refresh, true); + this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', "
Thread Watcher ×
"); + this.status = $('#watcher-status', this.dialog); + this.list = this.dialog.lastElementChild; $.on(d, 'QRPostSuccessful', this.cb.post); - $.sync('WatchedThreads', this.refresh); + if (g.VIEW === 'thread') { + $.on(d, 'ThreadUpdate', this.cb.threadUpdate); + } $.on(sc, 'click', this.toggleWatcher); $.on($('.move>.close', ThreadWatcher.dialog), 'click', this.toggleWatcher); + $.on(d, '4chanXInitFinished', this.ready); if (Conf['Toggleable Thread Watcher']) { Header.addShortcut(sc); $.addClass(doc, 'fixed-watcher'); } - $.ready(function() { - ThreadWatcher.refresh(); - $.add(d.body, ThreadWatcher.dialog); - if (Conf['Toggleable Thread Watcher']) { - return ThreadWatcher.dialog.hidden = true; + now = Date.now(); + if ((this.db.data.lastChecked || 0) < now - 2 * $.HOUR) { + this.db.data.lastChecked = now; + ThreadWatcher.fetchAllStatus(); + this.db.save(); + } + $.get('WatchedThreads', null, function(_arg) { + var WatchedThreads, boardID, data, threadID, threads, _ref; + WatchedThreads = _arg.WatchedThreads; + if (!WatchedThreads) { + return; } + _ref = ThreadWatcher.convert(WatchedThreads); + for (boardID in _ref) { + threads = _ref[boardID]; + for (threadID in threads) { + data = threads[threadID]; + ThreadWatcher.db.set({ + boardID: boardID, + threadID: threadID, + val: data + }); + } + } + return $["delete"]('WatchedThreads'); }); return Thread.prototype.callbacks.push({ name: 'Thread Watcher', @@ -7452,77 +7488,86 @@ }); }, node: function() { - var favicon, - _this = this; - favicon = $.el('a', { - className: 'watch-thread-link', - href: 'javascript:;' + var toggler; + toggler = $.el('img', { + className: 'watcher-toggler' }); - $.on(favicon, 'click', ThreadWatcher.cb.toggle); - $.before($('input', this.OP.nodes.post), favicon); - if (g.VIEW !== 'thread') { + $.on(toggler, 'click', ThreadWatcher.cb.toggle); + return $.before($('input', this.OP.nodes.post), toggler); + }, + ready: function() { + $.off(d, '4chanXInitFinished', ThreadWatcher.ready); + if (!Main.isThisPageLegit()) { return; } - return $.get('AutoWatch', 0, function(item) { - if (item['AutoWatch'] !== _this.ID) { + ThreadWatcher.refresh(); + $.add(d.body, ThreadWatcher.dialog); + if (Conf['Toggleable Thread Watcher']) { + ThreadWatcher.dialog.hidden = true; + } + if (!Conf['Auto Watch']) { + return; + } + return $.get('AutoWatch', 0, function(_arg) { + var AutoWatch, thread; + AutoWatch = _arg.AutoWatch; + if (!(thread = g.BOARD.threads[AutoWatch])) { return; } - ThreadWatcher.watch(_this); + ThreadWatcher.add(thread); return $["delete"]('AutoWatch'); }); }, - refresh: function(watched) { - var ID, board, div, favicon, id, link, nodes, props, thread, x, _ref, _ref1; - if (!watched) { - $.get('WatchedThreads', {}, function(item) { - return ThreadWatcher.refresh(item['WatchedThreads']); - }); - return; - } - nodes = [$('.move', ThreadWatcher.dialog)]; - for (board in watched) { - _ref = watched[board]; - for (id in _ref) { - props = _ref[id]; - x = $.el('a', { - textContent: '×', - className: 'close', - href: 'javascript:;' - }); - $.on(x, 'click', ThreadWatcher.cb.x); - link = $.el('a', props); - link.title = link.textContent; - div = $.el('div'); - $.add(div, [x, $.tn(' '), link]); - nodes.push(div); - } - } - $.rmAll(ThreadWatcher.dialog); - $.add(ThreadWatcher.dialog, nodes); - watched = watched[g.BOARD] || {}; - _ref1 = g.BOARD.threads; - for (ID in _ref1) { - thread = _ref1[ID]; - favicon = $('.watch-thread-link', thread.OP.nodes.post); - if (ID in watched) { - $.addClass(favicon, 'watched'); - } else { - $.rmClass(favicon, 'watched'); - } - } - }, toggleWatcher: function() { $.toggleClass(ThreadWatcher.shortcut, 'disabled'); return ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden; }, cb: { + openAll: function() { + var a, _i, _len, _ref; + if ($.hasClass(this, 'disabled')) { + return; + } + _ref = $$('a[title]', ThreadWatcher.list); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + a = _ref[_i]; + $.open(a.href); + } + return $.event('CloseMenu'); + }, + checkThreads: function() { + if ($.hasClass(this, 'disabled')) { + return; + } + return ThreadWatcher.fetchAllStatus(); + }, + pruneDeads: function() { + var boardID, data, threadID, _i, _len, _ref, _ref1; + if ($.hasClass(this, 'disabled')) { + return; + } + _ref = ThreadWatcher.getAll(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + _ref1 = _ref[_i], boardID = _ref1.boardID, threadID = _ref1.threadID, data = _ref1.data; + if (!data.isDead) { + continue; + } + delete ThreadWatcher.db.data.boards[boardID][threadID]; + ThreadWatcher.db.deleteIfEmpty({ + boardID: boardID + }); + } + ThreadWatcher.db.save(); + ThreadWatcher.refresh(); + return $.event('CloseMenu'); + }, toggle: function() { return ThreadWatcher.toggle(Get.postFromNode(this).thread); }, - x: function() { - var thread; - thread = this.nextElementSibling.pathname.split('/'); - return ThreadWatcher.unwatch(thread[1], thread[3]); + rm: function() { + var boardID, threadID, _ref; + _ref = this.parentNode.dataset.fullID.split('.'), boardID = _ref[0], threadID = _ref[1]; + return ThreadWatcher.rm(boardID, +threadID); }, post: function(e) { var board, postID, threadID, _ref; @@ -7532,41 +7577,332 @@ return $.set('AutoWatch', threadID); } } else if (Conf['Auto Watch Reply']) { - return ThreadWatcher.watch(board.threads[threadID]); + return ThreadWatcher.add(board.threads[threadID]); } + }, + threadUpdate: function(e) { + var thread; + thread = e.detail.thread; + if (!(e.detail[404] && ThreadWatcher.db.get({ + boardID: thread.board.ID, + threadID: thread.ID + }))) { + return; + } + return ThreadWatcher.add(thread); + } + }, + fetchCount: { + fetched: 0, + fetching: 0 + }, + fetchAllStatus: function() { + var thread, _i, _len, _ref; + ThreadWatcher.status.textContent = '...'; + _ref = ThreadWatcher.getAll(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + thread = _ref[_i]; + ThreadWatcher.fetchStatus(thread); + } + }, + fetchStatus: function(_arg) { + var boardID, data, fetchCount, threadID; + boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data; + if (data.isDead) { + return; + } + fetchCount = ThreadWatcher.fetchCount; + fetchCount.fetching++; + return $.ajax("//api.4chan.org/" + boardID + "/res/" + threadID + ".json", { + onloadend: function() { + var status; + fetchCount.fetched++; + if (fetchCount.fetched === fetchCount.fetching) { + fetchCount.fetched = 0; + fetchCount.fetching = 0; + status = ''; + } else { + status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%"; + } + ThreadWatcher.status.textContent = status; + if (this.status !== 404) { + return; + } + if (Conf['Auto Prune']) { + ThreadWatcher.rm(boardID, threadID); + } else { + data.isDead = true; + ThreadWatcher.db.set({ + boardID: boardID, + threadID: threadID, + val: data + }); + } + return ThreadWatcher.refresh(); + } + }, { + type: 'head' + }); + }, + getAll: function() { + var all, boardID, data, threadID, threads, _ref; + all = []; + _ref = ThreadWatcher.db.data.boards; + for (boardID in _ref) { + threads = _ref[boardID]; + if (Conf['Current Board'] && boardID !== g.BOARD.ID) { + continue; + } + for (threadID in threads) { + data = threads[threadID]; + all.push({ + boardID: boardID, + threadID: threadID, + data: data + }); + } + } + return all; + }, + makeLine: function(boardID, threadID, data) { + var div, fullID, href, link, x; + x = $.el('a', { + textContent: '×', + href: 'javascript:;' + }); + $.on(x, 'click', ThreadWatcher.cb.rm); + if (data.isDead) { + href = Redirect.to('thread', { + boardID: boardID, + threadID: threadID + }); + } + link = $.el('a', { + href: href || ("/" + boardID + "/res/" + threadID), + textContent: data.excerpt, + title: data.excerpt + }); + div = $.el('div'); + fullID = "" + boardID + "." + threadID; + div.dataset.fullID = fullID; + if (g.VIEW === 'thread' && fullID === ("" + g.BOARD + "." + g.THREADID)) { + $.addClass(div, 'current'); + } + if (data.isDead) { + $.addClass(div, 'dead-thread'); + } + $.add(div, [x, $.tn(' '), link]); + return div; + }, + refresh: function() { + var boardID, data, list, nodes, refresher, thread, threadID, toggler, watched, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3; + nodes = []; + _ref = ThreadWatcher.getAll(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + _ref1 = _ref[_i], boardID = _ref1.boardID, threadID = _ref1.threadID, data = _ref1.data; + nodes.push(ThreadWatcher.makeLine(boardID, threadID, data)); + } + list = ThreadWatcher.list; + $.rmAll(list); + $.add(list, nodes); + _ref2 = g.BOARD.threads; + for (threadID in _ref2) { + thread = _ref2[threadID]; + toggler = $('.watcher-toggler', thread.OP.nodes.post); + watched = ThreadWatcher.db.get({ + boardID: thread.board.ID, + threadID: threadID + }); + $[watched ? 'addClass' : 'rmClass'](toggler, 'watched'); + } + _ref3 = ThreadWatcher.menu.refreshers; + for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { + refresher = _ref3[_j]; + refresher(); } }, toggle: function(thread) { - if (!$.hasClass($('.watch-thread-link', thread.OP.nodes.post), 'watched')) { - return ThreadWatcher.watch(thread); + var boardID, threadID; + boardID = thread.board.ID; + threadID = thread.ID; + if (ThreadWatcher.db.get({ + boardID: boardID, + threadID: threadID + })) { + return ThreadWatcher.rm(boardID, threadID); } else { - return ThreadWatcher.unwatch(thread.board, thread.ID); + return ThreadWatcher.add(thread); } }, - unwatch: function(board, threadID) { - return $.get('WatchedThreads', {}, function(item) { - var watched; - watched = item['WatchedThreads']; - delete watched[board][threadID]; - if (!Object.keys(watched[board]).length) { - delete watched[board]; + add: function(thread) { + var boardID, data, threadID; + data = {}; + boardID = thread.board.ID; + threadID = thread.ID; + if (thread.isDead) { + if (Conf['Auto Prune'] && ThreadWatcher.db.get({ + boardID: boardID, + threadID: threadID + })) { + ThreadWatcher.rm(boardID, threadID); + return; } - ThreadWatcher.refresh(watched); - return $.set('WatchedThreads', watched); + data.isDead = true; + } + data.excerpt = Get.threadExcerpt(thread); + ThreadWatcher.db.set({ + boardID: boardID, + threadID: threadID, + val: data }); + return ThreadWatcher.refresh(); }, - watch: function(thread) { - return $.get('WatchedThreads', {}, function(item) { - var watched, _name; - watched = item['WatchedThreads']; - watched[_name = thread.board] || (watched[_name] = {}); - watched[thread.board][thread] = { - href: "/" + thread.board + "/res/" + thread, - textContent: Get.threadExcerpt(thread) - }; - ThreadWatcher.refresh(watched); - return $.set('WatchedThreads', watched); + rm: function(boardID, threadID) { + ThreadWatcher.db["delete"]({ + boardID: boardID, + threadID: threadID }); + return ThreadWatcher.refresh(); + }, + convert: function(oldFormat) { + var boardID, data, newFormat, threadID, threads; + newFormat = {}; + for (boardID in oldFormat) { + threads = oldFormat[boardID]; + for (threadID in threads) { + data = threads[threadID]; + (newFormat[boardID] || (newFormat[boardID] = {}))[threadID] = { + excerpt: data.textContent + }; + } + } + return newFormat; + }, + menu: { + refreshers: [], + init: function() { + var menu; + if (!Conf['Thread Watcher']) { + return; + } + menu = new UI.Menu('thread watcher'); + $.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) { + return menu.toggle(e, this, ThreadWatcher); + }); + this.addHeaderMenuEntry(); + return this.addMenuEntries(); + }, + addHeaderMenuEntry: function() { + var entryEl; + if (g.VIEW !== 'thread') { + return; + } + entryEl = $.el('a', { + href: 'javascript:;' + }); + $.event('AddMenuEntry', { + type: 'header', + el: entryEl, + order: 60 + }); + $.on(entryEl, 'click', function() { + return ThreadWatcher.toggle(g.threads["" + g.BOARD + "." + g.THREADID]); + }); + return this.refreshers.push(function() { + var addClass, rmClass, text, _ref; + _ref = $('.current', ThreadWatcher.list) ? ['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); + return entryEl.textContent = text; + }); + }, + addMenuEntries: function() { + var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1, _results; + entries = []; + entries.push({ + cb: ThreadWatcher.cb.openAll, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Open all threads' + }) + }, + refresh: function() { + return (ThreadWatcher.list.firstElementChild ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + entries.push({ + cb: ThreadWatcher.cb.checkThreads, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Check 404\'d threads' + }) + }, + refresh: function() { + return ($('div:not(.dead-thread)', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + entries.push({ + cb: ThreadWatcher.cb.pruneDeads, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Prune 404\'d threads' + }) + }, + refresh: function() { + return ($('.dead-thread', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + subEntries = []; + _ref = Config.threadWatcher; + for (name in _ref) { + conf = _ref[name]; + subEntries.push(this.createSubEntry(name, conf[1])); + } + entries.push({ + entry: { + type: 'thread watcher', + el: $.el('span', { + textContent: 'Settings' + }), + subEntries: subEntries + } + }); + _results = []; + for (_i = 0, _len = entries.length; _i < _len; _i++) { + _ref1 = entries[_i], entry = _ref1.entry, cb = _ref1.cb, refresh = _ref1.refresh; + if (entry.el.nodeName === 'A') { + entry.el.href = 'javascript:;'; + } + if (cb) { + $.on(entry.el, 'click', cb); + } + if (refresh) { + this.refreshers.push(refresh.bind(entry)); + } + _results.push($.event('AddMenuEntry', entry)); + } + return _results; + }, + createSubEntry: function(name, desc) { + var entry, input; + entry = { + type: 'thread watcher', + el: $.el('label', { + innerHTML: " " + name, + title: desc + }) + }; + input = entry.el.firstElementChild; + input.checked = Conf[name]; + $.on(input, 'change', $.cb.checked); + if (name === 'Current Board') { + $.on(input, 'change', ThreadWatcher.refresh); + } + return entry; + } } }; @@ -7898,8 +8234,8 @@ http: true, https: true, software: 'foolfuuka', - boards: ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv'], - files: ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv'] + boards: ['adv', 'asp', 'cm', 'd', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y'], + files: ['cm', 'd', 'e', 'i', 'n', 'o', 'p', 's', 'trv', 'y'] }, 'Foolz Beta': { domain: 'beta.foolz.us', @@ -8305,10 +8641,8 @@ }, callbacks: [], cb: function(e) { - var post; e.preventDefault(); - post = Get.postFromNode(this); - return ExpandComment.expand(post); + return ExpandComment.expand(Get.postFromNode(this)); }, expand: function(post) { var a; @@ -8368,6 +8702,12 @@ } quote.href = "/" + post.board + "/res/" + href; } + Build.capcodeReplies({ + boardID: post.board.ID, + threadID: post.thread.ID, + bq: clone, + capcodeReplies: postObj.capcode_replies + }); post.nodes.shortComment = comment; $.replace(comment, clone); post.nodes.comment = post.nodes.longComment = clone; @@ -9378,7 +9718,8 @@ $.on(d, '4chanXInitFinished', Settings.open); } return $.set({ - lastchecked: Date.now(), + archives: Conf['archives'], + lastarchivecheck: now, previousversion: g.VERSION }); }); @@ -9564,7 +9905,6 @@ version: g.VERSION, date: now }; - Conf['WatchedThreads'] = {}; for (_i = 0, _len = DataBoards.length; _i < _len; _i++) { db = DataBoards[_i]; Conf[db] = { @@ -9694,14 +10034,13 @@ }); } } - data.Conf.WatchedThreads = data.WatchedThreads; - } else if (version[0] === '3') { - data = Settings.convertSettings(data, { - 'Reply Hiding': 'Reply Hiding Buttons', - 'Thread Hiding': 'Thread Hiding Buttons', - 'Bottom header': 'Bottom Header', - 'Unread Tab Icon': 'Unread Favicon' - }); + data.Conf['WatchedThreads'] = data.WatchedThreads; + } + if (data.Conf['WatchedThreads']) { + data.Conf['watchedThreads'] = { + boards: ThreadWatcher.convert(data.Conf['WatchedThreads']) + }; + delete data.Conf['WatchedThreads']; } return $.set(data.Conf); }, @@ -10128,6 +10467,7 @@ 'Thread Stats': ThreadStats, 'Thread Updater': ThreadUpdater, 'Thread Watcher': ThreadWatcher, + 'Thread Watcher (Menu)': ThreadWatcher.menu, 'Index Navigation': Nav, 'Keybinds': Keybinds, 'Show Dice Roll': Dice @@ -10394,7 +10734,7 @@ } return Main.thisPageIsLegit; }, - css: "/* General */\n.dialog {\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder: 1px solid;\ndisplay: block;\npadding: 0;\n}\n.captcha-img,\n.field {\nbackground-color: #FFF;\nborder: 1px solid #CCC;\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\ncolor: #333;\nfont: 13px sans-serif;\noutline: none;\ntransition: color .25s, border-color .25s;\ntransition: color .25s, border-color .25s;\n}\n.field::-moz-placeholder,\n.field:hover::-moz-placeholder {\ncolor: #AAA !important;\nfont-size: 13px !important;\nopacity: 1.0 !important;\n}\n.captch-img:hover,\n.field:hover {\nborder-color: #999;\n}\n.field:hover, .field:focus {\ncolor: #000;\n}\n.field[disabled] {\nbackground-color: #F2F2F2;\ncolor: #888;\n}\n.move {\ncursor: move;\noverflow: hidden;\n}\nlabel, .favicon {\ncursor: pointer;\n}\na[href=\"javascript:;\"] {\ntext-decoration: none;\n}\n.warning {\ncolor: red;\n}\n#boardNavDesktop {\ndisplay: none !important;\n}\na {\noutline: none !important;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#navlinks, .fixed #header-bar,\n:root.float #updater,\n:root.float #thread-stats,\n#qr {\nposition: fixed;\n}\n#fourchanx-settings {\nz-index: 999;\n}\n#overlay {\nz-index: 900;\n}\n#notifications {\nz-index: 70;\n}\n#qp, #ihover {\nz-index: 60;\n}\n#menu {\nz-index: 50;\n}\n#navlinks, #updater, #thread-stats {\nz-index: 40;\n}\n.fixed #header-bar.autohide {\nz-index: 35;\n}\n#qr {\nz-index: 30;\n}\n#watcher {\nz-index: 8;\n}\n:root.fixed-watcher #watcher {\nz-index: 20;\n}\n.fixed #header-bar {\nz-index: 10;\n}\n/* Header */\n.fixed.top body {\npadding-top: 2em;\n}\n.fixed.bottom body {\npadding-bottom: 2em;\n}\n.fixed #header-bar {\nright: 0;\nleft: 0;\npadding: 3px 4px 4px;\n}\n.fixed.top #header-bar {\ntop: 0;\n}\n.fixed.bottom #header-bar {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\ntransition: all .1s .05s ease-in-out;\n}\n:root.centered-links #shortcuts {\nwidth: 300px;\ntext-align: right;\n}\n:root.centered-links #header-bar {\ntext-align: center;\n}\n:root.centered-links #custom-board-list {\nposition: relative;\nleft: 150px;\n}\n.fixed.top #header-bar {\nborder-bottom-width: 1px;\n}\n.fixed.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n.fixed.bottom #header-bar .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n.fixed #header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n.fixed.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#scroll-marker {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n:root:not(.autohide) #scroll-marker {\npointer-events: none;\n}\n#header-bar #scroll-marker {\ndisplay: none;\n}\n.fixed #header-bar #scroll-marker {\ndisplay: block;\n}\n.fixed.top #header-bar #scroll-marker {\ntop: 100%;\n}\n.fixed.bottom #header-bar #scroll-marker {\nbottom: 100%;\n}\n#header-bar a:not(.entry):not(.close) {\ntext-decoration: none;\npadding: 1px;\n}\n#header-bar input {\nmargin: 0;\nvertical-align: bottom;\n}\n#shortcuts:empty {\ndisplay: none;\n}\n.brackets-wrap::before {\ncontent: \"\\00a0[\";\n}\n.brackets-wrap::after {\ncontent: \"]\\00a0\";\n}\n.disabled,\n.expand-all-shortcut {\nopacity: .45;\n}\n#shortcuts {\nfloat: right;\n}\n.shortcut {\nmargin-left: 3px;\n}\n#navbotright,\n#navtopright {\ndisplay: none;\n}\n#toggleMsgBtn {\ndisplay: none !important;\n}\n.current {\nfont-weight: bold;\n}\n/* 4chan X link brackets */\n.fourchanx-link::after {\ncontent: \"]\";\n}\n.fourchanx-link::before {\ncontent: \"[\";\n}\n/* Notifications */\n#notifications {\nposition: fixed;\ntop: 0;\nheight: 0;\ntext-align: center;\nright: 0;\nleft: 0;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar #notifications {\nposition: absolute;\ntop: 100%;\n}\n.notification {\ncolor: #FFF;\nfont-weight: 700;\ntext-shadow: 0 1px 2px rgba(0, 0, 0, .5);\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder-radius: 2px;\nmargin: 1px auto;\nwidth: 500px;\nmax-width: 100%;\nposition: relative;\ntransition: all .25s ease-in-out;\n}\n.notification.error {\nbackground-color: hsla(0, 100%, 38%, .9);\n}\n.notification.warning {\nbackground-color: hsla(36, 100%, 38%, .9);\n}\n.notification.info {\nbackground-color: hsla(200, 100%, 38%, .9);\n}\n.notification.success {\nbackground-color: hsla(104, 100%, 38%, .9);\n}\n.notification a {\ncolor: white;\n}\n.notification > .close {\npadding: 6px;\ntop: 0;\nright: 5px;\nposition: absolute;\n}\n.message {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\npadding: 6px 20px;\nmax-height: 200px;\nwidth: 100%;\noverflow: auto;\n}\n\n/* Settings */\n:root.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#overlay {\nbackground-color: rgba(0, 0, 0, .5);\ntop: 0;\nleft: 0;\nheight: 100%;\nwidth: 100%;\n}\n#fourchanx-settings {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nbox-shadow: 0 0 15px rgba(0, 0, 0, .15);\nheight: 600px;\nmax-height: 100%;\nwidth: 900px;\nmax-width: 100%;\nmargin: auto;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\nheight: 15px;\n}\n#fourchanx-settings > nav a {\ntext-decoration: underline;\n}\n#fourchanx-settings > nav a.close {\ntext-decoration: none;\npadding: 2px;\n}\n.section-container {\noverflow: auto;\nposition: absolute;\ntop: 2.1em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\npadding-right: 5px;\n}\n.sections-list {\npadding: 0 3px;\nfloat: left;\n}\n.credits {\nfloat: right;\n}\n.tab-selected {\nfont-weight: 700;\n}\n.section-sauce ul,\n.section-advanced ul {\nlist-style: none;\nmargin: 0;\n}\n.section-sauce ul {\npadding: 8px;\n}\n.section-advanced ul {\npadding: 0px;\n}\n.section-sauce li,\n.section-advanced li {\npadding-left: 4px;\n}\n.section-main label {\ntext-decoration: underline;\n}\n.section-filter ul {\npadding: 0;\n}\n.section-filter li {\nmargin: 10px 40px;\n}\n.section-filter textarea {\nheight: 500px;\n}\n.section-sauce textarea {\nheight: 350px;\n}\n.section-advanced .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-advanced textarea {\nheight: 150px;\n}\n.section-advanced .archive-cell {\nmin-width: 160px;\ntext-align: center;\n}\n.section-advanced #archive-board-select {\nposition: absolute;\n}\n.section-advanced .note {\nfont-size: 0.8em;\nfont-style: italic;\nmargin-left: 10px;\n}\n.section-advanced .note code {\nfont-style: normal;\nfont-size: 11px;\n}\n.section-keybinds .field {\nfont-family: monospace;\n} \n#fourchanx-settings fieldset {\nborder: 1px solid;\nborder-radius: 3px;\n}\n#fourchanx-settings legend {\nfont-weight: 700;\n}\n#fourchanx-settings textarea {\nfont-family: monospace;\nmin-width: 100%;\nmax-width: 100%;\n}\n#fourchanx-settings code {\ncolor: #000;\nbackground-color: #FFF;\npadding: 0 2px;\n}\n.unscroll {\noverflow: hidden;\n}\n\n/* Announcement Hiding */\n:root.hide-announcement #globalMessage {\ndisplay: none;\n}\na.hide-announcement {\nfloat: left;\n}\n\n/* Unread */\n#unread-line {\nmargin: 0;\nborder-color: rgb(255,0,0);\n}\n\n/* Thread Updater */\n#updater {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 5px 3px 0px;\nmargin-bottom: -3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n:root.float #updater {\npadding: 0px 3px;\n}\n.new {\ncolor: limegreen;\n}\n#update-status.new {\nmargin-right: 5px;\n}\n#update-timer {\ncursor: pointer;\n}\n\n/* Thread Watcher */\n#watcher {\nposition: absolute;\n}\n#watcher {\npadding-bottom: 3px;\noverflow: hidden;\nwhite-space: nowrap;\nmin-width: 120px;\nmax-height: 92%;\noverflow-y: auto;\n}\n:root.fixed-watcher #watcher {\nposition: fixed;\n}\n:root:not(.fixed-watcher) #watcher:not(:hover) {\nmax-height: 210px;\noverflow-y: hidden;\n}\n#watcher > .move {\npadding-top: 3px;\n}\n#watcher > div {\nmax-width: 250px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#watcher a {\ntext-decoration: none;\n}\n#watcher .move>.close {\nposition: absolute;\nright: 0px;\ntop: 0px;\npadding: 0px 4px;\n}\n.watch-thread-link {\npadding-top: 18px;\nwidth: 18px;\nheight: 0px;\ndisplay: inline-block;\nbackground-repeat: no-repeat;\nopacity: 0.2;\nposition: relative;\ntop: 1px;\n}\n.watch-thread-link.watched {\nopacity: 1;\n}\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n:root.float #post-count, :root.float #file-count {\npointer-events: none;\n}\n:root.float #thread-stats {\npadding: 0px 3px;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink),\n.quotelink.deadlink:not(.forwardlink) {\ntext-decoration: underline !important;\n}\n.inlined {\nopacity: .5;\n}\n#qp input, .forwarded {\ndisplay: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\ntext-decoration: none;\nborder-bottom: 1px dashed;\n}\n.filtered {\ntext-decoration: underline line-through;\n}\n:root.hide-backlinks .backlink.filtered {\ndisplay: none;\n}\n.inline {\nborder: 1px solid;\ndisplay: table;\nmargin: 2px 0;\n}\n.inline .post {\nborder: 0 !important;\nbackground-color: transparent !important;\ndisplay: table !important;\nmargin: 0 !important;\npadding: 1px 2px !important;\n}\n#qp > .opContainer::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n#qp .post {\nborder: none;\nmargin: 0;\npadding: 2px 2px 5px;\n}\n#qp img {\nmax-height: 80vh;\nmax-width: 50vw;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\n}\n:root.highlight-own .yourPost > .reply,\n:root.highlight-you .quotesYou > .reply {\nborder-left: 2px solid rgba(221,0,0,.5);\n}\n/* Quote Threading */\n.threadContainer {\nmargin-left: 20px;\nborder-left: 1px solid rgba(128,128,128,.3);\n}\n.threadOP {\nclear: both;\n} \n\n/* File */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull,\n.expanded-image > .post > .file > .fileThumb > img[data-md5],\n:not(.expanded-image) > .post > .file > .fileThumb > .full-image {\ndisplay: none;\n}\n.expanding {\nopacity: .5;\n}\n:root.fit-height .full-image {\nmax-height: 100vh;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image {\nwidth: 100%;\n}\n#ihover {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nmax-height: 100%;\nmax-width: 75%;\npadding-bottom: 16px;\n}\n.fappeTyme .thread > .noFile,\n.fappeTyme .threadContainer > .noFile {\ndisplay: none;\n}\n\n/* Index/Reply Navigation */\n#navlinks {\nfont-size: 16px;\ntop: 25px;\nright: 10px;\n}\n\n/* Filter */\n.opContainer.filter-highlight {\nbox-shadow: inset 5px 0 rgba(255, 0, 0, .5);\n}\n.filter-highlight > .reply {\nbox-shadow: -5px 0 rgba(255, 0, 0, .5);\n}\n\n/* Spoiler text */\n:root.reveal-spoilers s {\ncolor: white !important;\n}\n\n/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ * {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n:root.hide-original-post-form #postForm,\n:root.hide-original-post-form .postingMode,\n:root.hide-original-post-form #togglePostForm,\n#qr.autohide:not(.has-focus):not(:hover) > form,\n.postingMode ~ #qr select,\n#file-n-submit:not(.has-file) #qr-filerm {\ndisplay: none;\n}\n#qr select,\n#dump-button,\n.remove,\n.captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 300px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nmargin-bottom: 1px;\n}\n#qr .close {\nfloat: right;\npadding: 0 3px;\n}\n#qr .warning {\nmin-height: 1.6em;\nvertical-align: middle;\npadding: 0 1px;\nborder-width: 1px;\nborder-style: solid;\n}\n.qr-link-container {\ntext-align: center;\n}\n.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nwidth: 10%;\nmargin: 0;\nmargin-right: 4px;\nfont: 13px sans-serif;\npadding: 1px 0px 2px;\nopacity: 0.6;\n}\n.persona .field:not(#dump) {\nwidth: 95px;\nmin-width: 33.3%;\nmax-width: 33.3%;\n}\n#qr textarea.field {\nheight: 14.8em;\nmin-height: 9em;\n}\n#qr.has-captcha textarea.field {\nheight: 9em;\n}\ninput.field.tripped:not(:hover):not(:focus) {\ncolor: transparent !important;\ntext-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 0px;\ntext-align: center;\nbackground-image: #fff;\nfont-size: 0px;\nmin-height: 59px;\nmin-width: 302px;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.captcha-input.error:focus {\nborder-color: rgb(255,0,0) !important;\n}\n.field {\n-moz-box-sizing: border-box;\nmargin: 0px;\npadding: 2px 4px 3px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\nvertical-align: top;\n}\n:root.webkit #qr [type='submit'] {\nheight: 24px;\n}\n/* Fake File Input */\n#qr-filename,\n.has-file #qr-no-file {\ndisplay: none;\n}\n#qr-no-file,\n.has-file #qr-filename {\ndisplay: inline-block;\npadding: 0px 4px;\nmargin-bottom: 2px;\noverflow: hidden;\ntext-overflow: ellipsis;\nmax-width: 88%;\n}\n#qr-no-file {\ncolor: #AAA;\n}\n#qr-filename-container {\n-moz-box-sizing: border-box;\ndisplay: inline-block;\nposition: relative;\nwidth: 100px;\nmin-width: 74.6%;\nmax-width: 74.6%;\nmargin-right: 0.4%;\nmargin-top: 1px;\noverflow: hidden;\npadding: 2px 1px 0;\nheight: 22px;\n}\n#qr-filename-container:hover {\ncursor: text;\n}\n#qr-extras-container {\nposition: absolute;\nright: 0px;\n}\n#qr-filerm {\nmargin-right: 2px;\nz-index: 2;\n}\n#file-n-submit {\nheight: 23px;\n}\n#qr input[type=file] {\nvisibility: hidden;\nposition: absolute;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\n}\n#qr.has-spoiler .has-file #qr-spoiler-label {\nwidth: 6.7%;\nmin-width: 6.7%;\nmax-width: 6.7%;\ndisplay: inline-block;\ntext-align: center;\nvertical-align: top;\n}\n#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {\ndisplay: none;\n}\n#qr.has-spoiler .has-file #qr-filename-container {\nmax-width: 67.9%;\nmin-width: 67.9%;\n}\n#qr-spoiler-label input {\nposition: relative;\ntop: 3px;\n}\n/* Dumping UI */\n.dump #dump-list-container {\ndisplay: block;\n}\n#dump-list-container {\ndisplay: none;\nposition: relative;\noverflow-y: hidden;\nmargin-top: 1px;\n}\n#dump-list {\noverflow-x: auto;\noverflow-y: hidden;\nwhite-space: nowrap;\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-list:hover {\noverflow-x: auto;\n}\n.qr-preview {\n-moz-box-sizing: border-box;\ncounter-increment: thumbnails;\ncursor: move;\ndisplay: inline-block;\nheight: 90px;\nwidth: 90px;\npadding: 2px;\nopacity: .5;\noverflow: hidden;\nposition: relative;\ntext-shadow: 0 1px 1px #000;\n-moz-transition: opacity .25s ease-in-out;\nvertical-align: top;\nbackground-size: cover;\n}\n.qr-preview:hover,\n.qr-preview:focus {\nopacity: .9;\n}\n.qr-preview::before {\ncontent: counter(thumbnails);\ncolor: #fff;\nposition: absolute;\ntop: 3px;\nright: 3px;\ntext-shadow: 0 0 3px #000, 0 0 8px #000;\n}\n.qr-preview#selected {\nopacity: 1;\n}\n.qr-preview.drag {\nbox-shadow: 0 0 10px rgba(0,0,0,.5);\n}\n.qr-preview.over {\nborder-color: #fff;\n}\n.qr-preview > span {\ncolor: #fff;\n}\n.remove {\nbackground: none;\ncolor: #e00;\nfont-weight: 700;\npadding: 3px;\n}\na:only-of-type > .remove {\ndisplay: none;\n}\n.remove:hover::after {\ncontent: \" Remove\";\n}\n.qr-preview > label {\nbackground: rgba(0,0,0,.5);\ncolor: #fff;\nright: 0;\nbottom: 0;\nleft: 0;\nposition: absolute;\ntext-align: center;\n}\n.qr-preview > label > input {\nmargin: 0;\n}\n#add-post {\ncursor: pointer;\nfont-size: 2em;\nposition: absolute;\ntop: 50%;\nright: 10px;\n-moz-transform: translateY(-50%);\n}\n.textarea {\nposition: relative;\n}\n:root.webkit .textarea {\nmargin-bottom: -2px;\n}\n#char-count {\ncolor: #000;\nbackground: hsla(0, 0%, 100%, .5);\nfont-size: 8pt;\nposition: absolute;\nbottom: 1px;\nright: 1px;\npointer-events: none;\n}\n\n/* Menu */\n.menu-button {\ndisplay: inline-block;\nposition: relative;\ncursor: pointer;\n}\n.menu-button i {\nborder-top: 6px solid;\nborder-right: 4px solid transparent;\nborder-left: 4px solid transparent;\ndisplay: inline-block;\nmargin: 2px;\nvertical-align: middle;\n}\n#menu {\nposition: fixed;\noutline: none;\n}\n.entry {\nborder-bottom: 1px solid rgba(0,0,0,.25);\ncursor: pointer;\ndisplay: block;\noutline: none;\npadding: 3px 7px;\nposition: relative;\ntext-decoration: none;\nwhite-space: nowrap;\n}\n.left>.entry.has-submenu {\npadding-right: 17px !important;\n}\n.entry:last-child {\nborder-bottom: 0;\n}\n.has-submenu::after {\ncontent: \"\";\nborder-left: .5em solid;\nborder-top: .3em solid transparent;\nborder-bottom: .3em solid transparent;\ndisplay: inline-block;\nmargin: .3em;\nposition: absolute;\nright: 3px;\n}\n.left .has-submenu::after {\nborder-left: 0;\nborder-right: .5em solid;\n}\n.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\n}\n.imp-exp-result {\nposition: absolute;\ntext-align: center;\nmargin: auto;\nright: 0px;\nleft: 0px;\nwidth: 200px;\n}\n.export, .import {\ncursor: pointer;\ntext-decoration: none !important;\n}\n/* Link Title Favicons */\n.linkify.YouTube {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vimeo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.SoundCloud {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.audio {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.LiveLeak {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vocaroo {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.pastebin {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.gist {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.image {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.InstallGentoo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n\n/* General */\n:root.yotsuba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.yotsuba #header-bar, :root.yotsuba #notifications {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar a, :root.yotsuba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.yotsuba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.yotsuba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.yotsuba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba #menu {\ncolor: #800000;\n}\n:root.yotsuba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 10pt;\n}\n:root.yotsuba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.yotsuba-b .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.yotsuba-b #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.yotsuba-b .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.yotsuba-b .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba-b #menu {\ncolor: #000;\n}\n:root.yotsuba-b .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 10pt;\n}\n:root.yotsuba-b .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba-b .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.futaba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.futaba #header-bar, :root.futaba #notifications {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar a, :root.futaba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.futaba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.futaba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.futaba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.futaba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.futaba #menu {\ncolor: #800000;\n}\n:root.futaba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 12pt;\n}\n:root.futaba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.futaba .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.burichan .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.burichan #header-bar, :root.burichan #header-bar #notifications {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.burichan #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.burichan .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.burichan .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.burichan #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.burichan #menu {\ncolor: #000000;\n}\n:root.burichan .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 12pt;\n}\n:root.burichan .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.burichan .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n\n/* Header */\n:root.tomorrow #header-bar, :root.tomorrow #notifications {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\ncolor: #81A2BE;\n}\n\n/* Settings */\n:root.tomorrow #fourchanx-settings fieldset {\nborder-color: #111;\n}\n\n/* Quote */\n:root.tomorrow .backlink.deadlink {\ncolor: #81A2BE !important;\n}\n:root.tomorrow .inline {\nborder-color: #111;\nbackground-color: rgba(0, 0, 0, .14);\n}\n\n/* QR */\n.tomorrow #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\n}\n:root.tomorrow #qr .field {\nbackground-color: rgb(26, 27, 29);\ncolor: rgb(197,200,198);\nborder-color: rgb(40, 41, 42);\n}\n:root.tomorrow #qr .field:focus {\nborder-color: rgb(129, 162, 190) !important;\nbackground-color: rgb(30,32,36);\n}\n\n/* Menu */\n:root.tomorrow #menu {\ncolor: #C5C8C6;\n}\n:root.tomorrow .entry {\nborder-bottom: 1px solid #111;\nfont-size: 10pt;\n}\n:root.tomorrow .focused.entry {\nbackground: rgba(0, 0, 0, .33);\n}\n\n/* Watcher Favicon */\n:root.tomorrow .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.photon .dialog {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.photon #header-bar, :root.photon #notifications {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar a, :root.photon #notifications a {\ncolor: #FF6600;\n}\n\n/* Settings */\n:root.photon #fourchanx-settings fieldset {\nborder-color: #CCC;\n}\n\n/* Quote */\n:root.photon .backlink.deadlink {\ncolor: #F60 !important;\n}\n:root.photon .inline {\nborder-color: #CCC;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.photon #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.photon #menu {\ncolor: #333;\n}\n:root.photon .entry {\nborder-bottom: 1px solid #CCC;\nfont-size: 10pt;\n}\n:root.photon .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.photon .watch-thread-link\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n" + css: "/* General */\n.dialog {\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder: 1px solid;\ndisplay: block;\npadding: 0;\n}\n.captcha-img,\n.field {\nbackground-color: #FFF;\nborder: 1px solid #CCC;\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\ncolor: #333;\nfont: 13px sans-serif;\noutline: none;\ntransition: color .25s, border-color .25s;\ntransition: color .25s, border-color .25s;\n}\n.field::-moz-placeholder,\n.field:hover::-moz-placeholder {\ncolor: #AAA !important;\nfont-size: 13px !important;\nopacity: 1.0 !important;\n}\n.captch-img:hover,\n.field:hover {\nborder-color: #999;\n}\n.field:hover, .field:focus {\ncolor: #000;\n}\n.field[disabled] {\nbackground-color: #F2F2F2;\ncolor: #888;\n}\n.move {\ncursor: move;\noverflow: hidden;\n}\nlabel,\n.watcher-toggler {\ncursor: pointer;\n}\na[href=\"javascript:;\"] {\ntext-decoration: none;\n}\n.warning {\ncolor: red;\n}\n#boardNavDesktop {\ndisplay: none !important;\n}\na {\noutline: none !important;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#navlinks, .fixed #header-bar,\n:root.float #updater,\n:root.float #thread-stats,\n#qr {\nposition: fixed;\n}\n#fourchanx-settings {\nz-index: 999;\n}\n#overlay {\nz-index: 900;\n}\n#notifications {\nz-index: 70;\n}\n#qp, #ihover {\nz-index: 60;\n}\n#menu {\nz-index: 50;\n}\n#navlinks, #updater, #thread-stats {\nz-index: 40;\n}\n.fixed #header-bar.autohide {\nz-index: 35;\n}\n#qr {\nz-index: 30;\n}\n#thread-watcher {\nz-index: 8;\n}\n:root.fixed-watcher #thread-watcher {\nz-index: 20;\n}\n.fixed #header-bar {\nz-index: 10;\n}\n/* Header */\n.fixed.top body {\npadding-top: 2em;\n}\n.fixed.bottom body {\npadding-bottom: 2em;\n}\n.fixed #header-bar {\nright: 0;\nleft: 0;\npadding: 3px 4px 4px;\n}\n.fixed.top #header-bar {\ntop: 0;\n}\n.fixed.bottom #header-bar {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\ntransition: all .1s .05s ease-in-out;\n}\n:root.centered-links #shortcuts {\nwidth: 300px;\ntext-align: right;\n}\n:root.centered-links #header-bar {\ntext-align: center;\n}\n:root.centered-links #custom-board-list {\nposition: relative;\nleft: 150px;\n}\n.fixed.top #header-bar {\nborder-bottom-width: 1px;\n}\n.fixed.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n.fixed.bottom #header-bar .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n.fixed #header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n.fixed.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#scroll-marker {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n:root:not(.autohide) #scroll-marker {\npointer-events: none;\n}\n#header-bar #scroll-marker {\ndisplay: none;\n}\n.fixed #header-bar #scroll-marker {\ndisplay: block;\n}\n.fixed.top #header-bar #scroll-marker {\ntop: 100%;\n}\n.fixed.bottom #header-bar #scroll-marker {\nbottom: 100%;\n}\n#header-bar a:not(.entry):not(.close) {\ntext-decoration: none;\npadding: 1px;\n}\n#header-bar input {\nmargin: 0;\nvertical-align: bottom;\n}\n#shortcuts:empty {\ndisplay: none;\n}\n.brackets-wrap::before {\ncontent: \"\\00a0[\";\n}\n.brackets-wrap::after {\ncontent: \"]\\00a0\";\n}\n.dead-thread,\n.disabled,\n.expand-all-shortcut {\nopacity: .45;\n}\n#shortcuts {\nfloat: right;\n}\n.shortcut {\nmargin-left: 3px;\n}\n#navbotright,\n#navtopright {\ndisplay: none;\n}\n#toggleMsgBtn {\ndisplay: none !important;\n}\n.current {\nfont-weight: bold;\n}\n/* 4chan X link brackets */\n.fourchanx-link::after {\ncontent: \"]\";\n}\n.fourchanx-link::before {\ncontent: \"[\";\n}\n/* Notifications */\n#notifications {\nposition: fixed;\ntop: 0;\nheight: 0;\ntext-align: center;\nright: 0;\nleft: 0;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n.fixed.top #header-bar #notifications {\nposition: absolute;\ntop: 100%;\n}\n.notification {\ncolor: #FFF;\nfont-weight: 700;\ntext-shadow: 0 1px 2px rgba(0, 0, 0, .5);\nbox-shadow: 0 1px 2px rgba(0, 0, 0, .15);\nborder-radius: 2px;\nmargin: 1px auto;\nwidth: 500px;\nmax-width: 100%;\nposition: relative;\ntransition: all .25s ease-in-out;\n}\n.notification.error {\nbackground-color: hsla(0, 100%, 38%, .9);\n}\n.notification.warning {\nbackground-color: hsla(36, 100%, 38%, .9);\n}\n.notification.info {\nbackground-color: hsla(200, 100%, 38%, .9);\n}\n.notification.success {\nbackground-color: hsla(104, 100%, 38%, .9);\n}\n.notification a {\ncolor: white;\n}\n.notification > .close {\npadding: 6px;\ntop: 0;\nright: 5px;\nposition: absolute;\n}\n.message {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\npadding: 6px 20px;\nmax-height: 200px;\nwidth: 100%;\noverflow: auto;\n}\n\n/* Settings */\n:root.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#overlay {\nbackground-color: rgba(0, 0, 0, .5);\ntop: 0;\nleft: 0;\nheight: 100%;\nwidth: 100%;\n}\n#fourchanx-settings {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nbox-shadow: 0 0 15px rgba(0, 0, 0, .15);\nheight: 600px;\nmax-height: 100%;\nwidth: 900px;\nmax-width: 100%;\nmargin: auto;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\nheight: 15px;\n}\n#fourchanx-settings > nav a {\ntext-decoration: underline;\n}\n#fourchanx-settings > nav a.close {\ntext-decoration: none;\npadding: 2px;\n}\n.section-container {\noverflow: auto;\nposition: absolute;\ntop: 2.1em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\npadding-right: 5px;\n}\n.sections-list {\npadding: 0 3px;\nfloat: left;\n}\n.credits {\nfloat: right;\n}\n.tab-selected {\nfont-weight: 700;\n}\n.section-sauce ul,\n.section-advanced ul {\nlist-style: none;\nmargin: 0;\n}\n.section-sauce ul {\npadding: 8px;\n}\n.section-advanced ul {\npadding: 0px;\n}\n.section-sauce li,\n.section-advanced li {\npadding-left: 4px;\n}\n.section-main label {\ntext-decoration: underline;\n}\n.section-filter ul {\npadding: 0;\n}\n.section-filter li {\nmargin: 10px 40px;\n}\n.section-filter textarea {\nheight: 500px;\n}\n.section-sauce textarea {\nheight: 350px;\n}\n.section-advanced .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-advanced textarea {\nheight: 150px;\n}\n.section-advanced .archive-cell {\nmin-width: 160px;\ntext-align: center;\n}\n.section-advanced #archive-board-select {\nposition: absolute;\n}\n.section-advanced .note {\nfont-size: 0.8em;\nfont-style: italic;\nmargin-left: 10px;\n}\n.section-advanced .note code {\nfont-style: normal;\nfont-size: 11px;\n}\n.section-keybinds .field {\nfont-family: monospace;\n} \n#fourchanx-settings fieldset {\nborder: 1px solid;\nborder-radius: 3px;\n}\n#fourchanx-settings legend {\nfont-weight: 700;\n}\n#fourchanx-settings textarea {\nfont-family: monospace;\nmin-width: 100%;\nmax-width: 100%;\n}\n#fourchanx-settings code {\ncolor: #000;\nbackground-color: #FFF;\npadding: 0 2px;\n}\n.unscroll {\noverflow: hidden;\n}\n\n/* Announcement Hiding */\n:root.hide-announcement #globalMessage {\ndisplay: none;\n}\na.hide-announcement {\nfloat: left;\n}\n\n/* Unread */\n#unread-line {\nmargin: 0;\nborder-color: rgb(255,0,0);\n}\n\n/* Thread Updater */\n#updater {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 5px 3px 0px;\nmargin-bottom: -3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n:root.float #updater {\npadding: 0px 3px;\n}\n.new {\ncolor: limegreen;\n}\n#update-status.new {\nmargin-right: 5px;\n}\n#update-timer {\ncursor: pointer;\n}\n\n/* Thread Watcher */\n#thread-watcher {\nposition: absolute;\n}\n#thread-watcher {\npadding-bottom: 3px;\npadding-left: 3px;\noverflow: hidden;\nwhite-space: nowrap;\nmin-width: 136px;\nmax-height: 92%;\noverflow-y: auto;\n}\n#thread-watcher .menu-button {\nbottom: 1px;\n}\n:root.fixed-watcher #thread-watcher {\nposition: fixed;\n}\n:root:not(.fixed-watcher) #thread-watcher:not(:hover) {\nmax-height: 210px;\noverflow-y: hidden;\n}\n#thread-watcher > .move {\npadding-top: 3px;\n}\n#watched-threads > div {\nmax-width: 250px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#thread-watcher a {\ntext-decoration: none;\n}\n#thread-watcher .move>.close {\nposition: absolute;\nright: 0px;\ntop: 0px;\npadding: 0px 4px;\n}\n.watcher-toggler {\npadding-top: 18px;\nwidth: 18px;\nheight: 0px;\ndisplay: inline-block;\nbackground-repeat: no-repeat;\nopacity: 0.2;\nposition: relative;\ntop: 1px;\n}\n.watcher-toggler.watched {\nopacity: 1;\n}\n\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n:root.float #post-count, :root.float #file-count {\npointer-events: none;\n}\n:root.float #thread-stats {\npadding: 0px 3px;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink),\n.quotelink.deadlink:not(.forwardlink) {\ntext-decoration: underline !important;\n}\n.inlined {\nopacity: .5;\n}\n#qp input, .forwarded {\ndisplay: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\ntext-decoration: none;\nborder-bottom: 1px dashed;\n}\n.filtered {\ntext-decoration: underline line-through;\n}\n:root.hide-backlinks .backlink.filtered {\ndisplay: none;\n}\n.inline {\nborder: 1px solid;\ndisplay: table;\nmargin: 2px 0;\n}\n.inline .post {\nborder: 0 !important;\nbackground-color: transparent !important;\ndisplay: table !important;\nmargin: 0 !important;\npadding: 1px 2px !important;\n}\n#qp > .opContainer::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n#qp .post {\nborder: none;\nmargin: 0;\npadding: 2px 2px 5px;\n}\n#qp img {\nmax-height: 80vh;\nmax-width: 50vw;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\n}\n:root.highlight-own .yourPost > .reply,\n:root.highlight-you .quotesYou > .reply {\nborder-left: 2px solid rgba(221,0,0,.5);\n}\n/* Quote Threading */\n.threadContainer {\nmargin-left: 20px;\nborder-left: 1px solid rgba(128,128,128,.3);\n}\n.threadOP {\nclear: both;\n} \n\n/* File */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull,\n.expanded-image > .post > .file > .fileThumb > img[data-md5],\n:not(.expanded-image) > .post > .file > .fileThumb > .full-image {\ndisplay: none;\n}\n.expanding {\nopacity: .5;\n}\n:root.fit-height .full-image {\nmax-height: 100vh;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image {\nwidth: 100%;\n}\n#ihover {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\nmax-height: 100%;\nmax-width: 75%;\npadding-bottom: 16px;\n}\n.fappeTyme .thread > .noFile,\n.fappeTyme .threadContainer > .noFile {\ndisplay: none;\n}\n\n/* Index/Reply Navigation */\n#navlinks {\nfont-size: 16px;\ntop: 25px;\nright: 10px;\n}\n\n/* Filter */\n.opContainer.filter-highlight {\nbox-shadow: inset 5px 0 rgba(255, 0, 0, .5);\n}\n.filter-highlight > .reply {\nbox-shadow: -5px 0 rgba(255, 0, 0, .5);\n}\n\n/* Spoiler text */\n:root.reveal-spoilers s {\ncolor: white !important;\n}\n\n/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ * {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n:root.hide-original-post-form #postForm,\n:root.hide-original-post-form .postingMode,\n:root.hide-original-post-form #togglePostForm,\n#qr.autohide:not(.has-focus):not(:hover) > form,\n.postingMode ~ #qr select,\n#file-n-submit:not(.has-file) #qr-filerm {\ndisplay: none;\n}\n#qr select,\n#dump-button,\n.remove,\n.captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 300px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nmargin-bottom: 1px;\n}\n#qr .close {\nfloat: right;\npadding: 0 3px;\n}\n#qr .warning {\nmin-height: 1.6em;\nvertical-align: middle;\npadding: 0 1px;\nborder-width: 1px;\nborder-style: solid;\n}\n.qr-link-container {\ntext-align: center;\n}\n.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nwidth: 10%;\nmargin: 0;\nmargin-right: 4px;\nfont: 13px sans-serif;\npadding: 1px 0px 2px;\nopacity: 0.6;\n}\n.persona .field:not(#dump) {\nwidth: 95px;\nmin-width: 33.3%;\nmax-width: 33.3%;\n}\n#qr textarea.field {\nheight: 14.8em;\nmin-height: 9em;\n}\n#qr.has-captcha textarea.field {\nheight: 9em;\n}\ninput.field.tripped:not(:hover):not(:focus) {\ncolor: transparent !important;\ntext-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 0px;\ntext-align: center;\nbackground-image: #fff;\nfont-size: 0px;\nmin-height: 59px;\nmin-width: 302px;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.captcha-input.error:focus {\nborder-color: rgb(255,0,0) !important;\n}\n.field {\n-moz-box-sizing: border-box;\nmargin: 0px;\npadding: 2px 4px 3px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\nvertical-align: top;\n}\n:root.webkit #qr [type='submit'] {\nheight: 24px;\n}\n/* Fake File Input */\n#qr-filename,\n.has-file #qr-no-file {\ndisplay: none;\n}\n#qr-no-file,\n.has-file #qr-filename {\ndisplay: inline-block;\npadding: 0px 4px;\nmargin-bottom: 2px;\noverflow: hidden;\ntext-overflow: ellipsis;\nmax-width: 88%;\n}\n#qr-no-file {\ncolor: #AAA;\n}\n#qr-filename-container {\n-moz-box-sizing: border-box;\ndisplay: inline-block;\nposition: relative;\nwidth: 100px;\nmin-width: 74.6%;\nmax-width: 74.6%;\nmargin-right: 0.4%;\nmargin-top: 1px;\noverflow: hidden;\npadding: 2px 1px 0;\nheight: 22px;\n}\n#qr-filename-container:hover {\ncursor: text;\n}\n#qr-extras-container {\nposition: absolute;\nright: 0px;\n}\n#qr-filerm {\nmargin-right: 2px;\nz-index: 2;\n}\n#file-n-submit {\nheight: 23px;\n}\n#qr input[type=file] {\nvisibility: hidden;\nposition: absolute;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\n}\n#qr.has-spoiler .has-file #qr-spoiler-label {\nwidth: 6.7%;\nmin-width: 6.7%;\nmax-width: 6.7%;\ndisplay: inline-block;\ntext-align: center;\nvertical-align: top;\n}\n#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {\ndisplay: none;\n}\n#qr.has-spoiler .has-file #qr-filename-container {\nmax-width: 67.9%;\nmin-width: 67.9%;\n}\n#qr-spoiler-label input {\nposition: relative;\ntop: 3px;\n}\n/* Dumping UI */\n.dump #dump-list-container {\ndisplay: block;\n}\n#dump-list-container {\ndisplay: none;\nposition: relative;\noverflow-y: hidden;\nmargin-top: 1px;\n}\n#dump-list {\noverflow-x: auto;\noverflow-y: hidden;\nwhite-space: nowrap;\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-list:hover {\noverflow-x: auto;\n}\n.qr-preview {\n-moz-box-sizing: border-box;\ncounter-increment: thumbnails;\ncursor: move;\ndisplay: inline-block;\nheight: 90px;\nwidth: 90px;\npadding: 2px;\nopacity: .5;\noverflow: hidden;\nposition: relative;\ntext-shadow: 0 1px 1px #000;\n-moz-transition: opacity .25s ease-in-out;\nvertical-align: top;\nbackground-size: cover;\n}\n.qr-preview:hover,\n.qr-preview:focus {\nopacity: .9;\n}\n.qr-preview::before {\ncontent: counter(thumbnails);\ncolor: #fff;\nposition: absolute;\ntop: 3px;\nright: 3px;\ntext-shadow: 0 0 3px #000, 0 0 8px #000;\n}\n.qr-preview#selected {\nopacity: 1;\n}\n.qr-preview.drag {\nbox-shadow: 0 0 10px rgba(0,0,0,.5);\n}\n.qr-preview.over {\nborder-color: #fff;\n}\n.qr-preview > span {\ncolor: #fff;\n}\n.remove {\nbackground: none;\ncolor: #e00;\nfont-weight: 700;\npadding: 3px;\n}\na:only-of-type > .remove {\ndisplay: none;\n}\n.remove:hover::after {\ncontent: \" Remove\";\n}\n.qr-preview > label {\nbackground: rgba(0,0,0,.5);\ncolor: #fff;\nright: 0;\nbottom: 0;\nleft: 0;\nposition: absolute;\ntext-align: center;\n}\n.qr-preview > label > input {\nmargin: 0;\n}\n#add-post {\ncursor: pointer;\nfont-size: 2em;\nposition: absolute;\ntop: 50%;\nright: 10px;\n-moz-transform: translateY(-50%);\n}\n.textarea {\nposition: relative;\n}\n:root.webkit .textarea {\nmargin-bottom: -2px;\n}\n#char-count {\ncolor: #000;\nbackground: hsla(0, 0%, 100%, .5);\nfont-size: 8pt;\nposition: absolute;\nbottom: 1px;\nright: 1px;\npointer-events: none;\n}\n\n/* Menu */\n.menu-button {\ndisplay: inline-block;\nposition: relative;\ncursor: pointer;\n}\n.menu-button i {\nborder-top: 6px solid;\nborder-right: 4px solid transparent;\nborder-left: 4px solid transparent;\ndisplay: inline-block;\nmargin: 2px;\nvertical-align: middle;\n}\n#menu {\nposition: fixed;\noutline: none;\n}\n.entry {\nborder-bottom: 1px solid rgba(0,0,0,.25);\ncursor: pointer;\ndisplay: block;\noutline: none;\npadding: 3px 7px;\nposition: relative;\ntext-decoration: none;\nwhite-space: nowrap;\n}\n.left>.entry.has-submenu {\npadding-right: 17px !important;\n}\n.entry:last-child {\nborder-bottom: 0;\n}\n.has-submenu::after {\ncontent: \"\";\nborder-left: .5em solid;\nborder-top: .3em solid transparent;\nborder-bottom: .3em solid transparent;\ndisplay: inline-block;\nmargin: .3em;\nposition: absolute;\nright: 3px;\n}\n.left .has-submenu::after {\nborder-left: 0;\nborder-right: .5em solid;\n}\n.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\n}\n.imp-exp-result {\nposition: absolute;\ntext-align: center;\nmargin: auto;\nright: 0px;\nleft: 0px;\nwidth: 200px;\n}\n.export, .import {\ncursor: pointer;\ntext-decoration: none !important;\n}\n/* Link Title Favicons */\n.linkify.YouTube {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vimeo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.SoundCloud {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.audio {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.LiveLeak {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.Vocaroo {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.pastebin {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.gist {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') center left no-repeat!important;\npadding-left: 18px;\n}\n.linkify.image {\nbackground: 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;\npadding-left: 18px;\n}\n.linkify.InstallGentoo {\nbackground: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') center left no-repeat!important;\npadding-left: 18px;\n}\n\n/* General */\n:root.yotsuba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.yotsuba #header-bar, :root.yotsuba #notifications {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar a, :root.yotsuba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.yotsuba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.yotsuba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.yotsuba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.yotsuba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba #menu {\ncolor: #800000;\n}\n:root.yotsuba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 10pt;\n}\n:root.yotsuba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.yotsuba-b .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.yotsuba-b #header-bar, :root.yotsuba-b #notifications {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.yotsuba-b #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.yotsuba-b .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.yotsuba-b .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.yotsuba-b #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.yotsuba-b .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.yotsuba-b #menu {\ncolor: #000;\n}\n:root.yotsuba-b .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 10pt;\n}\n:root.yotsuba-b .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.yotsuba-b .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.futaba .dialog {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.futaba #header-bar, :root.futaba #notifications {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar a, :root.futaba #notifications a {\ncolor: #800000;\n}\n\n/* Settings */\n:root.futaba #fourchanx-settings fieldset {\nborder-color: #D9BFB7;\n}\n\n/* Quote */\n:root.futaba .backlink.deadlink {\ncolor: #00E !important;\n}\n:root.futaba .inline {\nborder-color: #D9BFB7;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.futaba #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #F0E0D6;\nborder-color: #D9BFB7;\n}\n:root.futaba .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.futaba #menu {\ncolor: #800000;\n}\n:root.futaba .entry {\nborder-bottom: 1px solid #D9BFB7;\nfont-size: 12pt;\n}\n:root.futaba .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.futaba .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.burichan .dialog {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .field:focus {\nborder-color: #98E;\n}\n\n/* Header */\n:root.burichan #header-bar, :root.burichan #header-bar #notifications {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar a, :root.burichan #header-bar #notifications a {\ncolor: #34345C;\n}\n\n/* Settings */\n:root.burichan #fourchanx-settings fieldset {\nborder-color: #B7C5D9;\n}\n\n/* Quote */\n:root.burichan .backlink.deadlink {\ncolor: #34345C !important;\n}\n:root.burichan .inline {\nborder-color: #B7C5D9;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.burichan #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #D6DAF0;\nborder-color: #B7C5D9;\n}\n:root.burichan .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.burichan #menu {\ncolor: #000000;\n}\n:root.burichan .entry {\nborder-bottom: 1px solid #B7C5D9;\nfont-size: 12pt;\n}\n:root.burichan .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.burichan .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n\n/* Header */\n:root.tomorrow #header-bar, :root.tomorrow #notifications {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar a, :root.tomorrow #notifications a {\ncolor: #81A2BE;\n}\n\n/* Settings */\n:root.tomorrow #fourchanx-settings fieldset {\nborder-color: #111;\n}\n\n/* Quote */\n:root.tomorrow .backlink.deadlink {\ncolor: #81A2BE !important;\n}\n:root.tomorrow .inline {\nborder-color: #111;\nbackground-color: rgba(0, 0, 0, .14);\n}\n\n/* QR */\n.tomorrow #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\n}\n:root.tomorrow #qr .field {\nbackground-color: rgb(26, 27, 29);\ncolor: rgb(197,200,198);\nborder-color: rgb(40, 41, 42);\n}\n:root.tomorrow #qr .field:focus {\nborder-color: rgb(129, 162, 190) !important;\nbackground-color: rgb(30,32,36);\n}\n\n/* Menu */\n:root.tomorrow #menu {\ncolor: #C5C8C6;\n}\n:root.tomorrow .entry {\nborder-bottom: 1px solid #111;\nfont-size: 10pt;\n}\n:root.tomorrow .focused.entry {\nbackground: rgba(0, 0, 0, .33);\n}\n\n/* Watcher Favicon */\n:root.tomorrow .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n\n/* General */\n:root.photon .dialog {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .field:focus {\nborder-color: #EA8;\n}\n\n/* Header */\n:root.photon #header-bar, :root.photon #notifications {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar a, :root.photon #notifications a {\ncolor: #FF6600;\n}\n\n/* Settings */\n:root.photon #fourchanx-settings fieldset {\nborder-color: #CCC;\n}\n\n/* Quote */\n:root.photon .backlink.deadlink {\ncolor: #F60 !important;\n}\n:root.photon .inline {\nborder-color: #CCC;\nbackground-color: rgba(255, 255, 255, .14);\n}\n\n/* QR */\n.photon #dump-list::-webkit-scrollbar-thumb {\nbackground-color: #DDD;\nborder-color: #CCC;\n}\n:root.photon .qr-preview {\nbackground-color: rgba(0, 0, 0, .15);\n}\n\n/* Menu */\n:root.photon #menu {\ncolor: #333;\n}\n:root.photon .entry {\nborder-bottom: 1px solid #CCC;\nfont-size: 10pt;\n}\n:root.photon .focused.entry {\nbackground: rgba(255, 255, 255, .33);\n}\n\n/* Watcher Favicon */\n:root.photon .watcher-toggler\n{\nbackground-image: url(\"data:image/svg+xml,\");\n}\n" }; Main.init(); diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 3f3904287..9eaa101c0 100755 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -50,8 +50,8 @@ Redirect = http: true https: true software: 'foolfuuka' - boards: ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv'] - files: ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv'] + boards: ['adv', 'asp', 'cm', 'd', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y'] + files: ['cm', 'd', 'e', 'i', 'n', 'o', 'p', 's', 'trv', 'y'] 'Foolz Beta': domain: 'beta.foolz.us' diff --git a/src/General/Build.coffee b/src/General/Build.coffee index cd4f73e01..6a1683a38 100755 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -27,7 +27,7 @@ Build = date: data.now dateUTC: data.time comment: data.com - capReps: data.capcode_replies + capcodeReplies: data.capcode_replies # thread status isSticky: !!data.sticky isClosed: !!data.closed @@ -59,7 +59,7 @@ Build = postID, threadID, boardID name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC isSticky, isClosed - comment, capReps + comment, capcodeReplies file } = o isOP = postID is threadID @@ -191,26 +191,6 @@ Build = else '' - capcodeReplies = '' - if capReps - generateCapcodeReplies = (capcodeType, array) -> - "#{ - switch capcodeType - when 'admin' - 'Administrator' - when 'mod' - 'Moderator' - when 'developer' - 'Developer' - } Repl#{if array.length > 1 then 'ies' else 'y'}: #{ - array.map (ID) -> - ">>#{ID}" - .join ' ' - }
" - for capcodeType, array of capReps - capcodeReplies += generateCapcodeReplies capcodeType, array - capcodeReplies = "

#{capcodeReplies}" - container = $.el 'div', id: "pc#{postID}" className: "postContainer #{if isOP then 'op' else 'reply'}Container" @@ -221,4 +201,36 @@ Build = continue if href[0] is '/' # Cross-board quote, or board link quote.href = "/#{boardID}/res/#{href}" # Fix pathnames + Build.capcodeReplies {boardID, threadID, root: container, capcodeReplies} + container + + capcodeReplies: ({boardID, threadID, bq, root, capcodeReplies}) -> + return unless capcodeReplies + + generateCapcodeReplies = (capcodeType, array) -> + "#{ + switch capcodeType + when 'admin' + 'Administrator' + when 'mod' + 'Moderator' + when 'developer' + 'Developer' + } Repl#{if array.length > 1 then 'ies' else 'y'}: #{ + array.map (ID) -> + ">>#{ID}" + .join ' ' + }
" + html = [] + for capcodeType, array of capcodeReplies + html.push generateCapcodeReplies capcodeType, array + + bq or= $ 'blockquote', root + $.add bq, [ + $.el 'br' + $.el 'br' + $.el 'span', + className: 'capcodeReplies' + innerHTML: html.join '' + ] diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 819779b89..83c2b3df9 100755 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -57,12 +57,6 @@ Config = true 'Show dice that were entered into the email field.' ] - <% if (type !== 'crx') { %> - 'Check for Updates': [ - true - 'Check for updated versions of <%= meta.name %>.' - ] - <% } %> 'Show Updated Notifications': [ true 'Show notifications when 4chan X is successfully updated.' @@ -255,14 +249,6 @@ Config = true 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.' ] - 'Auto Watch': [ - true - 'Automatically watch threads you start.' - ] - 'Auto Watch Reply': [ - false - 'Automatically watch threads you reply to.' - ] 'Posting': 'Quick Reply': [ @@ -399,7 +385,25 @@ Config = false 'Advance to next post when contracting an expanded image.' ] - + + threadWatcher: + 'Current Board': [ + false + 'Only show watched threads from the current board.' + ] + 'Auto Watch': [ + true + 'Automatically watch threads you start.' + ] + 'Auto Watch Reply': [ + false + 'Automatically watch threads you reply to.' + ] + 'Auto Prune': [ + false + 'Automatically prune 404\'d threads.' + ] + filter: name: """ # Filter any namefags: diff --git a/src/General/Header.coffee b/src/General/Header.coffee index d08b8cd99..947f084e4 100755 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -109,7 +109,6 @@ Header = fourchannav = $.id 'boardNavDesktop' if a = $ "a[href*='/#{g.BOARD}/']", fourchannav a.className = 'current' - boardList = $.el 'span', id: 'board-list' innerHTML: "" diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 86b92e391..f79be109b 100755 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -126,6 +126,7 @@ Main = 'Thread Stats': ThreadStats 'Thread Updater': ThreadUpdater 'Thread Watcher': ThreadWatcher + 'Thread Watcher (Menu)': ThreadWatcher.menu 'Index Navigation': Nav 'Keybinds': Keybinds 'Show Dice Roll': Dice @@ -205,9 +206,6 @@ Main = Main.callbackNodes Thread, threads Main.callbackNodesDB Post, posts, -> $.event '4chanXInitFinished' - <% if (type !== 'crx') { %> - Main.checkUpdate() - <% } %> if styleSelector = $.id 'styleSelector' passLink = $.el 'a', @@ -227,9 +225,6 @@ Main = new Notification 'warning', 'Cookies need to be enabled on 4chan for <%= meta.name %> to properly function.', 30 $.event '4chanXInitFinished' - <% if (type !== 'crx') { %> - Main.checkUpdate() - <% } %> callbackNodes: (klass, nodes) -> # get the nodes' length only once @@ -303,27 +298,6 @@ Main = obj.callback.isAddon = true Klass::callbacks.push obj.callback - <% if (type !== 'crx') { %> - message: (e) -> - {version} = e.data - if version and version isnt g.VERSION - el = $.el 'span', - innerHTML: "Update: <%= meta.name %> v#{version} is out, get it target=_blank>here." - new Notification 'info', el, 120 - - checkUpdate: -> - return unless Conf['Check for Updates'] and Main.isThisPageLegit() - now = Date.now() - $.get 'lastchecked', 0, ({lastchecked}) -> - if (lastchecked > now - $.DAY) - return - $.ready -> - $.on window, 'message', Main.message - $.set 'lastchecked', now - $.add d.head, $.el 'script', - src: '<%= meta.repo %>raw/<%= meta.mainBranch %>/latest.js' - <% } %> - handleErrors: (errors) -> unless errors instanceof Array error = errors diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 8af3c568f..eaa4e908a 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -21,7 +21,8 @@ Settings = else $.on d, '4chanXInitFinished', Settings.open $.set - lastchecked: Date.now() + archives: Conf['archives'] + lastarchivecheck: now previousversion: g.VERSION Settings.addSection 'Main', Settings.main @@ -153,7 +154,6 @@ Settings = data = version: g.VERSION date: now - Conf['WatchedThreads'] = {} for db in DataBoards Conf[db] = boards: {} # Make sure to export the most recent data. @@ -265,13 +265,10 @@ Settings = for key, val of Config.hotkeys when key of data.Conf data.Conf[key] = data.Conf[key].replace(/ctrl|alt|meta/g, (s) -> "#{s[0].toUpperCase()}#{s[1..]}").replace /(^|.+\+)[A-Z]$/g, (s) -> "Shift+#{s[0...-1]}#{s[-1..].toLowerCase()}" - data.Conf.WatchedThreads = data.WatchedThreads - else if version[0] is '3' - data = Settings.convertSettings data, - 'Reply Hiding': 'Reply Hiding Buttons' - 'Thread Hiding': 'Thread Hiding Buttons' - 'Bottom header': 'Bottom Header' - 'Unread Tab Icon': 'Unread Favicon' + data.Conf['WatchedThreads'] = data.WatchedThreads + if data.Conf['WatchedThreads'] + data.Conf['watchedThreads'] = boards: ThreadWatcher.convert data.Conf['WatchedThreads'] + delete data.Conf['WatchedThreads'] $.set data.Conf convertSettings: (data, map) -> for prevKey, newKey of map diff --git a/src/General/css/burichan.css b/src/General/css/burichan.css index 6476a5539..917dea36c 100755 --- a/src/General/css/burichan.css +++ b/src/General/css/burichan.css @@ -52,7 +52,7 @@ } /* Watcher Favicon */ -:root.burichan .watch-thread-link +:root.burichan .watcher-toggler { background-image: url("data:image/svg+xml,"); } diff --git a/src/General/css/futaba.css b/src/General/css/futaba.css index 9b56af61a..3a090155c 100755 --- a/src/General/css/futaba.css +++ b/src/General/css/futaba.css @@ -52,7 +52,7 @@ } /* Watcher Favicon */ -:root.futaba .watch-thread-link +:root.futaba .watcher-toggler { background-image: url("data:image/svg+xml,"); } diff --git a/src/General/css/photon.css b/src/General/css/photon.css index 7f2370192..8eb680e14 100755 --- a/src/General/css/photon.css +++ b/src/General/css/photon.css @@ -52,7 +52,7 @@ } /* Watcher Favicon */ -:root.photon .watch-thread-link +:root.photon .watcher-toggler { background-image: url("data:image/svg+xml,"); } diff --git a/src/General/css/style.css b/src/General/css/style.css index dbed7eeb0..07884666f 100755 --- a/src/General/css/style.css +++ b/src/General/css/style.css @@ -38,7 +38,8 @@ cursor: move; overflow: hidden; } -label, .favicon { +label, +.watcher-toggler { cursor: pointer; } a[href="javascript:;"] { @@ -97,10 +98,10 @@ a { #qr { z-index: 30; } -#watcher { +#thread-watcher { z-index: 8; } -:root.fixed-watcher #watcher { +:root.fixed-watcher #thread-watcher { z-index: 20; } .fixed #header-bar { @@ -204,6 +205,7 @@ a { .brackets-wrap::after { content: "]\\00a0"; } +.dead-thread, .disabled, .expand-all-shortcut { opacity: .45; @@ -465,44 +467,48 @@ a.hide-announcement { } /* Thread Watcher */ -#watcher { +#thread-watcher { position: absolute; } -#watcher { +#thread-watcher { padding-bottom: 3px; + padding-left: 3px; overflow: hidden; white-space: nowrap; - min-width: 120px; + min-width: 136px; max-height: 92%; overflow-y: auto; } -:root.fixed-watcher #watcher { +#thread-watcher .menu-button { + bottom: 1px; +} +:root.fixed-watcher #thread-watcher { position: fixed; } -:root:not(.fixed-watcher) #watcher:not(:hover) { +:root:not(.fixed-watcher) #thread-watcher:not(:hover) { max-height: 210px; overflow-y: hidden; } -#watcher > .move { +#thread-watcher > .move { padding-top: 3px; } -#watcher > div { +#watched-threads > div { max-width: 250px; overflow: hidden; padding-left: 3px; padding-right: 3px; text-overflow: ellipsis; } -#watcher a { +#thread-watcher a { text-decoration: none; } -#watcher .move>.close { +#thread-watcher .move>.close { position: absolute; right: 0px; top: 0px; padding: 0px 4px; } -.watch-thread-link { +.watcher-toggler { padding-top: 18px; width: 18px; height: 0px; @@ -512,10 +518,11 @@ a.hide-announcement { position: relative; top: 1px; } -.watch-thread-link.watched { +.watcher-toggler.watched { opacity: 1; } + /* Thread Stats */ #thread-stats { background: none; diff --git a/src/General/css/tomorrow.css b/src/General/css/tomorrow.css index 6f8d64b49..343b62c60 100755 --- a/src/General/css/tomorrow.css +++ b/src/General/css/tomorrow.css @@ -58,7 +58,7 @@ } /* Watcher Favicon */ -:root.tomorrow .watch-thread-link +:root.tomorrow .watcher-toggler { background-image: url("data:image/svg+xml,"); } diff --git a/src/General/css/yotsuba-b.css b/src/General/css/yotsuba-b.css index d3bdd0234..486554f35 100755 --- a/src/General/css/yotsuba-b.css +++ b/src/General/css/yotsuba-b.css @@ -52,7 +52,7 @@ } /* Watcher Favicon */ -:root.yotsuba-b .watch-thread-link +:root.yotsuba-b .watcher-toggler { background-image: url("data:image/svg+xml,"); } diff --git a/src/General/css/yotsuba.css b/src/General/css/yotsuba.css index a06ab02fb..0cc6f5533 100755 --- a/src/General/css/yotsuba.css +++ b/src/General/css/yotsuba.css @@ -52,7 +52,7 @@ } /* Watcher Favicon */ -:root.yotsuba .watch-thread-link +:root.yotsuba .watcher-toggler { background-image: url("data:image/svg+xml,"); } diff --git a/src/General/html/Build/post.html b/src/General/html/Build/post.html index cf833ebdd..239dec4f7 100755 --- a/src/General/html/Build/post.html +++ b/src/General/html/Build/post.html @@ -54,6 +54,6 @@ #{if isOP then '' else fileHTML} -
#{comment or ''}#{capcodeReplies}
#{" "} +
#{comment or ''}
#{" "} """ \ No newline at end of file diff --git a/src/General/html/Monitoring/ThreadWatcher.html b/src/General/html/Monitoring/ThreadWatcher.html new file mode 100644 index 000000000..7debb0041 --- /dev/null +++ b/src/General/html/Monitoring/ThreadWatcher.html @@ -0,0 +1,2 @@ +
Thread Watcher ×
+
diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee index 238c79a0b..295bc41cc 100755 --- a/src/General/lib/$.coffee +++ b/src/General/lib/$.coffee @@ -57,18 +57,23 @@ $.extend = (object, properties) -> object[key] = val return -$.ajax = (url, options, extra={}) -> - {type, headers, upCallbacks, form, sync} = extra - r = new XMLHttpRequest() - r.overrideMimeType 'text/html' - type or= form and 'post' or 'get' - r.open type, url, !sync - for key, val of headers - r.setRequestHeader key, val - $.extend r, options - $.extend r.upload, upCallbacks - r.send form - r +$.ajax = do -> + # Status Code 304: Not modified + # With the `If-Modified-Since` header we only receive the HTTP headers and no body for 304 responses. + # This saves a lot of bandwidth and CPU time for both the users and the servers. + lastModified = {} + (url, options, extra={}) -> + {type, whenModified, upCallbacks, form, sync} = extra + r = new XMLHttpRequest() + type or= form and 'post' or 'get' + r.open type, url, !sync + if whenModified + r.setRequestHeader 'If-Modified-Since', lastModified[url] or '0' + $.on r, 'load', -> lastModified[url] = r.getResponseHeader 'Last-Modified' + $.extend r, options + $.extend r.upload, upCallbacks + r.send form + r $.cache = do -> reqs = {} diff --git a/src/General/lib/databoard.class b/src/General/lib/databoard.class index 80c190f17..e1f58ed47 100755 --- a/src/General/lib/databoard.class +++ b/src/General/lib/databoard.class @@ -1,10 +1,10 @@ -DataBoards = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts'] +DataBoards = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads'] class DataBoard - constructor: (@key, sync) -> + constructor: (@key, sync, dontClean) -> @data = Conf[key] $.sync key, @onSync.bind @ - @clean() + @clean() unless dontClean return unless sync # Chrome also fires the onChanged callback on the current tab, # so we only start syncing when we're ready. @@ -13,6 +13,9 @@ class DataBoard @sync = sync $.on d, '4chanXInitFinished', init + save: -> + $.set @key, @data + delete: ({boardID, threadID, postID}) -> if postID delete @data.boards[boardID][threadID][postID] @@ -22,7 +25,7 @@ class DataBoard @deleteIfEmpty {boardID} else delete @data.boards[boardID] - $.set @key, @data + @save() deleteIfEmpty: ({boardID, threadID}) -> if threadID @@ -39,7 +42,7 @@ class DataBoard (@data.boards[boardID] or= {})[threadID] = val else @data.boards[boardID] = val - $.set @key, @data + @save() get: ({boardID, threadID, postID, defaultValue}) -> if board = @data.boards[boardID] @@ -67,8 +70,7 @@ class DataBoard @data.lastChecked = now for boardID of @data.boards @ajaxClean boardID - - $.set @key, @data + @save() ajaxClean: (boardID) -> $.cache "//api.4chan.org/#{boardID}/threads.json", (e) => @@ -84,7 +86,7 @@ class DataBoard threads[thread.no] = board[thread.no] @data.boards[boardID] = threads @deleteIfEmpty {boardID} - $.set @key, @data + @save() onSync: (data) -> @data = data or boards: {} diff --git a/src/General/lib/polyfill.coffee b/src/General/lib/polyfill.coffee index 5d9ddc124..3cec1905a 100755 --- a/src/General/lib/polyfill.coffee +++ b/src/General/lib/polyfill.coffee @@ -1,6 +1,16 @@ Polyfill = init: -> + Polyfill.toBlob() Polyfill.visibility() + toBlob: -> + HTMLCanvasElement::toBlob or= (cb) -> + data = atob @toDataURL()[22..] + # DataUrl to Binary code from Aeosynth's 4chan X repo + l = data.length + ui8a = new Uint8Array l + for i in [0...l] + ui8a[i] = data.charCodeAt i + cb new Blob [ui8a], type: 'image/png' visibility: -> # page visibility API return unless 'webkitHidden' of document diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index a02e5ec31..3d7751853 100755 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -169,8 +169,8 @@ ImageExpand = {createSubEntry} = ImageExpand.menu subEntries = [] - for key, conf of Config.imageExpansion - subEntries.push createSubEntry key, conf + for name, conf of Config.imageExpansion + subEntries.push createSubEntry name, conf[1] $.event 'AddMenuEntry', type: 'header' @@ -178,17 +178,16 @@ ImageExpand = order: 105 subEntries: subEntries - createSubEntry: (type, config) -> + createSubEntry: (name, desc) -> label = $.el 'label', - innerHTML: " #{type}" + innerHTML: " #{name}" + title: desc input = label.firstElementChild - if type in ['Fit width', 'Fit height'] + if name in ['Fit width', 'Fit height'] $.on input, 'change', ImageExpand.cb.setFitness - if config - label.title = config[1] - input.checked = Conf[type] - $.event 'change', null, input - $.on input, 'change', $.cb.checked + input.checked = Conf[name] + $.event 'change', null, input + $.on input, 'change', $.cb.checked el: label menuToggle: (e) -> diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 74dca4b22..ef1684dce 100755 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -4,23 +4,20 @@ Linkify = @regString = if Conf['Allow False Positives'] ///( - \b( - [-a-z]+:// - | - [a-z]{3,}\.[-a-z0-9]+\.[a-z] - | - [-a-z0-9]+\.[a-z] - | - [\d]+\.[\d]+\.[\d]+\.[\d]+/ - | - [a-z]{3,}:[a-z0-9?] - | - [^\s@]+@[a-z0-9.-]+\.[a-z0-9] - ) - [^\s'"]+ - )///gi + [-a-z]+:// + | + [a-z]{3,}\.[-a-z0-9]+\.[a-z] + | + [-a-z0-9]+\.[a-z] + | + [\d]+\.[\d]+\.[\d]+\.[\d]+/ + | + [a-z]{3,}:[a-z0-9?] + | + [^\s@]+@[a-z0-9.-]+\.[a-z0-9] + )///i else - /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi + /(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1})/i if Conf['Comment Expansion'] ExpandComment.callbacks.push @node @@ -43,16 +40,44 @@ Linkify = return + test = /[^\s'"]+/g + space = /[\s'"]/ + snapshot = $.X './/br|.//text()', @nodes.comment i = 0 while node = snapshot.snapshotItem i++ + links = [] + {data} = node + continue if node.parentElement.nodeName is "A" or not data - continue if node.parentElement.nodeName is "A" - links = [] + while result = test.exec data + {index} = result + endNode = node + if (length = index + result[0].length) is data.length - if Linkify.regString.test node.data - Linkify.regString.lastIndex = 0 - Linkify.gatherLinks snapshot, @, node, links, i + while (saved = snapshot.snapshotItem i++) + break if saved.nodeName is 'BR' + + endNode = saved + {length} = saved.data + + if end = space.exec saved.data + length = end.index + i-- + break + + if length is endNode.data.length then test.lastIndex = 0 + range = Linkify.makeRange node, endNode, index, length + if link = Linkify.regString.exec text = range.toString() + if lIndex = link.index + range.setStart node, lIndex + index + links.push [range, text] + break + + else + if link = Linkify.regString.exec result[0] + range = Linkify.makeRange node, node, link.index, link.length + links.push [range, link] for range in links.reverse() @nodes.links.push Linkify.makeLink range, @ @@ -68,66 +93,29 @@ Linkify = return - gatherLinks: (snapshot, post, node, links, i) -> - {data} = node - len = data.length - - while (match = Linkify.regString.exec data) - {index} = match - link = match[0] - len2 = index + link.length - - break if len is len2 - - range = document.createRange(); - range.setStart node, index - range.setEnd node, len2 - links.push range - - Linkify.regString.lastIndex = 0 - - if match - links.push Linkify.seek snapshot, post, node, links, match, i - - return - - seek: (snapshot, post, node, links, match, i) -> - link = match[0] - range = document.createRange() - range.setStart node, match.index - - while (next = snapshot.snapshotItem i++) and next.nodeName isnt 'BR' - node = next - data = node.data - if result = /[\s'"]/.exec data - {index} = result - range.setEnd node, index - Linkify.regString.lastIndex = index - Linkify.gatherLinks snapshot, post, node, links, i - return range - - if range.collapsed - range.setEndAfter node - + makeRange: (startNode, endNode, startOffset, endOffset) -> + range = document.createRange(); + range.setStart startNode, startOffset + range.setEnd endNode, endOffset range - makeLink: (range) -> - link = range.toString() - link = - if link.contains ':' - link + makeLink: ([range, text]) -> + text + text = + if text.contains ':' + text else ( - if link.contains '@' + if text.contains '@' 'mailto:' else 'http://' - ) + link + ) + text a = $.el 'a', className: 'linkify' rel: 'nofollow noreferrer' target: '_blank' - href: link + href: text $.add a, range.extractContents() range.insertNode a a diff --git a/src/Miscellaneous/ExpandComment.coffee b/src/Miscellaneous/ExpandComment.coffee index d3499eaea..ee7451dde 100755 --- a/src/Miscellaneous/ExpandComment.coffee +++ b/src/Miscellaneous/ExpandComment.coffee @@ -16,8 +16,7 @@ ExpandComment = callbacks: [] cb: (e) -> e.preventDefault() - post = Get.postFromNode @ - ExpandComment.expand post + ExpandComment.expand Get.postFromNode @ expand: (post) -> if post.nodes.longComment and !post.nodes.longComment.parentNode $.replace post.nodes.shortComment, post.nodes.longComment @@ -55,6 +54,11 @@ ExpandComment = href = quote.getAttribute 'href' continue if href[0] is '/' # Cross-board quote, or board link quote.href = "/#{post.board}/res/#{href}" # Fix pathnames + Build.capcodeReplies + boardID: post.board.ID + threadID: post.thread.ID + bq: clone + capcodeReplies: postObj.capcode_replies post.nodes.shortComment = comment $.replace comment, clone post.nodes.comment = post.nodes.longComment = clone diff --git a/src/Monitoring/ThreadStats.coffee b/src/Monitoring/ThreadStats.coffee index a8002a575..452caf4cd 100755 --- a/src/Monitoring/ThreadStats.coffee +++ b/src/Monitoring/ThreadStats.coffee @@ -18,7 +18,6 @@ ThreadStats = @postCountEl = $ '#post-count', sc @fileCountEl = $ '#file-count', sc @pageCountEl = $ '#page-count', sc - @lastModified = '0' Thread::callbacks.push name: 'Thread Stats' @@ -55,12 +54,10 @@ ThreadStats = return setTimeout ThreadStats.fetchPage, 2 * $.MINUTE $.ajax "//api.4chan.org/#{ThreadStats.thread.board}/threads.json", onload: ThreadStats.onThreadsLoad, - headers: 'If-Modified-Since': ThreadStats.lastModified + whenModified: true onThreadsLoad: -> - return if !Conf["Page Count in Stats"] - ThreadStats.lastModified = @getResponseHeader 'Last-Modified' - return if @status isnt 200 + return unless Conf["Page Count in Stats"] and @status is 200 pages = JSON.parse @response for page in pages for thread in page.threads diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index ed4255104..2c245cba3 100755 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -64,7 +64,6 @@ ThreadUpdater = ThreadUpdater.root = @OP.nodes.root.parentNode ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0] ThreadUpdater.outdateCount = 0 - ThreadUpdater.lastModified = '0' ThreadUpdater.cb.interval.call $.el 'input', value: Conf['Interval'] @@ -136,9 +135,7 @@ ThreadUpdater = when 200 g.DEAD = false ThreadUpdater.parse JSON.parse(req.response).posts - ThreadUpdater.lastModified = req.getResponseHeader 'Last-Modified' - if Conf['Auto Update'] - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() + ThreadUpdater.set 'timer', ThreadUpdater.getInterval() when 404 g.DEAD = true ThreadUpdater.set 'timer', null @@ -149,14 +146,8 @@ ThreadUpdater = 404: true thread: ThreadUpdater.thread else - if Conf['Auto Update'] - ThreadUpdater.outdateCount++ - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() - ### - Status Code 304: Not modified - By sending the `If-Modified-Since` header we get a proper status code, and no response. - This saves bandwidth for both the user and the servers and avoid unnecessary computation. - ### + ThreadUpdater.outdateCount++ + ThreadUpdater.set 'timer', ThreadUpdater.getInterval() [text, klass] = if req.status is 304 [null, null] else @@ -218,7 +209,7 @@ ThreadUpdater = ThreadUpdater.req.abort() url = "//api.4chan.org/#{ThreadUpdater.thread.board}/res/#{ThreadUpdater.thread}.json" ThreadUpdater.req = $.ajax url, onloadend: ThreadUpdater.cb.load, - headers: 'If-Modified-Since': ThreadUpdater.lastModified + whenModified: true updateThreadStatus: (title, OP) -> titleLC = title.toLowerCase() diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index f92eff25a..9a48fa021 100755 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -1,116 +1,299 @@ ThreadWatcher = init: -> - return unless Conf['Thread Watcher'] + return if !Conf['Thread Watcher'] + @shortcut = sc = $.el 'a', textContent: 'Watcher' id: 'watcher-link' href: 'javascript:;' className: 'disabled' - @dialog = UI.dialog 'watcher', 'top: 50px; left: 0px;', - '
Thread Watcher×
' + @db = new DataBoard 'watchedThreads', @refresh, true + @dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', """ + <%= grunt.file.read('src/General/html/Monitoring/ThreadWatcher.html').replace(/>\s+<').trim() %> + """ + @status = $ '#watcher-status', @dialog + @list = @dialog.lastElementChild $.on d, 'QRPostSuccessful', @cb.post - $.sync 'WatchedThreads', @refresh + $.on d, 'ThreadUpdate', @cb.threadUpdate if g.VIEW is 'thread' $.on sc, 'click', @toggleWatcher $.on $('.move>.close', ThreadWatcher.dialog), 'click', @toggleWatcher + $.on d, '4chanXInitFinished', @ready if Conf['Toggleable Thread Watcher'] Header.addShortcut sc $.addClass doc, 'fixed-watcher' - $.ready -> - ThreadWatcher.refresh() - $.add d.body, ThreadWatcher.dialog - if Conf['Toggleable Thread Watcher'] - ThreadWatcher.dialog.hidden = true + now = Date.now() + if (@db.data.lastChecked or 0) < now - 2 * $.HOUR + @db.data.lastChecked = now + ThreadWatcher.fetchAllStatus() + @db.save() + + # XXX tmp conversion from old to new format + $.get 'WatchedThreads', null, ({WatchedThreads}) -> + return unless WatchedThreads + for boardID, threads of ThreadWatcher.convert WatchedThreads + for threadID, data of threads + ThreadWatcher.db.set {boardID, threadID, val: data} + $.delete 'WatchedThreads' Thread::callbacks.push name: 'Thread Watcher' cb: @node node: -> - favicon = $.el 'a', - className: 'watch-thread-link' - href: 'javascript:;' - $.on favicon, 'click', ThreadWatcher.cb.toggle - $.before $('input', @OP.nodes.post), favicon - return if g.VIEW isnt 'thread' - $.get 'AutoWatch', 0, (item) => - return if item['AutoWatch'] isnt @ID - ThreadWatcher.watch @ + toggler = $.el 'img', + className: 'watcher-toggler' + $.on toggler, 'click', ThreadWatcher.cb.toggle + $.before $('input', @OP.nodes.post), toggler + + ready: -> + $.off d, '4chanXInitFinished', ThreadWatcher.ready + return unless Main.isThisPageLegit() + ThreadWatcher.refresh() + $.add d.body, ThreadWatcher.dialog + + if Conf['Toggleable Thread Watcher'] + ThreadWatcher.dialog.hidden = true + + return unless Conf['Auto Watch'] + $.get 'AutoWatch', 0, ({AutoWatch}) -> + return unless thread = g.BOARD.threads[AutoWatch] + ThreadWatcher.add thread $.delete 'AutoWatch' - refresh: (watched) -> - unless watched - $.get 'WatchedThreads', {}, (item) -> - ThreadWatcher.refresh item['WatchedThreads'] - return - nodes = [$('.move', ThreadWatcher.dialog)] - for board of watched - for id, props of watched[board] - x = $.el 'a', - textContent: '×' - className: 'close' - href: 'javascript:;' - $.on x, 'click', ThreadWatcher.cb.x - link = $.el 'a', props - link.title = link.textContent - - div = $.el 'div' - $.add div, [x, $.tn(' '), link] - nodes.push div - - $.rmAll ThreadWatcher.dialog - $.add ThreadWatcher.dialog, nodes - - watched = watched[g.BOARD] or {} - for ID, thread of g.BOARD.threads - favicon = $ '.watch-thread-link', thread.OP.nodes.post - if ID of watched - $.addClass favicon, 'watched' - else - $.rmClass favicon, 'watched' - return - toggleWatcher: -> $.toggleClass ThreadWatcher.shortcut, 'disabled' ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden cb: + openAll: -> + return if $.hasClass @, 'disabled' + for a in $$ 'a[title]', ThreadWatcher.list + $.open a.href + $.event 'CloseMenu' + checkThreads: -> + return if $.hasClass @, 'disabled' + ThreadWatcher.fetchAllStatus() + pruneDeads: -> + return if $.hasClass @, 'disabled' + for {boardID, threadID, data} in ThreadWatcher.getAll() when data.isDead + delete ThreadWatcher.db.data.boards[boardID][threadID] + ThreadWatcher.db.deleteIfEmpty {boardID} + ThreadWatcher.db.save() + ThreadWatcher.refresh() + $.event 'CloseMenu' toggle: -> ThreadWatcher.toggle Get.postFromNode(@).thread - x: -> - thread = @nextElementSibling.pathname.split '/' - ThreadWatcher.unwatch thread[1], thread[3] + rm: -> + [boardID, threadID] = @parentNode.dataset.fullID.split '.' + ThreadWatcher.rm boardID, +threadID post: (e) -> {board, postID, threadID} = e.detail if postID is threadID if Conf['Auto Watch'] $.set 'AutoWatch', threadID else if Conf['Auto Watch Reply'] - ThreadWatcher.watch board.threads[threadID] + ThreadWatcher.add board.threads[threadID] + threadUpdate: (e) -> + {thread} = e.detail + return unless e.detail[404] and ThreadWatcher.db.get {boardID: thread.board.ID, threadID: thread.ID} + # Update 404 status. + ThreadWatcher.add thread + + fetchCount: + fetched: 0 + fetching: 0 + fetchAllStatus: -> + ThreadWatcher.status.textContent = '...' + for thread in ThreadWatcher.getAll() + ThreadWatcher.fetchStatus thread + return + fetchStatus: ({boardID, threadID, data}) -> + return if data.isDead + {fetchCount} = ThreadWatcher + fetchCount.fetching++ + $.ajax "//api.4chan.org/#{boardID}/res/#{threadID}.json", + onloadend: -> + fetchCount.fetched++ + if fetchCount.fetched is fetchCount.fetching + fetchCount.fetched = 0 + fetchCount.fetching = 0 + status = '' + else + status = "#{Math.round fetchCount.fetched / fetchCount.fetching * 100}%" + ThreadWatcher.status.textContent = status + return if @status isnt 404 + if Conf['Auto Prune'] + ThreadWatcher.rm boardID, threadID + else + data.isDead = true + ThreadWatcher.db.set {boardID, threadID, val: data} + ThreadWatcher.refresh() + , + type: 'head' + + getAll: -> + all = [] + for boardID, threads of ThreadWatcher.db.data.boards + if Conf['Current Board'] and boardID isnt g.BOARD.ID + continue + for threadID, data of threads + all.push {boardID, threadID, data} + all + + makeLine: (boardID, threadID, data) -> + x = $.el 'a', + textContent: '×' + href: 'javascript:;' + $.on x, 'click', ThreadWatcher.cb.rm + + if data.isDead + href = Redirect.to 'thread', {boardID, threadID} + link = $.el 'a', + href: href or "/#{boardID}/res/#{threadID}" + textContent: data.excerpt + title: data.excerpt + + div = $.el 'div' + fullID = "#{boardID}.#{threadID}" + div.dataset.fullID = fullID + $.addClass div, 'current' if g.VIEW is 'thread' and fullID is "#{g.BOARD}.#{g.THREADID}" + $.addClass div, 'dead-thread' if data.isDead + $.add div, [x, $.tn(' '), link] + div + refresh: -> + nodes = [] + for {boardID, threadID, data} in ThreadWatcher.getAll() + nodes.push ThreadWatcher.makeLine boardID, threadID, data + + {list} = ThreadWatcher + $.rmAll list + $.add list, nodes + + for threadID, thread of g.BOARD.threads + toggler = $ '.watcher-toggler', thread.OP.nodes.post + watched = ThreadWatcher.db.get {boardID: thread.board.ID, threadID} + $[if watched then 'addClass' else 'rmClass'] toggler, 'watched' + + for refresher in ThreadWatcher.menu.refreshers + refresher() + return toggle: (thread) -> - unless $.hasClass $('.watch-thread-link', thread.OP.nodes.post), 'watched' - ThreadWatcher.watch thread + boardID = thread.board.ID + threadID = thread.ID + if ThreadWatcher.db.get {boardID, threadID} + ThreadWatcher.rm boardID, threadID else - ThreadWatcher.unwatch thread.board, thread.ID + ThreadWatcher.add thread + add: (thread) -> + data = {} + boardID = thread.board.ID + threadID = thread.ID + if thread.isDead + if Conf['Auto Prune'] and ThreadWatcher.db.get {boardID, threadID} + ThreadWatcher.rm boardID, threadID + return + data.isDead = true + data.excerpt = Get.threadExcerpt thread + ThreadWatcher.db.set {boardID, threadID, val: data} + ThreadWatcher.refresh() + rm: (boardID, threadID) -> + ThreadWatcher.db.delete {boardID, threadID} + ThreadWatcher.refresh() - unwatch: (board, threadID) -> - $.get 'WatchedThreads', {}, (item) -> - watched = item['WatchedThreads'] - delete watched[board][threadID] - delete watched[board] unless Object.keys(watched[board]).length - ThreadWatcher.refresh watched - $.set 'WatchedThreads', watched + convert: (oldFormat) -> + newFormat = {} + for boardID, threads of oldFormat + for threadID, data of threads + (newFormat[boardID] or= {})[threadID] = excerpt: data.textContent + newFormat - watch: (thread) -> - $.get 'WatchedThreads', {}, (item) -> - watched = item['WatchedThreads'] - watched[thread.board] or= {} - watched[thread.board][thread] = - href: "/#{thread.board}/res/#{thread}" - textContent: Get.threadExcerpt thread - ThreadWatcher.refresh watched - $.set 'WatchedThreads', watched \ No newline at end of file + menu: + refreshers: [] + init: -> + return if !Conf['Thread Watcher'] + menu = new UI.Menu 'thread watcher' + $.on $('.menu-button', ThreadWatcher.dialog), 'click', (e) -> + menu.toggle e, @, ThreadWatcher + @addHeaderMenuEntry() + @addMenuEntries() + + addHeaderMenuEntry: -> + return if g.VIEW isnt 'thread' + entryEl = $.el 'a', + href: 'javascript:;' + $.event 'AddMenuEntry', + type: 'header' + el: entryEl + order: 60 + $.on entryEl, 'click', -> ThreadWatcher.toggle g.threads["#{g.BOARD}.#{g.THREADID}"] + @refreshers.push -> + [addClass, rmClass, text] = if $ '.current', ThreadWatcher.list + ['unwatch-thread', 'watch-thread', 'Unwatch thread'] + else + ['watch-thread', 'unwatch-thread', 'Watch thread'] + $.addClass entryEl, addClass + $.rmClass entryEl, rmClass + entryEl.textContent = text + + addMenuEntries: -> + entries = [] + + # `Open all` entry + entries.push + cb: ThreadWatcher.cb.openAll + entry: + type: 'thread watcher' + el: $.el 'a', + textContent: 'Open all threads' + refresh: -> (if ThreadWatcher.list.firstElementChild then $.rmClass else $.addClass) @el, 'disabled' + + # `Check 404'd threads` entry + entries.push + cb: ThreadWatcher.cb.checkThreads + entry: + type: 'thread watcher' + el: $.el 'a', + textContent: 'Check 404\'d threads' + refresh: -> (if $('div:not(.dead-thread)', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled' + + # `Prune 404'd threads` entry + entries.push + cb: ThreadWatcher.cb.pruneDeads + entry: + type: 'thread watcher' + el: $.el 'a', + textContent: 'Prune 404\'d threads' + refresh: -> (if $('.dead-thread', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled' + + # `Settings` entries: + subEntries = [] + for name, conf of Config.threadWatcher + subEntries.push @createSubEntry name, conf[1] + entries.push + entry: + type: 'thread watcher' + el: $.el 'span', + textContent: 'Settings' + subEntries: subEntries + + for {entry, cb, refresh} in entries + entry.el.href = 'javascript:;' if entry.el.nodeName is 'A' + $.on entry.el, 'click', cb if cb + @refreshers.push refresh.bind entry if refresh + $.event 'AddMenuEntry', entry + createSubEntry: (name, desc) -> + entry = + type: 'thread watcher' + el: $.el 'label', + innerHTML: " #{name}" + title: desc + input = entry.el.firstElementChild + input.checked = Conf[name] + $.on input, 'change', $.cb.checked + $.on input, 'change', ThreadWatcher.refresh if name is 'Current Board' + entry diff --git a/src/Posting/QuickReply.coffee b/src/Posting/QuickReply.coffee index 9e83b6779..bb8231e0f 100755 --- a/src/Posting/QuickReply.coffee +++ b/src/Posting/QuickReply.coffee @@ -660,21 +660,9 @@ QR = cv.width = img.width = width cv.getContext('2d').drawImage img, 0, 0, width, height URL.revokeObjectURL fileURL - applyBlob = (blob) => + cv.toBlob (blob) => @URL = URL.createObjectURL blob @nodes.el.style.backgroundImage = "url(#{@URL})" - if cv.toBlob - cv.toBlob applyBlob - return - data = atob cv.toDataURL().split(',')[1] - - # DataUrl to Binary code from Aeosynth's 4chan X repo - l = data.length - ui8a = new Uint8Array l - for i in [0...l] - ui8a[i] = data.charCodeAt i - - applyBlob new Blob [ui8a], type: 'image/png' fileURL = URL.createObjectURL @file img.src = fileURL diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index b5b9e4a60..b66eb96da 100755 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -16,7 +16,6 @@ QuoteYou = cb: @node node: -> - # Stop there if it's a clone. return if @isClone if @info.yours