diff --git a/4chan-X.meta.js b/4chan-X.meta.js index cb6ccf69c..9640407d7 100644 --- a/4chan-X.meta.js +++ b/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 3.0.4 +// @version 3.0.6 // @namespace 4chan-X // @description Cross-browser extension for productive lurking on 4chan. // @copyright 2009-2011 James Campos diff --git a/4chan-X.user.js b/4chan-X.user.js index cf4495f2c..8b203d95e 100644 --- a/4chan-X.user.js +++ b/4chan-X.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 3.0.4 +// @version 3.0.6 // @namespace 4chan-X // @description Cross-browser extension for productive lurking on 4chan. // @copyright 2009-2011 James Campos @@ -20,7 +20,7 @@ // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC // ==/UserScript== -/* 4chan X - Version 3.0.4 - 2013-04-12 +/* 4chan X - Version 3.0.6 - 2013-04-13 * https://4chan-x.just-believe.in/ * * Copyright (c) 2009-2011 James Campos @@ -115,6 +115,7 @@ 'Auto Hide QR': [false, 'Automatically hide the quick reply when posting.'], 'Open Post in New Tab': [true, 'Open new threads or replies to a thread from the index in a new tab.'], 'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'], + 'Remember QR Size': [false, 'Remember the size of the Quick reply.'], 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'], 'Hide Original Post Form': [true, 'Hide the normal post form.'], 'Cooldown': [true, 'Prevent "flood detected" errors.'] @@ -212,7 +213,7 @@ doc = d.documentElement; g = { - VERSION: '3.0.4', + VERSION: '3.0.6', NAMESPACE: '4chan X.', boards: {}, threads: {}, @@ -230,7 +231,9 @@ innerHTML: html, id: id }); - el.style.cssText = localStorage.getItem("" + g.NAMESPACE + id + ".position") || position; + $.get("" + id + ".position", position, function(item) { + return el.style.cssText = item["" + id + ".position"]; + }); move = $('.move', el); $.on(move, 'touchstart mousedown', dragstart); _ref = move.children; @@ -565,7 +568,7 @@ $.off(d, 'mousemove', this.move); $.off(d, 'mouseup', this.up); } - return localStorage.setItem("" + g.NAMESPACE + this.id + ".position", this.style.cssText); + return $.set("" + this.id + ".position", this.style.cssText); }; hoverstart = function(_arg) { var asapTest, cb, el, endEvents, latestEvent, o, root; @@ -844,9 +847,19 @@ hasClass: function(el, className) { return el.classList.contains(className); }, - rm: function(el) { - return el.parentNode.removeChild(el); - }, + rm: (function() { + if ('remove' in Element.prototype) { + return function(el) { + return el.remove(); + }; + } else { + return function(el) { + var _ref; + + return (_ref = el.parentNode) != null ? _ref.removeChild(el) : void 0; + }; + } + })(), tn: function(s) { return d.createTextNode(s); }, @@ -1227,6 +1240,9 @@ if (!(post = $.id(this.location.hash.slice(1)))) { return; } + if ((Get.postFromRoot(post)).isHidden) { + return; + } return Header.scrollToPost(post); }, scrollToPost: function(post) { @@ -1298,9 +1314,7 @@ }; close = function() { - if (this.el.parentNode) { - return $.rm(this.el); - } + return $.rm(this.el); }; return Notification; @@ -2968,7 +2982,7 @@ init: function() { var div, fileEl, fileEntry, postEl, postEntry; - if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Delete Link'] || !Conf['Quick Reply']) { + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Delete Link']) { return; } div = $.el('div', { @@ -3025,7 +3039,7 @@ }); }, "delete": function() { - var form, link, m, post, pwd; + var fileOnly, form, link, m, post, pwd; post = DeleteLink.post; if (DeleteLink.cooldown.counting === post) { @@ -3034,16 +3048,17 @@ $.off(this, 'click', DeleteLink["delete"]); this.textContent = "Deleting " + this.textContent + "..."; pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $.id('delPassword').value; + fileOnly = $.hasClass(this, 'delete-file'); form = { mode: 'usrdel', - onlyimgdel: $.hasClass(this, 'delete-file'), + onlyimgdel: fileOnly, pwd: pwd }; form[post.ID] = 'delete'; link = this; return $.ajax($.id('delform').action.replace("/" + g.BOARD + "/", "/" + post.board + "/"), { onload: function() { - return DeleteLink.load(link, post, this.response); + return DeleteLink.load(link, post, fileOnly, this.response); }, onerror: function() { return DeleteLink.error(link); @@ -3053,7 +3068,7 @@ form: $.formData(form) }); }, - load: function(link, post, html) { + load: function(link, post, fileOnly, html) { var msg, s, tmpDoc; tmpDoc = d.implementation.createHTMLDocument(''); @@ -3065,7 +3080,7 @@ $.on(link, 'click', DeleteLink["delete"]); } else { if (tmpDoc.title === 'Updating index...') { - (post.origin || post).kill(); + (post.origin || post).kill(fileOnly); } s = 'Deleted'; } @@ -3515,8 +3530,19 @@ init: function() { var next, prev, span; - if (g.VIEW === 'index' && !Conf['Index Navigation'] || g.VIEW === 'thread' && !Conf['Reply Navigation']) { - return; + switch (g.VIEW) { + case 'index': + if (!Conf['Index Navigation']) { + return; + } + break; + case 'thread': + if (!Conf['Reply Navigation']) { + return; + } + break; + default: + return; } span = $.el('span', { id: 'navlinks' @@ -5701,7 +5727,7 @@ }); }, node: function() { - var ID, hash, post, posts, _ref; + var ID, hash, post, posts, root, _ref; Unread.thread = this; Unread.title = d.title; @@ -5719,17 +5745,26 @@ defaultValue: 0 }); Unread.addPosts(posts); - if ((hash = location.hash.match(/\d+/)) && (post = this.posts[hash[0]])) { - Header.scrollToPost(post.nodes.root); - } else if (Unread.posts.length) { - $.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView(false); - } else if (posts.length) { - Header.scrollToPost(posts[posts.length - 1].nodes.root); - } $.on(d, 'ThreadUpdate', Unread.onUpdate); $.on(d, 'scroll visibilitychange', Unread.read); if (Conf['Unread Line']) { - return $.on(d, 'visibilitychange', Unread.setLine); + $.on(d, 'visibilitychange', Unread.setLine); + } + if (!Conf['Scroll to Last Read Post']) { + return; + } + if ((hash = location.hash.match(/\d+/)) && hash[0] in this.posts) { + return; + } + if (Unread.posts.length) { + while (root = $.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root)) { + if (!(Get.postFromRoot(root)).isHidden) { + break; + } + } + return root.scrollIntoView(false); + } else if (posts.length) { + return Header.scrollToPost(posts[posts.length - 1].nodes.root); } }, sync: function() { @@ -5869,7 +5904,7 @@ if (root !== $('.thread > .replyContainer', root.parentNode)) { return $.before(root, Unread.hr); } - } else if (Unread.hr.parentNode) { + } else { return $.rm(Unread.hr); } }, @@ -6769,9 +6804,9 @@ }); $.on(sc, 'click', function() { if (!QR.nodes || QR.nodes.el.hidden) { + $.event('CloseMenu'); QR.open(); QR.nodes.com.focus(); - QR.resetThreadSelector(); } else { QR.close(); } @@ -6863,6 +6898,12 @@ return QR.nodes.spoiler.click(); } }, + focusin: function() { + return $.addClass(QR.nodes.el, 'has-focus'); + }, + focusout: function() { + return $.rmClass(QR.nodes.el, 'has-focus'); + }, hide: function() { d.activeElement.blur(); $.addClass(QR.nodes.el, 'autohide'); @@ -7028,7 +7069,7 @@ setTimeout(QR.cooldown.count, $.SECOND); now = Date.now(); post = QR.posts[0]; - isReply = QR.nodes.thread.value !== 'new'; + isReply = post.thread !== 'new'; isSage = /sage/i.test(post.email); hasFile = !!post.file; seconds = null; @@ -7070,7 +7111,7 @@ } }, quote: function(e) { - var OP, caretPos, post, range, s, sel, selectionRoot, ta, text; + var OP, caretPos, com, post, range, s, sel, selectionRoot, text, thread, _ref; if (e != null) { e.preventDefault(); @@ -7088,16 +7129,17 @@ text += ">" + s + "\n"; } QR.open(); - ta = QR.nodes.com; - if (!ta.value) { - QR.nodes.thread.value = OP.ID; + _ref = QR.nodes, com = _ref.com, thread = _ref.thread; + if (!com.value) { + thread.value = OP.ID; } - caretPos = ta.selectionStart; - ta.value = ta.value.slice(0, caretPos) + text + ta.value.slice(ta.selectionEnd); + caretPos = com.selectionStart; + com.value = com.value.slice(0, caretPos) + text + com.value.slice(com.selectionEnd); range = caretPos + text.length; - ta.setSelectionRange(range, range); - ta.focus(); - return $.event('input', null, ta); + com.setSelectionRange(range, range); + com.focus(); + QR.selected.save(com); + return QR.selected.save(thread); }, characterCount: function() { var count, counter; @@ -7202,17 +7244,10 @@ } return $.addClass(QR.nodes.el, 'dump'); }, - resetThreadSelector: function() { - if (g.VIEW === 'thread') { - return QR.nodes.thread.value = g.THREADID; - } else { - return QR.nodes.thread.value = 'new'; - } - }, posts: [], post: (function() { function _Class(select) { - var el, event, prev, _i, _len, _ref, + var el, elm, event, prev, _i, _j, _len, _len1, _ref, _ref1, _this = this; el = $.el('a', { @@ -7228,7 +7263,12 @@ spoiler: $('input', el), span: el.lastChild }; - this.nodes.spoiler.checked = this.spoiler; + _ref = $$('*', el); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + elm = _ref[_i]; + $.on(elm, 'blur', QR.focusout); + $.on(elm, 'focus', QR.focusin); + } $.on(el, 'click', this.select.bind(this)); $.on(this.nodes.rm, 'click', function(e) { e.stopPropagation(); @@ -7244,14 +7284,15 @@ } }); $.add(QR.nodes.dumpList, el); - _ref = ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - event = _ref[_i]; + _ref1 = ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + event = _ref1[_j]; $.on(el, event.toLowerCase(), this[event]); } + this.thread = g.VIEW === 'thread' ? g.THREADID : 'new'; prev = QR.posts[QR.posts.length - 1]; QR.posts.push(this); - this.spoiler = prev && Conf['Remember Spoiler'] ? prev.spoiler : false; + this.nodes.spoiler.checked = this.spoiler = prev && Conf['Remember Spoiler'] ? prev.spoiler : false; $.get('QR.persona', {}, function(item) { var persona; @@ -7298,7 +7339,7 @@ if (this !== QR.selected) { return; } - _ref = ['name', 'email', 'sub', 'com', 'spoiler']; + _ref = ['thread', 'name', 'email', 'sub', 'com', 'spoiler']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; QR.nodes[name].disabled = lock; @@ -7332,7 +7373,7 @@ _Class.prototype.load = function() { var name, _i, _len, _ref; - _ref = ['name', 'email', 'sub', 'com']; + _ref = ['thread', 'name', 'email', 'sub', 'com']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; QR.nodes[name].value = this[name] || null; @@ -7344,6 +7385,10 @@ _Class.prototype.save = function(input) { var value, _ref; + if (input.type === 'checkbox') { + this.spoiler = input.checked; + return; + } value = input.value; this[input.dataset.name] = value; if (input.nodeName !== 'TEXTAREA') { @@ -7362,7 +7407,7 @@ if (this !== QR.selected) { return; } - _ref = ['name', 'email', 'sub', 'com']; + _ref = ['thread', 'name', 'email', 'sub', 'com', 'spoiler']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; this.save(QR.nodes[name]); @@ -7472,7 +7517,7 @@ return URL.revokeObjectURL(this.URL); }; - _Class.prototype.showFileData = function(hide) { + _Class.prototype.showFileData = function() { if (this.file) { QR.nodes.filename.textContent = this.filename; QR.nodes.filename.title = this.filename; @@ -7610,6 +7655,8 @@ }); $.sync('captchas', this.sync); this.reload(); + $.on(input, 'blur', QR.focusout); + $.on(input, 'focus', QR.focusin); $.addClass(QR.nodes.el, 'has-captcha'); return $.after(QR.nodes.com.parentNode, [imgContainer, input]); }, @@ -7722,9 +7769,9 @@ } }, dialog: function() { - var dialog, mimeTypes, name, nodes, thread, _i, _len, _ref; + var dialog, elm, mimeTypes, name, nodes, thread, _i, _j, _len, _len1, _ref, _ref1; - dialog = UI.dialog('qr', 'top:0;right:0;', "
\n \n ×\n \n
\n
\n
\n \n \n \n \n
\n
\n \n \n
\n
\n
\n +\n
\n
\n \n No selected file\n \n ×\n \n \n
\n \n \n
".replace(/>\s+<')); + dialog = UI.dialog('qr', 'top:0;right:0;', "
\n \n ×\n \n
\n
\n
\n \n \n \n \n
\n
\n \n \n
\n
\n
\n +\n
\n
\n \n No selected file\n \n ×\n \n \n
\n \n \n
".replace(/>\s+<')); QR.nodes = nodes = { el: dialog, move: $('.move', dialog), @@ -7780,8 +7827,15 @@ textContent: "Thread No." + thread })); } - QR.resetThreadSelector(); $.on(nodes.filename.parentNode, 'click keyup', QR.openFileInput); + _ref = $$('*', QR.nodes.el); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + elm = _ref[_i]; + $.on(elm, 'blur', QR.focusout); + $.on(elm, 'focus', QR.focusin); + } + $.on(QR.nodes.el, 'focusin', QR.focusin); + $.on(QR.nodes.el, 'focusout', QR.focusout); $.on(nodes.autohide, 'change', QR.toggleHide); $.on(nodes.close, 'click', QR.close); $.on(nodes.dumpButton, 'click', function() { @@ -7798,20 +7852,28 @@ return QR.selected.nodes.spoiler.click(); }); $.on(nodes.fileInput, 'change', QR.fileInput); - new QR.post(true); - _ref = ['name', 'email', 'sub', 'com']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; + _ref1 = ['name', 'email', 'sub', 'com']; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + name = _ref1[_j]; $.on(nodes[name], 'input', function() { return QR.selected.save(this); }); - $.on(nodes[name], 'focus', function() { - return $.addClass(nodes.el, 'focus'); + } + $.on(nodes.thread, 'change', function() { + return QR.selected.save(this); + }); + if (Conf['Remember QR Size']) { + $.get('QR Size', '', function(item) { + return nodes.com.style.cssText = item['QR Size']; }); - $.on(nodes[name], 'blur', function() { - return $.rmClass(nodes.el, 'focus'); + $.on(nodes.com, 'mouseup', function(e) { + if (e.button !== 0) { + return; + } + return $.set('QR Size', this.style.cssText); }); } + new QR.post(true); QR.status(); QR.cooldown.init(); QR.captcha.init(); @@ -7838,7 +7900,7 @@ if (g.BOARD.ID === 'f') { filetag = QR.nodes.flashTag.value; } - threadID = QR.nodes.thread.value; + threadID = post.thread; thread = g.BOARD.threads[threadID]; if (threadID === 'new') { threadID = null; @@ -7924,7 +7986,7 @@ return QR.status(); }, response: function() { - var URL, ban, board, err, h1, isReply, post, postID, req, threadID, tmpDoc, _, _ref, _ref1; + var URL, ban, board, err, h1, isReply, m, post, postID, req, threadID, tmpDoc, _, _ref, _ref1; req = QR.req; delete QR.req; @@ -7955,6 +8017,11 @@ QR.cooldown.set({ delay: 2 }); + } else if (err.textContent && (m = err.textContent.match(/wait\s(\d+)\ssecond/i))) { + QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : true; + QR.cooldown.set({ + delay: m[1] + }); } else { QR.cooldown.auto = false; } @@ -8988,7 +9055,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;\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\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\n}\n.post {\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#updater, #thread-stats,\n#navlinks, #header,\n#watcher,\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#qr {\nz-index: 30;\n}\n#watcher:hover {\nz-index: 20;\n}\n#header {\nz-index: 10;\n}\n#watcher {\nz-index: 5;\n}\n\n/* Header */\n.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#header {\nright: 0;\nleft: 0;\n}\n#header.top {\ntop: 0;\n}\n#header.bottom {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\npadding: 3px 4px 4px;\nposition: relative;\ntransition: all .1s .05s ease-in-out;\n}\n#header.top #header-bar {\nborder-bottom-width: 1px;\n}\n#header.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n#header.bottom .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n#header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n#header.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n#header.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#toggle-header-bar {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n#header.top #toggle-header-bar {\ncursor: n-resize;\nbottom: -8px;\n}\n#header.bottom #toggle-header-bar {\ncursor: s-resize;\ntop: -8px;\n}\n#header-bar.autohide:not(:hover) #toggle-header-bar,\n#toggle-header-bar:hover {\nheight: 18px;\n}\n#header.top #header-bar.autohide:not(:hover) #toggle-header-bar,\n#header.top #toggle-header-bar:hover {\nbottom: -16px;\n}\n#header.bottom #header-bar.autohide:not(:hover) #toggle-header-bar,\n#header.bottom #toggle-header-bar:hover {\ntop: -16px;\n}\n#header.top #header-bar.autohide #toggle-header-bar {\ncursor: s-resize;\n}\n#header.bottom #header-bar.autohide #toggle-header-bar {\ncursor: n-resize;\n}\n#header-bar a:not(.entry) {\ntext-decoration: none;\npadding: 1px;\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\n/* Notifications */\n#notifications {\nheight: 0;\ntext-align: center;\nposition: fixed;\ntop: 0;\nright: 0;\nleft: 0;\n}\n#header.bottom #notifications {\nposition: fixed;\ntop: 0;\nleft: 0;\nwidth: 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: 0;\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#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;\nmin-height: 0;\nmax-height: 100%;\nwidth: 900px;\nmin-width: 0;\nmax-width: 100%;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\n-o-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\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: 1.7em;\nright: 5px;\nbottom: 5px;\nleft: 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-container {\noverflow: auto;\nposition: absolute;\ntop: 1.7em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\n}\n.section-sauce ul,\n.section-rice ul {\nlist-style: none;\nmargin: 0;\npadding: 8px;\n}\n.section-sauce li,\n.section-rice 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-rice .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-rice textarea {\nheight: 150px;\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/* Unread */\n#unread-line {\nmargin: 0;\n}\n\n/* Thread Updater */\n#updater:not(:hover) {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 0 3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n#updater:not(:hover) > div:not(.move) {\ndisplay: none;\n}\n#updater input[type=\"button\"] {\nwidth: 100%;\n}\n.new {\ncolor: limegreen;\n}\n\n/* Thread Watcher */\n#watcher {\npadding-bottom: 3px;\noverflow: hidden;\nwhite-space: nowrap;\n}\n#watcher:not(:hover) {\nmax-height: 220px;\n}\n#watcher > .move {\npadding-top: 3px;\n}\n#watcher > div {\nmax-width: 200px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#watcher a {\ntext-decoration: none;\n}\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink), .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.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: 300px;\nmax-width: 500px;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\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.expanded-image {\nclear: both;\n}\n.expanded-image > .op > .file::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image,\n:root.presto.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\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/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ .sideArrows,\n.stub ~ .hide-reply-button,\n.stub ~ .post {\ndisplay: none !important;\n}\n.stub input {\ndisplay: inline-block;\n}\n\n/* QR */\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 248px;\nborder-radius: 3px 3px 0 0;\n}\n#qrtab {\nborder-radius: 3px 3px 0 0;\n}\n.autohide:not(:hover):not(.focus) > form {\ndisplay: none !important;\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.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nbackground: linear-gradient(#EEE, #CCC);\nborder: 1px solid #CCC; \nwidth: 10%;\nmargin: 0;\n}\n.persona input.field {\nwidth: 29.6%;\nmargin: 0 0 0 0.4%;\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; text-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 1px 0 0;\ntext-align: center;\nbackground-image: #fff;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.field,\n.selectrice,\nbutton,\ninput:not([type=radio]) {\n-moz-box-sizing: border-box;\nfont-size: 12px;\nheight: 1.6em;\nmargin: 1px 0 0;\nvertical-align: bottom;\npadding: 0 1px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\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: block;\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%;\noverflow: hidden;\npadding: 2px 1px 0;\n}\n#qr-filerm {\nposition: absolute;\nright: 3px;\ntop: 2px;\nz-index: 2;\n}\n#qr input[type=file] {\ndisplay: none;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\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: pre;\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;\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; bottom: 0; left: 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#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;\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;\nz-index: 22;\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.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.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\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 {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar 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/* 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 {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar 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/* 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 {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar 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/* 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 {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar 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/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .field:focus {\nborder-color: #000;\n}\n\n/* Header */\n:root.tomorrow #header-bar {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar 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 select {\ncolor: #C5C8C6;\n}\n:root.tomorrow #qr option {\ncolor: #000;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\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/* 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 {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar 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" + 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;\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\n/* 4chan style fixes */\n.opContainer, .op {\ndisplay: block !important;\n}\n.post {\noverflow: visible !important;\n}\n[hidden] {\ndisplay: none !important;\n}\n\n/* fixed, z-index */\n#overlay,\n#fourchanx-settings,\n#qp, #ihover,\n#updater, #thread-stats,\n#navlinks, #header,\n#watcher,\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#qr {\nz-index: 30;\n}\n#watcher:hover {\nz-index: 20;\n}\n#header {\nz-index: 10;\n}\n#watcher {\nz-index: 5;\n}\n\n/* Header */\n.fourchan-x body {\n-moz-box-sizing: border-box;\nbox-sizing: border-box;\n}\n#header {\nright: 0;\nleft: 0;\n}\n#header.top {\ntop: 0;\n}\n#header.bottom {\nbottom: 0;\n}\n#header-bar {\nborder-width: 0;\npadding: 3px 4px 4px;\nposition: relative;\ntransition: all .1s .05s ease-in-out;\n}\n#header.top #header-bar {\nborder-bottom-width: 1px;\n}\n#header.bottom #header-bar {\nbox-shadow: 0 -1px 2px rgba(0, 0, 0, .15);\nborder-top-width: 1px;\n}\n#header.bottom .menu-button i {\nborder-top: none;\nborder-bottom: 6px solid;\n}\n#board-list {\ntext-align: center;\n}\n#header-bar.autohide:not(:hover) {\nbox-shadow: none;\ntransition: all .8s .6s cubic-bezier(.55, .055, .675, .19);\n}\n#header.top #header-bar.autohide:not(:hover) {\nmargin-bottom: -1em;\n-webkit-transform: translateY(-100%);\ntransform: translateY(-100%);\n}\n#header.bottom #header-bar.autohide:not(:hover) {\n-webkit-transform: translateY(100%);\ntransform: translateY(100%);\n}\n#toggle-header-bar {\nleft: 0;\nright: 0;\nheight: 10px;\nposition: absolute;\n}\n#header.top #toggle-header-bar {\ncursor: n-resize;\nbottom: -8px;\n}\n#header.bottom #toggle-header-bar {\ncursor: s-resize;\ntop: -8px;\n}\n#header-bar.autohide:not(:hover) #toggle-header-bar,\n#toggle-header-bar:hover {\nheight: 18px;\n}\n#header.top #header-bar.autohide:not(:hover) #toggle-header-bar,\n#header.top #toggle-header-bar:hover {\nbottom: -16px;\n}\n#header.bottom #header-bar.autohide:not(:hover) #toggle-header-bar,\n#header.bottom #toggle-header-bar:hover {\ntop: -16px;\n}\n#header.top #header-bar.autohide #toggle-header-bar {\ncursor: s-resize;\n}\n#header.bottom #header-bar.autohide #toggle-header-bar {\ncursor: n-resize;\n}\n#header-bar a:not(.entry) {\ntext-decoration: none;\npadding: 1px;\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\n/* Notifications */\n#notifications {\nheight: 0;\ntext-align: center;\nposition: fixed;\ntop: 0;\nright: 0;\nleft: 0;\n}\n#header.bottom #notifications {\nposition: fixed;\ntop: 0;\nleft: 0;\nwidth: 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: 0;\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#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;\nmin-height: 0;\nmax-height: 100%;\nwidth: 900px;\nmin-width: 0;\nmax-width: 100%;\npadding: 3px;\ntop: 50%;\nleft: 50%;\n-moz-transform: translate(-50%, -50%);\n-webkit-transform: translate(-50%, -50%);\n-o-transform: translate(-50%, -50%);\ntransform: translate(-50%, -50%);\n}\n#fourchanx-settings > nav {\npadding: 2px 2px 0;\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: 1.7em;\nright: 5px;\nbottom: 5px;\nleft: 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-container {\noverflow: auto;\nposition: absolute;\ntop: 1.7em;\nright: 5px;\nbottom: 5px;\nleft: 5px;\n}\n.section-sauce ul,\n.section-rice ul {\nlist-style: none;\nmargin: 0;\npadding: 8px;\n}\n.section-sauce li,\n.section-rice 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-rice .field[name=\"boardnav\"] {\nwidth: 100%;\n}\n.section-rice textarea {\nheight: 150px;\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/* Unread */\n#unread-line {\nmargin: 0;\n}\n\n/* Thread Updater */\n#updater:not(:hover) {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n#updater > .move {\npadding: 0 3px;\n}\n#updater > div:last-child {\ntext-align: center;\n}\n#updater input[type=number] {\nwidth: 4em;\n}\n#updater:not(:hover) > div:not(.move) {\ndisplay: none;\n}\n#updater input[type=\"button\"] {\nwidth: 100%;\n}\n.new {\ncolor: limegreen;\n}\n\n/* Thread Watcher */\n#watcher {\npadding-bottom: 3px;\noverflow: hidden;\nwhite-space: nowrap;\n}\n#watcher:not(:hover) {\nmax-height: 220px;\n}\n#watcher > .move {\npadding-top: 3px;\n}\n#watcher > div {\nmax-width: 200px;\noverflow: hidden;\npadding-left: 3px;\npadding-right: 3px;\ntext-overflow: ellipsis;\n}\n#watcher a {\ntext-decoration: none;\n}\n\n/* Thread Stats */\n#thread-stats {\nbackground: none;\nborder: none;\nbox-shadow: none;\n}\n\n/* Quote */\n.deadlink {\ntext-decoration: none !important;\n}\n.backlink.deadlink:not(.forwardlink), .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.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: 300px;\nmax-width: 500px;\n}\n.qphl {\noutline: 2px solid rgba(216, 94, 49, .7);\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.expanded-image {\nclear: both;\n}\n.expanded-image > .op > .file::after {\ncontent: '';\nclear: both;\ndisplay: table;\n}\n:root.fit-width .full-image {\nmax-width: 100%;\n}\n:root.gecko.fit-width .full-image,\n:root.presto.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\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/* Thread & Reply Hiding */\n.hide-thread-button,\n.hide-reply-button {\nfloat: left;\nmargin-right: 2px;\n}\n.stub ~ .sideArrows,\n.stub ~ .hide-reply-button,\n.stub ~ .post {\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 {\ndisplay: none;\n}\n#qr select, #dump-button, .remove, .captcha-img {\ncursor: pointer;\n}\n#qr {\nz-index: 20;\nposition: fixed;\npadding: 1px;\nborder: 1px solid transparent;\nmin-width: 248px;\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.persona {\nwidth: 248px;\nmax-width: 100%;\nmin-width: 100%;\n}\n#dump-button {\nbackground: linear-gradient(#EEE, #CCC);\nborder: 1px solid #CCC; \nwidth: 10%;\nmargin: 0;\n}\n.persona input.field {\nwidth: 29.6%;\nmargin: 0 0 0 0.4%;\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; text-shadow: none !important;\n}\n#qr textarea {\nresize: both;\n}\n.captcha-img {\nmargin: 1px 0 0;\ntext-align: center;\nbackground-image: #fff;\n}\n.captcha-input {\nwidth: 100%;\nmargin: 1px 0 0;\n}\n.field,\n.selectrice,\nbutton,\ninput:not([type=radio]) {\n-moz-box-sizing: border-box;\nfont-size: 12px;\nheight: 1.6em;\nmargin: 1px 0 0;\nvertical-align: bottom;\npadding: 0 1px;\n}\n#qr textarea {\nmin-width: 100%;\n}\n#qr [type='submit'] {\nwidth: 25%;\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: block;\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%;\noverflow: hidden;\npadding: 2px 1px 0;\n}\n#qr-filerm {\nposition: absolute;\nright: 3px;\ntop: 2px;\nz-index: 2;\n}\n#qr input[type=file] {\ndisplay: none;\n}\n/* Thread Select / Spoiler Label */\n#qr select {\nfloat: right;\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: pre;\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;\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; bottom: 0; left: 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#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;\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;\nz-index: 22;\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.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.submenu {\ndisplay: none;\nposition: absolute;\nleft: 100%;\ntop: -1px;\n}\n.focused .submenu {\ndisplay: block;\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 {\nfont-size: 9pt;\ncolor: #B86;\n}\n:root.yotsuba #header-bar 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/* 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 {\nfont-size: 9pt;\ncolor: #89A;\n}\n:root.yotsuba-b #header-bar 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/* 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 {\nfont-size: 11pt;\ncolor: #B86;\n}\n:root.futaba #header-bar 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/* 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 {\nfont-size: 11pt;\ncolor: #89A;\n}\n:root.burichan #header-bar 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/* General */\n:root.tomorrow .dialog {\nbackground-color: #282A2E;\nborder-color: #111;\n}\n:root.tomorrow .field:focus {\nborder-color: #000;\n}\n\n/* Header */\n:root.tomorrow #header-bar {\nfont-size: 9pt;\ncolor: #C5C8C6;\n}\n:root.tomorrow #header-bar 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 select {\ncolor: #C5C8C6;\n}\n:root.tomorrow #qr option {\ncolor: #000;\n}\n:root.tomorrow .qr-preview {\nbackground-color: rgba(255, 255, 255, .15);\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/* 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 {\nfont-size: 9pt;\ncolor: #333;\n}\n:root.photon #header-bar 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" }; Main.init(); diff --git a/CHANGELOG.md b/CHANGELOG.md index a71bbf936..2ed9212e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +### 3.0.6 - *2013-04-14* + +- Fix regression concerning thread selection when quoting on the index. + +### 3.0.5 - *2013-04-14* + +- `Scroll to Last Read Post` is now optional, enabled by default. +- The QR won't auto-hide when auto-hide is enabled and one of its input is focused. Doesn't work on Firefox. +- Added the `Remember QR Size` setting back in, disabled by default. Only on Firefox. +- Fix QR remembering the file spoiler state when it shouldn't. +- Fix QR cooldown in Opera. + ### 3.0.4 - *2013-04-11* - More minor fixes. @@ -9,7 +21,7 @@ ### 3.0.2 - *2013-04-09* - Added a setting in the Header's menu to move it at the bottom of the screen. -- Added Cooldown setting back in. +- Added the `Cooldown` setting back in. - Fixed the Header going above posts when following quotelinks for example. - Fixed a bug where dead quotelinks would disappear. diff --git a/css/style.css b/css/style.css index 286794dea..035e354b2 100644 --- a/css/style.css +++ b/css/style.css @@ -542,6 +542,15 @@ a[href="javascript:;"] { } /* QR */ +:root.hide-original-post-form #postForm, +:root.hide-original-post-form .postingMode, +:root.hide-original-post-form #togglePostForm, +#qr.autohide:not(.has-focus):not(:hover) > form { + display: none; +} +#qr select, #dump-button, .remove, .captcha-img { + cursor: pointer; +} #qr { z-index: 20; position: fixed; @@ -553,9 +562,6 @@ a[href="javascript:;"] { #qrtab { border-radius: 3px 3px 0 0; } -.autohide:not(:hover):not(.focus) > form { - display: none !important; -} #qrtab { margin-bottom: 1px; } diff --git a/lib/$.coffee b/lib/$.coffee index afea3c691..ec67c6f17 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -138,8 +138,11 @@ $.extend $, el.classList.toggle className hasClass: (el, className) -> el.classList.contains className - rm: (el) -> - el.parentNode.removeChild el + rm: do -> + if 'remove' of Element.prototype + (el) -> el.remove() + else + (el) -> el.parentNode?.removeChild el tn: (s) -> d.createTextNode s frag: -> diff --git a/lib/ui.coffee b/lib/ui.coffee index 328466e0c..75db550d5 100644 --- a/lib/ui.coffee +++ b/lib/ui.coffee @@ -4,7 +4,8 @@ UI = do -> className: 'dialog' innerHTML: html id: id - el.style.cssText = localStorage.getItem("#{g.NAMESPACE}#{id}.position") or position + $.get "#{id}.position", position, (item) -> + el.style.cssText = item["#{id}.position"] move = $ '.move', el $.on move, 'touchstart mousedown', dragstart for child in move.children @@ -276,7 +277,7 @@ UI = do -> else # mouseup $.off d, 'mousemove', @move $.off d, 'mouseup', @up - localStorage.setItem "#{g.NAMESPACE}#{@id}.position", @style.cssText + $.set "#{@id}.position", @style.cssText hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb}) -> o = { diff --git a/package.json b/package.json index 065c3ed6a..4626f8ff3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "3.0.4", + "version": "3.0.6", "description": "Cross-browser extension for productive lurking on 4chan.", "meta": { "name": "4chan X", diff --git a/src/config.coffee b/src/config.coffee index 52ab127ae..8ee0b8116 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -221,6 +221,12 @@ Config = false 'Remember the subject field, instead of resetting after posting.' ] + <% if (type === 'userscript') { %> + 'Remember QR Size': [ + false + 'Remember the size of the Quick reply.' + ] + <% } %> 'Remember Spoiler': [ false 'Remember the spoiler state, instead of resetting after posting.' diff --git a/src/features.coffee b/src/features.coffee index 0e2e706c2..464a18c68 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -95,6 +95,7 @@ Header = hashScroll: -> return unless post = $.id @location.hash[1..] + return if (Get.postFromRoot post).isHidden Header.scrollToPost post scrollToPost: (post) -> @@ -145,7 +146,7 @@ class Notification setTimeout @close, @timeout * $.SECOND if @timeout close = -> - $.rm @el if @el.parentNode + $.rm @el CatalogLinks = init: -> @@ -1536,7 +1537,7 @@ ReportLink = DeleteLink = init: -> - return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Delete Link'] or !Conf['Quick Reply'] + return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Delete Link'] div = $.el 'div', className: 'delete-link' @@ -1588,20 +1589,22 @@ DeleteLink = else $.id('delPassword').value + fileOnly = $.hasClass @, 'delete-file' + form = mode: 'usrdel' - onlyimgdel: $.hasClass @, 'delete-file' + onlyimgdel: fileOnly pwd: pwd form[post.ID] = 'delete' link = @ $.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"), - onload: -> DeleteLink.load link, post, @response + onload: -> DeleteLink.load link, post, fileOnly, @response onerror: -> DeleteLink.error link , cred: true form: $.formData form - load: (link, post, html) -> + load: (link, post, fileOnly, html) -> tmpDoc = d.implementation.createHTMLDocument '' tmpDoc.documentElement.innerHTML = html if tmpDoc.title is '4chan - Banned' # Ban/warn check @@ -1612,7 +1615,7 @@ DeleteLink = else if tmpDoc.title is 'Updating index...' # We're 100% sure. - (post.origin or post).kill() + (post.origin or post).kill fileOnly s = 'Deleted' link.textContent = s error: (link) -> @@ -1924,7 +1927,13 @@ Keybinds = Nav = init: -> - return if g.VIEW is 'index' and !Conf['Index Navigation'] or g.VIEW is 'thread' and !Conf['Reply Navigation'] + switch g.VIEW + when 'index' + return unless Conf['Index Navigation'] + when 'thread' + return unless Conf['Reply Navigation'] + else # catalog + return span = $.el 'span', id: 'navlinks' @@ -3693,18 +3702,22 @@ Unread = threadID: @ID defaultValue: 0 Unread.addPosts posts - if (hash = location.hash.match /\d+/) and post = @posts[hash[0]] - Header.scrollToPost post.nodes.root - else if Unread.posts.length - # Scroll to before the first unread post. - $.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false - else if posts.length - # Scroll to the last read post. - Header.scrollToPost posts[posts.length - 1].nodes.root $.on d, 'ThreadUpdate', Unread.onUpdate $.on d, 'scroll visibilitychange', Unread.read $.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line'] + return unless Conf['Scroll to Last Read Post'] + # Let the header's onload callback handle it. + return if (hash = location.hash.match /\d+/) and hash[0] of @posts + if Unread.posts.length + # Scroll to before the first unread post. + while root = $.x 'preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root + break unless (Get.postFromRoot root).isHidden + root.scrollIntoView false + else if posts.length + # Scroll to the last read post. + Header.scrollToPost posts[posts.length - 1].nodes.root + sync: -> lastReadPost = Unread.db.get boardID: Unread.thread.board.ID @@ -3790,7 +3803,7 @@ Unread = {root} = post.nodes if root isnt $ '.thread > .replyContainer', root.parentNode # not the first reply $.before root, Unread.hr - else if Unread.hr.parentNode + else $.rm Unread.hr update: <% if (type === 'crx') { %>(dontrepeat) <% } %>-> diff --git a/src/main.coffee b/src/main.coffee index a83ecb98f..15402ecb4 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -402,7 +402,7 @@ Main = # c.timeEnd 'All initializations' - $.on d, 'AddCallback', Main.addCallback + $.on d, 'AddCallback', Main.addCallback $.ready Main.initReady initStyle: -> diff --git a/src/qr.coffee b/src/qr.coffee index 0111ce532..7208b5344 100644 --- a/src/qr.coffee +++ b/src/qr.coffee @@ -11,9 +11,9 @@ QR = href: 'javascript:;' $.on sc, 'click', -> if !QR.nodes or QR.nodes.el.hidden + $.event 'CloseMenu' QR.open() QR.nodes.com.focus() - QR.resetThreadSelector() else QR.close() $.toggleClass @, 'disabled' @@ -21,8 +21,7 @@ QR = Header.addShortcut sc if Conf['Hide Original Post Form'] - $.asap (-> doc), -> - $.addClass doc, 'hide-original-post-form' + $.asap (-> doc), -> $.addClass doc, 'hide-original-post-form' $.on d, '4chanXInitFinished', @initReady @@ -79,6 +78,10 @@ QR = QR.status() if !Conf['Remember Spoiler'] and QR.nodes.spoiler.checked QR.nodes.spoiler.click() + focusin: -> + $.addClass QR.nodes.el, 'has-focus' + focusout: -> + $.rmClass QR.nodes.el, 'has-focus' hide: -> d.activeElement.blur() $.addClass QR.nodes.el, 'autohide' @@ -207,7 +210,7 @@ QR = now = Date.now() post = QR.posts[0] - isReply = QR.nodes.thread.value isnt 'new' + isReply = post.thread isnt 'new' isSage = /sage/i.test post.email hasFile = !!post.file seconds = null @@ -266,19 +269,19 @@ QR = text += ">#{s}\n" QR.open() - ta = QR.nodes.com - QR.nodes.thread.value = OP.ID unless ta.value + {com, thread} = QR.nodes + thread.value = OP.ID unless com.value - caretPos = ta.selectionStart + caretPos = com.selectionStart # Replace selection for text. - ta.value = ta.value[...caretPos] + text + ta.value[ta.selectionEnd..] + com.value = com.value[...caretPos] + text + com.value[com.selectionEnd..] # Move the caret to the end of the new quote. range = caretPos + text.length - ta.setSelectionRange range, range - ta.focus() + com.setSelectionRange range, range + com.focus() - # Fire the 'input' event - $.event 'input', null, ta + QR.selected.save com + QR.selected.save thread characterCount: -> counter = QR.nodes.charCount @@ -351,11 +354,6 @@ QR = post = new QR.post() post.setFile file $.addClass QR.nodes.el, 'dump' - resetThreadSelector: -> - if g.VIEW is 'thread' - QR.nodes.thread.value = g.THREADID - else - QR.nodes.thread.value = 'new' posts: [] post: class @@ -373,8 +371,12 @@ QR = spoiler: $ 'input', el span: el.lastChild - @nodes.spoiler.checked = @spoiler - + <% if (type === 'userscript') { %> + # XXX Firefox lacks focusin/focusout support. + for elm in $$ '*', el + $.on elm, 'blur', QR.focusout + $.on elm, 'focus', QR.focusin + <% } %> $.on el, 'click', @select.bind @ $.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm() $.on @nodes.label, 'click', (e) => e.stopPropagation() @@ -386,9 +388,14 @@ QR = for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'] $.on el, event.toLowerCase(), @[event] + @thread = if g.VIEW is 'thread' + g.THREADID + else + 'new' + prev = QR.posts[QR.posts.length - 1] QR.posts.push @ - @spoiler = if prev and Conf['Remember Spoiler'] + @nodes.spoiler.checked = @spoiler = if prev and Conf['Remember Spoiler'] prev.spoiler else false @@ -420,7 +427,7 @@ QR = lock: (lock=true) -> @isLocked = lock return unless @ is QR.selected - for name in ['name', 'email', 'sub', 'com', 'spoiler'] + for name in ['thread', 'name', 'email', 'sub', 'com', 'spoiler'] QR.nodes[name].disabled = lock @nodes.rm.style.visibility = QR.nodes.fileRM.style.visibility = if lock then 'hidden' else '' @@ -443,11 +450,14 @@ QR = @load() load: -> # Load this post's values. - for name in ['name', 'email', 'sub', 'com'] + for name in ['thread', 'name', 'email', 'sub', 'com'] QR.nodes[name].value = @[name] or null @showFileData() QR.characterCount() save: (input) -> + if input.type is 'checkbox' + @spoiler = input.checked + return {value} = input @[input.dataset.name] = value return if input.nodeName isnt 'TEXTAREA' @@ -461,7 +471,7 @@ QR = return unless @ is QR.selected # Do this in case people use extensions # that do not trigger the `input` event. - for name in ['name', 'email', 'sub', 'com'] + for name in ['thread', 'name', 'email', 'sub', 'com', 'spoiler'] @save QR.nodes[name] return setFile: (@file) -> @@ -542,7 +552,7 @@ QR = @showFileData() return unless window.URL URL.revokeObjectURL @URL - showFileData: (hide) -> + showFileData: -> if @file QR.nodes.filename.textContent = @filename QR.nodes.filename.title = @filename @@ -628,6 +638,12 @@ QR = # start with an uncached captcha @reload() + <% if (type === 'userscript') { %> + # XXX Firefox lacks focusin/focusout support. + $.on input, 'blur', QR.focusout + $.on input, 'focus', QR.focusin + <% } %> + $.addClass QR.nodes.el, 'has-captcha' $.after QR.nodes.com.parentNode, [imgContainer, input] sync: (@captchas) -> @@ -702,7 +718,7 @@ QR =
× -
@@ -797,10 +813,17 @@ QR = $.add nodes.thread, $.el 'option', value: thread textContent: "Thread No.#{thread}" - QR.resetThreadSelector() $.on nodes.filename.parentNode, 'click keyup', QR.openFileInput + <% if (type === 'userscript') { %> + # XXX Firefox lacks focusin/focusout support. + for elm in $$ '*', QR.nodes.el + $.on elm, 'blur', QR.focusout + $.on elm, 'focus', QR.focusin + <% } %> + $.on QR.nodes.el, 'focusin', QR.focusin + $.on QR.nodes.el, 'focusout', QR.focusout $.on nodes.autohide, 'change', QR.toggleHide $.on nodes.close, 'click', QR.close $.on nodes.dumpButton, 'click', -> nodes.el.classList.toggle 'dump' @@ -809,13 +832,21 @@ QR = $.on nodes.fileRM, 'click', -> QR.selected.rmFile() $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() $.on nodes.fileInput, 'change', QR.fileInput - - new QR.post true # save selected post's data for name in ['name', 'email', 'sub', 'com'] - $.on nodes[name], 'input', -> QR.selected.save @ - $.on nodes[name], 'focus', -> $.addClass nodes.el, 'focus' - $.on nodes[name], 'blur', -> $.rmClass nodes.el, 'focus' + $.on nodes[name], 'input', -> QR.selected.save @ + $.on nodes.thread, 'change', -> QR.selected.save @ + + <% if (type === 'userscript') { %> + if Conf['Remember QR Size'] + $.get 'QR Size', '', (item) -> + nodes.com.style.cssText = item['QR Size'] + $.on nodes.com, 'mouseup', (e) -> + return if e.button isnt 0 + $.set 'QR Size', @style.cssText + <% } %> + + new QR.post true QR.status() QR.cooldown.init() @@ -842,7 +873,7 @@ QR = post.forceSave() if g.BOARD.ID is 'f' filetag = QR.nodes.flashTag.value - threadID = QR.nodes.thread.value + threadID = post.thread thread = g.BOARD.threads[threadID] # prevent errors @@ -930,6 +961,11 @@ QR = QR.status() response: -> + <% if (type === 'userjs') { %> + # The upload.onload callback is not called + # or at least not in time with Opera. + QR.req.upload.onload() + <% } %> {req} = QR delete QR.req @@ -971,6 +1007,12 @@ QR = # Too many frequent mistyped captchas will auto-ban you! # On connection error, the post most likely didn't go through. QR.cooldown.set delay: 2 + else if err.textContent and m = err.textContent.match /wait\s(\d+)\ssecond/i + QR.cooldown.auto = if QR.captcha.isEnabled + !!QR.captcha.captchas.length + else + true + QR.cooldown.set delay: m[1] else # stop auto-posting QR.cooldown.auto = false QR.status()